# bbpress/2.6.17/includes/core/abstraction.php

bbPress, version 2.6.17. 755 lines.

- Page: https://pluginprobe.com/plugins/bbpress/2.6.17/code/includes/core/abstraction.php
- Raw: https://pluginprobe.com/plugins/bbpress/2.6.17/raw/includes/core/abstraction.php
- Modified: 2026-09-16T07:00:16+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/bbpress/2.6.17/code/includes/core/abstraction.php#L10-L20`.

```php
<?php

/**
 * bbPress Abstractions
 *
 * This file contains functions for abstracting WordPress core functionality
 * into convenient wrappers so they can be used more reliably.
 *
 * Many of the functions in this file are considered superfluous by
 * WordPress coding standards, but they're handy for plugins of plugins to use.
 *
 * @package bbPress
 * @subpackage Core
 */

// Exit if accessed directly
defined( 'ABSPATH' ) || exit;

/**
 * Setup Admin
 *
 * This exists outside of "/includes/admin/" because the converter may need to
 * be setup to convert the passwords of users that were migrated from another
 * forum platform.
 *
 * @since 2.6.0 bbPress (r2596)
 */
function bbp_setup_admin() {
	$bbp = bbpress();

	// Skip if already setup
	if ( empty( $bbp->admin ) ) {

		// Require the admin class
		require_once $bbp->includes_dir . 'admin/classes/class-bbp-admin.php';

		// Setup
		$bbp->admin = class_exists( 'BBP_Admin' )
			? new BBP_Admin()
			: new stdClass();
	}

	// Return the admin object
	return $bbp->admin;
}

/**
 * Setup Converter
 *
 * This exists outside of "/includes/admin/" because the converter may need to
 * be setup to convert the passwords of users that were migrated from another
 * forum platform.
 *
 * @since 2.6.0 bbPress (r2596)
 */
function bbp_setup_converter() {
	$bbp_admin = bbp_setup_admin();

	// Skip if already setup
	if ( empty( $bbp_admin->converter ) ) {

		// Require the converter files
		require_once $bbp_admin->admin_dir . 'tools/converter.php';
		require_once $bbp_admin->admin_dir . 'classes/class-bbp-converter.php';
		require_once $bbp_admin->admin_dir . 'classes/class-bbp-converter-db.php';
		require_once $bbp_admin->admin_dir . 'classes/class-bbp-converter-base.php';

		// Setup
		$bbp_admin->converter = class_exists( 'BBP_Converter' )
			? new BBP_Converter()
			: new stdClass();
	}

	// Return the converter
	return $bbp_admin->converter;
}

/** Globals *******************************************************************/

/**
 * Lookup and return a global variable
 *
 * @since 2.5.8 bbPress (r5814)
 *
 * @param  string  $name     Name of global variable
 * @param  string  $type     Type of variable to check with `is_a()`
 * @param  mixed   $default  Default value to return if no global found
 *
 * @return mixed   Verified object if valid, Default or null if invalid
 */
function bbp_get_global_object( $name = '', $type = '', $default = null ) {

	// If no name passed
	if ( empty( $name ) ) {
		$retval = $default;

	// If no global exists
	} elseif ( ! isset( $GLOBALS[ $name ] ) ) {
		$retval = $default;

	// If not the correct type of global
	} elseif ( ! empty( $type ) && ! is_a( $GLOBALS[ $name ], $type ) ) {
		$retval = $default;

	// Global variable exists
	} else {
		$retval = $GLOBALS[ $name ];
	}

	// Filter & return
	return apply_filters( 'bbp_get_global_object', $retval, $name, $type, $default );
}

/**
 * Get the `$wp_query` global without needing to declare it everywhere
 *
 * @since 2.6.0 bbPress (r6582)
 *
 * @return WP_Roles
 */
function bbp_get_wp_query() {
	return bbp_get_global_object( 'wp_query', 'WP_Query' );
}

/**
 * Get the `$wp_roles` global without needing to declare it everywhere
 *
 * @since 2.2.0 bbPress (r4293)
 *
 * @return WP_Roles
 */
function bbp_get_wp_roles() {
	return bbp_get_global_object( 'wp_roles', 'WP_Roles' );
}

/**
 * Return the database class being used to interface with the environment.
 *
 * This function is abstracted to avoid global touches to the primary database
 * class. bbPress supports WordPress's `$wpdb` global by default, and can be
 * filtered to support other configurations if needed.
 *
 * @since 2.5.8 bbPress (r5814)
 *
 * @return object
 */
function bbp_db() {
	return bbp_get_global_object( 'wpdb', 'WPDB' );
}

/**
 * Atomically bump a numeric metadata value using compare-and-swap retries.
 *
 * Count updates normally require reading a value, changing it in PHP, and
 * writing it back. Two requests can read the same value and overwrite one
 * another's changes. This function avoids that lost update by making the write
 * conditional on the value that was read. If another request changes the value
 * first, the condition matches no rows, so the current value is read directly
 * from the database and the calculation is retried.
 *
 * The first read uses the WordPress metadata API and its cache. Missing
 * metadata is added through add_metadata() so its standard lifecycle continues
 * to run. Existing metadata uses a conditional database update while preserving
 * the standard update metadata short-circuit filter and before/after actions.
 * The short-circuit filter runs once against the first sanitized candidate.
 * Before actions run for every conditional attempt, while after actions run
 * only after a successful write. Metadata caches are cleared between attempts
 * and after successful writes. Values are sanitized through sanitize_meta(),
 * cast to integers, and prevented from falling below zero.
 *
 * Retries are bounded and filterable. This function does not lock rows or hold
 * a database transaction open, and returns false when there is no change, a
 * database operation fails, or all attempts lose to concurrent writes. It is
 * intended for uniquely keyed numeric count metadata; WordPress metadata tables
 * do not enforce uniqueness during simultaneous first-time inserts.
 *
 * @since 2.6.17
 *
 * @see https://bbpress.trac.wordpress.org/ticket/3678
 *
 * @param string $meta_type  Type of object metadata is for.
 * @param int    $object_id  ID of the object metadata is for.
 * @param string $meta_key   Metadata key.
 * @param int    $difference Amount to add to the stored value.
 * @param int    $default    Existing value to use when metadata is missing.
 * @return bool True on success, false on failure or no change.
 */
function bbp_bump_count_meta( $meta_type = '', $object_id = 0, $meta_key = '', $difference = 1, $default = 0 ) {

	$object_id  = (int) $object_id;
	$difference = (int) $difference;
	$default    = (int) $default;

	// Bail if required values are missing
	if ( empty( $object_id ) || empty( $meta_key ) || empty( $difference ) ) {
		return false;
	}

	/**
	 * Short-circuits bumping numeric metadata.
	 *
	 * Returning a non-null value prevents the normal metadata update.
	 *
	 * @since 2.6.17
	 *
	 * @param null|bool $check      Whether to short-circuit the metadata update.
	 * @param string    $meta_type  Type of object metadata is for.
	 * @param int       $object_id  ID of the object metadata is for.
	 * @param string    $meta_key   Metadata key.
	 * @param int       $difference Amount to add to the stored value.
	 * @param int       $default    Existing value to use when metadata is missing.
	 */
	$check = apply_filters( 'bbp_pre_bump_count_meta', null, $meta_type, $object_id, $meta_key, $difference, $default );
	if ( null !== $check ) {
		return (bool) $check;
	}

	/**
	 * Filters the metadata types that support atomic count updates.
	 *
	 * @since 2.6.17
	 *
	 * @param array  $meta_types Supported metadata types.
	 * @param string $meta_type  Requested metadata type.
	 * @param int    $object_id  ID of the object metadata is for.
	 * @param string $meta_key   Metadata key.
	 */
	$meta_types = (array) apply_filters( 'bbp_bump_count_meta_types', array( 'post', 'user', 'term', 'comment' ), $meta_type, $object_id, $meta_key );

	// Bail if the metadata type is unsupported
	if ( ! in_array( $meta_type, $meta_types, true ) ) {
		return false;
	}

	$bbp_db     = bbp_db();
	$table_name = sanitize_key( $meta_type . 'meta' );
	$table      = isset( $bbp_db->{$table_name} ) ? $bbp_db->{$table_name} : '';
	$column     = sanitize_key( $meta_type . '_id' );
	$id_column  = ( 'user' === $meta_type ) ? 'umeta_id' : 'meta_id';

	// Bail if the metadata table does not exist
	if ( empty( $table ) ) {
		return false;
	}

	/**
	 * Filters the maximum number of conditional metadata write attempts.
	 *
	 * @since 2.6.17
	 *
	 * @param int    $max_attempts Maximum number of attempts.
	 * @param string $meta_type    Type of object metadata is for.
	 * @param int    $object_id    ID of the object metadata is for.
	 * @param string $meta_key     Metadata key.
	 * @param int    $difference   Amount to add to the stored value.
	 * @param int    $default      Existing value to use when metadata is missing.
	 */
	$max_attempts = (int) apply_filters( 'bbp_bump_count_meta_max_attempts', 5, $meta_type, $object_id, $meta_key, $difference, $default );
	$max_attempts = max( 1, $max_attempts );
	$checked      = false;
	$subtype      = get_object_subtype( $meta_type, $object_id );
	$count_query  = $bbp_db->prepare( "SELECT meta_value FROM {$table} WHERE meta_key = %s AND {$column} = %d LIMIT 1", $meta_key, $object_id );

	// Retry when another request updates the same value first
	for ( $attempt = 0; $attempt < $max_attempts; $attempt++ ) {
		if ( empty( $attempt ) ) {
			$exists = metadata_exists( $meta_type, $object_id, $meta_key );
			$count  = $exists
				? (int) get_metadata( $meta_type, $object_id, $meta_key, true )
				: $default;
		} else {
			$stored = $bbp_db->get_var( $count_query );

			// Bail on a database error
			if ( ! empty( $bbp_db->last_error ) ) {
				return false;
			}

			$exists = null !== $stored;
			$count  = $exists ? (int) $stored : $default;
		}

		$new_count = sanitize_meta( $meta_key, bbp_number_not_negative( $count + $difference ), $meta_type, $subtype );
		$new_count = (int) $new_count;

		// Allow metadata updates to be short-circuited as usual
		if ( ! $checked ) {
			$checked = true;
			$check   = apply_filters( "update_{$meta_type}_metadata", null, $object_id, $meta_key, $new_count, '' );

			if ( null !== $check ) {
				return (bool) $check;
			}
		}

		// Bail if the count is already at its lower bound
		if ( $new_count === $count ) {
			if ( ! empty( $attempt ) ) {
				return false;
			}

			$stored = $bbp_db->get_var( $count_query );

			// Bail on a database error
			if ( ! empty( $bbp_db->last_error ) ) {
				return false;
			}

			$current_exists = null !== $stored;
			$current_count  = $current_exists ? (int) $stored : $default;

			if ( ( $current_exists === $exists ) && ( $current_count === $count ) ) {
				return false;
			}

			wp_cache_delete( $object_id, $meta_type . '_meta' );
			continue;
		}

		// Add missing metadata using the standard WordPress lifecycle
		if ( ! $exists ) {
			if ( ! empty( add_metadata( $meta_type, $object_id, $meta_key, $new_count, true ) ) ) {
				return true;
			}

			$stored = $bbp_db->get_var( $count_query );

			// Bail if the add failed without a concurrent insert or on a database error
			if ( ! empty( $bbp_db->last_error ) || ( null === $stored ) ) {
				return false;
			}

			wp_cache_delete( $object_id, $meta_type . '_meta' );
			continue;
		}

		$meta_ids = $bbp_db->get_col( $bbp_db->prepare( "SELECT {$id_column} FROM {$table} WHERE meta_key = %s AND {$column} = %d", $meta_key, $object_id ) );

		// Bail on a database error
		if ( ! empty( $bbp_db->last_error ) ) {
			return false;
		}

		// Retry if metadata was removed after the cached existence check
		if ( empty( $meta_ids ) ) {
			wp_cache_delete( $object_id, $meta_type . '_meta' );
			continue;
		}

		// Run the standard actions immediately before the conditional update
		foreach ( $meta_ids as $meta_id ) {
			do_action( "update_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $new_count );

			if ( 'post' === $meta_type ) {
				do_action( 'update_postmeta', $meta_id, $object_id, $meta_key, $new_count );
			}
		}

		// Compare count metadata numerically to normalize stored numeric strings
		$updated = $bbp_db->update(
			$table,
			array( 'meta_value' => $new_count ),
			array(
				$column      => $object_id,
				'meta_key'   => $meta_key,
				'meta_value' => $count
			),
			array( '%d' ),
			array( '%d', '%s', '%d' )
		);

		// Bail on a database error
		if ( false === $updated ) {
			return false;
		}

		wp_cache_delete( $object_id, $meta_type . '_meta' );

		// Retry when another request updated the count first
		if ( empty( $updated ) ) {
			continue;
		}

		// Run the standard actions immediately after the conditional update
		foreach ( $meta_ids as $meta_id ) {
			do_action( "updated_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $new_count );

			if ( 'post' === $meta_type ) {
				do_action( 'updated_postmeta', $meta_id, $object_id, $meta_key, $new_count );
			}
		}

		return true;
	}

	return false;
}

/** Pagination ****************************************************************/

/**
 * Return the rewrite rules class being used to interact with URLs.
 *
 * This function is abstracted to avoid global touches to the primary rewrite
 * rules class. bbPress supports WordPress's `$wp_rewrite` by default, but can
 * be filtered to support other configurations if needed.
 *
 * @since 2.5.8 bbPress (r5814)
 *
 * @return object
 */
function bbp_rewrite() {
	return bbp_get_global_object(
		'wp_rewrite',
		'WP_Rewrite',
		(object) array(
			'root'            => '',
			'pagination_base' => 'page',
		)
	);
}

/**
 * Get the root URL
 *
 * @since 2.5.8 bbPress (r5814)
 *
 * @return string
 */
function bbp_get_root_url() {

	// Default
	$retval  = '';
	$rewrite = bbp_rewrite();

	// Use $wp_rewrite->root if available
	if ( property_exists( $rewrite, 'root' ) ) {
		$retval = $rewrite->root;
	}

	// Filter & return
	return apply_filters( 'bbp_get_root_url', $retval );
}

/**
 * Get the slug used for paginated requests
 *
 * @since 2.4.0 bbPress (r4926)
 *
 * @return string
 */
function bbp_get_paged_slug() {

	// Default
	$retval  = 'page';
	$rewrite = bbp_rewrite();

	// Use $wp_rewrite->pagination_base if available
	if ( property_exists( $rewrite, 'pagination_base' ) ) {
		$retval = $rewrite->pagination_base;
	}

	// Filter & return
	return apply_filters( 'bbp_get_paged_slug', $retval );
}

/**
 * Is the environment using pretty URLs?
 *
 * @since 2.5.8 bbPress (r5814)
 *
 * @global object $wp_rewrite The WP_Rewrite object
 *
 * @return bool
 */
function bbp_use_pretty_urls() {

	// Default
	$retval  = false;
	$rewrite = bbp_rewrite();

	// Use $wp_rewrite->using_permalinks() if available
	if ( method_exists( $rewrite, 'using_permalinks' ) ) {
		$retval = $rewrite->using_permalinks();
	}

	// Filter & return
	return apply_filters( 'bbp_pretty_urls', $retval );
}

/**
 * Remove the first-page from a pagination links result set, ensuring that it
 * points to the canonical first page URL.
 *
 * This is a bit of an SEO hack, to guarantee that the first page in a loop will
 * never have pagination appended to the end of it, regardless of what the other
 * functions have decided for us.
 *
 * @since 2.6.0 bbPress (r6678)
 *
 * @param string $pagination_links The HTML links used for pagination
 *
 * @return string
 */
function bbp_make_first_page_canonical( $pagination_links = '' ) {

	// Default value
	$retval = $pagination_links;

	// Remove first page from pagination
	if ( ! empty( $pagination_links ) ) {
		$retval = bbp_use_pretty_urls()
			? str_replace( bbp_get_paged_slug() . '/1/', '', $pagination_links )
			: preg_replace( '/&#038;paged=1(?=[^0-9])/m', '', $pagination_links );
	}

	// Filter & return
	return apply_filters( 'bbp_make_first_page_canonical', $retval, $pagination_links );
}

/**
 * A convenient wrapper for common calls to paginate_links(), complete with
 * support for parameters that aren't used internally by bbPress.
 *
 * @since 2.6.0 bbPress (r6679)
 *
 * @param array $args
 *
 * @return string
 */
function bbp_paginate_links( $args = array() ) {

	// Maybe add view-all args
	$add_args = empty( $args['add_args'] ) && bbp_get_view_all()
		? array( 'view' => 'all' )
		: false;

	// Pagination settings with filter
	$r = bbp_parse_args(
		$args,
		array(

			// Used by callers
			'base'      => '',
			'total'     => 1,
			'current'   => bbp_get_paged(),
			'prev_next' => true,
			'prev_text' => is_rtl() ? '&rarr;' : '&larr;',
			'next_text' => is_rtl() ? '&larr;' : '&rarr;',
			'mid_size'  => 1,
			'end_size'  => 3,
			'add_args'  => $add_args,

			// Unused by callers
			'show_all'           => false,
			'type'               => 'plain',
			'format'             => '',
			'add_fragment'       => '',
			'before_page_number' => '',
			'after_page_number'  => ''
		),
		'paginate_links'
	);

	// Return paginated links
	return bbp_make_first_page_canonical( paginate_links( $r ) );
}

/**
 * Parse the WordPress core version number
 *
 * @since 2.6.0 bbPress (r6051)
 *
 * @global string $wp_version
 *
 * @return string $wp_version
 */
function bbp_get_major_wp_version() {
	global $wp_version;

	return (float) $wp_version;
}

/** Multisite *****************************************************************/

/**
 * Is this a large bbPress installation?
 *
 * @since 2.6.0 bbPress (r6242)
 *
 * @return bool True if more than 10000 users, false not
 */
function bbp_is_large_install() {

	// Multisite has a function specifically for this
	$retval = function_exists( 'wp_is_large_network' )
		? wp_is_large_network( 'users' )
		: ( bbp_get_total_users() > 10000 );

	// Filter & return
	return (bool) apply_filters( 'bbp_is_large_install', $retval );
}

/**
 * Get the total number of users on the forums
 *
 * @since 2.0.0 bbPress (r2769)
 *
 * @return int Total number of users
 */
function bbp_get_total_users() {
	$bbp_db = bbp_db();
	$count  = $bbp_db->get_var( "SELECT COUNT(ID) as c FROM {$bbp_db->users} WHERE user_status = '0'" );

	// Filter & return
	return (int) apply_filters( 'bbp_get_total_users', (int) $count );
}

/**
 * Switch to a site in a multisite installation.
 *
 * If not a multisite installation, no switching will occur.
 *
 * @since 2.6.0 bbPress (r6733)
 *
 * @param int $site_id
 */
function bbp_switch_to_site( $site_id = 0 ) {

	// Switch to a specific site
	if ( is_multisite() ) {
		switch_to_blog( $site_id );
	}
}

/**
 * Switch back to the original site in a multisite installation.
 *
 * If not a multisite installation, no switching will occur.
 *
 * @since 2.6.0 bbPress (r6733)
 */
function bbp_restore_current_site() {

	// Switch back to the original site
	if ( is_multisite() ) {
		restore_current_blog();
	}
}

/** Interception **************************************************************/

/**
 * Generate a default intercept value.
 *
 * @since 2.6.0
 *
 * @staticvar mixed $rand Null by default, random string on first call
 *
 * @return string
 */
function bbp_default_intercept() {
	static $rand = null;

	// Generate a new random and unique string
	if ( null === $rand ) {

		// If ext/hash is not present, compat.php's hash_hmac() does not support sha256.
		$algo = function_exists( 'hash' )
			? 'sha256'
			: 'sha1';

		// Old WP installs may not have AUTH_SALT defined.
		$salt = defined( 'AUTH_SALT' ) && AUTH_SALT
			? AUTH_SALT
			: (string) wp_rand();

		// Create unique ID
		$rand = hash_hmac( $algo, uniqid( $salt, true ), $salt );
	}

	// Return random string (from locally static variable)
	return $rand;
}

/**
 * Whether a value has been intercepted
 *
 * @since 2.6.0
 *
 * @param bool $value
 */
function bbp_is_intercepted( $value = '' ) {
	return ( bbp_default_intercept() !== $value );
}

/**
 * Allow interception of a method or function call.
 *
 * @since 2.6.0
 *
 * @param string $action Typically the name of the caller function
 * @param array  $args   Typically the results of caller function func_get_args()
 *
 * @return mixed         Intercept results. Default bbp_default_intercept().
 */
function bbp_maybe_intercept( $action = '', $args = array() ) {

	// Backwards compatibility juggle
	$hook = ( false === strpos( $action, 'pre_' ) )
		? "pre_{$action}"
		: $action;

	// Default value
	$default = bbp_default_intercept();

	// Parse args
	$r = bbp_parse_args( (array) $args, array(), 'maybe_intercept' );

	// Bail if no args
	if ( empty( $r ) ) {
		return $default;
	}

	// Filter
	$args     = array_merge( array( $hook ), $r );
	$filtered = call_user_func_array( 'apply_filters', $args );

	// Return filtered value, or default if not intercepted
	return ( reset( $r ) === $filtered )
		? $default
		: $filtered;
}

/** Date/Time *****************************************************************/

/**
 * Get an empty datetime value.
 *
 * @since 2.6.6 bbPress (r7094)
 *
 * @return string
 */
function bbp_get_empty_datetime() {

	// Get the database version
	$db_version = bbp_db()->db_version();

	// Default return value
	$retval = '0000-00-00 00:00:00';

	// Filter & return
	return (string) apply_filters( 'bbp_get_default_zero_date', $retval, $db_version );
}

```
