Make WordPress Core

Ticket #31985: 31985.01.patch

File 31985.01.patch, 40.2 KB (added by johnjamesjacoby, 10 years ago)

First pass at WP_Network class

  • 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 = '';
     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 $options;
     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                $active_plugins  = $network_options['active_sitewide_plugins'];
     425                if ( empty( $active_plugins ) ) {
     426                        return array();
     427                }
     428
     429                // Setup the plugins array
     430                $plugins        = array();
     431                $active_plugins = array_keys( $active_plugins );
     432
     433                // Sort plugins array
     434                sort( $active_plugins );
     435
     436                foreach ( $active_plugins as $plugin ) {
     437                        if ( self::validate_network_plugin( $plugin ) ) {
     438                                $plugins[] = WP_PLUGIN_DIR . '/' . $plugin;
     439                        }
     440                }
     441
     442                return $plugins;
     443        }
     444
     445        /**
     446         * Ensure a single network-wide plugin is valid
     447         *
     448         * @since WordPress (4.3.0)
     449         *
     450         * @param type $plugin
     451         *
     452         * @return boolean
     453         */
     454        private static function validate_network_plugin( $plugin = '' ) {
     455
     456                // $plugin must validate as file
     457                if ( ! validate_file( $plugin ) ) {
     458                        return false;
     459                }
     460
     461                // $plugin must end with '.php'
     462                if ( '.php' !== substr( $plugin, -4 ) ) {
     463                        return false;
     464                }
     465
     466                // $plugin must exist
     467                if ( ! file_exists( WP_PLUGIN_DIR . '/' . $plugin ) ) {
     468                        return false;
     469                }
     470
     471                return true;
     472        }
     473
     474        public static function add( $domain = '', $path = '' ) {
     475               
     476        }
     477
     478        /**
     479         * Update a networks domain & path based on its ID
     480         *
     481         * @since WordPress (4.3.0)
     482         *
     483         * @global type $wpdb
     484         *
     485         * @param int    $network_id Existing network ID
     486         * @param string $domain     New network domain
     487         * @param string $path       New network path
     488         *
     489         * @return mixed False if network not found. Results of query if updated.
     490         */
     491        public static function update( $network_id = 0, $domain = '', $path = '' ) {
     492                global $wpdb;
     493
     494                // Does network exist
     495                $network = self::get_by_id( $network_id );
     496
     497                // Bail if no network to update
     498                if ( false === $network ) {
     499                        return false;
     500                }
     501
     502                // Set the domain
     503                if ( ! empty( $domain ) ) {
     504                        $network->domain = $domain;
     505                }
     506
     507                // Set the path
     508                if ( ! empty( $path ) ) {
     509                        $network->path = $path;
     510                }
     511
     512                // Query for network data
     513                $sql     = $wpdb->prepare( "UPDATE {$wpdb->site} SET domain = %s, path = %s WHERE id = %d", $network->domain, $network->path, $network->id );
     514                $network = $wpdb->query( $sql );
     515
     516                // Bail if network cannot be found
     517                if ( empty( $network ) || is_wp_error( $network ) ) {
     518                        return false;
     519                }
     520
     521                // Return results of WPDB::query()
     522                return $network;
     523        }
     524
     525        public static function delete( $network_id = 0 ) {
     526                global $wpdb;
     527
     528                // Does network exist
     529                $network = self::get_by_id( $network_id );
     530
     531                // Bail if no network to delete
     532                if ( false === $network ) {
     533                        return false;
     534                }
     535
     536                // Query for network data
     537                $sql     = $wpdb->prepare( "DELETE FROM {$wpdb->site} WHERE id = %d", $network->id );
     538                $network = $wpdb->query( $sql );
     539        }
     540}
  • 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}
     
    3740 * @return array Files to include.
    3841 */
    3942function wp_get_active_network_plugins() {
    40         $active_plugins = (array) get_site_option( 'active_sitewide_plugins', array() );
    41         if ( empty( $active_plugins ) )
    42                 return array();
    43 
    44         $plugins = array();
    45         $active_plugins = array_keys( $active_plugins );
    46         sort( $active_plugins );
    47 
    48         foreach ( $active_plugins as $plugin ) {
    49                 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;
    54         }
    55         return $plugins;
     43        return WP_Network::get_active_network_plugins();
    5644}
    5745
    5846/**
     
    113101}
    114102
    115103/**
    116  * Retrieve a network object by its domain and path.
     104 * Set relevant multisite globals
    117105 *
    118  * @since 3.9.0
     106 * @since WordPress (4.3.0)
    119107 *
    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.
     108 * @global object $current_site
     109 * @global object $current_blog
     110 * @global object $wpdb
     111 *
     112 * @return if already set by sunrise.php
    124113 */
    125 function get_network_by_path( $domain, $path, $segments = null ) {
    126         global $wpdb;
     114function ms_set_current_globals() {
     115        global $current_site, $current_blog, $wpdb;
    127116
    128         $domains = array( $domain );
    129         $pieces = explode( '.', $domain );
     117        // Bail if network and site are already set
     118        if ( isset( $current_site ) && isset( $current_blog ) ) {
     119                return;
     120        }
    130121
    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                 }
     122        // Given the domain and path, let's try to identify the network and site.
     123        // Usually, it's easier to query the site first, which declares its network.
     124        // In limited situations, though, we either can or must find the network first.
     125
     126        $domain = strtolower( stripslashes( $_SERVER['HTTP_HOST'] ) );
     127        if ( substr( $domain, -3 ) == ':80' ) {
     128                $domain               = substr( $domain, 0, -3 );
     129                $_SERVER['HTTP_HOST'] = substr( $_SERVER['HTTP_HOST'], 0, -3 );
     130        } elseif ( substr( $domain, -4 ) == ':443' ) {
     131                $domain               = substr( $domain, 0, -4 );
     132                $_SERVER['HTTP_HOST'] = substr( $_SERVER['HTTP_HOST'], 0, -4 );
    139133        }
    140134
     135        $path = stripslashes( $_SERVER['REQUEST_URI'] );
     136        if ( is_admin() ) {
     137                $path = preg_replace( '#(.*)/wp-admin/.*#', '$1/', $path );
     138        }
     139        list( $path ) = explode( '?', $path );
     140
     141        // If the network is defined in wp-config.php, we can simply use that.
     142        if ( defined( 'DOMAIN_CURRENT_SITE' ) && defined( 'PATH_CURRENT_SITE' ) ) {
     143                $current_site         = new WP_Network();
     144                $current_site->id     = defined( 'SITE_ID_CURRENT_SITE' ) ? SITE_ID_CURRENT_SITE : 1;
     145                $current_site->domain = DOMAIN_CURRENT_SITE;
     146                $current_site->path   = PATH_CURRENT_SITE;
     147
     148                if ( defined( 'BLOG_ID_CURRENT_SITE' ) ) {
     149                        $current_site->blog_id = BLOG_ID_CURRENT_SITE;
     150                } elseif ( defined( 'BLOGID_CURRENT_SITE' ) ) { // deprecated.
     151                        $current_site->blog_id = BLOGID_CURRENT_SITE;
     152                }
     153
     154                if ( 0 === strcasecmp( $current_site->domain, $domain ) && 0 === strcasecmp( $current_site->path, $path ) ) {
     155                        $current_blog = get_site_by_path( $domain, $path );
     156
     157                // If the current network has a path and also matches the domain and
     158                // path of the request, we need to look for a site using the first path
     159                // segment following the network's path.
     160                } elseif ( '/' !== $current_site->path && 0 === strcasecmp( $current_site->domain, $domain ) && 0 === stripos( $path, $current_site->path ) ) {
     161                        $current_blog = get_site_by_path( $domain, $path, 1 + count( explode( '/', trim( $current_site->path, '/' ) ) ) );
     162
     163                // Otherwise, use the first path segment (as usual).
     164                } else {
     165                        $current_blog = get_site_by_path( $domain, $path, 1 );
     166                }
     167
    141168        /*
    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.
     169         * A "subdomain" install can be re-interpreted to mean "can support anydomain".
     170         * If we're not dealing with one of these installs, then the important part
     171         * is determining the network first, because we need the network's path to
     172         * identify any sites.
    149173         */
    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'  );
     174        } elseif ( ! is_subdomain_install() ) {
     175
     176                // Attempt to get the current network from cache
     177                $current_site = wp_cache_get( 'current_network', 'site-options' );
     178                if ( false === $current_site ) {
     179
     180                        // Are there even two networks installed?
     181                        $one_network = $wpdb->get_row( "SELECT * FROM {$wpdb->site} LIMIT 2" );
     182                        if ( 1 === $wpdb->num_rows ) {
     183                                $current_site = new WP_Network( $one_network );
     184                                wp_cache_add( 'current_network', $current_site, 'site-options' );
     185                        } elseif ( 0 === $wpdb->num_rows ) {
     186                                ms_not_installed();
     187                        }
    156188                }
    157         }
    158189
    159         $paths = array();
    160         if ( $using_paths ) {
    161                 $path_segments = array_filter( explode( '/', trim( $path, "/" ) ) );
     190                // No network found, so attempt to query it by path
     191                if ( empty( $current_site ) ) {
     192                        $current_site = get_network_by_path( $domain, $path, 1 );
     193                }
    162194
    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 );
     195                // Bail if no network found
     196                if ( empty( $current_site ) ) {
     197                        ms_not_installed();
     198       
     199                // Set current site if path matches
     200                } elseif ( $path === $current_site->path ) {
     201                        $current_blog = get_site_by_path( $domain, $path );
    175202
    176                 if ( null !== $segments && count($path_segments ) > $segments ) {
    177                         $path_segments = array_slice( $path_segments, 0, $segments );
     203                // Search the network path + one more path segment (on top of the network path).
     204                } else {
     205                        $current_blog = get_site_by_path( $domain, $path, substr_count( $current_site->path, '/' ) );
    178206                }
     207        } else {
    179208
    180                 while ( count( $path_segments ) ) {
    181                         $paths[] = '/' . implode( '/', $path_segments ) . '/';
    182                         array_pop( $path_segments );
     209                // Find the site by the domain and at most the first path segment.
     210                $current_blog = get_site_by_path( $domain, $path, 1 );
     211                if ( ! empty( $current_blog ) ) {
     212                        $network_id   = $current_blog->site_id ? $current_blog->site_id : 1;
     213                        $current_site = new WP_Network( $network_id );
     214
     215                // If you don't have a site with the same domain/path as a network,
     216                // you're pretty screwed, but...
     217                } else {
     218                        $current_site = get_network_by_path( $domain, $path, 1 );
    183219                }
     220        }
    184221
    185                 $paths[] = '/';
     222        // The network declared by the site trumps any constants.
     223        if ( ! empty( $current_blog ) && ( $current_blog->site_id !== $current_site->id ) ) {
     224                $current_site = new WP_Network( $current_blog->site_id );
    186225        }
    187226
    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;
     227        // Bail if no network has been found by now
     228        if ( empty( $current_site ) ) {
     229                ms_not_installed();
    210230        }
    211231
    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)?
     232        // @todo Investigate when exactly this can occur.
     233        if ( empty( $current_blog ) && defined( 'WP_INSTALLING' ) ) {
     234                $current_blog = new stdClass;
     235                $current_blog->blog_id = $blog_id = 1;
     236        }
    214237
    215         $search_domains = "'" . implode( "', '", $wpdb->_escape( $domains ) ) . "'";
     238        // No site has been found, bail.
     239        if ( empty( $current_blog ) ) {
    216240
    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;
     241                // We're going to redirect to the network URL, with some possible modifications.
     242                $scheme      = is_ssl() ? 'https' : 'http';
     243                $destination = "$scheme://{$current_site->domain}{$current_site->path}";
    224244
    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         }
     245                /**
     246                 * Fires when a network can be determined but a site cannot.
     247                 *
     248                 * At the time of this action, the only recourse is to redirect somewhere
     249                 * and exit. If you want to declare a particular site, do so earlier.
     250                 *
     251                 * @since 3.9.0
     252                 *
     253                 * @param object $current_site The network that had been determined.
     254                 * @param string $domain       The domain used to search for a site.
     255                 * @param string $path         The path used to search for a site.
     256                 */
     257                do_action( 'ms_site_not_found', $current_site, $domain, $path );
    231258
    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;
     259                // For a "subdomain" install, redirect to the signup form specifically.
     260                if ( is_subdomain_install() && ! defined( 'NOBLOGREDIRECT' ) ) {
     261                        $destination .= 'wp-signup.php?new=' . str_replace( '.' . $current_site->domain, '', $domain );
     262
     263                // For a "subdomain" install, the NOBLOGREDIRECT constant
     264                // can be used to avoid a redirect to the signup form.
     265                // Using the ms_site_not_found action is preferred to the constant.
     266                } elseif ( is_subdomain_install() ) {
     267                        if ( '%siteurl%' !== NOBLOGREDIRECT ) {
     268                                $destination = NOBLOGREDIRECT;
    243269                        }
     270
     271                // If the domain we were searching for matches the network's domain,
     272                // it's no use redirecting back to ourselves -- it'll cause a loop.
     273                // As we couldn't find a site, we're simply not installed.
     274                } elseif ( 0 === strcasecmp( $current_site->domain, $domain ) ) {
     275                        ms_not_installed();
    244276                }
    245                 if ( $network->path === '/' ) {
    246                         $found = true;
    247                         break;
    248                 }
     277
     278                header( 'Location: ' . $destination );
     279                exit;
    249280        }
    250281
    251         if ( $found ) {
    252                 return wp_get_network( $network );
     282        // Figure out the current network's main site.
     283        if ( ! isset( $current_site->blog_id ) ) {
     284                if ( ( $current_blog->domain === $current_site->domain ) && ( $current_blog->path === $current_site->path ) ) {
     285                        $current_site->blog_id = $current_blog->blog_id;
     286                } elseif ( ! $current_site->blog_id = wp_cache_get( 'network:' . $current_site->id . ':main_site', 'site-options' ) ) {
     287                        $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 ) );
     288                        wp_cache_add( 'network:' . $current_site->id . ':main_site', $current_site->blog_id, 'site-options' );
     289                }
    253290        }
    254291
    255         return false;
     292        // Set some legacy globals
     293        $GLOBALS['blog_id'] = $current_blog->blog_id;
     294        $GLOBALS['public']  = $current_blog->public;
     295        $GLOBALS['site_id'] = $current_blog->site_id;
     296
     297        // Load the site options
     298        wp_load_core_site_options( $current_site->id );
    256299}
    257300
    258301/**
     302 * Set the table prefix according to current site
     303 *
     304 * @since WordPress (4.3.0)
     305 *
     306 * @global object $wpdb
     307 * @global string $table_prefix
     308 * @global object $current_blog
     309 */
     310function ms_set_table_prefix() {
     311        global $wpdb, $table_prefix, $current_blog;
     312
     313        $wpdb->set_prefix( $table_prefix, false );
     314        $wpdb->set_blog_id( $current_blog->blog_id, $current_blog->site_id );
     315        $table_prefix = $wpdb->get_blog_prefix();
     316}
     317
     318/**
     319 * Set globals used when switching between sites
     320 *
     321 * @since WordPress (4.3.0)
     322 *
     323 * @see switch_to_blog()
     324 * @see restore_current_blog()
     325 */
     326function ms_set_switched_stacks() {
     327        $GLOBALS['_wp_switched_stack'] = array();
     328        $GLOBALS['switched']           = false;
     329}
     330
     331/**
     332 * Retrieve a network object by its domain and path.
     333 *
     334 * @since 3.9.0
     335 *
     336 * @param string   $domain   Domain to check.
     337 * @param string   $path     Path to check.
     338 * @param int|null $segments Path segments to use. Defaults to null, or the full path.
     339 * @return object|bool Network object if successful. False when no network is found.
     340 */
     341function get_network_by_path( $domain, $path, $segments = null ) {
     342        return WP_Network::get_by_path( $domain, $path, $segments );
     343}
     344
     345/**
    259346 * Retrieve an object containing information about the requested network.
    260347 *
    261348 * @since 3.9.0
     
    264351 * @return object|bool Object containing network information if found, false if not.
    265352 */
    266353function 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;
     354        return new WP_Network( $network );
    277355}
    278356
    279357/**
  • wp-includes/ms-settings.php

     
    1010 * @since 3.0.0
    1111 */
    1212
    13 /** Include Multisite initialization functions */
     13// Require multisite initialization functions
    1414require_once( ABSPATH . WPINC . '/ms-load.php' );
    1515require_once( ABSPATH . WPINC . '/ms-default-constants.php' );
    1616
     17// Allow custom multisite/network detection & configuration
    1718if ( defined( 'SUNRISE' ) ) {
    1819        include_once( WP_CONTENT_DIR . '/sunrise.php' );
    1920}
    2021
    21 /** Check for and define SUBDOMAIN_INSTALL and the deprecated VHOST constant. */
     22// Check for and define SUBDOMAIN_INSTALL (and the deprecated VHOST constant)
    2223ms_subdomain_constants();
    2324
    24 if ( !isset( $current_site ) || !isset( $current_blog ) ) {
     25// Attempt to set the relevant globals
     26ms_set_current_globals();
    2527
    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.
     28// $table_prefix can be set in sunrise.php
     29ms_set_table_prefix();
    2930
    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         }
     31// Setup site-switching globals
     32ms_set_switched_stacks();
    3833
    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 
    20234// need to init cache again after blog_id is set
    20335wp_start_object_cache();
    20436
    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 
    21237// Define upload directory constants
    21338ms_upload_constants();
  • 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;
    197 
    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' );
     199        // Use the current
     200        if ( empty( $network_id ) ) {
     201                $network_id = $wpdb->siteid;
    209202        }
     203
     204        // Load core options for this network
     205        WP_Network::get_core_options( $network_id );
    210206}
    211207
    212208/**