WordPress.org

Make WordPress Core

Ticket #31985: 31985.3.diff

File 31985.3.diff, 41.3 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 4.3.0
     14 */
     15class WP_Network {
     16
     17        /**
     18         * Unique numerical ID.
     19         *
     20         * @since 4.3.0
     21         *
     22         * @var int
     23         */
     24        public $id = 0;
     25
     26        /**
     27         * Domain of this network.
     28         *
     29         * @since 4.3.0
     30         *
     31         * @var string
     32         */
     33        public $domain = '';
     34
     35        /**
     36         * Path of this network
     37         *
     38         * @since 4.3.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. This site is
     48         * mapped to the network on network creation.
     49         *
     50         * @since 4.3.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.3.0
     60         *
     61         * @var int
     62         */
     63        public $cookie_domain = '';
     64
     65        /**
     66         * Array of core options for this network.
     67         *
     68         * @since 4.3.0
     69         *
     70         * @var array
     71         */
     72        public $core_options = array();
     73
     74        /**
     75         * Name of this network.
     76         *
     77         * Named "site" vs. "network" for legacy reasons.
     78         *
     79         * @since 4.3.0
     80         *
     81         * @var string
     82         */
     83        public $site_name = '';
     84
     85        /**
     86         * Create a new WP_Network object.
     87         *
     88         * Will populate all relevant data if a network ID or object is passed.
     89         *
     90         * @since 4.3.0
     91         *
     92         * @param mixed $network stdClass object or network ID
     93         * @return WP_Network|null
     94         */
     95        public function __construct( $network = false ) {
     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                return;
     117        }
     118
     119        /**
     120         * Fill the properties of the network object.
     121         *
     122         * @since 4.3.0
     123         *
     124         * @param int $network_id ID of the network.
     125         * @return bool False if no network is available to update. True otherwise.
     126         */
     127        public function populate( $network_id = 0 ) {
     128                // Query for the network by ID.
     129                $network = self::get_by_id( $network_id );
     130
     131                // Bail if no matching network exists.
     132                if ( false === $network ) {
     133                        return false;
     134                }
     135
     136                // Set the main data.
     137                $this->set_main_data( $network );
     138
     139                // Set supplemental data based on the needs of multisite.
     140                $this->set_cookie_domain();
     141                $this->set_core_options();
     142                $this->set_site_name();
     143
     144                return true;
     145        }
     146
     147        /**
     148         * Set the network ID, domain, and path properties.
     149         *
     150         * @since 4.3.0
     151         */
     152        private function set_main_data( $network ) {
     153                $this->id     = $network->id;
     154                $this->domain = $network->domain;
     155                $this->path   = $network->path;
     156        }
     157
     158        /**
     159         * Set the cookie domain, based on the network domain.
     160         *
     161         * @todo What if the domain of the network doesn't match the current site?
     162         *
     163         * @since 4.3.0
     164         */
     165        private function set_cookie_domain() {
     166                $this->cookie_domain = $this->domain;
     167                if ( 'www.' === substr( $this->cookie_domain, 0, 4 ) ) {
     168                        $this->cookie_domain = substr( $this->cookie_domain, 4 );
     169                }
     170        }
     171
     172        /**
     173         * Set the core options for this network object.
     174         *
     175         * @since 4.3.0
     176         */
     177        private function set_core_options() {
     178                $this->core_options = self::get_core_options( $this->id );
     179        }
     180
     181        /**
     182         * Set the name for this network's object.
     183         *
     184         * @todo What if the domain of the network doesn't match the current site?
     185         *
     186         * @since 4.3.0
     187         */
     188        private function set_site_name() {
     189                // Attempt to use core option
     190                if ( ! empty( $this->core_options['site_name'] ) ) {
     191                        $this->site_name = $this->core_options['site_name'];
     192
     193                // Fallback to domain
     194                } else {
     195                        $this->site_name = ucfirst( $this->domain );
     196                }
     197        }
     198
     199        /**
     200         * Retrieve a network from the database by its ID.
     201         *
     202         * @since 4.3.0
     203         *
     204         * @global wpdb $wpdb
     205         *
     206         * @param int $network_id The ID of the network to retrieve.
     207         *
     208         * @return bool|object
     209         */
     210        public static function get_by_id( $network_id = 0 ) {
     211                global $wpdb;
     212
     213                // Check cache
     214                $network = wp_cache_get( $network_id, 'networks' );
     215
     216                // Cache miss
     217                if ( false === $network ) {
     218                        // Query for network data
     219                        $network = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->site} WHERE id = %d", $network_id ) );
     220
     221                        // Bail if network cannot be found
     222                        if ( empty( $network ) || is_wp_error( $network ) ) {
     223                                $network = false;
     224                        }
     225
     226                        // Add network to cache
     227                        wp_cache_add( $network_id, $network, 'networks' );
     228                }
     229
     230                // Return the network
     231                return $network;
     232        }
     233
     234        /**
     235         * Retrieve a network by its domain and path.
     236         *
     237         * @since 4.3.0
     238         *
     239         * @param string   $domain   Domain to check.
     240         * @param string   $path     Path to check.
     241         * @param int|null $segments Path segments to use. Defaults to null, or the full path.
     242         *
     243         * @return object|bool Network object if successful. False when no network is found.
     244         */
     245        public static function get_by_path( $domain = '', $path = '', $segments = null ) {
     246                global $wpdb;
     247
     248                $domains = array( $domain );
     249                $pieces  = explode( '.', $domain );
     250
     251                /*
     252                 * It's possible one domain to search is 'com', but it might as well
     253                 * be 'localhost' or some other locally mapped domain.
     254                 */
     255                while ( array_shift( $pieces ) ) {
     256                        if ( ! empty( $pieces ) ) {
     257                                $domains[] = implode( '.', $pieces );
     258                        }
     259                }
     260
     261                /*
     262                 * If we've gotten to this function during normal execution, there is
     263                 * more than one network installed. At this point, who knows how many
     264                 * we have. Attempt to optimize for the situation where networks are
     265                 * only domains, thus meaning paths never need to be considered.
     266                 *
     267                 * This is a very basic optimization; anything further could have
     268                 * drawbacks depending on the setup, so this is best done per-install.
     269                 */
     270                $using_paths = true;
     271                if ( wp_using_ext_object_cache() ) {
     272                        $using_paths = wp_cache_get( 'networks_have_paths', 'site-options' );
     273                        if ( false === $using_paths ) {
     274                                $using_paths = (bool) $wpdb->get_var( "SELECT id FROM {$wpdb->site} WHERE path <> '/' LIMIT 1" );
     275                                wp_cache_add( 'networks_have_paths', (int) $using_paths, 'site-options'  );
     276                        }
     277                }
     278
     279                $paths = array();
     280                if ( true === $using_paths ) {
     281                        $path_segments = array_filter( explode( '/', trim( $path, '/' ) ) );
     282
     283                        /**
     284                         * Filter the number of path segments to consider when searching for a site.
     285                         *
     286                         * @since 3.9.0
     287                         *
     288                         * @param int|null $segments The number of path segments to consider. WordPress by default looks at
     289                         *                           one path segment. The function default of null only makes sense when you
     290                         *                           know the requested path should match a network.
     291                         * @param string   $domain   The requested domain.
     292                         * @param string   $path     The requested path, in full.
     293                         */
     294                        $segments = apply_filters( 'network_by_path_segments_count', $segments, $domain, $path );
     295
     296                        if ( ( null !== $segments ) && count( $path_segments ) > $segments ) {
     297                                $path_segments = array_slice( $path_segments, 0, $segments );
     298                        }
     299
     300                        while ( count( $path_segments ) ) {
     301                                $paths[] = '/' . implode( '/', $path_segments ) . '/';
     302                                array_pop( $path_segments );
     303                        }
     304
     305                        $paths[] = '/';
     306                }
     307
     308                /**
     309                 * Determine a network by its domain and path.
     310                 *
     311                 * This allows one to short-circuit the default logic, perhaps by
     312                 * replacing it with a routine that is more optimal for your setup.
     313                 *
     314                 * Return null to avoid the short-circuit. Return false if no network
     315                 * can be found at the requested domain and path. Otherwise, return
     316                 * an object from wp_get_network().
     317                 *
     318                 * @since 3.9.0
     319                 *
     320                 * @param null|bool|object $network  Network value to return by path.
     321                 * @param string           $domain   The requested domain.
     322                 * @param string           $path     The requested path, in full.
     323                 * @param int|null         $segments The suggested number of paths to consult.
     324                 *                                   Default null, meaning the entire path was to be consulted.
     325                 * @param array            $paths    The paths to search for, based on $path and $segments.
     326                 */
     327                $pre = apply_filters( 'pre_get_network_by_path', null, $domain, $path, $segments, $paths );
     328                if ( null !== $pre ) {
     329                        return $pre;
     330                }
     331
     332                // @todo Consider additional optimization routes, perhaps as an opt-in for plugins.
     333                // We already have paths covered. What about how far domains should be drilled down (including www)?
     334
     335                $search_domains = "'" . implode( "', '", $wpdb->_escape( $domains ) ) . "'";
     336
     337                if ( false === $using_paths ) {
     338                        $network = $wpdb->get_row( "SELECT * FROM {$wpdb->site}
     339                                                                                WHERE domain IN ({$search_domains})
     340                                                                                ORDER BY CHAR_LENGTH(domain)
     341                                                                                DESC LIMIT 1" );
     342
     343                        if ( ! empty( $network ) && ! is_wp_error( $network ) ) {
     344                                return new WP_Network( $network->id );
     345                        }
     346
     347                        return false;
     348
     349                } else {
     350                        $search_paths = "'" . implode( "', '", $wpdb->_escape( $paths ) ) . "'";
     351                        $networks     = $wpdb->get_results( "SELECT * FROM {$wpdb->site}
     352                                                                                                 WHERE domain IN ({$search_domains})
     353                                                                                                 AND path IN ({$search_paths})
     354                                                                                                 ORDER BY CHAR_LENGTH(domain) DESC, CHAR_LENGTH(path) DESC" );
     355                }
     356
     357                /*
     358                 * Domains are sorted by length of domain, then by length of path.
     359                 * The domain must match for the path to be considered. Otherwise,
     360                 * a network with the path of / will suffice.
     361                 */
     362                $found = false;
     363                foreach ( $networks as $network ) {
     364                        if ( ( $network->domain === $domain ) || ( "www.{$network->domain}" === $domain ) ) {
     365                                if ( in_array( $network->path, $paths, true ) ) {
     366                                        $found = true;
     367                                        break;
     368                                }
     369                        }
     370                        if ( $network->path === '/' ) {
     371                                $found = true;
     372                                break;
     373                        }
     374                }
     375
     376                if ( true === $found ) {
     377                        return new WP_Network( $network->id );
     378                }
     379
     380                return false;
     381        }
     382
     383        /**
     384         * Get the set of core options for the network from the database.
     385         *
     386         * @since 4.3.0
     387         *
     388         * @param int $network_id
     389         *
     390         * @return array
     391         */
     392        public static function get_core_options( $network_id = 0 ) {
     393                global $wpdb;
     394
     395                // Setup core options query
     396                $meta_keys       = self::get_core_option_keys();
     397                $core_options_in = "'" . implode( "', '", $meta_keys ) . "'";
     398                $sql             = $wpdb->prepare( "SELECT meta_key, meta_value
     399                                                                                        FROM {$wpdb->sitemeta}
     400                                                                                        WHERE meta_key IN ({$core_options_in})
     401                                                                                        AND site_id = %d", $network_id );
     402
     403                // Query for core network options
     404                $options = $wpdb->get_results( $sql );
     405
     406                // Bail if no options found
     407                if ( empty( $options ) || is_wp_error( $options ) ) {
     408                        return array();
     409                }
     410
     411                // Setup return value array
     412                $retval = array();
     413
     414                // Loop through options and add them to the object cache
     415                foreach ( $options as $option ) {
     416
     417                        // Setup option values to cache
     418                        $key                = $option->meta_key;
     419                        $cache_key          = "{$network_id}:$key";
     420                        $option->meta_value = maybe_unserialize( $option->meta_value );
     421
     422                        // Cache the option value
     423                        wp_cache_set( $cache_key, $option->meta_value, 'site-options' );
     424
     425                        // Add option to array
     426                        $retval[ $option->meta_key ] = $option->meta_value;
     427                }
     428
     429                // Return the options
     430                return $retval;
     431        }
     432
     433        /*
     434         * Provide a list of core option keys used for network metadata.
     435         *
     436         * @since 4.3.0
     437         *
     438         * @return array
     439         */
     440        public static function get_core_option_keys() {
     441                return array(
     442                        'site_name',
     443                        'siteurl',
     444                        'active_sitewide_plugins',
     445                        '_site_transient_timeout_theme_roots',
     446                        '_site_transient_theme_roots',
     447                        'site_admins',
     448                        'can_compress_scripts',
     449                        'global_terms_enabled',
     450                        'ms_files_rewriting'
     451                );
     452        }
     453
     454        /**
     455         * Get all active network-wide plugins for this network.
     456         *
     457         * @since 4.3.0
     458         *
     459         * @param int $network_id
     460         * @return array
     461         */
     462        public static function get_active_network_plugins( $network_id = 0 ) {
     463                $network_options = self::get_core_options( $network_id );
     464
     465                if ( empty( $network_options['active_sitewide_plugins'] ) ) {
     466                        return array();
     467                }
     468                $active_plugins  = $network_options['active_sitewide_plugins'];
     469
     470                // Setup the plugins array
     471                $plugins        = array();
     472                $active_plugins = array_keys( $active_plugins );
     473
     474                // Sort plugins array
     475                sort( $active_plugins );
     476
     477                foreach ( $active_plugins as $plugin ) {
     478                        if ( self::validate_network_plugin( $plugin ) ) {
     479                                $plugins[] = WP_PLUGIN_DIR . '/' . $plugin;
     480                        }
     481                }
     482
     483                return $plugins;
     484        }
     485
     486        /**
     487         * Ensure a single network-wide plugin is valid.
     488         *
     489         * @since 4.3.0
     490         *
     491         * @param string $plugin
     492         *
     493         * @return bool True if the plugin specified is valid. False if it is not.
     494         */
     495        private static function validate_network_plugin( $plugin = '' ) {
     496
     497                // $plugin must validate as file
     498                if ( ! validate_file( $plugin ) ) {
     499                        return false;
     500                }
     501
     502                // $plugin must end with '.php'
     503                if ( '.php' !== substr( $plugin, -4 ) ) {
     504                        return false;
     505                }
     506
     507                // $plugin must exist
     508                if ( ! file_exists( WP_PLUGIN_DIR . '/' . $plugin ) ) {
     509                        return false;
     510                }
     511
     512                return true;
     513        }
     514
     515        public static function add( $domain = '', $path = '' ) {
     516               
     517        }
     518
     519        /**
     520         * Update a networks domain & path based on its ID.
     521         *
     522         * @since 4.3.0
     523         *
     524         * @global wpdb $wpdb
     525         *
     526         * @param int    $network_id Existing network ID.
     527         * @param string $domain     New network domain.
     528         * @param string $path       New network path.
     529         *
     530         * @return mixed False if network not found. Results of query if updated.
     531         */
     532        public static function update( $network_id = 0, $domain = '', $path = '' ) {
     533                global $wpdb;
     534
     535                // Does network exist
     536                $network = self::get_by_id( $network_id );
     537
     538                // Bail if no network to update
     539                if ( false === $network ) {
     540                        return false;
     541                }
     542
     543                // Set the domain
     544                if ( ! empty( $domain ) ) {
     545                        $network->domain = $domain;
     546                }
     547
     548                // Set the path
     549                if ( ! empty( $path ) ) {
     550                        $network->path = $path;
     551                }
     552
     553                // Query for network data
     554                $sql     = $wpdb->prepare( "UPDATE {$wpdb->site} SET domain = %s, path = %s WHERE id = %d", $network->domain, $network->path, $network->id );
     555                $network = $wpdb->query( $sql );
     556
     557                // Bail if network cannot be found
     558                if ( empty( $network ) || is_wp_error( $network ) ) {
     559                        return false;
     560                }
     561
     562                // Return results of WPDB::query()
     563                return $network;
     564        }
     565
     566        public static function delete( $network_id = 0 ) {
     567                global $wpdb;
     568
     569                // Does network exist
     570                $network = self::get_by_id( $network_id );
     571
     572                // Bail if no network to delete
     573                if ( false === $network ) {
     574                        return false;
     575                }
     576
     577                // Query for network data
     578                $sql     = $wpdb->prepare( "DELETE FROM {$wpdb->site} WHERE id = %d", $network->id );
     579                $network = $wpdb->query( $sql );
     580        }
     581}
  • 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        if ( defined( 'SUBDOMAIN_INSTALL' ) ) {
    2020                return SUBDOMAIN_INSTALL;
     21        }
    2122
    2223        return ( defined( 'VHOST' ) && VHOST == 'yes' );
    2324}
     
    4445
    4546        foreach ( $active_plugins as $plugin ) {
    4647                if ( ! validate_file( $plugin ) // $plugin must validate as file
    47                         && '.php' == substr( $plugin, -4 ) // $plugin must end with '.php'
    48                         && file_exists( WP_PLUGIN_DIR . '/' . $plugin ) // $plugin must exist
    49                         )
    50                 $plugins[] = WP_PLUGIN_DIR . '/' . $plugin;
     48                     && '.php' == substr( $plugin, -4 ) // $plugin must end with '.php'
     49                     && file_exists( WP_PLUGIN_DIR . '/' . $plugin ) // $plugin must exist
     50                )
     51                        $plugins[] = WP_PLUGIN_DIR . '/' . $plugin;
    5152        }
    5253        return $plugins;
    5354}
     
    110111}
    111112
    112113/**
    113  * Retrieve a network object by its domain and path.
     114 * Setup multisite globals.
    114115 *
    115  * @since 3.9.0
     116 * @since 4.3.0
    116117 *
    117  * @global wpdb $wpdb
     118 * @global object $current_site
     119 * @global object $current_blog
     120 * @global wpdb   $wpdb
    118121 *
    119  * @param string   $domain   Domain to check.
    120  * @param string   $path     Path to check.
    121  * @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.
     122 * @return null If already set by sunrise.php.
    123123 */
    124 function get_network_by_path( $domain, $path, $segments = null ) {
    125         global $wpdb;
     124function ms_set_current_globals() {
     125        global $current_site, $current_blog, $wpdb;
    126126
    127         $domains = array( $domain );
    128         $pieces = explode( '.', $domain );
     127        // Bail if network and site are already set
     128        if ( isset( $current_site ) && isset( $current_blog ) ) {
     129                return;
     130        }
    129131
    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                 }
     132        // Given the domain and path, let's try to identify the network and site.
     133        // Usually, it's easier to query the site first, which declares its network.
     134        // In limited situations, though, we either can or must find the network first.
     135
     136        $domain = strtolower( stripslashes( $_SERVER['HTTP_HOST'] ) );
     137        if ( substr( $domain, -3 ) == ':80' ) {
     138                $domain               = substr( $domain, 0, -3 );
     139                $_SERVER['HTTP_HOST'] = substr( $_SERVER['HTTP_HOST'], 0, -3 );
     140        } elseif ( substr( $domain, -4 ) == ':443' ) {
     141                $domain               = substr( $domain, 0, -4 );
     142                $_SERVER['HTTP_HOST'] = substr( $_SERVER['HTTP_HOST'], 0, -4 );
    138143        }
    139144
     145        $path = stripslashes( $_SERVER['REQUEST_URI'] );
     146        if ( is_admin() ) {
     147                $path = preg_replace( '#(.*)/wp-admin/.*#', '$1/', $path );
     148        }
     149        list( $path ) = explode( '?', $path );
     150
     151        // If the network is defined in wp-config.php, we can simply use that.
     152        if ( defined( 'DOMAIN_CURRENT_SITE' ) && defined( 'PATH_CURRENT_SITE' ) ) {
     153                $current_site         = new WP_Network();
     154                $current_site->id     = defined( 'SITE_ID_CURRENT_SITE' ) ? SITE_ID_CURRENT_SITE : 1;
     155                $current_site->domain = DOMAIN_CURRENT_SITE;
     156                $current_site->path   = PATH_CURRENT_SITE;
     157
     158                if ( defined( 'BLOG_ID_CURRENT_SITE' ) ) {
     159                        $current_site->blog_id = BLOG_ID_CURRENT_SITE;
     160                } elseif ( defined( 'BLOGID_CURRENT_SITE' ) ) { // deprecated.
     161                        $current_site->blog_id = BLOGID_CURRENT_SITE;
     162                }
     163
     164                if ( 0 === strcasecmp( $current_site->domain, $domain ) && 0 === strcasecmp( $current_site->path, $path ) ) {
     165                        $current_blog = get_site_by_path( $domain, $path );
     166
     167                // If the current network has a path and also matches the domain and
     168                // path of the request, we need to look for a site using the first path
     169                // segment following the network's path.
     170                } elseif ( '/' !== $current_site->path && 0 === strcasecmp( $current_site->domain, $domain ) && 0 === stripos( $path, $current_site->path ) ) {
     171                        $current_blog = get_site_by_path( $domain, $path, 1 + count( explode( '/', trim( $current_site->path, '/' ) ) ) );
     172
     173                // Otherwise, use the first path segment (as usual).
     174                } else {
     175                        $current_blog = get_site_by_path( $domain, $path, 1 );
     176                }
     177
    140178        /*
    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.
     179         * A "subdomain" install can be re-interpreted to mean "can support anydomain".
     180         * If we're not dealing with one of these installs, then the important part
     181         * is determining the network first, because we need the network's path to
     182         * identify any sites.
    148183         */
    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'  );
     184        } elseif ( ! is_subdomain_install() ) {
     185
     186                // Attempt to get the current network from cache
     187                $current_site = wp_cache_get( 'current_network', 'site-options' );
     188                if ( false === $current_site ) {
     189
     190                        // Are there even two networks installed?
     191                        $one_network = $wpdb->get_row( "SELECT * FROM {$wpdb->site} LIMIT 2" );
     192                        if ( 1 === $wpdb->num_rows ) {
     193                                $current_site = new WP_Network( $one_network );
     194                                wp_cache_add( 'current_network', $current_site, 'site-options' );
     195                        } elseif ( 0 === $wpdb->num_rows ) {
     196                                ms_not_installed();
     197                        }
    155198                }
    156         }
    157199
    158         $paths = array();
    159         if ( $using_paths ) {
    160                 $path_segments = array_filter( explode( '/', trim( $path, "/" ) ) );
     200                // No network found, so attempt to query it by path
     201                if ( empty( $current_site ) ) {
     202                        $current_site = get_network_by_path( $domain, $path, 1 );
     203                }
    161204
    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 );
     205                // Bail if no network found
     206                if ( empty( $current_site ) ) {
     207                        ms_not_installed();
     208       
     209                // Set current site if path matches
     210                } elseif ( $path === $current_site->path ) {
     211                        $current_blog = get_site_by_path( $domain, $path );
    174212
    175                 if ( null !== $segments && count($path_segments ) > $segments ) {
    176                         $path_segments = array_slice( $path_segments, 0, $segments );
     213                // Search the network path + one more path segment (on top of the network path).
     214                } else {
     215                        $current_blog = get_site_by_path( $domain, $path, substr_count( $current_site->path, '/' ) );
    177216                }
     217        } else {
    178218
    179                 while ( count( $path_segments ) ) {
    180                         $paths[] = '/' . implode( '/', $path_segments ) . '/';
    181                         array_pop( $path_segments );
     219                // Find the site by the domain and at most the first path segment.
     220                $current_blog = get_site_by_path( $domain, $path, 1 );
     221                if ( ! empty( $current_blog ) ) {
     222                        $network_id   = $current_blog->site_id ? $current_blog->site_id : 1;
     223                        $current_site = new WP_Network( $network_id );
     224
     225                // If you don't have a site with the same domain/path as a network,
     226                // you're pretty screwed, but...
     227                } else {
     228                        $current_site = get_network_by_path( $domain, $path, 1 );
    182229                }
     230        }
    183231
    184                 $paths[] = '/';
     232        // The network declared by the site trumps any constants.
     233        if ( ! empty( $current_blog ) && ( $current_blog->site_id !== $current_site->id ) ) {
     234                $current_site = new WP_Network( $current_blog->site_id );
    185235        }
    186236
    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;
     237        // Bail if no network has been found by now
     238        if ( empty( $current_site ) ) {
     239                ms_not_installed();
    209240        }
    210241
    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)?
     242        // @todo Investigate when exactly this can occur.
     243        if ( empty( $current_blog ) && defined( 'WP_INSTALLING' ) ) {
     244                $current_blog = new stdClass;
     245                $current_blog->blog_id = $blog_id = 1;
     246        }
    213247
    214         $search_domains = "'" . implode( "', '", $wpdb->_escape( $domains ) ) . "'";
     248        // No site has been found, bail.
     249        if ( empty( $current_blog ) ) {
    215250
    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;
     251                // We're going to redirect to the network URL, with some possible modifications.
     252                $scheme      = is_ssl() ? 'https' : 'http';
     253                $destination = "$scheme://{$current_site->domain}{$current_site->path}";
    223254
    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         }
     255                /**
     256                 * Fires when a network can be determined but a site cannot.
     257                 *
     258                 * At the time of this action, the only recourse is to redirect somewhere
     259                 * and exit. If you want to declare a particular site, do so earlier.
     260                 *
     261                 * @since 3.9.0
     262                 *
     263                 * @param object $current_site The network that had been determined.
     264                 * @param string $domain       The domain used to search for a site.
     265                 * @param string $path         The path used to search for a site.
     266                 */
     267                do_action( 'ms_site_not_found', $current_site, $domain, $path );
    230268
    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;
     269                // For a "subdomain" install, redirect to the signup form specifically.
     270                if ( is_subdomain_install() && ! defined( 'NOBLOGREDIRECT' ) ) {
     271                        $destination .= 'wp-signup.php?new=' . str_replace( '.' . $current_site->domain, '', $domain );
     272
     273                // For a "subdomain" install, the NOBLOGREDIRECT constant
     274                // can be used to avoid a redirect to the signup form.
     275                // Using the ms_site_not_found action is preferred to the constant.
     276                } elseif ( is_subdomain_install() ) {
     277                        if ( '%siteurl%' !== NOBLOGREDIRECT ) {
     278                                $destination = NOBLOGREDIRECT;
    242279                        }
     280
     281                // If the domain we were searching for matches the network's domain,
     282                // it's no use redirecting back to ourselves -- it'll cause a loop.
     283                // As we couldn't find a site, we're simply not installed.
     284                } elseif ( 0 === strcasecmp( $current_site->domain, $domain ) ) {
     285                        ms_not_installed();
    243286                }
    244                 if ( $network->path === '/' ) {
    245                         $found = true;
    246                         break;
    247                 }
     287
     288                header( 'Location: ' . $destination );
     289                exit;
    248290        }
    249291
    250         if ( $found ) {
    251                 return wp_get_network( $network );
     292        // Figure out the current network's main site.
     293        if ( ! isset( $current_site->blog_id ) || 0 === $current_site->blog_id ) {
     294                if ( ( $current_blog->domain === $current_site->domain ) && ( $current_blog->path === $current_site->path ) ) {
     295                        $current_site->blog_id = $current_blog->blog_id;
     296                } elseif ( ! $current_site->blog_id = wp_cache_get( 'network:' . $current_site->id . ':main_site', 'site-options' ) ) {
     297                        $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 ) );
     298                        wp_cache_add( 'network:' . $current_site->id . ':main_site', $current_site->blog_id, 'site-options' );
     299                }
    252300        }
    253301
    254         return false;
     302        // Set some legacy globals
     303        $GLOBALS['blog_id'] = $current_blog->blog_id;
     304        $GLOBALS['public']  = $current_blog->public;
     305        $GLOBALS['site_id'] = $current_blog->site_id;
     306
     307        // Load the site options
     308        wp_load_core_site_options( $current_site->id );
    255309}
    256310
    257311/**
     312 * Set the table prefix according to current site
     313 *
     314 * @since 4.3.0
     315 *
     316 * @global object $wpdb
     317 * @global string $table_prefix
     318 * @global object $current_blog
     319 */
     320function ms_set_table_prefix() {
     321        global $wpdb, $table_prefix, $current_blog;
     322
     323        $wpdb->set_prefix( $table_prefix, false );
     324        $wpdb->set_blog_id( $current_blog->blog_id, $current_blog->site_id );
     325        $table_prefix = $wpdb->get_blog_prefix();
     326}
     327
     328/**
     329 * Set globals used when switching between sites
     330 *
     331 * @since 4.3.0
     332 *
     333 * @see switch_to_blog()
     334 * @see restore_current_blog()
     335 */
     336function ms_set_switched_stacks() {
     337        $GLOBALS['_wp_switched_stack'] = array();
     338        $GLOBALS['switched']           = false;
     339}
     340
     341/**
     342 * Retrieve a network object by its domain and path.
     343 *
     344 * @since 3.9.0
     345 *
     346 * @param string   $domain   Domain to check.
     347 * @param string   $path     Path to check.
     348 * @param int|null $segments Path segments to use. Defaults to null, or the full path.
     349 * @return object|bool Network object if successful. False when no network is found.
     350 */
     351function get_network_by_path( $domain, $path, $segments = null ) {
     352        return WP_Network::get_by_path( $domain, $path, $segments );
     353}
     354
     355/**
    258356 * Retrieve an object containing information about the requested network.
    259357 *
    260358 * @since 3.9.0
    261359 *
    262  * @global wpdb $wpdb
    263  *
    264360 * @param object|int $network The network's database row or ID.
    265361 * @return object|false Object containing network information if found, false if not.
    266362 */
    267363function wp_get_network( $network ) {
    268         global $wpdb;
    269 
    270         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                 }
    275         }
    276 
    277         return $network;
     364        return new WP_Network( $network );
    278365}
    279366
    280367/**
  • 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

     
    184184}
    185185
    186186/**
    187  * Loads and caches certain often requested site options if is_multisite() and a persistent cache is not being used.
     187 * Loads and caches certain often requested site options if is_multisite() and
     188 * a persistent cache is not being used.
    188189 *
    189190 * @since 3.0.0
    190191 *
    191192 * @global wpdb $wpdb
    192193 *
    193  * @param int $site_id Optional site ID for which to query the options. Defaults to the current site.
     194 * @param int $network_id Optional site ID for which to query the options.
     195 *                        Defaults to the current site.
    194196 */
    195 function wp_load_core_site_options( $site_id = null ) {
     197function wp_load_core_site_options( $network_id = null ) {
    196198        global $wpdb;
    197199
    198         if ( !is_multisite() || wp_using_ext_object_cache() || defined( 'WP_INSTALLING' ) )
     200        // Bail if not multisite
     201        if ( ! is_multisite() || wp_using_ext_object_cache() || defined( 'WP_INSTALLING' ) ) {
    199202                return;
     203        }
    200204
    201         if ( empty($site_id) )
    202                 $site_id = $wpdb->siteid;
     205        // Use the current
     206        if ( empty( $network_id ) ) {
     207                $network_id = $wpdb->siteid;
     208        }
    203209
    204         $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' );
    205 
    206         $core_options_in = "'" . implode("', '", $core_options) . "'";
    207         $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) );
    208 
    209         foreach ( $options as $option ) {
    210                 $key = $option->meta_key;
    211                 $cache_key = "{$site_id}:$key";
    212                 $option->meta_value = maybe_unserialize( $option->meta_value );
    213 
    214                 wp_cache_set( $cache_key, $option->meta_value, 'site-options' );
    215         }
     210        // Load core options for this network
     211        WP_Network::get_core_options( $network_id );
    216212}
    217213
    218214/**