[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ -> class-wp-network.php (source)

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


Generated : Tue Sep 8 08:20:28 2026 Cross-referenced by PHPXref