balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

class-wp-network.php (12379B)


      1 <?php
      2 /**
      3  * Network API: WP_Network class
      4  *
      5  * @package WordPress
      6  * @subpackage Multisite
      7  * @since 4.4.0
      8  */
      9 
     10 /**
     11  * Core class used for interacting with a multisite network.
     12  *
     13  * This class is used during load to populate the `$current_site` global and
     14  * setup the current network.
     15  *
     16  * This class is most useful in WordPress multi-network installations where the
     17  * ability to interact with any network of sites is required.
     18  *
     19  * @since 4.4.0
     20  *
     21  * @property int $id
     22  * @property int $site_id
     23  */
     24 class WP_Network {
     25 
     26 	/**
     27 	 * Network ID.
     28 	 *
     29 	 * @since 4.4.0
     30 	 * @since 4.6.0 Converted from public to private to explicitly enable more intuitive
     31 	 *              access via magic methods. As part of the access change, the type was
     32 	 *              also changed from `string` to `int`.
     33 	 * @var int
     34 	 */
     35 	private $id;
     36 
     37 	/**
     38 	 * Domain of the network.
     39 	 *
     40 	 * @since 4.4.0
     41 	 * @var string
     42 	 */
     43 	public $domain = '';
     44 
     45 	/**
     46 	 * Path of the network.
     47 	 *
     48 	 * @since 4.4.0
     49 	 * @var string
     50 	 */
     51 	public $path = '';
     52 
     53 	/**
     54 	 * The ID of the network's main site.
     55 	 *
     56 	 * Named "blog" vs. "site" for legacy reasons. A main site is mapped to
     57 	 * the network when the network is created.
     58 	 *
     59 	 * A numeric string, for compatibility reasons.
     60 	 *
     61 	 * @since 4.4.0
     62 	 * @var string
     63 	 */
     64 	private $blog_id = '0';
     65 
     66 	/**
     67 	 * Domain used to set cookies for this network.
     68 	 *
     69 	 * @since 4.4.0
     70 	 * @var string
     71 	 */
     72 	public $cookie_domain = '';
     73 
     74 	/**
     75 	 * Name of this network.
     76 	 *
     77 	 * Named "site" vs. "network" for legacy reasons.
     78 	 *
     79 	 * @since 4.4.0
     80 	 * @var string
     81 	 */
     82 	public $site_name = '';
     83 
     84 	/**
     85 	 * Retrieve a network from the database by its ID.
     86 	 *
     87 	 * @since 4.4.0
     88 	 *
     89 	 * @global wpdb $wpdb WordPress database abstraction object.
     90 	 *
     91 	 * @param int $network_id The ID of the network to retrieve.
     92 	 * @return WP_Network|false The network's object if found. False if not.
     93 	 */
     94 	public static function get_instance( $network_id ) {
     95 		global $wpdb;
     96 
     97 		$network_id = (int) $network_id;
     98 		if ( ! $network_id ) {
     99 			return false;
    100 		}
    101 
    102 		$_network = wp_cache_get( $network_id, 'networks' );
    103 
    104 		if ( false === $_network ) {
    105 			$_network = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->site} WHERE id = %d LIMIT 1", $network_id ) );
    106 
    107 			if ( empty( $_network ) || is_wp_error( $_network ) ) {
    108 				$_network = -1;
    109 			}
    110 
    111 			wp_cache_add( $network_id, $_network, 'networks' );
    112 		}
    113 
    114 		if ( is_numeric( $_network ) ) {
    115 			return false;
    116 		}
    117 
    118 		return new WP_Network( $_network );
    119 	}
    120 
    121 	/**
    122 	 * Create a new WP_Network object.
    123 	 *
    124 	 * Will populate object properties from the object provided and assign other
    125 	 * default properties based on that information.
    126 	 *
    127 	 * @since 4.4.0
    128 	 *
    129 	 * @param WP_Network|object $network A network object.
    130 	 */
    131 	public function __construct( $network ) {
    132 		foreach ( get_object_vars( $network ) as $key => $value ) {
    133 			$this->$key = $value;
    134 		}
    135 
    136 		$this->_set_site_name();
    137 		$this->_set_cookie_domain();
    138 	}
    139 
    140 	/**
    141 	 * Getter.
    142 	 *
    143 	 * Allows current multisite naming conventions when getting properties.
    144 	 *
    145 	 * @since 4.6.0
    146 	 *
    147 	 * @param string $key Property to get.
    148 	 * @return mixed Value of the property. Null if not available.
    149 	 */
    150 	public function __get( $key ) {
    151 		switch ( $key ) {
    152 			case 'id':
    153 				return (int) $this->id;
    154 			case 'blog_id':
    155 				return (string) $this->get_main_site_id();
    156 			case 'site_id':
    157 				return $this->get_main_site_id();
    158 		}
    159 
    160 		return null;
    161 	}
    162 
    163 	/**
    164 	 * Isset-er.
    165 	 *
    166 	 * Allows current multisite naming conventions when checking for properties.
    167 	 *
    168 	 * @since 4.6.0
    169 	 *
    170 	 * @param string $key Property to check if set.
    171 	 * @return bool Whether the property is set.
    172 	 */
    173 	public function __isset( $key ) {
    174 		switch ( $key ) {
    175 			case 'id':
    176 			case 'blog_id':
    177 			case 'site_id':
    178 				return true;
    179 		}
    180 
    181 		return false;
    182 	}
    183 
    184 	/**
    185 	 * Setter.
    186 	 *
    187 	 * Allows current multisite naming conventions while setting properties.
    188 	 *
    189 	 * @since 4.6.0
    190 	 *
    191 	 * @param string $key   Property to set.
    192 	 * @param mixed  $value Value to assign to the property.
    193 	 */
    194 	public function __set( $key, $value ) {
    195 		switch ( $key ) {
    196 			case 'id':
    197 				$this->id = (int) $value;
    198 				break;
    199 			case 'blog_id':
    200 			case 'site_id':
    201 				$this->blog_id = (string) $value;
    202 				break;
    203 			default:
    204 				$this->$key = $value;
    205 		}
    206 	}
    207 
    208 	/**
    209 	 * Returns the main site ID for the network.
    210 	 *
    211 	 * Internal method used by the magic getter for the 'blog_id' and 'site_id'
    212 	 * properties.
    213 	 *
    214 	 * @since 4.9.0
    215 	 *
    216 	 * @return int The ID of the main site.
    217 	 */
    218 	private function get_main_site_id() {
    219 		/**
    220 		 * Filters the main site ID.
    221 		 *
    222 		 * Returning a positive integer will effectively short-circuit the function.
    223 		 *
    224 		 * @since 4.9.0
    225 		 *
    226 		 * @param int|null   $main_site_id If a positive integer is returned, it is interpreted as the main site ID.
    227 		 * @param WP_Network $network      The network object for which the main site was detected.
    228 		 */
    229 		$main_site_id = (int) apply_filters( 'pre_get_main_site_id', null, $this );
    230 		if ( 0 < $main_site_id ) {
    231 			return $main_site_id;
    232 		}
    233 
    234 		if ( 0 < (int) $this->blog_id ) {
    235 			return (int) $this->blog_id;
    236 		}
    237 
    238 		if ( ( defined( 'DOMAIN_CURRENT_SITE' ) && defined( 'PATH_CURRENT_SITE' ) && DOMAIN_CURRENT_SITE === $this->domain && PATH_CURRENT_SITE === $this->path )
    239 			|| ( defined( 'SITE_ID_CURRENT_SITE' ) && SITE_ID_CURRENT_SITE == $this->id ) ) {
    240 			if ( defined( 'BLOG_ID_CURRENT_SITE' ) ) {
    241 				$this->blog_id = (string) BLOG_ID_CURRENT_SITE;
    242 
    243 				return (int) $this->blog_id;
    244 			}
    245 
    246 			if ( defined( 'BLOGID_CURRENT_SITE' ) ) { // Deprecated.
    247 				$this->blog_id = (string) BLOGID_CURRENT_SITE;
    248 
    249 				return (int) $this->blog_id;
    250 			}
    251 		}
    252 
    253 		$site = get_site();
    254 		if ( $site->domain === $this->domain && $site->path === $this->path ) {
    255 			$main_site_id = (int) $site->id;
    256 		} else {
    257 			$cache_key = 'network:' . $this->id . ':main_site';
    258 
    259 			$main_site_id = wp_cache_get( $cache_key, 'site-options' );
    260 			if ( false === $main_site_id ) {
    261 				$_sites       = get_sites(
    262 					array(
    263 						'fields'     => 'ids',
    264 						'number'     => 1,
    265 						'domain'     => $this->domain,
    266 						'path'       => $this->path,
    267 						'network_id' => $this->id,
    268 					)
    269 				);
    270 				$main_site_id = ! empty( $_sites ) ? array_shift( $_sites ) : 0;
    271 
    272 				wp_cache_add( $cache_key, $main_site_id, 'site-options' );
    273 			}
    274 		}
    275 
    276 		$this->blog_id = (string) $main_site_id;
    277 
    278 		return (int) $this->blog_id;
    279 	}
    280 
    281 	/**
    282 	 * Set the site name assigned to the network if one has not been populated.
    283 	 *
    284 	 * @since 4.4.0
    285 	 */
    286 	private function _set_site_name() {
    287 		if ( ! empty( $this->site_name ) ) {
    288 			return;
    289 		}
    290 
    291 		$default         = ucfirst( $this->domain );
    292 		$this->site_name = get_network_option( $this->id, 'site_name', $default );
    293 	}
    294 
    295 	/**
    296 	 * Set the cookie domain based on the network domain if one has
    297 	 * not been populated.
    298 	 *
    299 	 * @todo What if the domain of the network doesn't match the current site?
    300 	 *
    301 	 * @since 4.4.0
    302 	 */
    303 	private function _set_cookie_domain() {
    304 		if ( ! empty( $this->cookie_domain ) ) {
    305 			return;
    306 		}
    307 
    308 		$this->cookie_domain = $this->domain;
    309 		if ( 'www.' === substr( $this->cookie_domain, 0, 4 ) ) {
    310 			$this->cookie_domain = substr( $this->cookie_domain, 4 );
    311 		}
    312 	}
    313 
    314 	/**
    315 	 * Retrieve the closest matching network for a domain and path.
    316 	 *
    317 	 * This will not necessarily return an exact match for a domain and path. Instead, it
    318 	 * breaks the domain and path into pieces that are then used to match the closest
    319 	 * possibility from a query.
    320 	 *
    321 	 * The intent of this method is to match a network during bootstrap for a
    322 	 * requested site address.
    323 	 *
    324 	 * @since 4.4.0
    325 	 *
    326 	 * @param string   $domain   Domain to check.
    327 	 * @param string   $path     Path to check.
    328 	 * @param int|null $segments Path segments to use. Defaults to null, or the full path.
    329 	 * @return WP_Network|false Network object if successful. False when no network is found.
    330 	 */
    331 	public static function get_by_path( $domain = '', $path = '', $segments = null ) {
    332 		$domains = array( $domain );
    333 		$pieces  = explode( '.', $domain );
    334 
    335 		/*
    336 		 * It's possible one domain to search is 'com', but it might as well
    337 		 * be 'localhost' or some other locally mapped domain.
    338 		 */
    339 		while ( array_shift( $pieces ) ) {
    340 			if ( ! empty( $pieces ) ) {
    341 				$domains[] = implode( '.', $pieces );
    342 			}
    343 		}
    344 
    345 		/*
    346 		 * If we've gotten to this function during normal execution, there is
    347 		 * more than one network installed. At this point, who knows how many
    348 		 * we have. Attempt to optimize for the situation where networks are
    349 		 * only domains, thus meaning paths never need to be considered.
    350 		 *
    351 		 * This is a very basic optimization; anything further could have
    352 		 * drawbacks depending on the setup, so this is best done per-installation.
    353 		 */
    354 		$using_paths = true;
    355 		if ( wp_using_ext_object_cache() ) {
    356 			$using_paths = wp_cache_get( 'networks_have_paths', 'site-options' );
    357 			if ( false === $using_paths ) {
    358 				$using_paths = get_networks(
    359 					array(
    360 						'number'       => 1,
    361 						'count'        => true,
    362 						'path__not_in' => '/',
    363 					)
    364 				);
    365 				wp_cache_add( 'networks_have_paths', $using_paths, 'site-options' );
    366 			}
    367 		}
    368 
    369 		$paths = array();
    370 		if ( $using_paths ) {
    371 			$path_segments = array_filter( explode( '/', trim( $path, '/' ) ) );
    372 
    373 			/**
    374 			 * Filters the number of path segments to consider when searching for a site.
    375 			 *
    376 			 * @since 3.9.0
    377 			 *
    378 			 * @param int|null $segments The number of path segments to consider. WordPress by default looks at
    379 			 *                           one path segment. The function default of null only makes sense when you
    380 			 *                           know the requested path should match a network.
    381 			 * @param string   $domain   The requested domain.
    382 			 * @param string   $path     The requested path, in full.
    383 			 */
    384 			$segments = apply_filters( 'network_by_path_segments_count', $segments, $domain, $path );
    385 
    386 			if ( ( null !== $segments ) && count( $path_segments ) > $segments ) {
    387 				$path_segments = array_slice( $path_segments, 0, $segments );
    388 			}
    389 
    390 			while ( count( $path_segments ) ) {
    391 				$paths[] = '/' . implode( '/', $path_segments ) . '/';
    392 				array_pop( $path_segments );
    393 			}
    394 
    395 			$paths[] = '/';
    396 		}
    397 
    398 		/**
    399 		 * Determine a network by its domain and path.
    400 		 *
    401 		 * This allows one to short-circuit the default logic, perhaps by
    402 		 * replacing it with a routine that is more optimal for your setup.
    403 		 *
    404 		 * Return null to avoid the short-circuit. Return false if no network
    405 		 * can be found at the requested domain and path. Otherwise, return
    406 		 * an object from wp_get_network().
    407 		 *
    408 		 * @since 3.9.0
    409 		 *
    410 		 * @param null|false|WP_Network $network  Network value to return by path. Default null
    411 		 *                                       to continue retrieving the network.
    412 		 * @param string               $domain   The requested domain.
    413 		 * @param string               $path     The requested path, in full.
    414 		 * @param int|null             $segments The suggested number of paths to consult.
    415 		 *                                       Default null, meaning the entire path was to be consulted.
    416 		 * @param string[]             $paths    Array of paths to search for, based on `$path` and `$segments`.
    417 		 */
    418 		$pre = apply_filters( 'pre_get_network_by_path', null, $domain, $path, $segments, $paths );
    419 		if ( null !== $pre ) {
    420 			return $pre;
    421 		}
    422 
    423 		if ( ! $using_paths ) {
    424 			$networks = get_networks(
    425 				array(
    426 					'number'     => 1,
    427 					'orderby'    => array(
    428 						'domain_length' => 'DESC',
    429 					),
    430 					'domain__in' => $domains,
    431 				)
    432 			);
    433 
    434 			if ( ! empty( $networks ) ) {
    435 				return array_shift( $networks );
    436 			}
    437 
    438 			return false;
    439 		}
    440 
    441 		$networks = get_networks(
    442 			array(
    443 				'orderby'    => array(
    444 					'domain_length' => 'DESC',
    445 					'path_length'   => 'DESC',
    446 				),
    447 				'domain__in' => $domains,
    448 				'path__in'   => $paths,
    449 			)
    450 		);
    451 
    452 		/*
    453 		 * Domains are sorted by length of domain, then by length of path.
    454 		 * The domain must match for the path to be considered. Otherwise,
    455 		 * a network with the path of / will suffice.
    456 		 */
    457 		$found = false;
    458 		foreach ( $networks as $network ) {
    459 			if ( ( $network->domain === $domain ) || ( "www.{$network->domain}" === $domain ) ) {
    460 				if ( in_array( $network->path, $paths, true ) ) {
    461 					$found = true;
    462 					break;
    463 				}
    464 			}
    465 			if ( '/' === $network->path ) {
    466 				$found = true;
    467 				break;
    468 			}
    469 		}
    470 
    471 		if ( true === $found ) {
    472 			return $network;
    473 		}
    474 
    475 		return false;
    476 	}
    477 }