WordPress.org

Make WordPress Core

Ticket #31985: 31985.4.diff

File 31985.4.diff, 21.4 KB (added by jeremyfelt, 5 years ago)

Another riff

  • src/wp-includes/class-wp-network.php

     
     1<?php
     2/**
     3 * WordPress Network class.
     4 *
     5 * This class contains the values and functional methods for interacting with
     6 * a network of WordPress sites. Traditionally used for multisite, this class
     7 * is used by the $current_site global to setup the current network.
     8 *
     9 * This class is most useful in WordPress multi-network installations where the
     10 * ability to interact with any network of sites is required.
     11 *
     12 * @since 4.4.0
     13 * @package WordPress
     14 */
     15class WP_Network {
     16
     17        /**
     18         * Network ID.
     19         *
     20         * @since 4.4.0
     21         *
     22         * @var int
     23         */
     24        public $id;
     25
     26        /**
     27         * Domain of the network.
     28         *
     29         * @since 4.4.0
     30         *
     31         * @var string
     32         */
     33        public $domain = '';
     34
     35        /**
     36         * Path of the network.
     37         *
     38         * @since 4.4.0
     39         *
     40         * @var string
     41         */
     42        public $path = '';
     43
     44        /**
     45         * The ID of the network's main site.
     46         *
     47         * Named "blog" vs. "site" for legacy reasons. A main site is mapped to
     48         * the network when the network is created.
     49         *
     50         * @since 4.4.0
     51         *
     52         * @var int
     53         */
     54        public $blog_id = 0;
     55
     56        /**
     57         * Domain used to set cookies for this network.
     58         *
     59         * @since 4.4.0
     60         *
     61         * @var int
     62         */
     63        public $cookie_domain = '';
     64
     65        /**
     66         * Name of this network.
     67         *
     68         * Named "site" vs. "network" for legacy reasons.
     69         *
     70         * @since 4.4.0
     71         *
     72         * @var string
     73         */
     74        public $site_name = '';
     75
     76        /**
     77         * Retrieve a network from the database by its ID.
     78         *
     79         * @since 4.4.0
     80         *
     81         * @global wpdb $wpdb
     82         *
     83         * @param int $network_id The ID of the network to retrieve.
     84         *
     85         * @return WP_Network|bool The network's object if found. False if not.
     86         */
     87        public static function get_instance( $network_id ) {
     88                global $wpdb;
     89
     90                $network = wp_cache_get( $network_id, 'networks' );
     91
     92                if ( false === $network ) {
     93                        $network = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->site} WHERE id = %d LIMIT 1", $network_id ) );
     94
     95                        if ( empty( $network ) || is_wp_error( $network ) ) {
     96                                return false;
     97                        }
     98
     99                        wp_cache_add( $network_id, $network, 'networks' );
     100                }
     101
     102                return new WP_Network( $network );
     103        }
     104
     105        /**
     106         * Create a new WP_Network object.
     107         *
     108         * Will populate object properties from the object provided and assign other
     109         * default properties based on that information.
     110         *
     111         * @since 4.4.0
     112         *
     113         * @param WP_Network|object $network A network object.
     114         */
     115        public function __construct( $network ) {
     116                foreach( get_object_vars( $network ) as $key => $value ) {
     117                        $this->$key = $value;
     118                }
     119
     120                self::load_core_options( $this->id );
     121                $this->set_cookie_domain();
     122                $this->set_site_name();
     123        }
     124
     125        /**
     126         * Set the cookie domain based on the network domain.
     127         *
     128         * @todo What if the domain of the network doesn't match the current site?
     129         *
     130         * @since 4.4.0
     131         */
     132        private function set_cookie_domain() {
     133                // The cookie domain may already be set during sunrise.
     134                if ( ! empty( $this->cookie_domain ) ) {
     135                        return;
     136                }
     137
     138                $this->cookie_domain = $this->domain;
     139                if ( 'www.' === substr( $this->cookie_domain, 0, 4 ) ) {
     140                        $this->cookie_domain = substr( $this->cookie_domain, 4 );
     141                }
     142        }
     143
     144        /**
     145         * Set the name for this network's object.
     146         *
     147         * @since 4.4.0
     148         */
     149        private function set_site_name() {
     150                // The site name may already be set during sunrise.
     151                if ( ! empty( $this->site_name ) ) {
     152                        return;
     153                }
     154
     155                $this->site_name = get_site_option( 'site_name' );
     156                if ( empty( $this->site_name ) ) {
     157                        $this->site_name = ucfirst( $this->domain );
     158                }
     159        }
     160
     161        /**
     162         * Retrieve a network by its domain and path.
     163         *
     164         * @since 4.4.0
     165         *
     166         * @param string   $domain   Domain to check.
     167         * @param string   $path     Path to check.
     168         * @param int|null $segments Path segments to use. Defaults to null, or the full path.
     169         *
     170         * @return WP_Network|bool Network object if successful. False when no network is found.
     171         */
     172        public static function get_by_path( $domain = '', $path = '', $segments = null ) {
     173                global $wpdb;
     174
     175                $domains = array( $domain );
     176                $pieces  = explode( '.', $domain );
     177
     178                /*
     179                 * It's possible one domain to search is 'com', but it might as well
     180                 * be 'localhost' or some other locally mapped domain.
     181                 */
     182                while ( array_shift( $pieces ) ) {
     183                        if ( ! empty( $pieces ) ) {
     184                                $domains[] = implode( '.', $pieces );
     185                        }
     186                }
     187
     188                /*
     189                 * If we've gotten to this function during normal execution, there is
     190                 * more than one network installed. At this point, who knows how many
     191                 * we have. Attempt to optimize for the situation where networks are
     192                 * only domains, thus meaning paths never need to be considered.
     193                 *
     194                 * This is a very basic optimization; anything further could have
     195                 * drawbacks depending on the setup, so this is best done per-install.
     196                 */
     197                $using_paths = true;
     198                if ( wp_using_ext_object_cache() ) {
     199                        $using_paths = wp_cache_get( 'networks_have_paths', 'site-options' );
     200                        if ( false === $using_paths ) {
     201                                $using_paths = (bool) $wpdb->get_var( "SELECT id FROM {$wpdb->site} WHERE path <> '/' LIMIT 1" );
     202                                wp_cache_add( 'networks_have_paths', (int) $using_paths, 'site-options'  );
     203                        }
     204                }
     205
     206                $paths = array();
     207                if ( true === $using_paths ) {
     208                        $path_segments = array_filter( explode( '/', trim( $path, '/' ) ) );
     209
     210                        /**
     211                         * Filter the number of path segments to consider when searching for a site.
     212                         *
     213                         * @since 3.9.0
     214                         *
     215                         * @param int|null $segments The number of path segments to consider. WordPress by default looks at
     216                         *                           one path segment. The function default of null only makes sense when you
     217                         *                           know the requested path should match a network.
     218                         * @param string   $domain   The requested domain.
     219                         * @param string   $path     The requested path, in full.
     220                         */
     221                        $segments = apply_filters( 'network_by_path_segments_count', $segments, $domain, $path );
     222
     223                        if ( ( null !== $segments ) && count( $path_segments ) > $segments ) {
     224                                $path_segments = array_slice( $path_segments, 0, $segments );
     225                        }
     226
     227                        while ( count( $path_segments ) ) {
     228                                $paths[] = '/' . implode( '/', $path_segments ) . '/';
     229                                array_pop( $path_segments );
     230                        }
     231
     232                        $paths[] = '/';
     233                }
     234
     235                /**
     236                 * Determine a network by its domain and path.
     237                 *
     238                 * This allows one to short-circuit the default logic, perhaps by
     239                 * replacing it with a routine that is more optimal for your setup.
     240                 *
     241                 * Return null to avoid the short-circuit. Return false if no network
     242                 * can be found at the requested domain and path. Otherwise, return
     243                 * an object from wp_get_network().
     244                 *
     245                 * @since 3.9.0
     246                 *
     247                 * @param null|bool|object $network  Network value to return by path.
     248                 * @param string           $domain   The requested domain.
     249                 * @param string           $path     The requested path, in full.
     250                 * @param int|null         $segments The suggested number of paths to consult.
     251                 *                                   Default null, meaning the entire path was to be consulted.
     252                 * @param array            $paths    The paths to search for, based on $path and $segments.
     253                 */
     254                $pre = apply_filters( 'pre_get_network_by_path', null, $domain, $path, $segments, $paths );
     255                if ( null !== $pre ) {
     256                        return $pre;
     257                }
     258
     259                // @todo Consider additional optimization routes, perhaps as an opt-in for plugins.
     260                // We already have paths covered. What about how far domains should be drilled down (including www)?
     261
     262                $search_domains = "'" . implode( "', '", $wpdb->_escape( $domains ) ) . "'";
     263
     264                if ( false === $using_paths ) {
     265                        $network = $wpdb->get_row( "SELECT * FROM {$wpdb->site}
     266                                                                                WHERE domain IN ({$search_domains})
     267                                                                                ORDER BY CHAR_LENGTH(domain)
     268                                                                                DESC LIMIT 1" );
     269
     270                        if ( ! empty( $network ) && ! is_wp_error( $network ) ) {
     271                                return new WP_Network( $network );
     272                        }
     273
     274                        return false;
     275
     276                } else {
     277                        $search_paths = "'" . implode( "', '", $wpdb->_escape( $paths ) ) . "'";
     278                        $networks     = $wpdb->get_results( "SELECT * FROM {$wpdb->site}
     279                                                                                                 WHERE domain IN ({$search_domains})
     280                                                                                                 AND path IN ({$search_paths})
     281                                                                                                 ORDER BY CHAR_LENGTH(domain) DESC, CHAR_LENGTH(path) DESC" );
     282                }
     283
     284                /*
     285                 * Domains are sorted by length of domain, then by length of path.
     286                 * The domain must match for the path to be considered. Otherwise,
     287                 * a network with the path of / will suffice.
     288                 */
     289                $found = false;
     290                foreach ( $networks as $network ) {
     291                        if ( ( $network->domain === $domain ) || ( "www.{$network->domain}" === $domain ) ) {
     292                                if ( in_array( $network->path, $paths, true ) ) {
     293                                        $found = true;
     294                                        break;
     295                                }
     296                        }
     297                        if ( $network->path === '/' ) {
     298                                $found = true;
     299                                break;
     300                        }
     301                }
     302
     303                if ( true === $found ) {
     304                        return new WP_Network( $network );
     305                }
     306
     307                return false;
     308        }
     309
     310        /**
     311         * Load a set of core site options for the network from the database and
     312         * store them in cache for future retrieval.
     313         *
     314         * @since 4.4.0
     315         *
     316         * @param int $network_id
     317         */
     318        public static function load_core_options( $network_id ) {
     319                global $wpdb;
     320
     321                // Setup core options query
     322                $meta_keys       = self::get_core_option_keys();
     323                $core_options_in = "'" . implode( "', '", $meta_keys ) . "'";
     324                $sql             = $wpdb->prepare( "SELECT meta_key, meta_value
     325                                                                                        FROM {$wpdb->sitemeta}
     326                                                                                        WHERE meta_key IN ({$core_options_in})
     327                                                                                        AND site_id = %d", $network_id );
     328
     329                // Query for core network options
     330                $options = $wpdb->get_results( $sql );
     331
     332                // Bail if no options found
     333                if ( empty( $options ) || is_wp_error( $options ) ) {
     334                        return;
     335                }
     336
     337                // Loop through options and add them to the object cache
     338                foreach ( $options as $option ) {
     339
     340                        // Setup option values to cache
     341                        $key                = $option->meta_key;
     342                        $cache_key          = "{$network_id}:$key";
     343                        $option->meta_value = maybe_unserialize( $option->meta_value );
     344
     345                        // Cache the option value
     346                        wp_cache_set( $cache_key, $option->meta_value, 'site-options' );
     347                }
     348        }
     349
     350        /*
     351         * Provide a list of core option keys used for network metadata.
     352         *
     353         * @since 4.4.0
     354         *
     355         * @return array
     356         */
     357        private static function get_core_option_keys() {
     358                return array(
     359                        'site_name',
     360                        'siteurl',
     361                        'active_sitewide_plugins',
     362                        '_site_transient_timeout_theme_roots',
     363                        '_site_transient_theme_roots',
     364                        'site_admins',
     365                        'can_compress_scripts',
     366                        'global_terms_enabled',
     367                        'ms_files_rewriting'
     368                );
     369        }
     370}
     371 No newline at end of file
  • src/wp-includes/ms-load.php

     
    114114 *
    115115 * @since 3.9.0
    116116 *
    117  * @global wpdb $wpdb
    118  *
    119117 * @param string   $domain   Domain to check.
    120118 * @param string   $path     Path to check.
    121119 * @param int|null $segments Path segments to use. Defaults to null, or the full path.
    122  * @return object|false Network object if successful. False when no network is found.
     120 * @return WP_Network|false Network object if successful. False when no network is found.
    123121 */
    124122function get_network_by_path( $domain, $path, $segments = null ) {
    125         global $wpdb;
    126 
    127         $domains = array( $domain );
    128         $pieces = explode( '.', $domain );
    129 
    130         /*
    131          * It's possible one domain to search is 'com', but it might as well
    132          * be 'localhost' or some other locally mapped domain.
    133          */
    134         while ( array_shift( $pieces ) ) {
    135                 if ( $pieces ) {
    136                         $domains[] = implode( '.', $pieces );
    137                 }
    138         }
    139 
    140         /*
    141          * If we've gotten to this function during normal execution, there is
    142          * more than one network installed. At this point, who knows how many
    143          * we have. Attempt to optimize for the situation where networks are
    144          * only domains, thus meaning paths never need to be considered.
    145          *
    146          * This is a very basic optimization; anything further could have drawbacks
    147          * depending on the setup, so this is best done per-install.
    148          */
    149         $using_paths = true;
    150         if ( wp_using_ext_object_cache() ) {
    151                 $using_paths = wp_cache_get( 'networks_have_paths', 'site-options' );
    152                 if ( false === $using_paths ) {
    153                         $using_paths = (bool) $wpdb->get_var( "SELECT id FROM $wpdb->site WHERE path <> '/' LIMIT 1" );
    154                         wp_cache_add( 'networks_have_paths', (int) $using_paths, 'site-options'  );
    155                 }
    156         }
    157 
    158         $paths = array();
    159         if ( $using_paths ) {
    160                 $path_segments = array_filter( explode( '/', trim( $path, "/" ) ) );
    161 
    162                 /**
    163                  * Filter the number of path segments to consider when searching for a site.
    164                  *
    165                  * @since 3.9.0
    166                  *
    167                  * @param int|null $segments The number of path segments to consider. WordPress by default looks at
    168                  *                           one path segment. The function default of null only makes sense when you
    169                  *                           know the requested path should match a network.
    170                  * @param string   $domain   The requested domain.
    171                  * @param string   $path     The requested path, in full.
    172                  */
    173                 $segments = apply_filters( 'network_by_path_segments_count', $segments, $domain, $path );
    174 
    175                 if ( null !== $segments && count($path_segments ) > $segments ) {
    176                         $path_segments = array_slice( $path_segments, 0, $segments );
    177                 }
    178 
    179                 while ( count( $path_segments ) ) {
    180                         $paths[] = '/' . implode( '/', $path_segments ) . '/';
    181                         array_pop( $path_segments );
    182                 }
    183 
    184                 $paths[] = '/';
    185         }
    186 
    187         /**
    188          * Determine a network by its domain and path.
    189          *
    190          * This allows one to short-circuit the default logic, perhaps by
    191          * replacing it with a routine that is more optimal for your setup.
    192          *
    193          * Return null to avoid the short-circuit. Return false if no network
    194          * can be found at the requested domain and path. Otherwise, return
    195          * an object from wp_get_network().
    196          *
    197          * @since 3.9.0
    198          *
    199          * @param null|bool|object $network  Network value to return by path.
    200          * @param string           $domain   The requested domain.
    201          * @param string           $path     The requested path, in full.
    202          * @param int|null         $segments The suggested number of paths to consult.
    203          *                                   Default null, meaning the entire path was to be consulted.
    204          * @param array            $paths    The paths to search for, based on $path and $segments.
    205          */
    206         $pre = apply_filters( 'pre_get_network_by_path', null, $domain, $path, $segments, $paths );
    207         if ( null !== $pre ) {
    208                 return $pre;
    209         }
    210 
    211         // @todo Consider additional optimization routes, perhaps as an opt-in for plugins.
    212         // We already have paths covered. What about how far domains should be drilled down (including www)?
    213 
    214         $search_domains = "'" . implode( "', '", $wpdb->_escape( $domains ) ) . "'";
    215 
    216         if ( ! $using_paths ) {
    217                 $network = $wpdb->get_row( "SELECT id, domain, path FROM $wpdb->site
    218                         WHERE domain IN ($search_domains) ORDER BY CHAR_LENGTH(domain) DESC LIMIT 1" );
    219                 if ( $network ) {
    220                         return wp_get_network( $network );
    221                 }
    222                 return false;
    223 
    224         } else {
    225                 $search_paths = "'" . implode( "', '", $wpdb->_escape( $paths ) ) . "'";
    226                 $networks = $wpdb->get_results( "SELECT id, domain, path FROM $wpdb->site
    227                         WHERE domain IN ($search_domains) AND path IN ($search_paths)
    228                         ORDER BY CHAR_LENGTH(domain) DESC, CHAR_LENGTH(path) DESC" );
    229         }
    230 
    231         /*
    232          * Domains are sorted by length of domain, then by length of path.
    233          * The domain must match for the path to be considered. Otherwise,
    234          * a network with the path of / will suffice.
    235          */
    236         $found = false;
    237         foreach ( $networks as $network ) {
    238                 if ( $network->domain === $domain || "www.$network->domain" === $domain ) {
    239                         if ( in_array( $network->path, $paths, true ) ) {
    240                                 $found = true;
    241                                 break;
    242                         }
    243                 }
    244                 if ( $network->path === '/' ) {
    245                         $found = true;
    246                         break;
    247                 }
    248         }
    249 
    250         if ( $found ) {
    251                 return wp_get_network( $network );
    252         }
    253 
    254         return false;
     123        return WP_Network::get_by_path( $domain, $path, $segments );
    255124}
    256125
    257126/**
     
    259128 *
    260129 * @since 3.9.0
    261130 *
    262  * @global wpdb $wpdb
    263  *
    264131 * @param object|int $network The network's database row or ID.
    265  * @return object|false Object containing network information if found, false if not.
     132 * @return WP_Network|false Object containing network information if found, false if not.
    266133 */
    267134function wp_get_network( $network ) {
    268         global $wpdb;
    269 
    270135        if ( ! is_object( $network ) ) {
    271                 $network = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->site WHERE id = %d", $network ) );
    272                 if ( ! $network ) {
    273                         return false;
    274                 }
     136                $network = WP_Network::get_instance( $network );
     137        } else {
     138                $network = new WP_Network( $network );
    275139        }
    276140
    277141        return $network;
  • src/wp-includes/ms-settings.php

     
    1111 */
    1212
    1313/** Include Multisite initialization functions */
     14require_once( ABSPATH . WPINC . '/class-wp-network.php' );
    1415require_once( ABSPATH . WPINC . '/ms-load.php' );
    1516require_once( ABSPATH . WPINC . '/ms-default-constants.php' );
    1617
     
    7576                        // Are there even two networks installed?
    7677                        $one_network = $wpdb->get_row( "SELECT * FROM $wpdb->site LIMIT 2" ); // [sic]
    7778                        if ( 1 === $wpdb->num_rows ) {
    78                                 $current_site = wp_get_network( $one_network );
     79                                $current_site = new WP_Network( $one_network );
    7980                                wp_cache_add( 'current_network', $current_site, 'site-options' );
    8081                        } elseif ( 0 === $wpdb->num_rows ) {
    8182                                ms_not_installed();
    8283                        }
    8384                }
    8485                if ( empty( $current_site ) ) {
    85                         $current_site = get_network_by_path( $domain, $path, 1 );
     86                        $current_site = WP_Network::get_by_path( $domain, $path, 1 );
    8687                }
    8788
    8889                if ( empty( $current_site ) ) {
     
    9798                // Find the site by the domain and at most the first path segment.
    9899                $current_blog = get_site_by_path( $domain, $path, 1 );
    99100                if ( $current_blog ) {
    100                         $current_site = wp_get_network( $current_blog->site_id ? $current_blog->site_id : 1 );
     101                        $current_site = WP_Network::get_instance( $current_blog->site_id ? $current_blog->site_id : 1 );
    101102                } else {
    102103                        // If you don't have a site with the same domain/path as a network, you're pretty screwed, but:
    103                         $current_site = get_network_by_path( $domain, $path, 1 );
     104                        $current_site = WP_Network::get_by_path( $domain, $path, 1 );
    104105                }
    105106        }
    106107
    107108        // The network declared by the site trumps any constants.
    108109        if ( $current_blog && $current_blog->site_id != $current_site->id ) {
    109                 $current_site = wp_get_network( $current_blog->site_id );
     110                $current_site = WP_Network::get_instance( $current_blog->site_id );
    110111        }
    111112
    112113        // No network has been found, bail.
     
    163164                exit;
    164165        }
    165166
    166         // @todo What if the domain of the network doesn't match the current site?
    167         $current_site->cookie_domain = $current_site->domain;
    168         if ( 'www.' === substr( $current_site->cookie_domain, 0, 4 ) ) {
    169                 $current_site->cookie_domain = substr( $current_site->cookie_domain, 4 );
    170         }
    171 
    172167        // Figure out the current network's main site.
    173         if ( ! isset( $current_site->blog_id ) ) {
     168        if ( empty( $current_site->blog_id ) ) {
    174169                if ( $current_blog->domain === $current_site->domain && $current_blog->path === $current_site->path ) {
    175170                        $current_site->blog_id = $current_blog->blog_id;
    176171                } elseif ( ! $current_site->blog_id = wp_cache_get( 'network:' . $current_site->id . ':main_site', 'site-options' ) ) {
     
    190185        }
    191186
    192187        $site_id = $current_blog->site_id;
    193         wp_load_core_site_options( $site_id );
    194188}
    195189
    196190$wpdb->set_prefix( $table_prefix, false ); // $table_prefix can be set in sunrise.php
     
    202196// need to init cache again after blog_id is set
    203197wp_start_object_cache();
    204198
    205 if ( ! isset( $current_site->site_name ) ) {
    206         $current_site->site_name = get_site_option( 'site_name' );
    207         if ( ! $current_site->site_name ) {
    208                 $current_site->site_name = ucfirst( $current_site->domain );
    209         }
     199if ( ! $current_site instanceof WP_Network ) {
     200        $current_site = new WP_Network( $current_site );
    210201}
    211202
    212203// Define upload directory constants
  • src/wp-includes/option.php

     
    199199 *
    200200 * @global wpdb $wpdb
    201201 *
    202  * @param int $site_id Optional site ID for which to query the options. Defaults to the current site.
     202 * @param int $network_id Optional. ID of the network for which to load options. Defaults to the current network.
    203203 */
    204 function wp_load_core_site_options( $site_id = null ) {
     204function wp_load_core_site_options( $network_id = null ) {
    205205        global $wpdb;
    206206
    207207        if ( !is_multisite() || wp_using_ext_object_cache() || defined( 'WP_INSTALLING' ) )
    208208                return;
    209209
    210         if ( empty($site_id) )
    211                 $site_id = $wpdb->siteid;
    212 
    213         $core_options = array('site_name', 'siteurl', 'active_sitewide_plugins', '_site_transient_timeout_theme_roots', '_site_transient_theme_roots', 'site_admins', 'can_compress_scripts', 'global_terms_enabled', 'ms_files_rewriting' );
    214 
    215         $core_options_in = "'" . implode("', '", $core_options) . "'";
    216         $options = $wpdb->get_results( $wpdb->prepare("SELECT meta_key, meta_value FROM $wpdb->sitemeta WHERE meta_key IN ($core_options_in) AND site_id = %d", $site_id) );
    217 
    218         foreach ( $options as $option ) {
    219                 $key = $option->meta_key;
    220                 $cache_key = "{$site_id}:$key";
    221                 $option->meta_value = maybe_unserialize( $option->meta_value );
    222 
    223                 wp_cache_set( $cache_key, $option->meta_value, 'site-options' );
     210        if ( empty( $network_id ) ) {
     211                $network_id = $wpdb->siteid;
    224212        }
     213
     214        WP_Network::load_core_options( $network_id );
    225215}
    226216
    227217/**