Make WordPress Core

Ticket #40647: 40647.3.diff

File 40647.3.diff, 95.8 KB (added by flixos90, 6 years ago)
  • src/wp-includes/ms-blogs.php

     
    88 * @since MU (3.0.0)
    99 */
    1010
     11require_once( ABSPATH . WPINC . '/ms-site.php' );
     12require_once( ABSPATH . WPINC . '/ms-network.php' );
     13
    1114/**
    1215 * Update the last_updated field for the current site.
    1316 *
     
    231234
    232235        if ( ! $get_all ) {
    233236                wp_cache_set( $blog_id . $all, $details, 'blog-details' );
    234                 return $details;
    235         }
    236 
    237         switch_to_blog( $blog_id );
    238         $details->blogname   = get_option( 'blogname' );
    239         $details->siteurl    = get_option( 'siteurl' );
    240         $details->post_count = get_option( 'post_count' );
    241         $details->home       = get_option( 'home' );
    242         restore_current_blog();
    243 
    244         /**
    245          * Filters a blog's details.
    246          *
    247          * @since MU (3.0.0)
    248          * @deprecated 4.7.0 Use site_details
    249          *
    250          * @param object $details The blog details.
    251          */
    252         $details = apply_filters_deprecated( 'blog_details', array( $details ), '4.7.0', 'site_details' );
    253 
    254         wp_cache_set( $blog_id . $all, $details, 'blog-details' );
    255 
    256         $key = md5( $details->domain . $details->path );
    257         wp_cache_set( $key, $details, 'blog-lookup' );
    258 
    259         return $details;
    260 }
    261 
    262 /**
    263  * Clear the blog details cache.
    264  *
    265  * @since MU (3.0.0)
    266  *
    267  * @param int $blog_id Optional. Blog ID. Defaults to current blog.
    268  */
    269 function refresh_blog_details( $blog_id = 0 ) {
    270         $blog_id = (int) $blog_id;
    271         if ( ! $blog_id ) {
    272                 $blog_id = get_current_blog_id();
    273         }
    274 
    275         clean_blog_cache( $blog_id );
    276 }
    277 
    278 /**
    279  * Update the details for a blog. Updates the blogs table for a given blog id.
    280  *
    281  * @since MU (3.0.0)
    282  *
    283  * @global wpdb $wpdb WordPress database abstraction object.
    284  *
    285  * @param int   $blog_id Blog ID
    286  * @param array $details Array of details keyed by blogs table field names.
    287  * @return bool True if update succeeds, false otherwise.
    288  */
    289 function update_blog_details( $blog_id, $details = array() ) {
    290         global $wpdb;
    291 
    292         if ( empty( $details ) ) {
    293                 return false;
    294         }
    295 
    296         if ( is_object( $details ) ) {
    297                 $details = get_object_vars( $details );
    298         }
    299 
    300         $site = wp_update_site( $blog_id, $details );
    301 
    302         if ( is_wp_error( $site ) ) {
    303                 return false;
    304         }
    305 
    306         return true;
    307 }
    308 
    309 /**
    310  * Clean the blog cache
    311  *
    312  * @since 3.5.0
    313  *
    314  * @global bool $_wp_suspend_cache_invalidation
    315  *
    316  * @param WP_Site|int $blog The site object or ID to be cleared from cache.
    317  */
    318 function clean_blog_cache( $blog ) {
    319         global $_wp_suspend_cache_invalidation;
    320 
    321         if ( ! empty( $_wp_suspend_cache_invalidation ) ) {
    322                 return;
    323         }
    324 
    325         if ( empty( $blog ) ) {
    326                 return;
    327         }
    328 
    329         $blog_id = $blog;
    330         $blog    = get_site( $blog_id );
    331         if ( ! $blog ) {
    332                 if ( ! is_numeric( $blog_id ) ) {
    333                         return;
    334                 }
    335 
    336                 // Make sure a WP_Site object exists even when the site has been deleted.
    337                 $blog = new WP_Site(
    338                         (object) array(
    339                                 'blog_id' => $blog_id,
    340                                 'domain'  => null,
    341                                 'path'    => null,
    342                         )
    343                 );
    344         }
    345 
    346         $blog_id         = $blog->blog_id;
    347         $domain_path_key = md5( $blog->domain . $blog->path );
    348 
    349         wp_cache_delete( $blog_id, 'sites' );
    350         wp_cache_delete( $blog_id, 'site-details' );
    351         wp_cache_delete( $blog_id, 'blog-details' );
    352         wp_cache_delete( $blog_id . 'short', 'blog-details' );
    353         wp_cache_delete( $domain_path_key, 'blog-lookup' );
    354         wp_cache_delete( $domain_path_key, 'blog-id-cache' );
    355         wp_cache_delete( 'current_blog_' . $blog->domain, 'site-options' );
    356         wp_cache_delete( 'current_blog_' . $blog->domain . $blog->path, 'site-options' );
    357         wp_cache_delete( $blog_id, 'blog_meta' );
    358 
    359         /**
    360          * Fires immediately after a site has been removed from the object cache.
    361          *
    362          * @since 4.6.0
    363          *
    364          * @param int     $id              Blog ID.
    365          * @param WP_Site $blog            Site object.
    366          * @param string  $domain_path_key md5 hash of domain and path.
    367          */
    368         do_action( 'clean_site_cache', $blog_id, $blog, $domain_path_key );
    369 
    370         wp_cache_set( 'last_changed', microtime(), 'sites' );
    371 
    372         /**
    373          * Fires after the blog details cache is cleared.
    374          *
    375          * @since 3.4.0
    376          * @deprecated 4.9.0 Use clean_site_cache
    377          *
    378          * @param int $blog_id Blog ID.
    379          */
    380         do_action_deprecated( 'refresh_blog_details', array( $blog_id ), '4.9.0', 'clean_site_cache' );
    381 }
    382 
    383 /**
    384  * Cleans the site details cache for a site.
    385  *
    386  * @since 4.7.4
    387  *
    388  * @param int $site_id Optional. Site ID. Default is the current site ID.
    389  */
    390 function clean_site_details_cache( $site_id = 0 ) {
    391         $site_id = (int) $site_id;
    392         if ( ! $site_id ) {
    393                 $site_id = get_current_blog_id();
    394         }
    395 
    396         wp_cache_delete( $site_id, 'site-details' );
    397         wp_cache_delete( $site_id, 'blog-details' );
    398 }
    399 
    400 /**
    401  * Inserts a new site into the database.
    402  *
    403  * @since 5.1.0
    404  *
    405  * @global wpdb $wpdb WordPress database abstraction object.
    406  *
    407  * @param array $data {
    408  *     Data for the new site that should be inserted.
    409  *
    410  *     @type string $domain       Site domain. Default empty string.
    411  *     @type string $path         Site path. Default '/'.
    412  *     @type int    $network_id   The site's network ID. Default is the current network ID.
    413  *     @type string $registered   When the site was registered, in SQL datetime format. Default is
    414  *                                the current time.
    415  *     @type string $last_updated When the site was last updated, in SQL datetime format. Default is
    416  *                                the value of $registered.
    417  *     @type int    $public       Whether the site is public. Default 1.
    418  *     @type int    $archived     Whether the site is archived. Default 0.
    419  *     @type int    $mature       Whether the site is mature. Default 0.
    420  *     @type int    $spam         Whether the site is spam. Default 0.
    421  *     @type int    $deleted      Whether the site is deleted. Default 0.
    422  *     @type int    $lang_id      The site's language ID. Currently unused. Default 0.
    423  * }
    424  * @return int|WP_Error The new site's ID on success, or error object on failure.
    425  */
    426 function wp_insert_site( array $data ) {
    427         global $wpdb;
    428 
    429         $now = current_time( 'mysql', true );
    430 
    431         $defaults = array(
    432                 'domain'       => '',
    433                 'path'         => '/',
    434                 'network_id'   => get_current_network_id(),
    435                 'registered'   => $now,
    436                 'last_updated' => $now,
    437                 'public'       => 1,
    438                 'archived'     => 0,
    439                 'mature'       => 0,
    440                 'spam'         => 0,
    441                 'deleted'      => 0,
    442                 'lang_id'      => 0,
    443         );
    444 
    445         // Extract the passed arguments that may be relevant for site initialization.
    446         $args = array_diff_key( $data, $defaults );
    447         if ( isset( $args['site_id'] ) ) {
    448                 unset( $args['site_id'] );
    449         }
    450 
    451         $data = wp_prepare_site_data( $data, $defaults );
    452         if ( is_wp_error( $data ) ) {
    453                 return $data;
    454         }
    455 
    456         if ( false === $wpdb->insert( $wpdb->blogs, $data ) ) {
    457                 return new WP_Error( 'db_insert_error', __( 'Could not insert site into the database.' ), $wpdb->last_error );
    458         }
    459 
    460         $new_site = get_site( $wpdb->insert_id );
    461 
    462         clean_blog_cache( $new_site );
    463 
    464         /**
    465          * Fires once a site has been inserted into the database.
    466          *
    467          * @since 5.1.0
    468          *
    469          * @param WP_Site $new_site New site object.
    470          */
    471         do_action( 'wp_insert_site', $new_site );
    472 
    473         /**
    474          * Fires when a site's initialization routine should be executed.
    475          *
    476          * @since 5.1.0
    477          *
    478          * @param WP_Site $new_site New site object.
    479          * @param array   $args     Arguments for the initialization.
    480          */
    481         do_action( 'wp_initialize_site', $new_site, $args );
    482 
    483         // Only compute extra hook parameters if the deprecated hook is actually in use.
    484         if ( has_action( 'wpmu_new_blog' ) ) {
    485                 $user_id = ! empty( $args['user_id'] ) ? $args['user_id'] : 0;
    486                 $meta    = ! empty( $args['options'] ) ? $args['options'] : array();
    487 
    488                 /**
    489                  * Fires immediately after a new site is created.
    490                  *
    491                  * @since MU (3.0.0)
    492                  * @deprecated 5.1.0 Use wp_insert_site
    493                  *
    494                  * @param int    $site_id    Site ID.
    495                  * @param int    $user_id    User ID.
    496                  * @param string $domain     Site domain.
    497                  * @param string $path       Site path.
    498                  * @param int    $network_id Network ID. Only relevant on multi-network installations.
    499                  * @param array  $meta       Meta data. Used to set initial site options.
    500                  */
    501                 do_action_deprecated( 'wpmu_new_blog', array( $new_site->id, $user_id, $new_site->domain, $new_site->path, $new_site->network_id, $meta ), '5.1.0', 'wp_insert_site' );
    502         }
    503 
    504         return (int) $new_site->id;
    505 }
    506 
    507 /**
    508  * Updates a site in the database.
    509  *
    510  * @since 5.1.0
    511  *
    512  * @global wpdb $wpdb WordPress database abstraction object.
    513  *
    514  * @param int   $site_id ID of the site that should be updated.
    515  * @param array $data    Site data to update. See {@see wp_insert_site()} for the list of supported keys.
    516  * @return int|WP_Error The updated site's ID on success, or error object on failure.
    517  */
    518 function wp_update_site( $site_id, array $data ) {
    519         global $wpdb;
    520 
    521         if ( empty( $site_id ) ) {
    522                 return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) );
    523         }
    524 
    525         $old_site = get_site( $site_id );
    526         if ( ! $old_site ) {
    527                 return new WP_Error( 'site_not_exist', __( 'Site does not exist.' ) );
    528         }
    529 
    530         $defaults                 = $old_site->to_array();
    531         $defaults['network_id']   = (int) $defaults['site_id'];
    532         $defaults['last_updated'] = current_time( 'mysql', true );
    533         unset( $defaults['blog_id'], $defaults['site_id'] );
    534 
    535         $data = wp_prepare_site_data( $data, $defaults, $old_site );
    536         if ( is_wp_error( $data ) ) {
    537                 return $data;
    538         }
    539 
    540         if ( false === $wpdb->update( $wpdb->blogs, $data, array( 'blog_id' => $old_site->id ) ) ) {
    541                 return new WP_Error( 'db_update_error', __( 'Could not update site in the database.' ), $wpdb->last_error );
    542         }
    543 
    544         clean_blog_cache( $old_site );
    545 
    546         $new_site = get_site( $old_site->id );
    547 
    548         /**
    549          * Fires once a site has been updated in the database.
    550          *
    551          * @since 5.1.0
    552          *
    553          * @param WP_Site $new_site New site object.
    554          * @param WP_Site $old_site Old site object.
    555          */
    556         do_action( 'wp_update_site', $new_site, $old_site );
    557 
    558         return (int) $new_site->id;
    559 }
    560 
    561 /**
    562  * Deletes a site from the database.
    563  *
    564  * @since 5.1.0
    565  *
    566  * @global wpdb $wpdb WordPress database abstraction object.
    567  *
    568  * @param int $site_id ID of the site that should be deleted.
    569  * @return WP_Site|WP_Error The deleted site object on success, or error object on failure.
    570  */
    571 function wp_delete_site( $site_id ) {
    572         global $wpdb;
    573 
    574         if ( empty( $site_id ) ) {
    575                 return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) );
    576         }
    577 
    578         $old_site = get_site( $site_id );
    579         if ( ! $old_site ) {
    580                 return new WP_Error( 'site_not_exist', __( 'Site does not exist.' ) );
    581         }
    582 
    583         $errors = new WP_Error();
    584 
    585         /**
    586          * Fires before a site should be deleted from the database.
    587          *
    588          * Plugins should amend the `$errors` object via its `WP_Error::add()` method. If any errors
    589          * are present, the site will not be deleted.
    590          *
    591          * @since 5.1.0
    592          *
    593          * @param WP_Error $errors   Error object to add validation errors to.
    594          * @param WP_Site  $old_site The site object to be deleted.
    595          */
    596         do_action( 'wp_validate_site_deletion', $errors, $old_site );
    597 
    598         if ( ! empty( $errors->errors ) ) {
    599                 return $errors;
    600         }
    601 
    602         /**
    603          * Fires before a site is deleted.
    604          *
    605          * @since MU (3.0.0)
    606          * @deprecated 5.1.0
    607          *
    608          * @param int  $site_id The site ID.
    609          * @param bool $drop    True if site's table should be dropped. Default is false.
    610          */
    611         do_action_deprecated( 'delete_blog', array( $old_site->id, true ), '5.1.0' );
    612 
    613         /**
    614          * Fires when a site's uninitialization routine should be executed.
    615          *
    616          * @since 5.1.0
    617          *
    618          * @param WP_Site $old_site Deleted site object.
    619          */
    620         do_action( 'wp_uninitialize_site', $old_site );
    621 
    622         if ( is_site_meta_supported() ) {
    623                 $blog_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->blogmeta WHERE blog_id = %d ", $old_site->id ) );
    624                 foreach ( $blog_meta_ids as $mid ) {
    625                         delete_metadata_by_mid( 'blog', $mid );
    626                 }
    627         }
    628 
    629         if ( false === $wpdb->delete( $wpdb->blogs, array( 'blog_id' => $old_site->id ) ) ) {
    630                 return new WP_Error( 'db_delete_error', __( 'Could not delete site from the database.' ), $wpdb->last_error );
    631         }
    632 
    633         clean_blog_cache( $old_site );
    634 
    635         /**
    636          * Fires once a site has been deleted from the database.
    637          *
    638          * @since 5.1.0
    639          *
    640          * @param WP_Site $old_site Deleted site object.
    641          */
    642         do_action( 'wp_delete_site', $old_site );
    643 
    644         /**
    645          * Fires after the site is deleted from the network.
    646          *
    647          * @since 4.8.0
    648          * @deprecated 5.1.0
    649          *
    650          * @param int  $site_id The site ID.
    651          * @param bool $drop    True if site's tables should be dropped. Default is false.
    652          */
    653         do_action_deprecated( 'deleted_blog', array( $old_site->id, true ), '5.1.0' );
    654 
    655         return $old_site;
    656 }
    657 
    658 /**
    659  * Retrieves site data given a site ID or site object.
    660  *
    661  * Site data will be cached and returned after being passed through a filter.
    662  * If the provided site is empty, the current site global will be used.
    663  *
    664  * @since 4.6.0
    665  *
    666  * @param WP_Site|int|null $site Optional. Site to retrieve. Default is the current site.
    667  * @return WP_Site|null The site object or null if not found.
    668  */
    669 function get_site( $site = null ) {
    670         if ( empty( $site ) ) {
    671                 $site = get_current_blog_id();
    672         }
    673 
    674         if ( $site instanceof WP_Site ) {
    675                 $_site = $site;
    676         } elseif ( is_object( $site ) ) {
    677                 $_site = new WP_Site( $site );
    678         } else {
    679                 $_site = WP_Site::get_instance( $site );
    680         }
    681 
    682         if ( ! $_site ) {
    683                 return null;
    684         }
    685 
    686         /**
    687          * Fires after a site is retrieved.
    688          *
    689          * @since 4.6.0
    690          *
    691          * @param WP_Site $_site Site data.
    692          */
    693         $_site = apply_filters( 'get_site', $_site );
    694 
    695         return $_site;
    696 }
    697 
    698 /**
    699  * Adds any sites from the given ids to the cache that do not already exist in cache.
    700  *
    701  * @since 4.6.0
    702  * @since 5.1.0 Introduced the `$update_meta_cache` parameter.
    703  * @access private
    704  *
    705  * @see update_site_cache()
    706  * @global wpdb $wpdb WordPress database abstraction object.
    707  *
    708  * @param array $ids               ID list.
    709  * @param bool  $update_meta_cache Optional. Whether to update the meta cache. Default true.
    710  */
    711 function _prime_site_caches( $ids, $update_meta_cache = true ) {
    712         global $wpdb;
    713 
    714         $non_cached_ids = _get_non_cached_ids( $ids, 'sites' );
    715         if ( ! empty( $non_cached_ids ) ) {
    716                 $fresh_sites = $wpdb->get_results( sprintf( "SELECT * FROM $wpdb->blogs WHERE blog_id IN (%s)", join( ',', array_map( 'intval', $non_cached_ids ) ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
    717 
    718                 update_site_cache( $fresh_sites, $update_meta_cache );
    719         }
    720 }
    721 
    722 /**
    723  * Updates sites in cache.
    724  *
    725  * @since 4.6.0
    726  * @since 5.1.0 Introduced the `$update_meta_cache` parameter.
    727  *
    728  * @param array $sites             Array of site objects.
    729  * @param bool  $update_meta_cache Whether to update site meta cache. Default true.
    730  */
    731 function update_site_cache( $sites, $update_meta_cache = true ) {
    732         if ( ! $sites ) {
    733                 return;
    734         }
    735         $site_ids = array();
    736         foreach ( $sites as $site ) {
    737                 $site_ids[] = $site->blog_id;
    738                 wp_cache_add( $site->blog_id, $site, 'sites' );
    739                 wp_cache_add( $site->blog_id . 'short', $site, 'blog-details' );
    740         }
    741 
    742         if ( $update_meta_cache ) {
    743                 update_sitemeta_cache( $site_ids );
    744         }
    745 }
    746 
    747 /**
    748  * Updates metadata cache for list of site IDs.
    749  *
    750  * Performs SQL query to retrieve all metadata for the sites matching `$site_ids` and stores them in the cache.
    751  * Subsequent calls to `get_site_meta()` will not need to query the database.
    752  *
    753  * @since 5.1.0
    754  *
    755  * @param array $site_ids List of site IDs.
    756  * @return array|false Returns false if there is nothing to update. Returns an array of metadata on success.
    757  */
    758 function update_sitemeta_cache( $site_ids ) {
    759         return update_meta_cache( 'blog', $site_ids );
    760 }
    761 
    762 /**
    763  * Retrieves a list of sites matching requested arguments.
    764  *
    765  * @since 4.6.0
    766  * @since 4.8.0 Introduced the 'lang_id', 'lang__in', and 'lang__not_in' parameters.
    767  *
    768  * @see WP_Site_Query::parse_query()
    769  *
    770  * @param string|array $args {
    771  *     Optional. Array or query string of site query parameters. Default empty.
    772  *
    773  *     @type array        $site__in          Array of site IDs to include. Default empty.
    774  *     @type array        $site__not_in      Array of site IDs to exclude. Default empty.
    775  *     @type bool         $count             Whether to return a site count (true) or array of site objects.
    776  *                                           Default false.
    777  *     @type array        $date_query        Date query clauses to limit sites by. See WP_Date_Query.
    778  *                                           Default null.
    779  *     @type string       $fields            Site fields to return. Accepts 'ids' (returns an array of site IDs)
    780  *                                           or empty (returns an array of complete site objects). Default empty.
    781  *     @type int          $ID                A site ID to only return that site. Default empty.
    782  *     @type int          $number            Maximum number of sites to retrieve. Default 100.
    783  *     @type int          $offset            Number of sites to offset the query. Used to build LIMIT clause.
    784  *                                           Default 0.
    785  *     @type bool         $no_found_rows     Whether to disable the `SQL_CALC_FOUND_ROWS` query. Default true.
    786  *     @type string|array $orderby           Site status or array of statuses. Accepts 'id', 'domain', 'path',
    787  *                                           'network_id', 'last_updated', 'registered', 'domain_length',
    788  *                                           'path_length', 'site__in' and 'network__in'. Also accepts false,
    789  *                                           an empty array, or 'none' to disable `ORDER BY` clause.
    790  *                                           Default 'id'.
    791  *     @type string       $order             How to order retrieved sites. Accepts 'ASC', 'DESC'. Default 'ASC'.
    792  *     @type int          $network_id        Limit results to those affiliated with a given network ID. If 0,
    793  *                                           include all networks. Default 0.
    794  *     @type array        $network__in       Array of network IDs to include affiliated sites for. Default empty.
    795  *     @type array        $network__not_in   Array of network IDs to exclude affiliated sites for. Default empty.
    796  *     @type string       $domain            Limit results to those affiliated with a given domain. Default empty.
    797  *     @type array        $domain__in        Array of domains to include affiliated sites for. Default empty.
    798  *     @type array        $domain__not_in    Array of domains to exclude affiliated sites for. Default empty.
    799  *     @type string       $path              Limit results to those affiliated with a given path. Default empty.
    800  *     @type array        $path__in          Array of paths to include affiliated sites for. Default empty.
    801  *     @type array        $path__not_in      Array of paths to exclude affiliated sites for. Default empty.
    802  *     @type int          $public            Limit results to public sites. Accepts '1' or '0'. Default empty.
    803  *     @type int          $archived          Limit results to archived sites. Accepts '1' or '0'. Default empty.
    804  *     @type int          $mature            Limit results to mature sites. Accepts '1' or '0'. Default empty.
    805  *     @type int          $spam              Limit results to spam sites. Accepts '1' or '0'. Default empty.
    806  *     @type int          $deleted           Limit results to deleted sites. Accepts '1' or '0'. Default empty.
    807  *     @type int          $lang_id           Limit results to a language ID. Default empty.
    808  *     @type array        $lang__in          Array of language IDs to include affiliated sites for. Default empty.
    809  *     @type array        $lang__not_in      Array of language IDs to exclude affiliated sites for. Default empty.
    810  *     @type string       $search            Search term(s) to retrieve matching sites for. Default empty.
    811  *     @type array        $search_columns    Array of column names to be searched. Accepts 'domain' and 'path'.
    812  *                                           Default empty array.
    813  *     @type bool         $update_site_cache Whether to prime the cache for found sites. Default true.
    814  * }
    815  * @return array|int List of WP_Site objects, a list of site ids when 'fields' is set to 'ids',
    816  *                   or the number of sites when 'count' is passed as a query var.
    817  */
    818 function get_sites( $args = array() ) {
    819         $query = new WP_Site_Query();
    820 
    821         return $query->query( $args );
    822 }
    823 
    824 /**
    825  * Prepares site data for insertion or update in the database.
    826  *
    827  * @since 5.1.0
    828  *
    829  * @param array        $data     Associative array of site data passed to the respective function.
    830  *                               See {@see wp_insert_site()} for the possibly included data.
    831  * @param array        $defaults Site data defaults to parse $data against.
    832  * @param WP_Site|null $old_site Optional. Old site object if an update, or null if an insertion.
    833  *                               Default null.
    834  * @return array|WP_Error Site data ready for a database transaction, or WP_Error in case a validation
    835  *                        error occurred.
    836  */
    837 function wp_prepare_site_data( $data, $defaults, $old_site = null ) {
    838 
    839         // Maintain backward-compatibility with `$site_id` as network ID.
    840         if ( isset( $data['site_id'] ) ) {
    841                 if ( ! empty( $data['site_id'] ) && empty( $data['network_id'] ) ) {
    842                         $data['network_id'] = $data['site_id'];
    843                 }
    844                 unset( $data['site_id'] );
    845         }
    846 
    847         /**
    848          * Filters passed site data in order to normalize it.
    849          *
    850          * @since 5.1.0
    851          *
    852          * @param array $data Associative array of site data passed to the respective function.
    853          *                    See {@see wp_insert_site()} for the possibly included data.
    854          */
    855         $data = apply_filters( 'wp_normalize_site_data', $data );
    856 
    857         $whitelist = array( 'domain', 'path', 'network_id', 'registered', 'last_updated', 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id' );
    858         $data      = array_intersect_key( wp_parse_args( $data, $defaults ), array_flip( $whitelist ) );
    859 
    860         $errors = new WP_Error();
    861 
    862         /**
    863          * Fires when data should be validated for a site prior to inserting or updating in the database.
    864          *
    865          * Plugins should amend the `$errors` object via its `WP_Error::add()` method.
    866          *
    867          * @since 5.1.0
    868          *
    869          * @param WP_Error     $errors   Error object to add validation errors to.
    870          * @param array        $data     Associative array of complete site data. See {@see wp_insert_site()}
    871          *                               for the included data.
    872          * @param WP_Site|null $old_site The old site object if the data belongs to a site being updated,
    873          *                               or null if it is a new site being inserted.
    874          */
    875         do_action( 'wp_validate_site_data', $errors, $data, $old_site );
    876 
    877         if ( ! empty( $errors->errors ) ) {
    878                 return $errors;
    879         }
    880 
    881         // Prepare for database.
    882         $data['site_id'] = $data['network_id'];
    883         unset( $data['network_id'] );
    884 
    885         return $data;
    886 }
    887 
    888 /**
    889  * Normalizes data for a site prior to inserting or updating in the database.
    890  *
    891  * @since 5.1.0
    892  *
    893  * @param array $data Associative array of site data passed to the respective function.
    894  *                    See {@see wp_insert_site()} for the possibly included data.
    895  * @return array Normalized site data.
    896  */
    897 function wp_normalize_site_data( $data ) {
    898         // Sanitize domain if passed.
    899         if ( array_key_exists( 'domain', $data ) ) {
    900                 $data['domain'] = trim( $data['domain'] );
    901                 $data['domain'] = preg_replace( '/\s+/', '', sanitize_user( $data['domain'], true ) );
    902                 if ( is_subdomain_install() ) {
    903                         $data['domain'] = str_replace( '@', '', $data['domain'] );
    904                 }
    905         }
    906 
    907         // Sanitize path if passed.
    908         if ( array_key_exists( 'path', $data ) ) {
    909                 $data['path'] = trailingslashit( '/' . trim( $data['path'], '/' ) );
    910         }
    911 
    912         // Sanitize network ID if passed.
    913         if ( array_key_exists( 'network_id', $data ) ) {
    914                 $data['network_id'] = (int) $data['network_id'];
    915         }
    916 
    917         // Sanitize status fields if passed.
    918         $status_fields = array( 'public', 'archived', 'mature', 'spam', 'deleted' );
    919         foreach ( $status_fields as $status_field ) {
    920                 if ( array_key_exists( $status_field, $data ) ) {
    921                         $data[ $status_field ] = (int) $data[ $status_field ];
    922                 }
    923         }
    924 
    925         // Strip date fields if empty.
    926         $date_fields = array( 'registered', 'last_updated' );
    927         foreach ( $date_fields as $date_field ) {
    928                 if ( ! array_key_exists( $date_field, $data ) ) {
    929                         continue;
    930                 }
    931 
    932                 if ( empty( $data[ $date_field ] ) || '0000-00-00 00:00:00' === $data[ $date_field ] ) {
    933                         unset( $data[ $date_field ] );
    934                 }
    935         }
    936 
    937         return $data;
    938 }
    939 
    940 /**
    941  * Validates data for a site prior to inserting or updating in the database.
    942  *
    943  * @since 5.1.0
    944  *
    945  * @param WP_Error     $errors   Error object, passed by reference. Will contain validation errors if
    946  *                               any occurred.
    947  * @param array        $data     Associative array of complete site data. See {@see wp_insert_site()}
    948  *                               for the included data.
    949  * @param WP_Site|null $old_site The old site object if the data belongs to a site being updated,
    950  *                               or null if it is a new site being inserted.
    951  */
    952 function wp_validate_site_data( $errors, $data, $old_site = null ) {
    953         // A domain must always be present.
    954         if ( empty( $data['domain'] ) ) {
    955                 $errors->add( 'site_empty_domain', __( 'Site domain must not be empty.' ) );
    956         }
    957 
    958         // A path must always be present.
    959         if ( empty( $data['path'] ) ) {
    960                 $errors->add( 'site_empty_path', __( 'Site path must not be empty.' ) );
    961         }
    962 
    963         // A network ID must always be present.
    964         if ( empty( $data['network_id'] ) ) {
    965                 $errors->add( 'site_empty_network_id', __( 'Site network ID must be provided.' ) );
    966         }
    967 
    968         // Both registration and last updated dates must always be present and valid.
    969         $date_fields = array( 'registered', 'last_updated' );
    970         foreach ( $date_fields as $date_field ) {
    971                 if ( empty( $data[ $date_field ] ) ) {
    972                         $errors->add( 'site_empty_' . $date_field, __( 'Both registration and last updated dates must be provided.' ) );
    973                         break;
    974                 }
    975 
    976                 // Allow '0000-00-00 00:00:00', although it be stripped out at this point.
    977                 if ( '0000-00-00 00:00:00' !== $data[ $date_field ] ) {
    978                         $month      = substr( $data[ $date_field ], 5, 2 );
    979                         $day        = substr( $data[ $date_field ], 8, 2 );
    980                         $year       = substr( $data[ $date_field ], 0, 4 );
    981                         $valid_date = wp_checkdate( $month, $day, $year, $data[ $date_field ] );
    982                         if ( ! $valid_date ) {
    983                                 $errors->add( 'site_invalid_' . $date_field, __( 'Both registration and last updated dates must be valid dates.' ) );
    984                                 break;
    985                         }
    986                 }
    987         }
    988 
    989         if ( ! empty( $errors->errors ) ) {
    990                 return;
    991         }
    992 
    993         // If a new site, or domain/path/network ID have changed, ensure uniqueness.
    994         if ( ! $old_site
    995                 || $data['domain'] !== $old_site->domain
    996                 || $data['path'] !== $old_site->path
    997                 || $data['network_id'] !== $old_site->network_id
    998         ) {
    999                 if ( domain_exists( $data['domain'], $data['path'], $data['network_id'] ) ) {
    1000                         $errors->add( 'site_taken', __( 'Sorry, that site already exists!' ) );
    1001                 }
    1002         }
    1003 }
    1004 
    1005 /**
    1006  * Runs the initialization routine for a given site.
    1007  *
    1008  * This process includes creating the site's database tables and
    1009  * populating them with defaults.
    1010  *
    1011  * @since 5.1.0
    1012  *
    1013  * @global wpdb     $wpdb     WordPress database abstraction object.
    1014  * @global WP_Roles $wp_roles WordPress role management object.
    1015  *
    1016  * @param int|WP_Site $site_id Site ID or object.
    1017  * @param array       $args    {
    1018  *     Optional. Arguments to modify the initialization behavior.
    1019  *
    1020  *     @type int    $user_id Required. User ID for the site administrator.
    1021  *     @type string $title   Site title. Default is 'Site %d' where %d is the
    1022  *                           site ID.
    1023  *     @type array  $options Custom option $key => $value pairs to use. Default
    1024  *                           empty array.
    1025  *     @type array  $meta    Custom site metadata $key => $value pairs to use.
    1026  *                           Default empty array.
    1027  * }
    1028  * @return bool|WP_Error True on success, or error object on failure.
    1029  */
    1030 function wp_initialize_site( $site_id, array $args = array() ) {
    1031         global $wpdb, $wp_roles;
    1032 
    1033         if ( empty( $site_id ) ) {
    1034                 return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) );
    1035         }
    1036 
    1037         $site = get_site( $site_id );
    1038         if ( ! $site ) {
    1039                 return new WP_Error( 'site_invalid_id', __( 'Site with the ID does not exist.' ) );
    1040         }
    1041 
    1042         if ( wp_is_site_initialized( $site ) ) {
    1043                 return new WP_Error( 'site_already_initialized', __( 'The site appears to be already initialized.' ) );
    1044         }
    1045 
    1046         $network = get_network( $site->network_id );
    1047         if ( ! $network ) {
    1048                 $network = get_network();
    1049         }
    1050 
    1051         $args = wp_parse_args(
    1052                 $args,
    1053                 array(
    1054                         'user_id' => 0,
    1055                         /* translators: %d: site ID */
    1056                         'title'   => sprintf( __( 'Site %d' ), $site->id ),
    1057                         'options' => array(),
    1058                         'meta'    => array(),
    1059                 )
    1060         );
    1061 
    1062         /**
    1063          * Filters the arguments for initializing a site.
    1064          *
    1065          * @since 5.1.0
    1066          *
    1067          * @param array      $args    Arguments to modify the initialization behavior.
    1068          * @param WP_Site    $site    Site that is being initialized.
    1069          * @param WP_Network $network Network that the site belongs to.
    1070          */
    1071         $args = apply_filters( 'wp_initialize_site_args', $args, $site, $network );
    1072 
    1073         $orig_installing = wp_installing();
    1074         if ( ! $orig_installing ) {
    1075                 wp_installing( true );
    1076         }
    1077 
    1078         $switch = false;
    1079         if ( get_current_blog_id() !== $site->id ) {
    1080                 $switch = true;
    1081                 switch_to_blog( $site->id );
    1082         }
    1083 
    1084         require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
    1085 
    1086         // Set up the database tables.
    1087         make_db_current_silent( 'blog' );
    1088 
    1089         $home_scheme    = 'http';
    1090         $siteurl_scheme = 'http';
    1091         if ( ! is_subdomain_install() ) {
    1092                 if ( 'https' === parse_url( get_home_url( $network->site_id ), PHP_URL_SCHEME ) ) {
    1093                         $home_scheme = 'https';
    1094                 }
    1095                 if ( 'https' === parse_url( get_network_option( $network->id, 'siteurl' ), PHP_URL_SCHEME ) ) {
    1096                         $siteurl_scheme = 'https';
    1097                 }
    1098         }
    1099 
    1100         // Populate the site's options.
    1101         populate_options(
    1102                 array_merge(
    1103                         array(
    1104                                 'home'        => untrailingslashit( $home_scheme . '://' . $site->domain . $site->path ),
    1105                                 'siteurl'     => untrailingslashit( $siteurl_scheme . '://' . $site->domain . $site->path ),
    1106                                 'blogname'    => wp_unslash( $args['title'] ),
    1107                                 'admin_email' => '',
    1108                                 'upload_path' => get_network_option( $network->id, 'ms_files_rewriting' ) ? UPLOADBLOGSDIR . "/{$site->id}/files" : get_blog_option( $network->site_id, 'upload_path' ),
    1109                                 'blog_public' => (int) $site->public,
    1110                                 'WPLANG'      => get_network_option( $network->id, 'WPLANG' ),
    1111                         ),
    1112                         $args['options']
    1113                 )
    1114         );
    1115 
    1116         // Populate the site's roles.
    1117         populate_roles();
    1118         $wp_roles = new WP_Roles();
    1119 
    1120         // Populate metadata for the site.
    1121         populate_site_meta( $site->id, $args['meta'] );
    1122 
    1123         // Remove all permissions that may exist for the site.
    1124         $table_prefix = $wpdb->get_blog_prefix();
    1125         delete_metadata( 'user', 0, $table_prefix . 'user_level', null, true ); // delete all
    1126         delete_metadata( 'user', 0, $table_prefix . 'capabilities', null, true ); // delete all
    1127 
    1128         // Install default site content.
    1129         wp_install_defaults( $args['user_id'] );
    1130 
    1131         // Set the site administrator.
    1132         add_user_to_blog( $site->id, $args['user_id'], 'administrator' );
    1133         if ( ! user_can( $args['user_id'], 'manage_network' ) && ! get_user_meta( $args['user_id'], 'primary_blog', true ) ) {
    1134                 update_user_meta( $args['user_id'], 'primary_blog', $site->id );
    1135         }
    1136 
    1137         if ( $switch ) {
    1138                 restore_current_blog();
    1139         }
    1140 
    1141         wp_installing( $orig_installing );
    1142 
    1143         return true;
    1144 }
    1145 
    1146 /**
    1147  * Runs the uninitialization routine for a given site.
    1148  *
    1149  * This process includes dropping the site's database tables and deleting its uploads directory.
    1150  *
    1151  * @since 5.1.0
    1152  *
    1153  * @global wpdb $wpdb WordPress database abstraction object.
    1154  *
    1155  * @param int|WP_Site $site_id Site ID or object.
    1156  * @return bool|WP_Error True on success, or error object on failure.
    1157  */
    1158 function wp_uninitialize_site( $site_id ) {
    1159         global $wpdb;
    1160 
    1161         if ( empty( $site_id ) ) {
    1162                 return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) );
    1163         }
    1164 
    1165         $site = get_site( $site_id );
    1166         if ( ! $site ) {
    1167                 return new WP_Error( 'site_invalid_id', __( 'Site with the ID does not exist.' ) );
    1168         }
    1169 
    1170         if ( ! wp_is_site_initialized( $site ) ) {
    1171                 return new WP_Error( 'site_already_uninitialized', __( 'The site appears to be already uninitialized.' ) );
    1172         }
    1173 
    1174         $users = get_users(
    1175                 array(
    1176                         'blog_id' => $site->id,
    1177                         'fields'  => 'ids',
    1178                 )
    1179         );
    1180 
    1181         // Remove users from the site.
    1182         if ( ! empty( $users ) ) {
    1183                 foreach ( $users as $user_id ) {
    1184                         remove_user_from_blog( $user_id, $site->id );
    1185                 }
    1186         }
    1187 
    1188         $switch = false;
    1189         if ( get_current_blog_id() !== $site->id ) {
    1190                 $switch = true;
    1191                 switch_to_blog( $site->id );
    1192         }
    1193 
    1194         $uploads = wp_get_upload_dir();
    1195 
    1196         $tables = $wpdb->tables( 'blog' );
    1197 
    1198         /**
    1199          * Filters the tables to drop when the site is deleted.
    1200          *
    1201          * @since MU (3.0.0)
    1202          *
    1203          * @param string[] $tables  Array of names of the site tables to be dropped.
    1204          * @param int      $site_id The ID of the site to drop tables for.
    1205          */
    1206         $drop_tables = apply_filters( 'wpmu_drop_tables', $tables, $site->id );
    1207 
    1208         foreach ( (array) $drop_tables as $table ) {
    1209                 $wpdb->query( "DROP TABLE IF EXISTS `$table`" ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
     237                return $details;
    1210238        }
    1211239
     240        switch_to_blog( $blog_id );
     241        $details->blogname   = get_option( 'blogname' );
     242        $details->siteurl    = get_option( 'siteurl' );
     243        $details->post_count = get_option( 'post_count' );
     244        $details->home       = get_option( 'home' );
     245        restore_current_blog();
     246
    1212247        /**
    1213          * Filters the upload base directory to delete when the site is deleted.
     248         * Filters a blog's details.
    1214249         *
    1215250         * @since MU (3.0.0)
     251         * @deprecated 4.7.0 Use site_details
    1216252         *
    1217          * @param string $uploads['basedir'] Uploads path without subdirectory. @see wp_upload_dir()
    1218          * @param int    $site_id            The site ID.
     253         * @param object $details The blog details.
    1219254         */
    1220         $dir     = apply_filters( 'wpmu_delete_blog_upload_dir', $uploads['basedir'], $site->id );
    1221         $dir     = rtrim( $dir, DIRECTORY_SEPARATOR );
    1222         $top_dir = $dir;
    1223         $stack   = array( $dir );
    1224         $index   = 0;
    1225 
    1226         while ( $index < count( $stack ) ) {
    1227                 // Get indexed directory from stack
    1228                 $dir = $stack[ $index ];
    1229 
    1230                 // phpcs:disable WordPress.PHP.NoSilencedErrors.Discouraged
    1231                 $dh = @opendir( $dir );
    1232                 if ( $dh ) {
    1233                         $file = @readdir( $dh );
    1234                         while ( false !== $file ) {
    1235                                 if ( '.' === $file || '..' === $file ) {
    1236                                         $file = @readdir( $dh );
    1237                                         continue;
    1238                                 }
     255        $details = apply_filters_deprecated( 'blog_details', array( $details ), '4.7.0', 'site_details' );
    1239256
    1240                                 if ( @is_dir( $dir . DIRECTORY_SEPARATOR . $file ) ) {
    1241                                         $stack[] = $dir . DIRECTORY_SEPARATOR . $file;
    1242                                 } elseif ( @is_file( $dir . DIRECTORY_SEPARATOR . $file ) ) {
    1243                                         @unlink( $dir . DIRECTORY_SEPARATOR . $file );
    1244                                 }
     257        wp_cache_set( $blog_id . $all, $details, 'blog-details' );
    1245258
    1246                                 $file = @readdir( $dh );
    1247                         }
    1248                         @closedir( $dh );
    1249                 }
    1250                 $index++;
    1251         }
     259        $key = md5( $details->domain . $details->path );
     260        wp_cache_set( $key, $details, 'blog-lookup' );
    1252261
    1253         $stack = array_reverse( $stack ); // Last added dirs are deepest
    1254         foreach ( (array) $stack as $dir ) {
    1255                 if ( $dir != $top_dir ) {
    1256                         @rmdir( $dir );
    1257                 }
    1258         }
     262        return $details;
     263}
    1259264
    1260         // phpcs:enable WordPress.PHP.NoSilencedErrors.Discouraged
    1261         if ( $switch ) {
    1262                 restore_current_blog();
     265/**
     266 * Clear the blog details cache.
     267 *
     268 * @since MU (3.0.0)
     269 *
     270 * @param int $blog_id Optional. Blog ID. Defaults to current blog.
     271 */
     272function refresh_blog_details( $blog_id = 0 ) {
     273        $blog_id = (int) $blog_id;
     274        if ( ! $blog_id ) {
     275                $blog_id = get_current_blog_id();
    1263276        }
    1264277
    1265         return true;
     278        clean_blog_cache( $blog_id );
    1266279}
    1267280
    1268281/**
    1269  * Checks whether a site is initialized.
    1270  *
    1271  * A site is considered initialized when its database tables are present.
     282 * Update the details for a blog. Updates the blogs table for a given blog id.
    1272283 *
    1273  * @since 5.1.0
     284 * @since MU (3.0.0)
    1274285 *
    1275286 * @global wpdb $wpdb WordPress database abstraction object.
    1276287 *
    1277  * @param int|WP_Site $site_id Site ID or object.
    1278  * @return bool True if the site is initialized, false otherwise.
     288 * @param int   $blog_id Blog ID
     289 * @param array $details Array of details keyed by blogs table field names.
     290 * @return bool True if update succeeds, false otherwise.
    1279291 */
    1280 function wp_is_site_initialized( $site_id ) {
     292function update_blog_details( $blog_id, $details = array() ) {
    1281293        global $wpdb;
    1282294
    1283         if ( is_object( $site_id ) ) {
    1284                 $site_id = $site_id->blog_id;
     295        if ( empty( $details ) ) {
     296                return false;
    1285297        }
    1286         $site_id = (int) $site_id;
    1287298
    1288         /**
    1289          * Filters the check for whether a site is initialized before the database is accessed.
    1290          *
    1291          * Returning a non-null value will effectively short-circuit the function, returning
    1292          * that value instead.
    1293          *
    1294          * @since 5.1.0
    1295          *
    1296          * @param bool|null $pre     The value to return, if not null.
    1297          * @param int       $site_id The site ID that is being checked.
    1298          */
    1299         $pre = apply_filters( 'pre_wp_is_site_initialized', null, $site_id );
    1300         if ( null !== $pre ) {
    1301                 return (bool) $pre;
     299        if ( is_object( $details ) ) {
     300                $details = get_object_vars( $details );
    1302301        }
    1303302
    1304         $switch = false;
    1305         if ( get_current_blog_id() !== $site_id ) {
    1306                 $switch = true;
    1307                 remove_action( 'switch_blog', 'wp_switch_roles_and_user', 1 );
    1308                 switch_to_blog( $site_id );
     303        $site = wp_update_site( $blog_id, $details );
     304
     305        if ( is_wp_error( $site ) ) {
     306                return false;
    1309307        }
    1310308
    1311         $suppress = $wpdb->suppress_errors();
    1312         $result   = (bool) $wpdb->get_results( "DESCRIBE {$wpdb->posts}" );
    1313         $wpdb->suppress_errors( $suppress );
     309        return true;
     310}
    1314311
    1315         if ( $switch ) {
    1316                 restore_current_blog();
    1317                 add_action( 'switch_blog', 'wp_switch_roles_and_user', 1, 2 );
     312/**
     313 * Cleans the site details cache for a site.
     314 *
     315 * @since 4.7.4
     316 *
     317 * @param int $site_id Optional. Site ID. Default is the current site ID.
     318 */
     319function clean_site_details_cache( $site_id = 0 ) {
     320        $site_id = (int) $site_id;
     321        if ( ! $site_id ) {
     322                $site_id = get_current_blog_id();
    1318323        }
    1319324
    1320         return $result;
     325        wp_cache_delete( $site_id, 'site-details' );
     326        wp_cache_delete( $site_id, 'blog-details' );
    1321327}
    1322328
    1323329/**
     
    1459465}
    1460466
    1461467/**
    1462  * Adds metadata to a site.
    1463  *
    1464  * @since 5.1.0
    1465  *
    1466  * @param int    $site_id    Site ID.
    1467  * @param string $meta_key   Metadata name.
    1468  * @param mixed  $meta_value Metadata value. Must be serializable if non-scalar.
    1469  * @param bool   $unique     Optional. Whether the same key should not be added.
    1470  *                           Default false.
    1471  * @return int|false Meta ID on success, false on failure.
    1472  */
    1473 function add_site_meta( $site_id, $meta_key, $meta_value, $unique = false ) {
    1474         return add_metadata( 'blog', $site_id, $meta_key, $meta_value, $unique );
    1475 }
    1476 
    1477 /**
    1478  * Removes metadata matching criteria from a site.
    1479  *
    1480  * You can match based on the key, or key and value. Removing based on key and
    1481  * value, will keep from removing duplicate metadata with the same key. It also
    1482  * allows removing all metadata matching key, if needed.
    1483  *
    1484  * @since 5.1.0
    1485  *
    1486  * @param int    $site_id    Site ID.
    1487  * @param string $meta_key   Metadata name.
    1488  * @param mixed  $meta_value Optional. Metadata value. Must be serializable if
    1489  *                           non-scalar. Default empty.
    1490  * @return bool True on success, false on failure.
    1491  */
    1492 function delete_site_meta( $site_id, $meta_key, $meta_value = '' ) {
    1493         return delete_metadata( 'blog', $site_id, $meta_key, $meta_value );
    1494 }
    1495 
    1496 /**
    1497  * Retrieves metadata for a site.
    1498  *
    1499  * @since 5.1.0
    1500  *
    1501  * @param int    $site_id Site ID.
    1502  * @param string $key     Optional. The meta key to retrieve. By default, returns
    1503  *                        data for all keys. Default empty.
    1504  * @param bool   $single  Optional. Whether to return a single value. Default false.
    1505  * @return mixed Will be an array if $single is false. Will be value of meta data
    1506  *               field if $single is true.
    1507  */
    1508 function get_site_meta( $site_id, $key = '', $single = false ) {
    1509         return get_metadata( 'blog', $site_id, $key, $single );
    1510 }
    1511 
    1512 /**
    1513  * Updates metadata for a site.
    1514  *
    1515  * Use the $prev_value parameter to differentiate between meta fields with the
    1516  * same key and site ID.
    1517  *
    1518  * If the meta field for the site does not exist, it will be added.
    1519  *
    1520  * @since 5.1.0
    1521  *
    1522  * @param int    $site_id    Site ID.
    1523  * @param string $meta_key   Metadata key.
    1524  * @param mixed  $meta_value Metadata value. Must be serializable if non-scalar.
    1525  * @param mixed  $prev_value Optional. Previous value to check before removing.
    1526  *                           Default empty.
    1527  * @return int|bool Meta ID if the key didn't exist, true on successful update,
    1528  *                  false on failure.
    1529  */
    1530 function update_site_meta( $site_id, $meta_key, $meta_value, $prev_value = '' ) {
    1531         return update_metadata( 'blog', $site_id, $meta_key, $meta_value, $prev_value );
    1532 }
    1533 
    1534 /**
    1535  * Deletes everything from site meta matching meta key.
    1536  *
    1537  * @since 5.1.0
    1538  *
    1539  * @param string $meta_key Metadata key to search for when deleting.
    1540  * @return bool Whether the site meta key was deleted from the database.
    1541  */
    1542 function delete_site_meta_by_key( $meta_key ) {
    1543         return delete_metadata( 'blog', null, $meta_key, '', true );
    1544 }
    1545 
    1546 /**
    1547468 * Switch the current blog.
    1548469 *
    1549470 * This function is useful if you need to pull posts, or other information,
     
    1843764}
    1844765
    1845766/**
    1846  * Retrieves a list of networks.
    1847  *
    1848  * @since 4.6.0
    1849  *
    1850  * @param string|array $args Optional. Array or string of arguments. See WP_Network_Query::parse_query()
    1851  *                           for information on accepted arguments. Default empty array.
    1852  * @return array|int List of WP_Network objects, a list of network ids when 'fields' is set to 'ids',
    1853  *                   or the number of networks when 'count' is passed as a query var.
    1854  */
    1855 function get_networks( $args = array() ) {
    1856         $query = new WP_Network_Query();
    1857 
    1858         return $query->query( $args );
    1859 }
    1860 
    1861 /**
    1862  * Retrieves network data given a network ID or network object.
    1863  *
    1864  * Network data will be cached and returned after being passed through a filter.
    1865  * If the provided network is empty, the current network global will be used.
    1866  *
    1867  * @since 4.6.0
    1868  *
    1869  * @global WP_Network $current_site
    1870  *
    1871  * @param WP_Network|int|null $network Optional. Network to retrieve. Default is the current network.
    1872  * @return WP_Network|null The network object or null if not found.
    1873  */
    1874 function get_network( $network = null ) {
    1875         global $current_site;
    1876         if ( empty( $network ) && isset( $current_site ) ) {
    1877                 $network = $current_site;
    1878         }
    1879 
    1880         if ( $network instanceof WP_Network ) {
    1881                 $_network = $network;
    1882         } elseif ( is_object( $network ) ) {
    1883                 $_network = new WP_Network( $network );
    1884         } else {
    1885                 $_network = WP_Network::get_instance( $network );
    1886         }
    1887 
    1888         if ( ! $_network ) {
    1889                 return null;
    1890         }
    1891 
    1892         /**
    1893          * Fires after a network is retrieved.
    1894          *
    1895          * @since 4.6.0
    1896          *
    1897          * @param WP_Network $_network Network data.
    1898          */
    1899         $_network = apply_filters( 'get_network', $_network );
    1900 
    1901         return $_network;
    1902 }
    1903 
    1904 /**
    1905  * Removes a network from the object cache.
    1906  *
    1907  * @since 4.6.0
    1908  *
    1909  * @global bool $_wp_suspend_cache_invalidation
    1910  *
    1911  * @param int|array $ids Network ID or an array of network IDs to remove from cache.
    1912  */
    1913 function clean_network_cache( $ids ) {
    1914         global $_wp_suspend_cache_invalidation;
    1915 
    1916         if ( ! empty( $_wp_suspend_cache_invalidation ) ) {
    1917                 return;
    1918         }
    1919 
    1920         foreach ( (array) $ids as $id ) {
    1921                 wp_cache_delete( $id, 'networks' );
    1922 
    1923                 /**
    1924                  * Fires immediately after a network has been removed from the object cache.
    1925                  *
    1926                  * @since 4.6.0
    1927                  *
    1928                  * @param int $id Network ID.
    1929                  */
    1930                 do_action( 'clean_network_cache', $id );
    1931         }
    1932 
    1933         wp_cache_set( 'last_changed', microtime(), 'networks' );
    1934 }
    1935 
    1936 /**
    1937  * Updates the network cache of given networks.
    1938  *
    1939  * Will add the networks in $networks to the cache. If network ID already exists
    1940  * in the network cache then it will not be updated. The network is added to the
    1941  * cache using the network group with the key using the ID of the networks.
    1942  *
    1943  * @since 4.6.0
    1944  *
    1945  * @param array $networks Array of network row objects.
    1946  */
    1947 function update_network_cache( $networks ) {
    1948         foreach ( (array) $networks as $network ) {
    1949                 wp_cache_add( $network->id, $network, 'networks' );
    1950         }
    1951 }
    1952 
    1953 /**
    1954  * Adds any networks from the given IDs to the cache that do not already exist in cache.
    1955  *
    1956  * @since 4.6.0
    1957  * @access private
    1958  *
    1959  * @see update_network_cache()
    1960  * @global wpdb $wpdb WordPress database abstraction object.
    1961  *
    1962  * @param array $network_ids Array of network IDs.
    1963  */
    1964 function _prime_network_caches( $network_ids ) {
    1965         global $wpdb;
    1966 
    1967         $non_cached_ids = _get_non_cached_ids( $network_ids, 'networks' );
    1968         if ( ! empty( $non_cached_ids ) ) {
    1969                 $fresh_networks = $wpdb->get_results( sprintf( "SELECT $wpdb->site.* FROM $wpdb->site WHERE id IN (%s)", join( ',', array_map( 'intval', $non_cached_ids ) ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
    1970 
    1971                 update_network_cache( $fresh_networks );
    1972         }
    1973 }
    1974 
    1975 /**
    1976767 * Handler for updating the site's last updated date when a post is published or
    1977768 * an already published post is changed.
    1978769 *
     
    2062853
    2063854        update_posts_count();
    2064855}
    2065 
    2066 /**
    2067  * Updates the count of sites for a network based on a changed site.
    2068  *
    2069  * @since 5.1.0
    2070  *
    2071  * @param WP_Site      $new_site The site object that has been inserted, updated or deleted.
    2072  * @param WP_Site|null $old_site Optional. If $new_site has been updated, this must be the previous
    2073  *                               state of that site. Default null.
    2074  */
    2075 function wp_maybe_update_network_site_counts_on_update( $new_site, $old_site = null ) {
    2076         if ( null === $old_site ) {
    2077                 wp_maybe_update_network_site_counts( $new_site->network_id );
    2078                 return;
    2079         }
    2080 
    2081         if ( $new_site->network_id != $old_site->network_id ) {
    2082                 wp_maybe_update_network_site_counts( $new_site->network_id );
    2083                 wp_maybe_update_network_site_counts( $old_site->network_id );
    2084         }
    2085 }
    2086 
    2087 /**
    2088  * Triggers actions on site status updates.
    2089  *
    2090  * @since 5.1.0
    2091  *
    2092  * @param WP_Site      $new_site The site object after the update.
    2093  * @param WP_Site|null $old_site Optional. If $new_site has been updated, this must be the previous
    2094  *                               state of that site. Default null.
    2095  */
    2096 function wp_maybe_transition_site_statuses_on_update( $new_site, $old_site = null ) {
    2097         $site_id = $new_site->id;
    2098 
    2099         // Use the default values for a site if no previous state is given.
    2100         if ( ! $old_site ) {
    2101                 $old_site = new WP_Site( new stdClass() );
    2102         }
    2103 
    2104         if ( $new_site->spam != $old_site->spam ) {
    2105                 if ( 1 == $new_site->spam ) {
    2106 
    2107                         /**
    2108                          * Fires when the 'spam' status is added to a site.
    2109                          *
    2110                          * @since MU (3.0.0)
    2111                          *
    2112                          * @param int $site_id Site ID.
    2113                          */
    2114                         do_action( 'make_spam_blog', $site_id );
    2115                 } else {
    2116 
    2117                         /**
    2118                          * Fires when the 'spam' status is removed from a site.
    2119                          *
    2120                          * @since MU (3.0.0)
    2121                          *
    2122                          * @param int $site_id Site ID.
    2123                          */
    2124                         do_action( 'make_ham_blog', $site_id );
    2125                 }
    2126         }
    2127 
    2128         if ( $new_site->mature != $old_site->mature ) {
    2129                 if ( 1 == $new_site->mature ) {
    2130 
    2131                         /**
    2132                          * Fires when the 'mature' status is added to a site.
    2133                          *
    2134                          * @since 3.1.0
    2135                          *
    2136                          * @param int $site_id Site ID.
    2137                          */
    2138                         do_action( 'mature_blog', $site_id );
    2139                 } else {
    2140 
    2141                         /**
    2142                          * Fires when the 'mature' status is removed from a site.
    2143                          *
    2144                          * @since 3.1.0
    2145                          *
    2146                          * @param int $site_id Site ID.
    2147                          */
    2148                         do_action( 'unmature_blog', $site_id );
    2149                 }
    2150         }
    2151 
    2152         if ( $new_site->archived != $old_site->archived ) {
    2153                 if ( 1 == $new_site->archived ) {
    2154 
    2155                         /**
    2156                          * Fires when the 'archived' status is added to a site.
    2157                          *
    2158                          * @since MU (3.0.0)
    2159                          *
    2160                          * @param int $site_id Site ID.
    2161                          */
    2162                         do_action( 'archive_blog', $site_id );
    2163                 } else {
    2164 
    2165                         /**
    2166                          * Fires when the 'archived' status is removed from a site.
    2167                          *
    2168                          * @since MU (3.0.0)
    2169                          *
    2170                          * @param int $site_id Site ID.
    2171                          */
    2172                         do_action( 'unarchive_blog', $site_id );
    2173                 }
    2174         }
    2175 
    2176         if ( $new_site->deleted != $old_site->deleted ) {
    2177                 if ( 1 == $new_site->deleted ) {
    2178 
    2179                         /**
    2180                          * Fires when the 'deleted' status is added to a site.
    2181                          *
    2182                          * @since 3.5.0
    2183                          *
    2184                          * @param int $site_id Site ID.
    2185                          */
    2186                         do_action( 'make_delete_blog', $site_id );
    2187                 } else {
    2188 
    2189                         /**
    2190                          * Fires when the 'deleted' status is removed from a site.
    2191                          *
    2192                          * @since 3.5.0
    2193                          *
    2194                          * @param int $site_id Site ID.
    2195                          */
    2196                         do_action( 'make_undelete_blog', $site_id );
    2197                 }
    2198         }
    2199 
    2200         if ( $new_site->public != $old_site->public ) {
    2201 
    2202                 /**
    2203                  * Fires after the current blog's 'public' setting is updated.
    2204                  *
    2205                  * @since MU (3.0.0)
    2206                  *
    2207                  * @param int    $site_id Site ID.
    2208                  * @param string $value   The value of the site status.
    2209                  */
    2210                 do_action( 'update_blog_public', $site_id, $new_site->public );
    2211         }
    2212 }
    2213 
    2214 /**
    2215  * Cleans the necessary caches after specific site data has been updated.
    2216  *
    2217  * @since 5.1.0
    2218  *
    2219  * @param WP_Site $new_site The site object after the update.
    2220  * @param WP_Site $old_site The site obejct prior to the update.
    2221  */
    2222 function wp_maybe_clean_new_site_cache_on_update( $new_site, $old_site ) {
    2223         if ( $old_site->domain !== $new_site->domain || $old_site->path !== $new_site->path ) {
    2224                 clean_blog_cache( $new_site );
    2225         }
    2226 }
    2227 
    2228 /**
    2229  * Updates the `blog_public` option for a given site ID.
    2230  *
    2231  * @since 5.1.0
    2232  *
    2233  * @param int    $site_id Site ID.
    2234  * @param string $public  The value of the site status.
    2235  */
    2236 function wp_update_blog_public_option_on_site_update( $site_id, $public ) {
    2237 
    2238         // Bail if the site's database tables do not exist (yet).
    2239         if ( ! wp_is_site_initialized( $site_id ) ) {
    2240                 return;
    2241         }
    2242 
    2243         update_blog_option( $site_id, 'blog_public', $public );
    2244 }
    2245 
    2246 /**
    2247  * Sets the last changed time for the 'sites' cache group.
    2248  *
    2249  * @since 5.1.0
    2250  */
    2251 function wp_cache_set_sites_last_changed() {
    2252         wp_cache_set( 'last_changed', microtime(), 'sites' );
    2253 }
    2254 
    2255 /**
    2256  * Aborts calls to site meta if it is not supported.
    2257  *
    2258  * @since 5.1.0
    2259  *
    2260  * @global wpdb $wpdb WordPress database abstraction object.
    2261  *
    2262  * @param mixed $check Skip-value for whether to proceed site meta function execution.
    2263  * @return mixed Original value of $check, or false if site meta is not supported.
    2264  */
    2265 function wp_check_site_meta_support_prefilter( $check ) {
    2266         if ( ! is_site_meta_supported() ) {
    2267                 /* translators: %s: database table name */
    2268                 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The %s table is not installed. Please run the network database upgrade.' ), $GLOBALS['wpdb']->blogmeta ), '5.1.0' );
    2269                 return false;
    2270         }
    2271 
    2272         return $check;
    2273 }
  • src/wp-includes/ms-network.php

     
     1<?php
     2/**
     3 * Network API
     4 *
     5 * @package WordPress
     6 * @subpackage Multisite
     7 * @since 5.1.0
     8 */
     9
     10/**
     11 * Retrieves network data given a network ID or network object.
     12 *
     13 * Network data will be cached and returned after being passed through a filter.
     14 * If the provided network is empty, the current network global will be used.
     15 *
     16 * @since 4.6.0
     17 *
     18 * @global WP_Network $current_site
     19 *
     20 * @param WP_Network|int|null $network Optional. Network to retrieve. Default is the current network.
     21 * @return WP_Network|null The network object or null if not found.
     22 */
     23function get_network( $network = null ) {
     24        global $current_site;
     25        if ( empty( $network ) && isset( $current_site ) ) {
     26                $network = $current_site;
     27        }
     28
     29        if ( $network instanceof WP_Network ) {
     30                $_network = $network;
     31        } elseif ( is_object( $network ) ) {
     32                $_network = new WP_Network( $network );
     33        } else {
     34                $_network = WP_Network::get_instance( $network );
     35        }
     36
     37        if ( ! $_network ) {
     38                return null;
     39        }
     40
     41        /**
     42         * Fires after a network is retrieved.
     43         *
     44         * @since 4.6.0
     45         *
     46         * @param WP_Network $_network Network data.
     47         */
     48        $_network = apply_filters( 'get_network', $_network );
     49
     50        return $_network;
     51}
     52
     53/**
     54 * Retrieves a list of networks.
     55 *
     56 * @since 4.6.0
     57 *
     58 * @param string|array $args Optional. Array or string of arguments. See WP_Network_Query::parse_query()
     59 *                           for information on accepted arguments. Default empty array.
     60 * @return array|int List of WP_Network objects, a list of network ids when 'fields' is set to 'ids',
     61 *                   or the number of networks when 'count' is passed as a query var.
     62 */
     63function get_networks( $args = array() ) {
     64        $query = new WP_Network_Query();
     65
     66        return $query->query( $args );
     67}
     68
     69/**
     70 * Removes a network from the object cache.
     71 *
     72 * @since 4.6.0
     73 *
     74 * @global bool $_wp_suspend_cache_invalidation
     75 *
     76 * @param int|array $ids Network ID or an array of network IDs to remove from cache.
     77 */
     78function clean_network_cache( $ids ) {
     79        global $_wp_suspend_cache_invalidation;
     80
     81        if ( ! empty( $_wp_suspend_cache_invalidation ) ) {
     82                return;
     83        }
     84
     85        foreach ( (array) $ids as $id ) {
     86                wp_cache_delete( $id, 'networks' );
     87
     88                /**
     89                 * Fires immediately after a network has been removed from the object cache.
     90                 *
     91                 * @since 4.6.0
     92                 *
     93                 * @param int $id Network ID.
     94                 */
     95                do_action( 'clean_network_cache', $id );
     96        }
     97
     98        wp_cache_set( 'last_changed', microtime(), 'networks' );
     99}
     100
     101/**
     102 * Updates the network cache of given networks.
     103 *
     104 * Will add the networks in $networks to the cache. If network ID already exists
     105 * in the network cache then it will not be updated. The network is added to the
     106 * cache using the network group with the key using the ID of the networks.
     107 *
     108 * @since 4.6.0
     109 *
     110 * @param array $networks Array of network row objects.
     111 */
     112function update_network_cache( $networks ) {
     113        foreach ( (array) $networks as $network ) {
     114                wp_cache_add( $network->id, $network, 'networks' );
     115        }
     116}
     117
     118/**
     119 * Adds any networks from the given IDs to the cache that do not already exist in cache.
     120 *
     121 * @since 4.6.0
     122 * @access private
     123 *
     124 * @see update_network_cache()
     125 * @global wpdb $wpdb WordPress database abstraction object.
     126 *
     127 * @param array $network_ids Array of network IDs.
     128 */
     129function _prime_network_caches( $network_ids ) {
     130        global $wpdb;
     131
     132        $non_cached_ids = _get_non_cached_ids( $network_ids, 'networks' );
     133        if ( ! empty( $non_cached_ids ) ) {
     134                $fresh_networks = $wpdb->get_results( sprintf( "SELECT $wpdb->site.* FROM $wpdb->site WHERE id IN (%s)", join( ',', array_map( 'intval', $non_cached_ids ) ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
     135
     136                update_network_cache( $fresh_networks );
     137        }
     138}
  • src/wp-includes/ms-site.php

     
     1<?php
     2/**
     3 * Site API
     4 *
     5 * @package WordPress
     6 * @subpackage Multisite
     7 * @since 5.1.0
     8 */
     9
     10/**
     11 * Inserts a new site into the database.
     12 *
     13 * @since 5.1.0
     14 *
     15 * @global wpdb $wpdb WordPress database abstraction object.
     16 *
     17 * @param array $data {
     18 *     Data for the new site that should be inserted.
     19 *
     20 *     @type string $domain       Site domain. Default empty string.
     21 *     @type string $path         Site path. Default '/'.
     22 *     @type int    $network_id   The site's network ID. Default is the current network ID.
     23 *     @type string $registered   When the site was registered, in SQL datetime format. Default is
     24 *                                the current time.
     25 *     @type string $last_updated When the site was last updated, in SQL datetime format. Default is
     26 *                                the value of $registered.
     27 *     @type int    $public       Whether the site is public. Default 1.
     28 *     @type int    $archived     Whether the site is archived. Default 0.
     29 *     @type int    $mature       Whether the site is mature. Default 0.
     30 *     @type int    $spam         Whether the site is spam. Default 0.
     31 *     @type int    $deleted      Whether the site is deleted. Default 0.
     32 *     @type int    $lang_id      The site's language ID. Currently unused. Default 0.
     33 * }
     34 * @return int|WP_Error The new site's ID on success, or error object on failure.
     35 */
     36function wp_insert_site( array $data ) {
     37        global $wpdb;
     38
     39        $now = current_time( 'mysql', true );
     40
     41        $defaults = array(
     42                'domain'       => '',
     43                'path'         => '/',
     44                'network_id'   => get_current_network_id(),
     45                'registered'   => $now,
     46                'last_updated' => $now,
     47                'public'       => 1,
     48                'archived'     => 0,
     49                'mature'       => 0,
     50                'spam'         => 0,
     51                'deleted'      => 0,
     52                'lang_id'      => 0,
     53        );
     54
     55        // Extract the passed arguments that may be relevant for site initialization.
     56        $args = array_diff_key( $data, $defaults );
     57        if ( isset( $args['site_id'] ) ) {
     58                unset( $args['site_id'] );
     59        }
     60
     61        $data = wp_prepare_site_data( $data, $defaults );
     62        if ( is_wp_error( $data ) ) {
     63                return $data;
     64        }
     65
     66        if ( false === $wpdb->insert( $wpdb->blogs, $data ) ) {
     67                return new WP_Error( 'db_insert_error', __( 'Could not insert site into the database.' ), $wpdb->last_error );
     68        }
     69
     70        $new_site = get_site( $wpdb->insert_id );
     71
     72        clean_blog_cache( $new_site );
     73
     74        /**
     75         * Fires once a site has been inserted into the database.
     76         *
     77         * @since 5.1.0
     78         *
     79         * @param WP_Site $new_site New site object.
     80         */
     81        do_action( 'wp_insert_site', $new_site );
     82
     83        /**
     84         * Fires when a site's initialization routine should be executed.
     85         *
     86         * @since 5.1.0
     87         *
     88         * @param WP_Site $new_site New site object.
     89         * @param array   $args     Arguments for the initialization.
     90         */
     91        do_action( 'wp_initialize_site', $new_site, $args );
     92
     93        // Only compute extra hook parameters if the deprecated hook is actually in use.
     94        if ( has_action( 'wpmu_new_blog' ) ) {
     95                $user_id = ! empty( $args['user_id'] ) ? $args['user_id'] : 0;
     96                $meta    = ! empty( $args['options'] ) ? $args['options'] : array();
     97
     98                /**
     99                 * Fires immediately after a new site is created.
     100                 *
     101                 * @since MU (3.0.0)
     102                 * @deprecated 5.1.0 Use wp_insert_site
     103                 *
     104                 * @param int    $site_id    Site ID.
     105                 * @param int    $user_id    User ID.
     106                 * @param string $domain     Site domain.
     107                 * @param string $path       Site path.
     108                 * @param int    $network_id Network ID. Only relevant on multi-network installations.
     109                 * @param array  $meta       Meta data. Used to set initial site options.
     110                 */
     111                do_action_deprecated( 'wpmu_new_blog', array( $new_site->id, $user_id, $new_site->domain, $new_site->path, $new_site->network_id, $meta ), '5.1.0', 'wp_insert_site' );
     112        }
     113
     114        return (int) $new_site->id;
     115}
     116
     117/**
     118 * Updates a site in the database.
     119 *
     120 * @since 5.1.0
     121 *
     122 * @global wpdb $wpdb WordPress database abstraction object.
     123 *
     124 * @param int   $site_id ID of the site that should be updated.
     125 * @param array $data    Site data to update. See {@see wp_insert_site()} for the list of supported keys.
     126 * @return int|WP_Error The updated site's ID on success, or error object on failure.
     127 */
     128function wp_update_site( $site_id, array $data ) {
     129        global $wpdb;
     130
     131        if ( empty( $site_id ) ) {
     132                return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) );
     133        }
     134
     135        $old_site = get_site( $site_id );
     136        if ( ! $old_site ) {
     137                return new WP_Error( 'site_not_exist', __( 'Site does not exist.' ) );
     138        }
     139
     140        $defaults                 = $old_site->to_array();
     141        $defaults['network_id']   = (int) $defaults['site_id'];
     142        $defaults['last_updated'] = current_time( 'mysql', true );
     143        unset( $defaults['blog_id'], $defaults['site_id'] );
     144
     145        $data = wp_prepare_site_data( $data, $defaults, $old_site );
     146        if ( is_wp_error( $data ) ) {
     147                return $data;
     148        }
     149
     150        if ( false === $wpdb->update( $wpdb->blogs, $data, array( 'blog_id' => $old_site->id ) ) ) {
     151                return new WP_Error( 'db_update_error', __( 'Could not update site in the database.' ), $wpdb->last_error );
     152        }
     153
     154        clean_blog_cache( $old_site );
     155
     156        $new_site = get_site( $old_site->id );
     157
     158        /**
     159         * Fires once a site has been updated in the database.
     160         *
     161         * @since 5.1.0
     162         *
     163         * @param WP_Site $new_site New site object.
     164         * @param WP_Site $old_site Old site object.
     165         */
     166        do_action( 'wp_update_site', $new_site, $old_site );
     167
     168        return (int) $new_site->id;
     169}
     170
     171/**
     172 * Deletes a site from the database.
     173 *
     174 * @since 5.1.0
     175 *
     176 * @global wpdb $wpdb WordPress database abstraction object.
     177 *
     178 * @param int $site_id ID of the site that should be deleted.
     179 * @return WP_Site|WP_Error The deleted site object on success, or error object on failure.
     180 */
     181function wp_delete_site( $site_id ) {
     182        global $wpdb;
     183
     184        if ( empty( $site_id ) ) {
     185                return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) );
     186        }
     187
     188        $old_site = get_site( $site_id );
     189        if ( ! $old_site ) {
     190                return new WP_Error( 'site_not_exist', __( 'Site does not exist.' ) );
     191        }
     192
     193        $errors = new WP_Error();
     194
     195        /**
     196         * Fires before a site should be deleted from the database.
     197         *
     198         * Plugins should amend the `$errors` object via its `WP_Error::add()` method. If any errors
     199         * are present, the site will not be deleted.
     200         *
     201         * @since 5.1.0
     202         *
     203         * @param WP_Error $errors   Error object to add validation errors to.
     204         * @param WP_Site  $old_site The site object to be deleted.
     205         */
     206        do_action( 'wp_validate_site_deletion', $errors, $old_site );
     207
     208        if ( ! empty( $errors->errors ) ) {
     209                return $errors;
     210        }
     211
     212        /**
     213         * Fires before a site is deleted.
     214         *
     215         * @since MU (3.0.0)
     216         * @deprecated 5.1.0
     217         *
     218         * @param int  $site_id The site ID.
     219         * @param bool $drop    True if site's table should be dropped. Default is false.
     220         */
     221        do_action_deprecated( 'delete_blog', array( $old_site->id, true ), '5.1.0' );
     222
     223        /**
     224         * Fires when a site's uninitialization routine should be executed.
     225         *
     226         * @since 5.1.0
     227         *
     228         * @param WP_Site $old_site Deleted site object.
     229         */
     230        do_action( 'wp_uninitialize_site', $old_site );
     231
     232        if ( is_site_meta_supported() ) {
     233                $blog_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->blogmeta WHERE blog_id = %d ", $old_site->id ) );
     234                foreach ( $blog_meta_ids as $mid ) {
     235                        delete_metadata_by_mid( 'blog', $mid );
     236                }
     237        }
     238
     239        if ( false === $wpdb->delete( $wpdb->blogs, array( 'blog_id' => $old_site->id ) ) ) {
     240                return new WP_Error( 'db_delete_error', __( 'Could not delete site from the database.' ), $wpdb->last_error );
     241        }
     242
     243        clean_blog_cache( $old_site );
     244
     245        /**
     246         * Fires once a site has been deleted from the database.
     247         *
     248         * @since 5.1.0
     249         *
     250         * @param WP_Site $old_site Deleted site object.
     251         */
     252        do_action( 'wp_delete_site', $old_site );
     253
     254        /**
     255         * Fires after the site is deleted from the network.
     256         *
     257         * @since 4.8.0
     258         * @deprecated 5.1.0
     259         *
     260         * @param int  $site_id The site ID.
     261         * @param bool $drop    True if site's tables should be dropped. Default is false.
     262         */
     263        do_action_deprecated( 'deleted_blog', array( $old_site->id, true ), '5.1.0' );
     264
     265        return $old_site;
     266}
     267
     268/**
     269 * Retrieves site data given a site ID or site object.
     270 *
     271 * Site data will be cached and returned after being passed through a filter.
     272 * If the provided site is empty, the current site global will be used.
     273 *
     274 * @since 4.6.0
     275 *
     276 * @param WP_Site|int|null $site Optional. Site to retrieve. Default is the current site.
     277 * @return WP_Site|null The site object or null if not found.
     278 */
     279function get_site( $site = null ) {
     280        if ( empty( $site ) ) {
     281                $site = get_current_blog_id();
     282        }
     283
     284        if ( $site instanceof WP_Site ) {
     285                $_site = $site;
     286        } elseif ( is_object( $site ) ) {
     287                $_site = new WP_Site( $site );
     288        } else {
     289                $_site = WP_Site::get_instance( $site );
     290        }
     291
     292        if ( ! $_site ) {
     293                return null;
     294        }
     295
     296        /**
     297         * Fires after a site is retrieved.
     298         *
     299         * @since 4.6.0
     300         *
     301         * @param WP_Site $_site Site data.
     302         */
     303        $_site = apply_filters( 'get_site', $_site );
     304
     305        return $_site;
     306}
     307
     308/**
     309 * Adds any sites from the given ids to the cache that do not already exist in cache.
     310 *
     311 * @since 4.6.0
     312 * @since 5.1.0 Introduced the `$update_meta_cache` parameter.
     313 * @access private
     314 *
     315 * @see update_site_cache()
     316 * @global wpdb $wpdb WordPress database abstraction object.
     317 *
     318 * @param array $ids               ID list.
     319 * @param bool  $update_meta_cache Optional. Whether to update the meta cache. Default true.
     320 */
     321function _prime_site_caches( $ids, $update_meta_cache = true ) {
     322        global $wpdb;
     323
     324        $non_cached_ids = _get_non_cached_ids( $ids, 'sites' );
     325        if ( ! empty( $non_cached_ids ) ) {
     326                $fresh_sites = $wpdb->get_results( sprintf( "SELECT * FROM $wpdb->blogs WHERE blog_id IN (%s)", join( ',', array_map( 'intval', $non_cached_ids ) ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
     327
     328                update_site_cache( $fresh_sites, $update_meta_cache );
     329        }
     330}
     331
     332/**
     333 * Updates sites in cache.
     334 *
     335 * @since 4.6.0
     336 * @since 5.1.0 Introduced the `$update_meta_cache` parameter.
     337 *
     338 * @param array $sites             Array of site objects.
     339 * @param bool  $update_meta_cache Whether to update site meta cache. Default true.
     340 */
     341function update_site_cache( $sites, $update_meta_cache = true ) {
     342        if ( ! $sites ) {
     343                return;
     344        }
     345        $site_ids = array();
     346        foreach ( $sites as $site ) {
     347                $site_ids[] = $site->blog_id;
     348                wp_cache_add( $site->blog_id, $site, 'sites' );
     349                wp_cache_add( $site->blog_id . 'short', $site, 'blog-details' );
     350        }
     351
     352        if ( $update_meta_cache ) {
     353                update_sitemeta_cache( $site_ids );
     354        }
     355}
     356
     357/**
     358 * Updates metadata cache for list of site IDs.
     359 *
     360 * Performs SQL query to retrieve all metadata for the sites matching `$site_ids` and stores them in the cache.
     361 * Subsequent calls to `get_site_meta()` will not need to query the database.
     362 *
     363 * @since 5.1.0
     364 *
     365 * @param array $site_ids List of site IDs.
     366 * @return array|false Returns false if there is nothing to update. Returns an array of metadata on success.
     367 */
     368function update_sitemeta_cache( $site_ids ) {
     369        return update_meta_cache( 'blog', $site_ids );
     370}
     371
     372/**
     373 * Retrieves a list of sites matching requested arguments.
     374 *
     375 * @since 4.6.0
     376 * @since 4.8.0 Introduced the 'lang_id', 'lang__in', and 'lang__not_in' parameters.
     377 *
     378 * @see WP_Site_Query::parse_query()
     379 *
     380 * @param string|array $args {
     381 *     Optional. Array or query string of site query parameters. Default empty.
     382 *
     383 *     @type array        $site__in          Array of site IDs to include. Default empty.
     384 *     @type array        $site__not_in      Array of site IDs to exclude. Default empty.
     385 *     @type bool         $count             Whether to return a site count (true) or array of site objects.
     386 *                                           Default false.
     387 *     @type array        $date_query        Date query clauses to limit sites by. See WP_Date_Query.
     388 *                                           Default null.
     389 *     @type string       $fields            Site fields to return. Accepts 'ids' (returns an array of site IDs)
     390 *                                           or empty (returns an array of complete site objects). Default empty.
     391 *     @type int          $ID                A site ID to only return that site. Default empty.
     392 *     @type int          $number            Maximum number of sites to retrieve. Default 100.
     393 *     @type int          $offset            Number of sites to offset the query. Used to build LIMIT clause.
     394 *                                           Default 0.
     395 *     @type bool         $no_found_rows     Whether to disable the `SQL_CALC_FOUND_ROWS` query. Default true.
     396 *     @type string|array $orderby           Site status or array of statuses. Accepts 'id', 'domain', 'path',
     397 *                                           'network_id', 'last_updated', 'registered', 'domain_length',
     398 *                                           'path_length', 'site__in' and 'network__in'. Also accepts false,
     399 *                                           an empty array, or 'none' to disable `ORDER BY` clause.
     400 *                                           Default 'id'.
     401 *     @type string       $order             How to order retrieved sites. Accepts 'ASC', 'DESC'. Default 'ASC'.
     402 *     @type int          $network_id        Limit results to those affiliated with a given network ID. If 0,
     403 *                                           include all networks. Default 0.
     404 *     @type array        $network__in       Array of network IDs to include affiliated sites for. Default empty.
     405 *     @type array        $network__not_in   Array of network IDs to exclude affiliated sites for. Default empty.
     406 *     @type string       $domain            Limit results to those affiliated with a given domain. Default empty.
     407 *     @type array        $domain__in        Array of domains to include affiliated sites for. Default empty.
     408 *     @type array        $domain__not_in    Array of domains to exclude affiliated sites for. Default empty.
     409 *     @type string       $path              Limit results to those affiliated with a given path. Default empty.
     410 *     @type array        $path__in          Array of paths to include affiliated sites for. Default empty.
     411 *     @type array        $path__not_in      Array of paths to exclude affiliated sites for. Default empty.
     412 *     @type int          $public            Limit results to public sites. Accepts '1' or '0'. Default empty.
     413 *     @type int          $archived          Limit results to archived sites. Accepts '1' or '0'. Default empty.
     414 *     @type int          $mature            Limit results to mature sites. Accepts '1' or '0'. Default empty.
     415 *     @type int          $spam              Limit results to spam sites. Accepts '1' or '0'. Default empty.
     416 *     @type int          $deleted           Limit results to deleted sites. Accepts '1' or '0'. Default empty.
     417 *     @type int          $lang_id           Limit results to a language ID. Default empty.
     418 *     @type array        $lang__in          Array of language IDs to include affiliated sites for. Default empty.
     419 *     @type array        $lang__not_in      Array of language IDs to exclude affiliated sites for. Default empty.
     420 *     @type string       $search            Search term(s) to retrieve matching sites for. Default empty.
     421 *     @type array        $search_columns    Array of column names to be searched. Accepts 'domain' and 'path'.
     422 *                                           Default empty array.
     423 *     @type bool         $update_site_cache Whether to prime the cache for found sites. Default true.
     424 * }
     425 * @return array|int List of WP_Site objects, a list of site ids when 'fields' is set to 'ids',
     426 *                   or the number of sites when 'count' is passed as a query var.
     427 */
     428function get_sites( $args = array() ) {
     429        $query = new WP_Site_Query();
     430
     431        return $query->query( $args );
     432}
     433
     434/**
     435 * Prepares site data for insertion or update in the database.
     436 *
     437 * @since 5.1.0
     438 *
     439 * @param array        $data     Associative array of site data passed to the respective function.
     440 *                               See {@see wp_insert_site()} for the possibly included data.
     441 * @param array        $defaults Site data defaults to parse $data against.
     442 * @param WP_Site|null $old_site Optional. Old site object if an update, or null if an insertion.
     443 *                               Default null.
     444 * @return array|WP_Error Site data ready for a database transaction, or WP_Error in case a validation
     445 *                        error occurred.
     446 */
     447function wp_prepare_site_data( $data, $defaults, $old_site = null ) {
     448
     449        // Maintain backward-compatibility with `$site_id` as network ID.
     450        if ( isset( $data['site_id'] ) ) {
     451                if ( ! empty( $data['site_id'] ) && empty( $data['network_id'] ) ) {
     452                        $data['network_id'] = $data['site_id'];
     453                }
     454                unset( $data['site_id'] );
     455        }
     456
     457        /**
     458         * Filters passed site data in order to normalize it.
     459         *
     460         * @since 5.1.0
     461         *
     462         * @param array $data Associative array of site data passed to the respective function.
     463         *                    See {@see wp_insert_site()} for the possibly included data.
     464         */
     465        $data = apply_filters( 'wp_normalize_site_data', $data );
     466
     467        $whitelist = array( 'domain', 'path', 'network_id', 'registered', 'last_updated', 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id' );
     468        $data      = array_intersect_key( wp_parse_args( $data, $defaults ), array_flip( $whitelist ) );
     469
     470        $errors = new WP_Error();
     471
     472        /**
     473         * Fires when data should be validated for a site prior to inserting or updating in the database.
     474         *
     475         * Plugins should amend the `$errors` object via its `WP_Error::add()` method.
     476         *
     477         * @since 5.1.0
     478         *
     479         * @param WP_Error     $errors   Error object to add validation errors to.
     480         * @param array        $data     Associative array of complete site data. See {@see wp_insert_site()}
     481         *                               for the included data.
     482         * @param WP_Site|null $old_site The old site object if the data belongs to a site being updated,
     483         *                               or null if it is a new site being inserted.
     484         */
     485        do_action( 'wp_validate_site_data', $errors, $data, $old_site );
     486
     487        if ( ! empty( $errors->errors ) ) {
     488                return $errors;
     489        }
     490
     491        // Prepare for database.
     492        $data['site_id'] = $data['network_id'];
     493        unset( $data['network_id'] );
     494
     495        return $data;
     496}
     497
     498/**
     499 * Normalizes data for a site prior to inserting or updating in the database.
     500 *
     501 * @since 5.1.0
     502 *
     503 * @param array $data Associative array of site data passed to the respective function.
     504 *                    See {@see wp_insert_site()} for the possibly included data.
     505 * @return array Normalized site data.
     506 */
     507function wp_normalize_site_data( $data ) {
     508        // Sanitize domain if passed.
     509        if ( array_key_exists( 'domain', $data ) ) {
     510                $data['domain'] = trim( $data['domain'] );
     511                $data['domain'] = preg_replace( '/\s+/', '', sanitize_user( $data['domain'], true ) );
     512                if ( is_subdomain_install() ) {
     513                        $data['domain'] = str_replace( '@', '', $data['domain'] );
     514                }
     515        }
     516
     517        // Sanitize path if passed.
     518        if ( array_key_exists( 'path', $data ) ) {
     519                $data['path'] = trailingslashit( '/' . trim( $data['path'], '/' ) );
     520        }
     521
     522        // Sanitize network ID if passed.
     523        if ( array_key_exists( 'network_id', $data ) ) {
     524                $data['network_id'] = (int) $data['network_id'];
     525        }
     526
     527        // Sanitize status fields if passed.
     528        $status_fields = array( 'public', 'archived', 'mature', 'spam', 'deleted' );
     529        foreach ( $status_fields as $status_field ) {
     530                if ( array_key_exists( $status_field, $data ) ) {
     531                        $data[ $status_field ] = (int) $data[ $status_field ];
     532                }
     533        }
     534
     535        // Strip date fields if empty.
     536        $date_fields = array( 'registered', 'last_updated' );
     537        foreach ( $date_fields as $date_field ) {
     538                if ( ! array_key_exists( $date_field, $data ) ) {
     539                        continue;
     540                }
     541
     542                if ( empty( $data[ $date_field ] ) || '0000-00-00 00:00:00' === $data[ $date_field ] ) {
     543                        unset( $data[ $date_field ] );
     544                }
     545        }
     546
     547        return $data;
     548}
     549
     550/**
     551 * Validates data for a site prior to inserting or updating in the database.
     552 *
     553 * @since 5.1.0
     554 *
     555 * @param WP_Error     $errors   Error object, passed by reference. Will contain validation errors if
     556 *                               any occurred.
     557 * @param array        $data     Associative array of complete site data. See {@see wp_insert_site()}
     558 *                               for the included data.
     559 * @param WP_Site|null $old_site The old site object if the data belongs to a site being updated,
     560 *                               or null if it is a new site being inserted.
     561 */
     562function wp_validate_site_data( $errors, $data, $old_site = null ) {
     563        // A domain must always be present.
     564        if ( empty( $data['domain'] ) ) {
     565                $errors->add( 'site_empty_domain', __( 'Site domain must not be empty.' ) );
     566        }
     567
     568        // A path must always be present.
     569        if ( empty( $data['path'] ) ) {
     570                $errors->add( 'site_empty_path', __( 'Site path must not be empty.' ) );
     571        }
     572
     573        // A network ID must always be present.
     574        if ( empty( $data['network_id'] ) ) {
     575                $errors->add( 'site_empty_network_id', __( 'Site network ID must be provided.' ) );
     576        }
     577
     578        // Both registration and last updated dates must always be present and valid.
     579        $date_fields = array( 'registered', 'last_updated' );
     580        foreach ( $date_fields as $date_field ) {
     581                if ( empty( $data[ $date_field ] ) ) {
     582                        $errors->add( 'site_empty_' . $date_field, __( 'Both registration and last updated dates must be provided.' ) );
     583                        break;
     584                }
     585
     586                // Allow '0000-00-00 00:00:00', although it be stripped out at this point.
     587                if ( '0000-00-00 00:00:00' !== $data[ $date_field ] ) {
     588                        $month      = substr( $data[ $date_field ], 5, 2 );
     589                        $day        = substr( $data[ $date_field ], 8, 2 );
     590                        $year       = substr( $data[ $date_field ], 0, 4 );
     591                        $valid_date = wp_checkdate( $month, $day, $year, $data[ $date_field ] );
     592                        if ( ! $valid_date ) {
     593                                $errors->add( 'site_invalid_' . $date_field, __( 'Both registration and last updated dates must be valid dates.' ) );
     594                                break;
     595                        }
     596                }
     597        }
     598
     599        if ( ! empty( $errors->errors ) ) {
     600                return;
     601        }
     602
     603        // If a new site, or domain/path/network ID have changed, ensure uniqueness.
     604        if ( ! $old_site
     605                || $data['domain'] !== $old_site->domain
     606                || $data['path'] !== $old_site->path
     607                || $data['network_id'] !== $old_site->network_id
     608        ) {
     609                if ( domain_exists( $data['domain'], $data['path'], $data['network_id'] ) ) {
     610                        $errors->add( 'site_taken', __( 'Sorry, that site already exists!' ) );
     611                }
     612        }
     613}
     614
     615/**
     616 * Runs the initialization routine for a given site.
     617 *
     618 * This process includes creating the site's database tables and
     619 * populating them with defaults.
     620 *
     621 * @since 5.1.0
     622 *
     623 * @global wpdb     $wpdb     WordPress database abstraction object.
     624 * @global WP_Roles $wp_roles WordPress role management object.
     625 *
     626 * @param int|WP_Site $site_id Site ID or object.
     627 * @param array       $args    {
     628 *     Optional. Arguments to modify the initialization behavior.
     629 *
     630 *     @type int    $user_id Required. User ID for the site administrator.
     631 *     @type string $title   Site title. Default is 'Site %d' where %d is the
     632 *                           site ID.
     633 *     @type array  $options Custom option $key => $value pairs to use. Default
     634 *                           empty array.
     635 *     @type array  $meta    Custom site metadata $key => $value pairs to use.
     636 *                           Default empty array.
     637 * }
     638 * @return bool|WP_Error True on success, or error object on failure.
     639 */
     640function wp_initialize_site( $site_id, array $args = array() ) {
     641        global $wpdb, $wp_roles;
     642
     643        if ( empty( $site_id ) ) {
     644                return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) );
     645        }
     646
     647        $site = get_site( $site_id );
     648        if ( ! $site ) {
     649                return new WP_Error( 'site_invalid_id', __( 'Site with the ID does not exist.' ) );
     650        }
     651
     652        if ( wp_is_site_initialized( $site ) ) {
     653                return new WP_Error( 'site_already_initialized', __( 'The site appears to be already initialized.' ) );
     654        }
     655
     656        $network = get_network( $site->network_id );
     657        if ( ! $network ) {
     658                $network = get_network();
     659        }
     660
     661        $args = wp_parse_args(
     662                $args,
     663                array(
     664                        'user_id' => 0,
     665                        /* translators: %d: site ID */
     666                        'title'   => sprintf( __( 'Site %d' ), $site->id ),
     667                        'options' => array(),
     668                        'meta'    => array(),
     669                )
     670        );
     671
     672        /**
     673         * Filters the arguments for initializing a site.
     674         *
     675         * @since 5.1.0
     676         *
     677         * @param array      $args    Arguments to modify the initialization behavior.
     678         * @param WP_Site    $site    Site that is being initialized.
     679         * @param WP_Network $network Network that the site belongs to.
     680         */
     681        $args = apply_filters( 'wp_initialize_site_args', $args, $site, $network );
     682
     683        $orig_installing = wp_installing();
     684        if ( ! $orig_installing ) {
     685                wp_installing( true );
     686        }
     687
     688        $switch = false;
     689        if ( get_current_blog_id() !== $site->id ) {
     690                $switch = true;
     691                switch_to_blog( $site->id );
     692        }
     693
     694        require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
     695
     696        // Set up the database tables.
     697        make_db_current_silent( 'blog' );
     698
     699        $home_scheme    = 'http';
     700        $siteurl_scheme = 'http';
     701        if ( ! is_subdomain_install() ) {
     702                if ( 'https' === parse_url( get_home_url( $network->site_id ), PHP_URL_SCHEME ) ) {
     703                        $home_scheme = 'https';
     704                }
     705                if ( 'https' === parse_url( get_network_option( $network->id, 'siteurl' ), PHP_URL_SCHEME ) ) {
     706                        $siteurl_scheme = 'https';
     707                }
     708        }
     709
     710        // Populate the site's options.
     711        populate_options(
     712                array_merge(
     713                        array(
     714                                'home'        => untrailingslashit( $home_scheme . '://' . $site->domain . $site->path ),
     715                                'siteurl'     => untrailingslashit( $siteurl_scheme . '://' . $site->domain . $site->path ),
     716                                'blogname'    => wp_unslash( $args['title'] ),
     717                                'admin_email' => '',
     718                                'upload_path' => get_network_option( $network->id, 'ms_files_rewriting' ) ? UPLOADBLOGSDIR . "/{$site->id}/files" : get_blog_option( $network->site_id, 'upload_path' ),
     719                                'blog_public' => (int) $site->public,
     720                                'WPLANG'      => get_network_option( $network->id, 'WPLANG' ),
     721                        ),
     722                        $args['options']
     723                )
     724        );
     725
     726        // Populate the site's roles.
     727        populate_roles();
     728        $wp_roles = new WP_Roles();
     729
     730        // Populate metadata for the site.
     731        populate_site_meta( $site->id, $args['meta'] );
     732
     733        // Remove all permissions that may exist for the site.
     734        $table_prefix = $wpdb->get_blog_prefix();
     735        delete_metadata( 'user', 0, $table_prefix . 'user_level', null, true ); // delete all
     736        delete_metadata( 'user', 0, $table_prefix . 'capabilities', null, true ); // delete all
     737
     738        // Install default site content.
     739        wp_install_defaults( $args['user_id'] );
     740
     741        // Set the site administrator.
     742        add_user_to_blog( $site->id, $args['user_id'], 'administrator' );
     743        if ( ! user_can( $args['user_id'], 'manage_network' ) && ! get_user_meta( $args['user_id'], 'primary_blog', true ) ) {
     744                update_user_meta( $args['user_id'], 'primary_blog', $site->id );
     745        }
     746
     747        if ( $switch ) {
     748                restore_current_blog();
     749        }
     750
     751        wp_installing( $orig_installing );
     752
     753        return true;
     754}
     755
     756/**
     757 * Runs the uninitialization routine for a given site.
     758 *
     759 * This process includes dropping the site's database tables and deleting its uploads directory.
     760 *
     761 * @since 5.1.0
     762 *
     763 * @global wpdb $wpdb WordPress database abstraction object.
     764 *
     765 * @param int|WP_Site $site_id Site ID or object.
     766 * @return bool|WP_Error True on success, or error object on failure.
     767 */
     768function wp_uninitialize_site( $site_id ) {
     769        global $wpdb;
     770
     771        if ( empty( $site_id ) ) {
     772                return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) );
     773        }
     774
     775        $site = get_site( $site_id );
     776        if ( ! $site ) {
     777                return new WP_Error( 'site_invalid_id', __( 'Site with the ID does not exist.' ) );
     778        }
     779
     780        if ( ! wp_is_site_initialized( $site ) ) {
     781                return new WP_Error( 'site_already_uninitialized', __( 'The site appears to be already uninitialized.' ) );
     782        }
     783
     784        $users = get_users(
     785                array(
     786                        'blog_id' => $site->id,
     787                        'fields'  => 'ids',
     788                )
     789        );
     790
     791        // Remove users from the site.
     792        if ( ! empty( $users ) ) {
     793                foreach ( $users as $user_id ) {
     794                        remove_user_from_blog( $user_id, $site->id );
     795                }
     796        }
     797
     798        $switch = false;
     799        if ( get_current_blog_id() !== $site->id ) {
     800                $switch = true;
     801                switch_to_blog( $site->id );
     802        }
     803
     804        $uploads = wp_get_upload_dir();
     805
     806        $tables = $wpdb->tables( 'blog' );
     807
     808        /**
     809         * Filters the tables to drop when the site is deleted.
     810         *
     811         * @since MU (3.0.0)
     812         *
     813         * @param string[] $tables  Array of names of the site tables to be dropped.
     814         * @param int      $site_id The ID of the site to drop tables for.
     815         */
     816        $drop_tables = apply_filters( 'wpmu_drop_tables', $tables, $site->id );
     817
     818        foreach ( (array) $drop_tables as $table ) {
     819                $wpdb->query( "DROP TABLE IF EXISTS `$table`" ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
     820        }
     821
     822        /**
     823         * Filters the upload base directory to delete when the site is deleted.
     824         *
     825         * @since MU (3.0.0)
     826         *
     827         * @param string $uploads['basedir'] Uploads path without subdirectory. @see wp_upload_dir()
     828         * @param int    $site_id            The site ID.
     829         */
     830        $dir     = apply_filters( 'wpmu_delete_blog_upload_dir', $uploads['basedir'], $site->id );
     831        $dir     = rtrim( $dir, DIRECTORY_SEPARATOR );
     832        $top_dir = $dir;
     833        $stack   = array( $dir );
     834        $index   = 0;
     835
     836        while ( $index < count( $stack ) ) {
     837                // Get indexed directory from stack
     838                $dir = $stack[ $index ];
     839
     840                // phpcs:disable WordPress.PHP.NoSilencedErrors.Discouraged
     841                $dh = @opendir( $dir );
     842                if ( $dh ) {
     843                        $file = @readdir( $dh );
     844                        while ( false !== $file ) {
     845                                if ( '.' === $file || '..' === $file ) {
     846                                        $file = @readdir( $dh );
     847                                        continue;
     848                                }
     849
     850                                if ( @is_dir( $dir . DIRECTORY_SEPARATOR . $file ) ) {
     851                                        $stack[] = $dir . DIRECTORY_SEPARATOR . $file;
     852                                } elseif ( @is_file( $dir . DIRECTORY_SEPARATOR . $file ) ) {
     853                                        @unlink( $dir . DIRECTORY_SEPARATOR . $file );
     854                                }
     855
     856                                $file = @readdir( $dh );
     857                        }
     858                        @closedir( $dh );
     859                }
     860                $index++;
     861        }
     862
     863        $stack = array_reverse( $stack ); // Last added dirs are deepest
     864        foreach ( (array) $stack as $dir ) {
     865                if ( $dir != $top_dir ) {
     866                        @rmdir( $dir );
     867                }
     868        }
     869
     870        // phpcs:enable WordPress.PHP.NoSilencedErrors.Discouraged
     871        if ( $switch ) {
     872                restore_current_blog();
     873        }
     874
     875        return true;
     876}
     877
     878/**
     879 * Checks whether a site is initialized.
     880 *
     881 * A site is considered initialized when its database tables are present.
     882 *
     883 * @since 5.1.0
     884 *
     885 * @global wpdb $wpdb WordPress database abstraction object.
     886 *
     887 * @param int|WP_Site $site_id Site ID or object.
     888 * @return bool True if the site is initialized, false otherwise.
     889 */
     890function wp_is_site_initialized( $site_id ) {
     891        global $wpdb;
     892
     893        if ( is_object( $site_id ) ) {
     894                $site_id = $site_id->blog_id;
     895        }
     896        $site_id = (int) $site_id;
     897
     898        /**
     899         * Filters the check for whether a site is initialized before the database is accessed.
     900         *
     901         * Returning a non-null value will effectively short-circuit the function, returning
     902         * that value instead.
     903         *
     904         * @since 5.1.0
     905         *
     906         * @param bool|null $pre     The value to return, if not null.
     907         * @param int       $site_id The site ID that is being checked.
     908         */
     909        $pre = apply_filters( 'pre_wp_is_site_initialized', null, $site_id );
     910        if ( null !== $pre ) {
     911                return (bool) $pre;
     912        }
     913
     914        $switch = false;
     915        if ( get_current_blog_id() !== $site_id ) {
     916                $switch = true;
     917                remove_action( 'switch_blog', 'wp_switch_roles_and_user', 1 );
     918                switch_to_blog( $site_id );
     919        }
     920
     921        $suppress = $wpdb->suppress_errors();
     922        $result   = (bool) $wpdb->get_results( "DESCRIBE {$wpdb->posts}" );
     923        $wpdb->suppress_errors( $suppress );
     924
     925        if ( $switch ) {
     926                restore_current_blog();
     927                add_action( 'switch_blog', 'wp_switch_roles_and_user', 1, 2 );
     928        }
     929
     930        return $result;
     931}
     932
     933/**
     934 * Clean the blog cache
     935 *
     936 * @since 3.5.0
     937 *
     938 * @global bool $_wp_suspend_cache_invalidation
     939 *
     940 * @param WP_Site|int $blog The site object or ID to be cleared from cache.
     941 */
     942function clean_blog_cache( $blog ) {
     943        global $_wp_suspend_cache_invalidation;
     944
     945        if ( ! empty( $_wp_suspend_cache_invalidation ) ) {
     946                return;
     947        }
     948
     949        if ( empty( $blog ) ) {
     950                return;
     951        }
     952
     953        $blog_id = $blog;
     954        $blog    = get_site( $blog_id );
     955        if ( ! $blog ) {
     956                if ( ! is_numeric( $blog_id ) ) {
     957                        return;
     958                }
     959
     960                // Make sure a WP_Site object exists even when the site has been deleted.
     961                $blog = new WP_Site(
     962                        (object) array(
     963                                'blog_id' => $blog_id,
     964                                'domain'  => null,
     965                                'path'    => null,
     966                        )
     967                );
     968        }
     969
     970        $blog_id         = $blog->blog_id;
     971        $domain_path_key = md5( $blog->domain . $blog->path );
     972
     973        wp_cache_delete( $blog_id, 'sites' );
     974        wp_cache_delete( $blog_id, 'site-details' );
     975        wp_cache_delete( $blog_id, 'blog-details' );
     976        wp_cache_delete( $blog_id . 'short', 'blog-details' );
     977        wp_cache_delete( $domain_path_key, 'blog-lookup' );
     978        wp_cache_delete( $domain_path_key, 'blog-id-cache' );
     979        wp_cache_delete( 'current_blog_' . $blog->domain, 'site-options' );
     980        wp_cache_delete( 'current_blog_' . $blog->domain . $blog->path, 'site-options' );
     981        wp_cache_delete( $blog_id, 'blog_meta' );
     982
     983        /**
     984         * Fires immediately after a site has been removed from the object cache.
     985         *
     986         * @since 4.6.0
     987         *
     988         * @param int     $id              Blog ID.
     989         * @param WP_Site $blog            Site object.
     990         * @param string  $domain_path_key md5 hash of domain and path.
     991         */
     992        do_action( 'clean_site_cache', $blog_id, $blog, $domain_path_key );
     993
     994        wp_cache_set( 'last_changed', microtime(), 'sites' );
     995
     996        /**
     997         * Fires after the blog details cache is cleared.
     998         *
     999         * @since 3.4.0
     1000         * @deprecated 4.9.0 Use clean_site_cache
     1001         *
     1002         * @param int $blog_id Blog ID.
     1003         */
     1004        do_action_deprecated( 'refresh_blog_details', array( $blog_id ), '4.9.0', 'clean_site_cache' );
     1005}
     1006
     1007/**
     1008 * Adds metadata to a site.
     1009 *
     1010 * @since 5.1.0
     1011 *
     1012 * @param int    $site_id    Site ID.
     1013 * @param string $meta_key   Metadata name.
     1014 * @param mixed  $meta_value Metadata value. Must be serializable if non-scalar.
     1015 * @param bool   $unique     Optional. Whether the same key should not be added.
     1016 *                           Default false.
     1017 * @return int|false Meta ID on success, false on failure.
     1018 */
     1019function add_site_meta( $site_id, $meta_key, $meta_value, $unique = false ) {
     1020        return add_metadata( 'blog', $site_id, $meta_key, $meta_value, $unique );
     1021}
     1022
     1023/**
     1024 * Removes metadata matching criteria from a site.
     1025 *
     1026 * You can match based on the key, or key and value. Removing based on key and
     1027 * value, will keep from removing duplicate metadata with the same key. It also
     1028 * allows removing all metadata matching key, if needed.
     1029 *
     1030 * @since 5.1.0
     1031 *
     1032 * @param int    $site_id    Site ID.
     1033 * @param string $meta_key   Metadata name.
     1034 * @param mixed  $meta_value Optional. Metadata value. Must be serializable if
     1035 *                           non-scalar. Default empty.
     1036 * @return bool True on success, false on failure.
     1037 */
     1038function delete_site_meta( $site_id, $meta_key, $meta_value = '' ) {
     1039        return delete_metadata( 'blog', $site_id, $meta_key, $meta_value );
     1040}
     1041
     1042/**
     1043 * Retrieves metadata for a site.
     1044 *
     1045 * @since 5.1.0
     1046 *
     1047 * @param int    $site_id Site ID.
     1048 * @param string $key     Optional. The meta key to retrieve. By default, returns
     1049 *                        data for all keys. Default empty.
     1050 * @param bool   $single  Optional. Whether to return a single value. Default false.
     1051 * @return mixed Will be an array if $single is false. Will be value of meta data
     1052 *               field if $single is true.
     1053 */
     1054function get_site_meta( $site_id, $key = '', $single = false ) {
     1055        return get_metadata( 'blog', $site_id, $key, $single );
     1056}
     1057
     1058/**
     1059 * Updates metadata for a site.
     1060 *
     1061 * Use the $prev_value parameter to differentiate between meta fields with the
     1062 * same key and site ID.
     1063 *
     1064 * If the meta field for the site does not exist, it will be added.
     1065 *
     1066 * @since 5.1.0
     1067 *
     1068 * @param int    $site_id    Site ID.
     1069 * @param string $meta_key   Metadata key.
     1070 * @param mixed  $meta_value Metadata value. Must be serializable if non-scalar.
     1071 * @param mixed  $prev_value Optional. Previous value to check before removing.
     1072 *                           Default empty.
     1073 * @return int|bool Meta ID if the key didn't exist, true on successful update,
     1074 *                  false on failure.
     1075 */
     1076function update_site_meta( $site_id, $meta_key, $meta_value, $prev_value = '' ) {
     1077        return update_metadata( 'blog', $site_id, $meta_key, $meta_value, $prev_value );
     1078}
     1079
     1080/**
     1081 * Deletes everything from site meta matching meta key.
     1082 *
     1083 * @since 5.1.0
     1084 *
     1085 * @param string $meta_key Metadata key to search for when deleting.
     1086 * @return bool Whether the site meta key was deleted from the database.
     1087 */
     1088function delete_site_meta_by_key( $meta_key ) {
     1089        return delete_metadata( 'blog', null, $meta_key, '', true );
     1090}
     1091
     1092/**
     1093 * Updates the count of sites for a network based on a changed site.
     1094 *
     1095 * @since 5.1.0
     1096 *
     1097 * @param WP_Site      $new_site The site object that has been inserted, updated or deleted.
     1098 * @param WP_Site|null $old_site Optional. If $new_site has been updated, this must be the previous
     1099 *                               state of that site. Default null.
     1100 */
     1101function wp_maybe_update_network_site_counts_on_update( $new_site, $old_site = null ) {
     1102        if ( null === $old_site ) {
     1103                wp_maybe_update_network_site_counts( $new_site->network_id );
     1104                return;
     1105        }
     1106
     1107        if ( $new_site->network_id != $old_site->network_id ) {
     1108                wp_maybe_update_network_site_counts( $new_site->network_id );
     1109                wp_maybe_update_network_site_counts( $old_site->network_id );
     1110        }
     1111}
     1112
     1113/**
     1114 * Triggers actions on site status updates.
     1115 *
     1116 * @since 5.1.0
     1117 *
     1118 * @param WP_Site      $new_site The site object after the update.
     1119 * @param WP_Site|null $old_site Optional. If $new_site has been updated, this must be the previous
     1120 *                               state of that site. Default null.
     1121 */
     1122function wp_maybe_transition_site_statuses_on_update( $new_site, $old_site = null ) {
     1123        $site_id = $new_site->id;
     1124
     1125        // Use the default values for a site if no previous state is given.
     1126        if ( ! $old_site ) {
     1127                $old_site = new WP_Site( new stdClass() );
     1128        }
     1129
     1130        if ( $new_site->spam != $old_site->spam ) {
     1131                if ( 1 == $new_site->spam ) {
     1132
     1133                        /**
     1134                         * Fires when the 'spam' status is added to a site.
     1135                         *
     1136                         * @since MU (3.0.0)
     1137                         *
     1138                         * @param int $site_id Site ID.
     1139                         */
     1140                        do_action( 'make_spam_blog', $site_id );
     1141                } else {
     1142
     1143                        /**
     1144                         * Fires when the 'spam' status is removed from a site.
     1145                         *
     1146                         * @since MU (3.0.0)
     1147                         *
     1148                         * @param int $site_id Site ID.
     1149                         */
     1150                        do_action( 'make_ham_blog', $site_id );
     1151                }
     1152        }
     1153
     1154        if ( $new_site->mature != $old_site->mature ) {
     1155                if ( 1 == $new_site->mature ) {
     1156
     1157                        /**
     1158                         * Fires when the 'mature' status is added to a site.
     1159                         *
     1160                         * @since 3.1.0
     1161                         *
     1162                         * @param int $site_id Site ID.
     1163                         */
     1164                        do_action( 'mature_blog', $site_id );
     1165                } else {
     1166
     1167                        /**
     1168                         * Fires when the 'mature' status is removed from a site.
     1169                         *
     1170                         * @since 3.1.0
     1171                         *
     1172                         * @param int $site_id Site ID.
     1173                         */
     1174                        do_action( 'unmature_blog', $site_id );
     1175                }
     1176        }
     1177
     1178        if ( $new_site->archived != $old_site->archived ) {
     1179                if ( 1 == $new_site->archived ) {
     1180
     1181                        /**
     1182                         * Fires when the 'archived' status is added to a site.
     1183                         *
     1184                         * @since MU (3.0.0)
     1185                         *
     1186                         * @param int $site_id Site ID.
     1187                         */
     1188                        do_action( 'archive_blog', $site_id );
     1189                } else {
     1190
     1191                        /**
     1192                         * Fires when the 'archived' status is removed from a site.
     1193                         *
     1194                         * @since MU (3.0.0)
     1195                         *
     1196                         * @param int $site_id Site ID.
     1197                         */
     1198                        do_action( 'unarchive_blog', $site_id );
     1199                }
     1200        }
     1201
     1202        if ( $new_site->deleted != $old_site->deleted ) {
     1203                if ( 1 == $new_site->deleted ) {
     1204
     1205                        /**
     1206                         * Fires when the 'deleted' status is added to a site.
     1207                         *
     1208                         * @since 3.5.0
     1209                         *
     1210                         * @param int $site_id Site ID.
     1211                         */
     1212                        do_action( 'make_delete_blog', $site_id );
     1213                } else {
     1214
     1215                        /**
     1216                         * Fires when the 'deleted' status is removed from a site.
     1217                         *
     1218                         * @since 3.5.0
     1219                         *
     1220                         * @param int $site_id Site ID.
     1221                         */
     1222                        do_action( 'make_undelete_blog', $site_id );
     1223                }
     1224        }
     1225
     1226        if ( $new_site->public != $old_site->public ) {
     1227
     1228                /**
     1229                 * Fires after the current blog's 'public' setting is updated.
     1230                 *
     1231                 * @since MU (3.0.0)
     1232                 *
     1233                 * @param int    $site_id Site ID.
     1234                 * @param string $value   The value of the site status.
     1235                 */
     1236                do_action( 'update_blog_public', $site_id, $new_site->public );
     1237        }
     1238}
     1239
     1240/**
     1241 * Cleans the necessary caches after specific site data has been updated.
     1242 *
     1243 * @since 5.1.0
     1244 *
     1245 * @param WP_Site $new_site The site object after the update.
     1246 * @param WP_Site $old_site The site obejct prior to the update.
     1247 */
     1248function wp_maybe_clean_new_site_cache_on_update( $new_site, $old_site ) {
     1249        if ( $old_site->domain !== $new_site->domain || $old_site->path !== $new_site->path ) {
     1250                clean_blog_cache( $new_site );
     1251        }
     1252}
     1253
     1254/**
     1255 * Updates the `blog_public` option for a given site ID.
     1256 *
     1257 * @since 5.1.0
     1258 *
     1259 * @param int    $site_id Site ID.
     1260 * @param string $public  The value of the site status.
     1261 */
     1262function wp_update_blog_public_option_on_site_update( $site_id, $public ) {
     1263
     1264        // Bail if the site's database tables do not exist (yet).
     1265        if ( ! wp_is_site_initialized( $site_id ) ) {
     1266                return;
     1267        }
     1268
     1269        update_blog_option( $site_id, 'blog_public', $public );
     1270}
     1271
     1272/**
     1273 * Sets the last changed time for the 'sites' cache group.
     1274 *
     1275 * @since 5.1.0
     1276 */
     1277function wp_cache_set_sites_last_changed() {
     1278        wp_cache_set( 'last_changed', microtime(), 'sites' );
     1279}
     1280
     1281/**
     1282 * Aborts calls to site meta if it is not supported.
     1283 *
     1284 * @since 5.1.0
     1285 *
     1286 * @global wpdb $wpdb WordPress database abstraction object.
     1287 *
     1288 * @param mixed $check Skip-value for whether to proceed site meta function execution.
     1289 * @return mixed Original value of $check, or false if site meta is not supported.
     1290 */
     1291function wp_check_site_meta_support_prefilter( $check ) {
     1292        if ( ! is_site_meta_supported() ) {
     1293                /* translators: %s: database table name */
     1294                _doing_it_wrong( __FUNCTION__, sprintf( __( 'The %s table is not installed. Please run the network database upgrade.' ), $GLOBALS['wpdb']->blogmeta ), '5.1.0' );
     1295                return false;
     1296        }
     1297
     1298        return $check;
     1299}