WordPress.org

Make WordPress Core

Ticket #31985: 31985.diff

File 31985.diff, 40.6 KB (added by jeremyfelt, 6 years ago)
  • src/wp-includes/class-wp-network.php

     
     1<?php
     2
     3/**
     4 * Main network class
     5 *
     6 * This class contains the values and functional methods for interacting with
     7 * a network of WordPress sites. Traditionally used for multisite, this class
     8 * is used by the $current_site global to setup the current network
     9 *
     10 * This class is most useful in WordPress multi-network installations where the
     11 * ability to interact with any network of sites is required.
     12 *
     13 * @since WordPress (4.3.0)
     14 */
     15class WP_Network {
     16
     17        /**
     18         * Unique numerical ID
     19         *
     20         * @since WordPress (4.3.0)
     21         *
     22         * @var int
     23         */
     24        public $id = 0;
     25
     26        /**
     27         * Domain of this network
     28         *
     29         * @since WordPress (4.3.0)
     30         *
     31         * @var string
     32         */
     33        public $domain = '';
     34
     35        /**
     36         * Path of this network
     37         *
     38         * @since WordPress (4.3.0)
     39         *
     40         * @var string
     41         */
     42        public $path = '';
     43
     44        /**
     45         * The ID of the site mapped to this network
     46         *
     47         * Named "blog" vs. "site" for legacy reasons
     48         *
     49         * @since WordPress (4.3.0)
     50         *
     51         * @var int
     52         */
     53        public $blog_id = 0;
     54
     55        /**
     56         * Domain used for cookies for this network
     57         *
     58         * @since WordPress (4.3.0)
     59         *
     60         * @var int
     61         */
     62        public $cookie_domain = '';
     63
     64        /**
     65         * Array of core options for this network
     66         *
     67         * @since WordPress (4.3.0)
     68         *
     69         * @var array
     70         */
     71        public $core_options = array();
     72
     73        /**
     74         * Name of this network
     75         *
     76         * Named "nite" vs. "network" for legacy reasons
     77         *
     78         * @since WordPress (4.3.0)
     79         *
     80         * @var string
     81         */
     82        public $site_name = '';
     83
     84        /**
     85         * Create a new WP_Network object
     86         *
     87         * Will populate all relevant data if a network ID or object is passed
     88         *
     89         * @since WordPress (4.3.0)
     90         *
     91         * @param mixed $network stdClass object or network ID
     92         * @return object
     93         */
     94        public function __construct( $network = false ) {
     95
     96                // Bail if not populating from an existing network
     97                if ( empty( $network ) ) {
     98                        return;
     99                }
     100
     101                // Get the network ID
     102                if ( is_object( $network ) && isset( $network->id ) ) {
     103                        $network_id = $network->id;
     104                } elseif ( is_numeric( $network ) ) {
     105                        $network_id = (int) $network;
     106                } else {
     107                        return;
     108                }
     109
     110                // Populate network object and return it
     111                if ( ! empty( $network_id ) ) {
     112                        $this->populate( $network_id );
     113                        return $this;
     114                }
     115        }
     116
     117        public function populate( $network_id = 0 ) {
     118
     119                // Query for network
     120                $network = self::get_by_id( $network_id );
     121
     122                // Bail if no network to update
     123                if ( false === $network ) {
     124                        return false;
     125                }
     126
     127                // Set the main data
     128                $this->set_main_data( $network );
     129
     130                // Set supplemental data based on the needs of multisite
     131                $this->set_cookie_domain();
     132                $this->set_core_options();
     133                $this->set_site_name();
     134        }
     135
     136        /**
     137         * Set the network ID, domain, and path
     138         *
     139         * @since WordPress (4.3.0)
     140         */
     141        private function set_main_data( $network ) {
     142                $this->id     = $network->id;
     143                $this->domain = $network->domain;
     144                $this->path   = $network->path;
     145        }
     146
     147        /**
     148         * Set the cookie domain, based on the network domain
     149         *
     150         * @todo What if the domain of the network doesn't match the current site?
     151         *
     152         * @since WordPress (4.3.0)
     153         */
     154        private function set_cookie_domain() {
     155                $this->cookie_domain = $this->domain;
     156                if ( 'www.' === substr( $this->cookie_domain, 0, 4 ) ) {
     157                        $this->cookie_domain = substr( $this->cookie_domain, 4 );
     158                }
     159        }
     160
     161        /**
     162         * Set the core options for this network object
     163         *
     164         * @since WordPress (4.3.0)
     165         */
     166        private function set_core_options() {
     167                $this->core_options = self::get_core_options( $this->id );
     168        }
     169
     170        /**
     171         * @todo What if the domain of the network doesn't match the current site?
     172         */
     173        private function set_site_name() {
     174
     175                // Attempt to use core option
     176                if ( ! empty( $this->core_options['site_name'] ) ) {
     177                        $this->site_name = $this->core_options['site_name'];
     178
     179                // Fallback to domain
     180                } else {
     181                        $this->site_name = ucfirst( $this->domain );
     182                }
     183        }
     184
     185        public static function get_by_id( $network_id = 0 ) {
     186                global $wpdb;
     187
     188                // Check cache
     189                $network = wp_cache_get( $network_id, 'networks' );
     190
     191                // Cache miss
     192                if ( false === $network ) {
     193
     194                        // Query for network data
     195                        $network = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->site} WHERE id = %d", $network_id ) );
     196
     197                        // Bail if network cannot be found
     198                        if ( empty( $network ) || is_wp_error( $network ) ) {
     199                                $network = false;
     200                        }
     201
     202                        // Add network to cache
     203                        wp_cache_add( $network_id, $network, 'networks' );
     204                }
     205
     206                // Return the network
     207                return $network;
     208        }
     209
     210        /**
     211         * Retrieve a network by its domain and path.
     212         *
     213         * @since 4.3.0
     214         *
     215         * @param string   $domain   Domain to check.
     216         * @param string   $path     Path to check.
     217         * @param int|null $segments Path segments to use. Defaults to null, or the full path.
     218         *
     219         * @return object|bool Network object if successful. False when no network is found.
     220         */
     221        public static function get_by_path( $domain = '', $path = '', $segments = null ) {
     222                global $wpdb;
     223
     224                $domains = array( $domain );
     225                $pieces  = explode( '.', $domain );
     226
     227                /*
     228                 * It's possible one domain to search is 'com', but it might as well
     229                 * be 'localhost' or some other locally mapped domain.
     230                 */
     231                while ( array_shift( $pieces ) ) {
     232                        if ( ! empty( $pieces ) ) {
     233                                $domains[] = implode( '.', $pieces );
     234                        }
     235                }
     236
     237                /*
     238                 * If we've gotten to this function during normal execution, there is
     239                 * more than one network installed. At this point, who knows how many
     240                 * we have. Attempt to optimize for the situation where networks are
     241                 * only domains, thus meaning paths never need to be considered.
     242                 *
     243                 * This is a very basic optimization; anything further could have
     244                 * drawbacks depending on the setup, so this is best done per-install.
     245                 */
     246                $using_paths = true;
     247                if ( wp_using_ext_object_cache() ) {
     248                        $using_paths = wp_cache_get( 'networks_have_paths', 'site-options' );
     249                        if ( false === $using_paths ) {
     250                                $using_paths = (bool) $wpdb->get_var( "SELECT id FROM {$wpdb->site} WHERE path <> '/' LIMIT 1" );
     251                                wp_cache_add( 'networks_have_paths', (int) $using_paths, 'site-options'  );
     252                        }
     253                }
     254
     255                $paths = array();
     256                if ( true === $using_paths ) {
     257                        $path_segments = array_filter( explode( '/', trim( $path, '/' ) ) );
     258
     259                        /**
     260                         * Filter the number of path segments to consider when searching for a site.
     261                         *
     262                         * @since 3.9.0
     263                         *
     264                         * @param int|null $segments The number of path segments to consider. WordPress by default looks at
     265                         *                           one path segment. The function default of null only makes sense when you
     266                         *                           know the requested path should match a network.
     267                         * @param string   $domain   The requested domain.
     268                         * @param string   $path     The requested path, in full.
     269                         */
     270                        $segments = apply_filters( 'network_by_path_segments_count', $segments, $domain, $path );
     271
     272                        if ( ( null !== $segments ) && count( $path_segments ) > $segments ) {
     273                                $path_segments = array_slice( $path_segments, 0, $segments );
     274                        }
     275
     276                        while ( count( $path_segments ) ) {
     277                                $paths[] = '/' . implode( '/', $path_segments ) . '/';
     278                                array_pop( $path_segments );
     279                        }
     280
     281                        $paths[] = '/';
     282                }
     283
     284                /**
     285                 * Determine a network by its domain and path.
     286                 *
     287                 * This allows one to short-circuit the default logic, perhaps by
     288                 * replacing it with a routine that is more optimal for your setup.
     289                 *
     290                 * Return null to avoid the short-circuit. Return false if no network
     291                 * can be found at the requested domain and path. Otherwise, return
     292                 * an object from wp_get_network().
     293                 *
     294                 * @since 3.9.0
     295                 *
     296                 * @param null|bool|object $network  Network value to return by path.
     297                 * @param string           $domain   The requested domain.
     298                 * @param string           $path     The requested path, in full.
     299                 * @param int|null         $segments The suggested number of paths to consult.
     300                 *                                   Default null, meaning the entire path was to be consulted.
     301                 * @param array            $paths    The paths to search for, based on $path and $segments.
     302                 */
     303                $pre = apply_filters( 'pre_get_network_by_path', null, $domain, $path, $segments, $paths );
     304                if ( null !== $pre ) {
     305                        return $pre;
     306                }
     307
     308                // @todo Consider additional optimization routes, perhaps as an opt-in for plugins.
     309                // We already have paths covered. What about how far domains should be drilled down (including www)?
     310
     311                $search_domains = "'" . implode( "', '", $wpdb->_escape( $domains ) ) . "'";
     312
     313                if ( false === $using_paths ) {
     314                        $network = $wpdb->get_row( "SELECT * FROM {$wpdb->site}
     315                                                                                WHERE domain IN ({$search_domains})
     316                                                                                ORDER BY CHAR_LENGTH(domain)
     317                                                                                DESC LIMIT 1" );
     318
     319                        if ( ! empty( $network ) && ! is_wp_error( $network ) ) {
     320                                return new WP_Network( $network->id );
     321                        }
     322
     323                        return false;
     324
     325                } else {
     326                        $search_paths = "'" . implode( "', '", $wpdb->_escape( $paths ) ) . "'";
     327                        $networks     = $wpdb->get_results( "SELECT * FROM {$wpdb->site}
     328                                                                                                 WHERE domain IN ({$search_domains})
     329                                                                                                 AND path IN ({$search_paths})
     330                                                                                                 ORDER BY CHAR_LENGTH(domain) DESC, CHAR_LENGTH(path) DESC" );
     331                }
     332
     333                /*
     334                 * Domains are sorted by length of domain, then by length of path.
     335                 * The domain must match for the path to be considered. Otherwise,
     336                 * a network with the path of / will suffice.
     337                 */
     338                $found = false;
     339                foreach ( $networks as $network ) {
     340                        if ( ( $network->domain === $domain ) || ( "www.{$network->domain}" === $domain ) ) {
     341                                if ( in_array( $network->path, $paths, true ) ) {
     342                                        $found = true;
     343                                        break;
     344                                }
     345                        }
     346                        if ( $network->path === '/' ) {
     347                                $found = true;
     348                                break;
     349                        }
     350                }
     351
     352                if ( true === $found ) {
     353                        return new WP_Network( $network->id );
     354                }
     355
     356                return false;
     357        }
     358       
     359        public static function get_core_options( $network_id = 0 ) {
     360                global $wpdb;
     361
     362                // Setup core options query
     363                $meta_keys       = self::get_core_option_keys();
     364                $core_options_in = "'" . implode( "', '", $meta_keys ) . "'";
     365                $sql             = $wpdb->prepare( "SELECT meta_key, meta_value
     366                                                                                        FROM {$wpdb->sitemeta}
     367                                                                                        WHERE meta_key IN ({$core_options_in})
     368                                                                                        AND site_id = %d", $network_id );
     369
     370                // Query for core network options
     371                $options = $wpdb->get_results( $sql );
     372
     373                // Bail if no options found
     374                if ( empty( $options ) || is_wp_error( $options ) ) {
     375                        return array();
     376                }
     377
     378                // Setup return value array
     379                $retval = array();
     380
     381                // Loop through options and add them to the object cache
     382                foreach ( $options as $option ) {
     383
     384                        // Setup option values to cache
     385                        $key                = $option->meta_key;
     386                        $cache_key          = "{$network_id}:$key";
     387                        $option->meta_value = maybe_unserialize( $option->meta_value );
     388
     389                        // Cache the option value
     390                        wp_cache_set( $cache_key, $option->meta_value, 'site-options' );
     391
     392                        // Add option to array
     393                        $retval[ $option->meta_key ] = $option->meta_value;
     394                }
     395
     396                // Return the options
     397                return $retval;
     398        }
     399
     400        public static function get_core_option_keys() {
     401                return array(
     402                        'site_name',
     403                        'siteurl',
     404                        'active_sitewide_plugins',
     405                        '_site_transient_timeout_theme_roots',
     406                        '_site_transient_theme_roots',
     407                        'site_admins',
     408                        'can_compress_scripts',
     409                        'global_terms_enabled',
     410                        'ms_files_rewriting'
     411                );
     412        }
     413
     414        /**
     415         * Get all active network-wide plugins for this network
     416         *
     417         * @since WordPress (4.3.0)
     418         *
     419         * @param int $network_id
     420         * @return array
     421         */
     422        public static function get_active_network_plugins( $network_id = 0 ) {
     423                $network_options = self::get_core_options( $network_id );
     424
     425                if ( empty( $network_options['active_sitewide_plugins'] ) ) {
     426                        return array();
     427                }
     428                $active_plugins  = $network_options['active_sitewide_plugins'];
     429
     430                // Setup the plugins array
     431                $plugins        = array();
     432                $active_plugins = array_keys( $active_plugins );
     433
     434                // Sort plugins array
     435                sort( $active_plugins );
     436
     437                foreach ( $active_plugins as $plugin ) {
     438                        if ( self::validate_network_plugin( $plugin ) ) {
     439                                $plugins[] = WP_PLUGIN_DIR . '/' . $plugin;
     440                        }
     441                }
     442
     443                return $plugins;
     444        }
     445
     446        /**
     447         * Ensure a single network-wide plugin is valid
     448         *
     449         * @since WordPress (4.3.0)
     450         *
     451         * @param type $plugin
     452         *
     453         * @return boolean
     454         */
     455        private static function validate_network_plugin( $plugin = '' ) {
     456
     457                // $plugin must validate as file
     458                if ( ! validate_file( $plugin ) ) {
     459                        return false;
     460                }
     461
     462                // $plugin must end with '.php'
     463                if ( '.php' !== substr( $plugin, -4 ) ) {
     464                        return false;
     465                }
     466
     467                // $plugin must exist
     468                if ( ! file_exists( WP_PLUGIN_DIR . '/' . $plugin ) ) {
     469                        return false;
     470                }
     471
     472                return true;
     473        }
     474
     475        public static function add( $domain = '', $path = '' ) {
     476               
     477        }
     478
     479        /**
     480         * Update a networks domain & path based on its ID
     481         *
     482         * @since WordPress (4.3.0)
     483         *
     484         * @global type $wpdb
     485         *
     486         * @param int    $network_id Existing network ID
     487         * @param string $domain     New network domain
     488         * @param string $path       New network path
     489         *
     490         * @return mixed False if network not found. Results of query if updated.
     491         */
     492        public static function update( $network_id = 0, $domain = '', $path = '' ) {
     493                global $wpdb;
     494
     495                // Does network exist
     496                $network = self::get_by_id( $network_id );
     497
     498                // Bail if no network to update
     499                if ( false === $network ) {
     500                        return false;
     501                }
     502
     503                // Set the domain
     504                if ( ! empty( $domain ) ) {
     505                        $network->domain = $domain;
     506                }
     507
     508                // Set the path
     509                if ( ! empty( $path ) ) {
     510                        $network->path = $path;
     511                }
     512
     513                // Query for network data
     514                $sql     = $wpdb->prepare( "UPDATE {$wpdb->site} SET domain = %s, path = %s WHERE id = %d", $network->domain, $network->path, $network->id );
     515                $network = $wpdb->query( $sql );
     516
     517                // Bail if network cannot be found
     518                if ( empty( $network ) || is_wp_error( $network ) ) {
     519                        return false;
     520                }
     521
     522                // Return results of WPDB::query()
     523                return $network;
     524        }
     525
     526        public static function delete( $network_id = 0 ) {
     527                global $wpdb;
     528
     529                // Does network exist
     530                $network = self::get_by_id( $network_id );
     531
     532                // Bail if no network to delete
     533                if ( false === $network ) {
     534                        return false;
     535                }
     536
     537                // Query for network data
     538                $sql     = $wpdb->prepare( "DELETE FROM {$wpdb->site} WHERE id = %d", $network->id );
     539                $network = $wpdb->query( $sql );
     540        }
     541}
  • src/wp-includes/ms-load.php

     
    1616 * @return bool True if subdomain configuration is enabled, false otherwise.
    1717 */
    1818function is_subdomain_install() {
    19         if ( defined('SUBDOMAIN_INSTALL') )
     19
     20        if ( defined( 'SUBDOMAIN_INSTALL' ) ) {
    2021                return SUBDOMAIN_INSTALL;
     22        }
    2123
    22         if ( defined('VHOST') && VHOST == 'yes' )
     24        if ( defined( 'VHOST' ) && ( VHOST === 'yes' ) ) {
    2325                return true;
     26        }
    2427
    2528        return false;
    2629}
     
    4750
    4851        foreach ( $active_plugins as $plugin ) {
    4952                if ( ! validate_file( $plugin ) // $plugin must validate as file
    50                         && '.php' == substr( $plugin, -4 ) // $plugin must end with '.php'
    51                         && file_exists( WP_PLUGIN_DIR . '/' . $plugin ) // $plugin must exist
    52                         )
    53                 $plugins[] = WP_PLUGIN_DIR . '/' . $plugin;
     53                     && '.php' == substr( $plugin, -4 ) // $plugin must end with '.php'
     54                     && file_exists( WP_PLUGIN_DIR . '/' . $plugin ) // $plugin must exist
     55                )
     56                        $plugins[] = WP_PLUGIN_DIR . '/' . $plugin;
    5457        }
    5558        return $plugins;
    5659}
     
    113116}
    114117
    115118/**
    116  * Retrieve a network object by its domain and path.
     119 * Set relevant multisite globals
    117120 *
    118  * @since 3.9.0
     121 * @since WordPress (4.3.0)
    119122 *
    120  * @param string   $domain   Domain to check.
    121  * @param string   $path     Path to check.
    122  * @param int|null $segments Path segments to use. Defaults to null, or the full path.
    123  * @return object|bool Network object if successful. False when no network is found.
     123 * @global object $current_site
     124 * @global object $current_blog
     125 * @global object $wpdb
     126 *
     127 * @return if already set by sunrise.php
    124128 */
    125 function get_network_by_path( $domain, $path, $segments = null ) {
    126         global $wpdb;
     129function ms_set_current_globals() {
     130        global $current_site, $current_blog, $wpdb;
    127131
    128         $domains = array( $domain );
    129         $pieces = explode( '.', $domain );
     132        // Bail if network and site are already set
     133        if ( isset( $current_site ) && isset( $current_blog ) ) {
     134                return;
     135        }
    130136
    131         /*
    132          * It's possible one domain to search is 'com', but it might as well
    133          * be 'localhost' or some other locally mapped domain.
    134          */
    135         while ( array_shift( $pieces ) ) {
    136                 if ( $pieces ) {
    137                         $domains[] = implode( '.', $pieces );
    138                 }
     137        // Given the domain and path, let's try to identify the network and site.
     138        // Usually, it's easier to query the site first, which declares its network.
     139        // In limited situations, though, we either can or must find the network first.
     140
     141        $domain = strtolower( stripslashes( $_SERVER['HTTP_HOST'] ) );
     142        if ( substr( $domain, -3 ) == ':80' ) {
     143                $domain               = substr( $domain, 0, -3 );
     144                $_SERVER['HTTP_HOST'] = substr( $_SERVER['HTTP_HOST'], 0, -3 );
     145        } elseif ( substr( $domain, -4 ) == ':443' ) {
     146                $domain               = substr( $domain, 0, -4 );
     147                $_SERVER['HTTP_HOST'] = substr( $_SERVER['HTTP_HOST'], 0, -4 );
    139148        }
    140149
     150        $path = stripslashes( $_SERVER['REQUEST_URI'] );
     151        if ( is_admin() ) {
     152                $path = preg_replace( '#(.*)/wp-admin/.*#', '$1/', $path );
     153        }
     154        list( $path ) = explode( '?', $path );
     155
     156        // If the network is defined in wp-config.php, we can simply use that.
     157        if ( defined( 'DOMAIN_CURRENT_SITE' ) && defined( 'PATH_CURRENT_SITE' ) ) {
     158                $current_site         = new WP_Network();
     159                $current_site->id     = defined( 'SITE_ID_CURRENT_SITE' ) ? SITE_ID_CURRENT_SITE : 1;
     160                $current_site->domain = DOMAIN_CURRENT_SITE;
     161                $current_site->path   = PATH_CURRENT_SITE;
     162
     163                if ( defined( 'BLOG_ID_CURRENT_SITE' ) ) {
     164                        $current_site->blog_id = BLOG_ID_CURRENT_SITE;
     165                } elseif ( defined( 'BLOGID_CURRENT_SITE' ) ) { // deprecated.
     166                        $current_site->blog_id = BLOGID_CURRENT_SITE;
     167                }
     168
     169                if ( 0 === strcasecmp( $current_site->domain, $domain ) && 0 === strcasecmp( $current_site->path, $path ) ) {
     170                        $current_blog = get_site_by_path( $domain, $path );
     171
     172                // If the current network has a path and also matches the domain and
     173                // path of the request, we need to look for a site using the first path
     174                // segment following the network's path.
     175                } elseif ( '/' !== $current_site->path && 0 === strcasecmp( $current_site->domain, $domain ) && 0 === stripos( $path, $current_site->path ) ) {
     176                        $current_blog = get_site_by_path( $domain, $path, 1 + count( explode( '/', trim( $current_site->path, '/' ) ) ) );
     177
     178                // Otherwise, use the first path segment (as usual).
     179                } else {
     180                        $current_blog = get_site_by_path( $domain, $path, 1 );
     181                }
     182
    141183        /*
    142          * If we've gotten to this function during normal execution, there is
    143          * more than one network installed. At this point, who knows how many
    144          * we have. Attempt to optimize for the situation where networks are
    145          * only domains, thus meaning paths never need to be considered.
    146          *
    147          * This is a very basic optimization; anything further could have drawbacks
    148          * depending on the setup, so this is best done per-install.
     184         * A "subdomain" install can be re-interpreted to mean "can support anydomain".
     185         * If we're not dealing with one of these installs, then the important part
     186         * is determining the network first, because we need the network's path to
     187         * identify any sites.
    149188         */
    150         $using_paths = true;
    151         if ( wp_using_ext_object_cache() ) {
    152                 $using_paths = wp_cache_get( 'networks_have_paths', 'site-options' );
    153                 if ( false === $using_paths ) {
    154                         $using_paths = (bool) $wpdb->get_var( "SELECT id FROM $wpdb->site WHERE path <> '/' LIMIT 1" );
    155                         wp_cache_add( 'networks_have_paths', (int) $using_paths, 'site-options'  );
     189        } elseif ( ! is_subdomain_install() ) {
     190
     191                // Attempt to get the current network from cache
     192                $current_site = wp_cache_get( 'current_network', 'site-options' );
     193                if ( false === $current_site ) {
     194
     195                        // Are there even two networks installed?
     196                        $one_network = $wpdb->get_row( "SELECT * FROM {$wpdb->site} LIMIT 2" );
     197                        if ( 1 === $wpdb->num_rows ) {
     198                                $current_site = new WP_Network( $one_network );
     199                                wp_cache_add( 'current_network', $current_site, 'site-options' );
     200                        } elseif ( 0 === $wpdb->num_rows ) {
     201                                ms_not_installed();
     202                        }
    156203                }
    157         }
    158204
    159         $paths = array();
    160         if ( $using_paths ) {
    161                 $path_segments = array_filter( explode( '/', trim( $path, "/" ) ) );
     205                // No network found, so attempt to query it by path
     206                if ( empty( $current_site ) ) {
     207                        $current_site = get_network_by_path( $domain, $path, 1 );
     208                }
    162209
    163                 /**
    164                  * Filter the number of path segments to consider when searching for a site.
    165                  *
    166                  * @since 3.9.0
    167                  *
    168                  * @param int|null $segments The number of path segments to consider. WordPress by default looks at
    169                  *                           one path segment. The function default of null only makes sense when you
    170                  *                           know the requested path should match a network.
    171                  * @param string   $domain   The requested domain.
    172                  * @param string   $path     The requested path, in full.
    173                  */
    174                 $segments = apply_filters( 'network_by_path_segments_count', $segments, $domain, $path );
     210                // Bail if no network found
     211                if ( empty( $current_site ) ) {
     212                        ms_not_installed();
     213       
     214                // Set current site if path matches
     215                } elseif ( $path === $current_site->path ) {
     216                        $current_blog = get_site_by_path( $domain, $path );
    175217
    176                 if ( null !== $segments && count($path_segments ) > $segments ) {
    177                         $path_segments = array_slice( $path_segments, 0, $segments );
     218                // Search the network path + one more path segment (on top of the network path).
     219                } else {
     220                        $current_blog = get_site_by_path( $domain, $path, substr_count( $current_site->path, '/' ) );
    178221                }
     222        } else {
    179223
    180                 while ( count( $path_segments ) ) {
    181                         $paths[] = '/' . implode( '/', $path_segments ) . '/';
    182                         array_pop( $path_segments );
     224                // Find the site by the domain and at most the first path segment.
     225                $current_blog = get_site_by_path( $domain, $path, 1 );
     226                if ( ! empty( $current_blog ) ) {
     227                        $network_id   = $current_blog->site_id ? $current_blog->site_id : 1;
     228                        $current_site = new WP_Network( $network_id );
     229
     230                // If you don't have a site with the same domain/path as a network,
     231                // you're pretty screwed, but...
     232                } else {
     233                        $current_site = get_network_by_path( $domain, $path, 1 );
    183234                }
     235        }
    184236
    185                 $paths[] = '/';
     237        // The network declared by the site trumps any constants.
     238        if ( ! empty( $current_blog ) && ( $current_blog->site_id !== $current_site->id ) ) {
     239                $current_site = new WP_Network( $current_blog->site_id );
    186240        }
    187241
    188         /**
    189          * Determine a network by its domain and path.
    190          *
    191          * This allows one to short-circuit the default logic, perhaps by
    192          * replacing it with a routine that is more optimal for your setup.
    193          *
    194          * Return null to avoid the short-circuit. Return false if no network
    195          * can be found at the requested domain and path. Otherwise, return
    196          * an object from wp_get_network().
    197          *
    198          * @since 3.9.0
    199          *
    200          * @param null|bool|object $network  Network value to return by path.
    201          * @param string           $domain   The requested domain.
    202          * @param string           $path     The requested path, in full.
    203          * @param int|null         $segments The suggested number of paths to consult.
    204          *                                   Default null, meaning the entire path was to be consulted.
    205          * @param array            $paths    The paths to search for, based on $path and $segments.
    206          */
    207         $pre = apply_filters( 'pre_get_network_by_path', null, $domain, $path, $segments, $paths );
    208         if ( null !== $pre ) {
    209                 return $pre;
     242        // Bail if no network has been found by now
     243        if ( empty( $current_site ) ) {
     244                ms_not_installed();
    210245        }
    211246
    212         // @todo Consider additional optimization routes, perhaps as an opt-in for plugins.
    213         // We already have paths covered. What about how far domains should be drilled down (including www)?
     247        // @todo Investigate when exactly this can occur.
     248        if ( empty( $current_blog ) && defined( 'WP_INSTALLING' ) ) {
     249                $current_blog = new stdClass;
     250                $current_blog->blog_id = $blog_id = 1;
     251        }
    214252
    215         $search_domains = "'" . implode( "', '", $wpdb->_escape( $domains ) ) . "'";
     253        // No site has been found, bail.
     254        if ( empty( $current_blog ) ) {
    216255
    217         if ( ! $using_paths ) {
    218                 $network = $wpdb->get_row( "SELECT id, domain, path FROM $wpdb->site
    219                         WHERE domain IN ($search_domains) ORDER BY CHAR_LENGTH(domain) DESC LIMIT 1" );
    220                 if ( $network ) {
    221                         return wp_get_network( $network );
    222                 }
    223                 return false;
     256                // We're going to redirect to the network URL, with some possible modifications.
     257                $scheme      = is_ssl() ? 'https' : 'http';
     258                $destination = "$scheme://{$current_site->domain}{$current_site->path}";
    224259
    225         } else {
    226                 $search_paths = "'" . implode( "', '", $wpdb->_escape( $paths ) ) . "'";
    227                 $networks = $wpdb->get_results( "SELECT id, domain, path FROM $wpdb->site
    228                         WHERE domain IN ($search_domains) AND path IN ($search_paths)
    229                         ORDER BY CHAR_LENGTH(domain) DESC, CHAR_LENGTH(path) DESC" );
    230         }
     260                /**
     261                 * Fires when a network can be determined but a site cannot.
     262                 *
     263                 * At the time of this action, the only recourse is to redirect somewhere
     264                 * and exit. If you want to declare a particular site, do so earlier.
     265                 *
     266                 * @since 3.9.0
     267                 *
     268                 * @param object $current_site The network that had been determined.
     269                 * @param string $domain       The domain used to search for a site.
     270                 * @param string $path         The path used to search for a site.
     271                 */
     272                do_action( 'ms_site_not_found', $current_site, $domain, $path );
    231273
    232         /*
    233          * Domains are sorted by length of domain, then by length of path.
    234          * The domain must match for the path to be considered. Otherwise,
    235          * a network with the path of / will suffice.
    236          */
    237         $found = false;
    238         foreach ( $networks as $network ) {
    239                 if ( $network->domain === $domain || "www.$network->domain" === $domain ) {
    240                         if ( in_array( $network->path, $paths, true ) ) {
    241                                 $found = true;
    242                                 break;
     274                // For a "subdomain" install, redirect to the signup form specifically.
     275                if ( is_subdomain_install() && ! defined( 'NOBLOGREDIRECT' ) ) {
     276                        $destination .= 'wp-signup.php?new=' . str_replace( '.' . $current_site->domain, '', $domain );
     277
     278                // For a "subdomain" install, the NOBLOGREDIRECT constant
     279                // can be used to avoid a redirect to the signup form.
     280                // Using the ms_site_not_found action is preferred to the constant.
     281                } elseif ( is_subdomain_install() ) {
     282                        if ( '%siteurl%' !== NOBLOGREDIRECT ) {
     283                                $destination = NOBLOGREDIRECT;
    243284                        }
     285
     286                // If the domain we were searching for matches the network's domain,
     287                // it's no use redirecting back to ourselves -- it'll cause a loop.
     288                // As we couldn't find a site, we're simply not installed.
     289                } elseif ( 0 === strcasecmp( $current_site->domain, $domain ) ) {
     290                        ms_not_installed();
    244291                }
    245                 if ( $network->path === '/' ) {
    246                         $found = true;
    247                         break;
    248                 }
     292
     293                header( 'Location: ' . $destination );
     294                exit;
    249295        }
    250296
    251         if ( $found ) {
    252                 return wp_get_network( $network );
     297        // Figure out the current network's main site.
     298        if ( ! isset( $current_site->blog_id ) || 0 === $current_site->blog_id ) {
     299                if ( ( $current_blog->domain === $current_site->domain ) && ( $current_blog->path === $current_site->path ) ) {
     300                        $current_site->blog_id = $current_blog->blog_id;
     301                } elseif ( ! $current_site->blog_id = wp_cache_get( 'network:' . $current_site->id . ':main_site', 'site-options' ) ) {
     302                        $current_site->blog_id = $wpdb->get_var( $wpdb->prepare( "SELECT blog_id FROM $wpdb->blogs WHERE domain = %s AND path = %s", $current_site->domain, $current_site->path ) );
     303                        wp_cache_add( 'network:' . $current_site->id . ':main_site', $current_site->blog_id, 'site-options' );
     304                }
    253305        }
    254306
    255         return false;
     307        // Set some legacy globals
     308        $GLOBALS['blog_id'] = $current_blog->blog_id;
     309        $GLOBALS['public']  = $current_blog->public;
     310        $GLOBALS['site_id'] = $current_blog->site_id;
     311
     312        // Load the site options
     313        wp_load_core_site_options( $current_site->id );
    256314}
    257315
    258316/**
     317 * Set the table prefix according to current site
     318 *
     319 * @since WordPress (4.3.0)
     320 *
     321 * @global object $wpdb
     322 * @global string $table_prefix
     323 * @global object $current_blog
     324 */
     325function ms_set_table_prefix() {
     326        global $wpdb, $table_prefix, $current_blog;
     327
     328        $wpdb->set_prefix( $table_prefix, false );
     329        $wpdb->set_blog_id( $current_blog->blog_id, $current_blog->site_id );
     330        $table_prefix = $wpdb->get_blog_prefix();
     331}
     332
     333/**
     334 * Set globals used when switching between sites
     335 *
     336 * @since WordPress (4.3.0)
     337 *
     338 * @see switch_to_blog()
     339 * @see restore_current_blog()
     340 */
     341function ms_set_switched_stacks() {
     342        $GLOBALS['_wp_switched_stack'] = array();
     343        $GLOBALS['switched']           = false;
     344}
     345
     346/**
     347 * Retrieve a network object by its domain and path.
     348 *
     349 * @since 3.9.0
     350 *
     351 * @param string   $domain   Domain to check.
     352 * @param string   $path     Path to check.
     353 * @param int|null $segments Path segments to use. Defaults to null, or the full path.
     354 * @return object|bool Network object if successful. False when no network is found.
     355 */
     356function get_network_by_path( $domain, $path, $segments = null ) {
     357        return WP_Network::get_by_path( $domain, $path, $segments );
     358}
     359
     360/**
    259361 * Retrieve an object containing information about the requested network.
    260362 *
    261363 * @since 3.9.0
     
    264366 * @return object|bool Object containing network information if found, false if not.
    265367 */
    266368function wp_get_network( $network ) {
    267         global $wpdb;
    268 
    269         if ( ! is_object( $network ) ) {
    270                 $network = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->site WHERE id = %d", $network ) );
    271                 if ( ! $network ) {
    272                         return false;
    273                 }
    274         }
    275 
    276         return $network;
     369        return new WP_Network( $network );
    277370}
    278371
    279372/**
  • src/wp-includes/ms-settings.php

     
    1010 * @since 3.0.0
    1111 */
    1212
    13 /** Include Multisite initialization functions */
     13// Require 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
     18// Allow custom multisite/network detection & configuration
    1719if ( defined( 'SUNRISE' ) ) {
    1820        include_once( WP_CONTENT_DIR . '/sunrise.php' );
    1921}
    2022
    21 /** Check for and define SUBDOMAIN_INSTALL and the deprecated VHOST constant. */
     23// Check for and define SUBDOMAIN_INSTALL (and the deprecated VHOST constant)
    2224ms_subdomain_constants();
    2325
    24 if ( !isset( $current_site ) || !isset( $current_blog ) ) {
     26// Attempt to set the relevant globals
     27ms_set_current_globals();
    2528
    26         // Given the domain and path, let's try to identify the network and site.
    27         // Usually, it's easier to query the site first, which declares its network.
    28         // In limited situations, though, we either can or must find the network first.
     29// $table_prefix can be set in sunrise.php
     30ms_set_table_prefix();
    2931
    30         $domain = strtolower( stripslashes( $_SERVER['HTTP_HOST'] ) );
    31         if ( substr( $domain, -3 ) == ':80' ) {
    32                 $domain = substr( $domain, 0, -3 );
    33                 $_SERVER['HTTP_HOST'] = substr( $_SERVER['HTTP_HOST'], 0, -3 );
    34         } elseif ( substr( $domain, -4 ) == ':443' ) {
    35                 $domain = substr( $domain, 0, -4 );
    36                 $_SERVER['HTTP_HOST'] = substr( $_SERVER['HTTP_HOST'], 0, -4 );
    37         }
     32// Setup site-switching globals
     33ms_set_switched_stacks();
    3834
    39         $path = stripslashes( $_SERVER['REQUEST_URI'] );
    40         if ( is_admin() ) {
    41                 $path = preg_replace( '#(.*)/wp-admin/.*#', '$1/', $path );
    42         }
    43         list( $path ) = explode( '?', $path );
    44 
    45         // If the network is defined in wp-config.php, we can simply use that.
    46         if ( defined( 'DOMAIN_CURRENT_SITE' ) && defined( 'PATH_CURRENT_SITE' ) ) {
    47                 $current_site = new stdClass;
    48                 $current_site->id = defined( 'SITE_ID_CURRENT_SITE' ) ? SITE_ID_CURRENT_SITE : 1;
    49                 $current_site->domain = DOMAIN_CURRENT_SITE;
    50                 $current_site->path = PATH_CURRENT_SITE;
    51                 if ( defined( 'BLOG_ID_CURRENT_SITE' ) ) {
    52                         $current_site->blog_id = BLOG_ID_CURRENT_SITE;
    53                 } elseif ( defined( 'BLOGID_CURRENT_SITE' ) ) { // deprecated.
    54                         $current_site->blog_id = BLOGID_CURRENT_SITE;
    55                 }
    56 
    57                 if ( 0 === strcasecmp( $current_site->domain, $domain ) && 0 === strcasecmp( $current_site->path, $path ) ) {
    58                         $current_blog = get_site_by_path( $domain, $path );
    59                 } elseif ( '/' !== $current_site->path && 0 === strcasecmp( $current_site->domain, $domain ) && 0 === stripos( $path, $current_site->path ) ) {
    60                         // If the current network has a path and also matches the domain and path of the request,
    61                         // we need to look for a site using the first path segment following the network's path.
    62                         $current_blog = get_site_by_path( $domain, $path, 1 + count( explode( '/', trim( $current_site->path, '/' ) ) ) );
    63                 } else {
    64                         // Otherwise, use the first path segment (as usual).
    65                         $current_blog = get_site_by_path( $domain, $path, 1 );
    66                 }
    67 
    68         } elseif ( ! is_subdomain_install() ) {
    69                 /*
    70                  * A "subdomain" install can be re-interpreted to mean "can support any domain".
    71                  * If we're not dealing with one of these installs, then the important part is determining
    72                  * the network first, because we need the network's path to identify any sites.
    73                  */
    74                 if ( ! $current_site = wp_cache_get( 'current_network', 'site-options' ) ) {
    75                         // Are there even two networks installed?
    76                         $one_network = $wpdb->get_row( "SELECT * FROM $wpdb->site LIMIT 2" ); // [sic]
    77                         if ( 1 === $wpdb->num_rows ) {
    78                                 $current_site = wp_get_network( $one_network );
    79                                 wp_cache_add( 'current_network', $current_site, 'site-options' );
    80                         } elseif ( 0 === $wpdb->num_rows ) {
    81                                 ms_not_installed();
    82                         }
    83                 }
    84                 if ( empty( $current_site ) ) {
    85                         $current_site = get_network_by_path( $domain, $path, 1 );
    86                 }
    87 
    88                 if ( empty( $current_site ) ) {
    89                         ms_not_installed();
    90                 } elseif ( $path === $current_site->path ) {
    91                         $current_blog = get_site_by_path( $domain, $path );
    92                 } else {
    93                         // Search the network path + one more path segment (on top of the network path).
    94                         $current_blog = get_site_by_path( $domain, $path, substr_count( $current_site->path, '/' ) );
    95                 }
    96         } else {
    97                 // Find the site by the domain and at most the first path segment.
    98                 $current_blog = get_site_by_path( $domain, $path, 1 );
    99                 if ( $current_blog ) {
    100                         $current_site = wp_get_network( $current_blog->site_id ? $current_blog->site_id : 1 );
    101                 } else {
    102                         // 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                 }
    105         }
    106 
    107         // The network declared by the site trumps any constants.
    108         if ( $current_blog && $current_blog->site_id != $current_site->id ) {
    109                 $current_site = wp_get_network( $current_blog->site_id );
    110         }
    111 
    112         // No network has been found, bail.
    113         if ( empty( $current_site ) ) {
    114                 ms_not_installed();
    115         }
    116 
    117         // @todo Investigate when exactly this can occur.
    118         if ( empty( $current_blog ) && defined( 'WP_INSTALLING' ) ) {
    119                 $current_blog = new stdClass;
    120                 $current_blog->blog_id = $blog_id = 1;
    121         }
    122 
    123         // No site has been found, bail.
    124         if ( empty( $current_blog ) ) {
    125                 // We're going to redirect to the network URL, with some possible modifications.
    126                 $scheme = is_ssl() ? 'https' : 'http';
    127                 $destination = "$scheme://{$current_site->domain}{$current_site->path}";
    128 
    129                 /**
    130                  * Fires when a network can be determined but a site cannot.
    131                  *
    132                  * At the time of this action, the only recourse is to redirect somewhere
    133                  * and exit. If you want to declare a particular site, do so earlier.
    134                  *
    135                  * @since 3.9.0
    136                  *
    137                  * @param object $current_site The network that had been determined.
    138                  * @param string $domain       The domain used to search for a site.
    139                  * @param string $path         The path used to search for a site.
    140                  */
    141                 do_action( 'ms_site_not_found', $current_site, $domain, $path );
    142 
    143                 if ( is_subdomain_install() && ! defined( 'NOBLOGREDIRECT' ) ) {
    144                         // For a "subdomain" install, redirect to the signup form specifically.
    145                         $destination .= 'wp-signup.php?new=' . str_replace( '.' . $current_site->domain, '', $domain );
    146                 } elseif ( is_subdomain_install() ) {
    147                         // For a "subdomain" install, the NOBLOGREDIRECT constant
    148                         // can be used to avoid a redirect to the signup form.
    149                         // Using the ms_site_not_found action is preferred to the constant.
    150                         if ( '%siteurl%' !== NOBLOGREDIRECT ) {
    151                                 $destination = NOBLOGREDIRECT;
    152                         }
    153                 } elseif ( 0 === strcasecmp( $current_site->domain, $domain ) ) {
    154                         /*
    155                          * If the domain we were searching for matches the network's domain,
    156                          * it's no use redirecting back to ourselves -- it'll cause a loop.
    157                          * As we couldn't find a site, we're simply not installed.
    158                          */
    159                         ms_not_installed();
    160                 }
    161 
    162                 header( 'Location: ' . $destination );
    163                 exit;
    164         }
    165 
    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 
    172         // Figure out the current network's main site.
    173         if ( ! isset( $current_site->blog_id ) ) {
    174                 if ( $current_blog->domain === $current_site->domain && $current_blog->path === $current_site->path ) {
    175                         $current_site->blog_id = $current_blog->blog_id;
    176                 } elseif ( ! $current_site->blog_id = wp_cache_get( 'network:' . $current_site->id . ':main_site', 'site-options' ) ) {
    177                         $current_site->blog_id = $wpdb->get_var( $wpdb->prepare( "SELECT blog_id FROM $wpdb->blogs WHERE domain = %s AND path = %s",
    178                                 $current_site->domain, $current_site->path ) );
    179                         wp_cache_add( 'network:' . $current_site->id . ':main_site', $current_site->blog_id, 'site-options' );
    180                 }
    181         }
    182 
    183         $blog_id = $current_blog->blog_id;
    184         $public  = $current_blog->public;
    185 
    186         if ( empty( $current_blog->site_id ) ) {
    187                 // This dates to [MU134] and shouldn't be relevant anymore,
    188                 // but it could be possible for arguments passed to insert_blog() etc.
    189                 $current_blog->site_id = 1;
    190         }
    191 
    192         $site_id = $current_blog->site_id;
    193         wp_load_core_site_options( $site_id );
    194 }
    195 
    196 $wpdb->set_prefix( $table_prefix, false ); // $table_prefix can be set in sunrise.php
    197 $wpdb->set_blog_id( $current_blog->blog_id, $current_blog->site_id );
    198 $table_prefix = $wpdb->get_blog_prefix();
    199 $_wp_switched_stack = array();
    200 $switched = false;
    201 
    20235// need to init cache again after blog_id is set
    20336wp_start_object_cache();
    20437
    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         }
    210 }
    211 
    21238// Define upload directory constants
    21339ms_upload_constants();
  • src/wp-includes/option.php

     
    180180}
    181181
    182182/**
    183  * Loads and caches certain often requested site options if is_multisite() and a persistent cache is not being used.
     183 * Loads and caches certain often requested site options if is_multisite() and
     184 * a persistent cache is not being used.
    184185 *
    185186 * @since 3.0.0
    186187 *
    187  * @param int $site_id Optional site ID for which to query the options. Defaults to the current site.
     188 * @param int $network_id Optional site ID for which to query the options.
     189 *                        Defaults to the current site.
    188190 */
    189 function wp_load_core_site_options( $site_id = null ) {
     191function wp_load_core_site_options( $network_id = null ) {
    190192        global $wpdb;
    191193
    192         if ( !is_multisite() || wp_using_ext_object_cache() || defined( 'WP_INSTALLING' ) )
     194        // Bail if not multisite
     195        if ( ! is_multisite() || wp_using_ext_object_cache() || defined( 'WP_INSTALLING' ) ) {
    193196                return;
     197        }
    194198
    195         if ( empty($site_id) )
    196                 $site_id = $wpdb->siteid;
     199        // Use the current
     200        if ( empty( $network_id ) ) {
     201                $network_id = $wpdb->siteid;
     202        }
    197203
    198         $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' );
    199 
    200         $core_options_in = "'" . implode("', '", $core_options) . "'";
    201         $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) );
    202 
    203         foreach ( $options as $option ) {
    204                 $key = $option->meta_key;
    205                 $cache_key = "{$site_id}:$key";
    206                 $option->meta_value = maybe_unserialize( $option->meta_value );
    207 
    208                 wp_cache_set( $cache_key, $option->meta_value, 'site-options' );
    209         }
     204        // Load core options for this network
     205        WP_Network::get_core_options( $network_id );
    210206}
    211207
    212208/**