+
+ /*
+ * If we've gotten to this function during normal execution, there is
+ * more than one network installed. At this point, who knows how many
+ * we have. Attempt to optimize for the situation where networks are
+ * only domains, thus meaning paths never need to be considered.
+ *
+ * This is a very basic optimization; anything further could have drawbacks
+ * depending on the setup, so this is best done per-install.
+ */
+ $using_paths = true;
+ if ( wp_using_ext_object_cache() ) {
+ $using_paths = wp_cache_get( 'networks_have_paths', 'site-options' );
+ if ( false === $using_paths ) {
+ $using_paths = (bool) $wpdb->get_var( "SELECT id FROM $wpdb->site WHERE path <> '/' LIMIT 1" );
+ wp_cache_add( 'networks_have_paths', (int) $using_paths, 'site-options' );
+ }
+ }
+
+ $paths = array();
+ if ( $using_paths ) {
+ $path_segments = array_filter( explode( '/', trim( $path, "/" ) ) );
+
+ /**
+ * Filter the number of path segments to consider when searching for a site.
+ *
+ * @since 3.9.0
+ *
+ * @param int|null $segments The number of path segments to consider. WordPress by default looks at
+ * one path segment. The function default of null only makes sense when you
+ * know the requested path should match a network.
+ * @param string $domain The requested domain.
+ * @param string $path The requested path, in full.
+ */
+ $segments = apply_filters( 'network_by_path_segments_count', $segments, $domain, $path );
+
+ if ( null !== $segments && count($path_segments ) > $segments ) {
+ $path_segments = array_slice( $path_segments, 0, $segments );
+ }
+
+ while ( count( $path_segments ) ) {
+ $paths[] = '/' . implode( '/', $path_segments ) . '/';
+ array_pop( $path_segments );
+ }
+
+ $paths[] = '/';
+ }
+
+ /**
+ * Determine a network by its domain and path.
+ *
+ * This allows one to short-circuit the default logic, perhaps by
+ * replacing it with a routine that is more optimal for your setup.
+ *
+ * Return null to avoid the short-circuit. Return false if no network
+ * can be found at the requested domain and path. Otherwise, return
+ * an object from wp_get_network().
+ *
+ * @since 3.9.0
+ *
+ * @param null|bool|object $network Network value to return by path.
+ * @param string $domain The requested domain.
+ * @param string $path The requested path, in full.
+ * @param int|null $segments The suggested number of paths to consult.
+ * Default null, meaning the entire path was to be consulted.
+ * @param array $paths The paths to search for, based on $path and $segments.
+ */
+ $pre = apply_filters( 'pre_get_network_by_path', null, $domain, $path, $segments, $paths );
+ if ( null !== $pre ) {
+ return $pre;
+ }
+
+ // @todo Consider additional optimization routes, perhaps as an opt-in for plugins.
+ // We already have paths covered. What about how far domains should be drilled down (including www)?
+
+ $search_domains = "'" . implode( "', '", $wpdb->_escape( $domains ) ) . "'";
+
+ if ( ! $using_paths ) {
+ $network = $wpdb->get_row( "SELECT id, domain, path FROM $wpdb->site
+ WHERE domain IN ($search_domains) ORDER BY CHAR_LENGTH(domain) DESC LIMIT 1" );
+ if ( $network ) {
+ return wp_get_network( $network );
+ }
+ return false;
+
+ } else {
+ $search_paths = "'" . implode( "', '", $wpdb->_escape( $paths ) ) . "'";
+ $networks = $wpdb->get_results( "SELECT id, domain, path FROM $wpdb->site
+ WHERE domain IN ($search_domains) AND path IN ($search_paths)
+ ORDER BY CHAR_LENGTH(domain) DESC, CHAR_LENGTH(path) DESC" );
+ }
+
+ /*
+ * Domains are sorted by length of domain, then by length of path.
+ * The domain must match for the path to be considered. Otherwise,
+ * a network with the path of / will suffice.
+ */
+ $found = false;
+ foreach ( $networks as $network ) {
+ if ( $network->domain === $domain || "www.$network->domain" === $domain ) {
+ if ( in_array( $network->path, $paths, true ) ) {
+ $found = true;
+ break;
+ }
+ }
+ if ( $network->path === '/' ) {
+ $found = true;
+ break;
+ }
+ }
+
+ if ( $found ) {
+ return wp_get_network( $network );
+ }
+
+ return false;