* Inserts a new site into the database.
* @global wpdb $wpdb WordPress database abstraction object.
* Data for the new site that should be inserted.
* @type string $domain Site domain. Default empty string.
* @type string $path Site path. Default '/'.
* @type int $network_id The site's network ID. Default is the current network ID.
* @type string $registered When the site was registered, in SQL datetime format. Default is
* @type string $last_updated When the site was last updated, in SQL datetime format. Default is
* the value of $registered.
* @type int $public Whether the site is public. Default 1.
* @type int $archived Whether the site is archived. Default 0.
* @type int $mature Whether the site is mature. Default 0.
* @type int $spam Whether the site is spam. Default 0.
* @type int $deleted Whether the site is deleted. Default 0.
* @type int $lang_id The site's language ID. Currently unused. Default 0.
* @type int $user_id User ID for the site administrator. Passed to the
* `wp_initialize_site` hook.
* @type string $title Site title. Default is 'Site %d' where %d is the site ID. Passed
* to the `wp_initialize_site` hook.
* @type array $options Custom option $key => $value pairs to use. Default empty array. Passed
* to the `wp_initialize_site` hook.
* @type array $meta Custom site metadata $key => $value pairs to use. Default empty array.
* Passed to the `wp_initialize_site` hook.
* @return int|WP_Error The new site's ID on success, or error object on failure.
function wp_insert_site( array $data ) {
$now = current_time( 'mysql', true );
'network_id' => get_current_network_id(),
$prepared_data = wp_prepare_site_data( $data, $defaults );
if ( is_wp_error( $prepared_data ) ) {
if ( false === $wpdb->insert( $wpdb->blogs, $prepared_data ) ) {
return new WP_Error( 'db_insert_error', __( 'Could not insert site into the database.' ), $wpdb->last_error );
$site_id = (int) $wpdb->insert_id;
clean_blog_cache( $site_id );
$new_site = get_site( $site_id );
return new WP_Error( 'get_site_error', __( 'Could not retrieve site data.' ) );
* Fires once a site has been inserted into the database.
* @param WP_Site $new_site New site object.
do_action( 'wp_insert_site', $new_site );
// Extract the passed arguments that may be relevant for site initialization.
$args = array_diff_key( $data, $defaults );
if ( isset( $args['site_id'] ) ) {
unset( $args['site_id'] );
* Fires when a site's initialization routine should be executed.
* @param WP_Site $new_site New site object.
* @param array $args Arguments for the initialization.
do_action( 'wp_initialize_site', $new_site, $args );
// Only compute extra hook parameters if the deprecated hook is actually in use.
if ( has_action( 'wpmu_new_blog' ) ) {
$user_id = ! empty( $args['user_id'] ) ? $args['user_id'] : 0;
$meta = ! empty( $args['options'] ) ? $args['options'] : array();
// WPLANG was passed with `$meta` to the `wpmu_new_blog` hook prior to 5.1.0.
if ( ! array_key_exists( 'WPLANG', $meta ) ) {
$meta['WPLANG'] = get_network_option( $new_site->network_id, 'WPLANG' );
// Rebuild the data expected by the `wpmu_new_blog` hook prior to 5.1.0 using allowed keys.
// The `$allowed_data_fields` matches the one used in `wpmu_create_blog()`.
$allowed_data_fields = array( 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id' );
$meta = array_merge( array_intersect_key( $data, array_flip( $allowed_data_fields ) ), $meta );
* Fires immediately after a new site is created.
* @deprecated 5.1.0 Use {@see 'wp_insert_site'} instead.
* @param int $site_id Site ID.
* @param int $user_id User ID.
* @param string $domain Site domain.
* @param string $path Site path.
* @param int $network_id Network ID. Only relevant on multi-network installations.
* @param array $meta Meta data. Used to set initial site options.
array( $new_site->id, $user_id, $new_site->domain, $new_site->path, $new_site->network_id, $meta ),
return (int) $new_site->id;
* Updates a site in the database.
* @global wpdb $wpdb WordPress database abstraction object.
* @param int $site_id ID of the site that should be updated.
* @param array $data Site data to update. See {@see wp_insert_site()} for the list of supported keys.
* @return int|WP_Error The updated site's ID on success, or error object on failure.
function wp_update_site( $site_id, array $data ) {
if ( empty( $site_id ) ) {
return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) );
$old_site = get_site( $site_id );
return new WP_Error( 'site_not_exist', __( 'Site does not exist.' ) );
$defaults = $old_site->to_array();
$defaults['network_id'] = (int) $defaults['site_id'];
$defaults['last_updated'] = current_time( 'mysql', true );
unset( $defaults['blog_id'], $defaults['site_id'] );
$data = wp_prepare_site_data( $data, $defaults, $old_site );
if ( is_wp_error( $data ) ) {
if ( false === $wpdb->update( $wpdb->blogs, $data, array( 'blog_id' => $old_site->id ) ) ) {
return new WP_Error( 'db_update_error', __( 'Could not update site in the database.' ), $wpdb->last_error );
clean_blog_cache( $old_site );
$new_site = get_site( $old_site->id );
* Fires once a site has been updated in the database.
* @param WP_Site $new_site New site object.
* @param WP_Site $old_site Old site object.
do_action( 'wp_update_site', $new_site, $old_site );
return (int) $new_site->id;
* Deletes a site from the database.
* @global wpdb $wpdb WordPress database abstraction object.
* @param int $site_id ID of the site that should be deleted.
* @return WP_Site|WP_Error The deleted site object on success, or error object on failure.
function wp_delete_site( $site_id ) {
if ( empty( $site_id ) ) {
return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) );
$old_site = get_site( $site_id );
return new WP_Error( 'site_not_exist', __( 'Site does not exist.' ) );
$errors = new WP_Error();
* Fires before a site should be deleted from the database.
* Plugins should amend the `$errors` object via its `WP_Error::add()` method. If any errors
* are present, the site will not be deleted.
* @param WP_Error $errors Error object to add validation errors to.
* @param WP_Site $old_site The site object to be deleted.
do_action( 'wp_validate_site_deletion', $errors, $old_site );
if ( ! empty( $errors->errors ) ) {
* Fires before a site is deleted.
* @param int $site_id The site ID.
* @param bool $drop True if site's table should be dropped. Default false.
do_action_deprecated( 'delete_blog', array( $old_site->id, true ), '5.1.0' );
* Fires when a site's uninitialization routine should be executed.
* @param WP_Site $old_site Deleted site object.
do_action( 'wp_uninitialize_site', $old_site );
if ( is_site_meta_supported() ) {
$blog_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->blogmeta WHERE blog_id = %d ", $old_site->id ) );
foreach ( $blog_meta_ids as $mid ) {
delete_metadata_by_mid( 'blog', $mid );
if ( false === $wpdb->delete( $wpdb->blogs, array( 'blog_id' => $old_site->id ) ) ) {
return new WP_Error( 'db_delete_error', __( 'Could not delete site from the database.' ), $wpdb->last_error );
clean_blog_cache( $old_site );
* Fires once a site has been deleted from the database.
* @param WP_Site $old_site Deleted site object.
do_action( 'wp_delete_site', $old_site );
* Fires after the site is deleted from the network.
* @param int $site_id The site ID.
* @param bool $drop True if site's tables should be dropped. Default false.
do_action_deprecated( 'deleted_blog', array( $old_site->id, true ), '5.1.0' );
* Retrieves site data given a site ID or site object.
* Site data will be cached and returned after being passed through a filter.
* If the provided site is empty, the current site global will be used.
* @param WP_Site|int|null $site Optional. Site to retrieve. Default is the current site.
* @return WP_Site|null The site object or null if not found.
function get_site( $site = null ) {
$site = get_current_blog_id();
if ( $site instanceof WP_Site ) {
} elseif ( is_object( $site ) ) {
$_site = new WP_Site( $site );
$_site = WP_Site::get_instance( $site );
* Fires after a site is retrieved.
* @param WP_Site $_site Site data.
$_site = apply_filters( 'get_site', $_site );
* Adds any sites from the given IDs to the cache that do not already exist in cache.
* @since 5.1.0 Introduced the `$update_meta_cache` parameter.
* @see update_site_cache()
* @global wpdb $wpdb WordPress database abstraction object.
* @param array $ids ID list.
* @param bool $update_meta_cache Optional. Whether to update the meta cache. Default true.
function _prime_site_caches( $ids, $update_meta_cache = true ) {
$non_cached_ids = _get_non_cached_ids( $ids, 'sites' );
if ( ! empty( $non_cached_ids ) ) {
$fresh_sites = $wpdb->get_results( sprintf( "SELECT * FROM $wpdb->blogs WHERE blog_id IN (%s)", implode( ',', array_map( 'intval', $non_cached_ids ) ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
update_site_cache( $fresh_sites, $update_meta_cache );
* Updates sites in cache.
* @since 5.1.0 Introduced the `$update_meta_cache` parameter.
* @param array $sites Array of site objects.
* @param bool $update_meta_cache Whether to update site meta cache. Default true.
function update_site_cache( $sites, $update_meta_cache = true ) {
foreach ( $sites as $site ) {
$site_ids[] = $site->blog_id;
wp_cache_add( $site->blog_id, $site, 'sites' );
wp_cache_add( $site->blog_id . 'short', $site, 'blog-details' );
if ( $update_meta_cache ) {
update_sitemeta_cache( $site_ids );
* Updates metadata cache for list of site IDs.
* Performs SQL query to retrieve all metadata for the sites matching `$site_ids` and stores them in the cache.
* Subsequent calls to `get_site_meta()` will not need to query the database.
* @param array $site_ids List of site IDs.
* @return array|false An array of metadata on success, false if there is nothing to update.
function update_sitemeta_cache( $site_ids ) {
// Ensure this filter is hooked in even if the function is called early.
if ( ! has_filter( 'update_blog_metadata_cache', 'wp_check_site_meta_support_prefilter' ) ) {
add_filter( 'update_blog_metadata_cache', 'wp_check_site_meta_support_prefilter' );
return update_meta_cache( 'blog', $site_ids );
* Retrieves a list of sites matching requested arguments.
* @since 4.8.0 Introduced the 'lang_id', 'lang__in', and 'lang__not_in' parameters.
* @see WP_Site_Query::parse_query()
* @param string|array $args {
* Optional. Array or query string of site query parameters. Default empty.
* @type int[] $site__in Array of site IDs to include. Default empty.
* @type int[] $site__not_in Array of site IDs to exclude. Default empty.
* @type bool $count Whether to return a site count (true) or array of site objects.
* @type array $date_query Date query clauses to limit sites by. See WP_Date_Query.
* @type string $fields Site fields to return. Accepts 'ids' (returns an array of site IDs)
* or empty (returns an array of complete site objects). Default empty.
* @type int $ID A site ID to only return that site. Default empty.
* @type int $number Maximum number of sites to retrieve. Default 100.
* @type int $offset Number of sites to offset the query. Used to build LIMIT clause.
* @type bool $no_found_rows Whether to disable the `SQL_CALC_FOUND_ROWS` query. Default true.
* @type string|array $orderby Site status or array of statuses. Accepts 'id', 'domain', 'path',
* 'network_id', 'last_updated', 'registered', 'domain_length',
* 'path_length', 'site__in' and 'network__in'. Also accepts false,
* an empty array, or 'none' to disable `ORDER BY` clause.
* @type string $order How to order retrieved sites. Accepts 'ASC', 'DESC'. Default 'ASC'.
* @type int $network_id Limit results to those affiliated with a given network ID. If 0,
* include all networks. Default 0.
* @type int[] $network__in Array of network IDs to include affiliated sites for. Default empty.
* @type int[] $network__not_in Array of network IDs to exclude affiliated sites for. Default empty.
* @type string $domain Limit results to those affiliated with a given domain. Default empty.
* @type string[] $domain__in Array of domains to include affiliated sites for. Default empty.
* @type string[] $domain__not_in Array of domains to exclude affiliated sites for. Default empty.
* @type string $path Limit results to those affiliated with a given path. Default empty.
* @type string[] $path__in Array of paths to include affiliated sites for. Default empty.
* @type string[] $path__not_in Array of paths to exclude affiliated sites for. Default empty.
* @type int $public Limit results to public sites. Accepts '1' or '0'. Default empty.
* @type int $archived Limit results to archived sites. Accepts '1' or '0'. Default empty.
* @type int $mature Limit results to mature sites. Accepts '1' or '0'. Default empty.
* @type int $spam Limit results to spam sites. Accepts '1' or '0'. Default empty.
* @type int $deleted Limit results to deleted sites. Accepts '1' or '0'. Default empty.
* @type int $lang_id Limit results to a language ID. Default empty.
* @type string[] $lang__in Array of language IDs to include affiliated sites for. Default empty.
* @type string[] $lang__not_in Array of language IDs to exclude affiliated sites for. Default empty.
* @type string $search Search term(s) to retrieve matching sites for. Default empty.
* @type string[] $search_columns Array of column names to be searched. Accepts 'domain' and 'path'.
* @type bool $update_site_cache Whether to prime the cache for found sites. Default true.
* @return array|int List of WP_Site objects, a list of site IDs when 'fields' is set to 'ids',
* or the number of sites when 'count' is passed as a query var.
function get_sites( $args = array() ) {
$query = new WP_Site_Query();
return $query->query( $args );
* Prepares site data for insertion or update in the database.
* @param array $data Associative array of site data passed to the respective function.
* See {@see wp_insert_site()} for the possibly included data.
* @param array $defaults Site data defaults to parse $data against.
* @param WP_Site|null $old_site Optional. Old site object if an update, or null if an insertion.
* @return array|WP_Error Site data ready for a database transaction, or WP_Error in case a validation
function wp_prepare_site_data( $data, $defaults, $old_site = null ) {
// Maintain backward-compatibility with `$site_id` as network ID.
if ( isset( $data['site_id'] ) ) {
if ( ! empty( $data['site_id'] ) && empty( $data['network_id'] ) ) {
$data['network_id'] = $data['site_id'];
unset( $data['site_id'] );
* Filters passed site data in order to normalize it.
* @param array $data Associative array of site data passed to the respective function.
* See {@see wp_insert_site()} for the possibly included data.
$data = apply_filters( 'wp_normalize_site_data', $data );
$allowed_data_fields = array( 'domain', 'path', 'network_id', 'registered', 'last_updated', 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id' );