WP_Network_Query Generator

get_networks() arguments, with the honest note that almost every install has one network and the query is only worth writing on a multi-network setup.

Enable JavaScript to customise; default output below.

With both slashes. A root network's path is just /.

Matches the domain and path columns of the networks table. Nothing inside the networks.

Direction
Live preview network-query.php
<?php
/**
 * Networks matching the arguments below.
 *
 * Almost every multisite install has exactly one network, so this query returns one row and there
 * is no reason to write it: is_multisite() plus get_current_network_id() answers the question.
 * It earns its place on a multi-network install, which means WordPress MU-style hosting where one
 * codebase serves several independent networks.
 */

// Networks only exist on multisite, and get_networks() returns an empty array otherwise.
if ( ! is_multisite() ) {
	return;
}

$args = array(
	'orderby'        => 'domain',
	'order'          => 'ASC',
	'number'         => 20,
);

$networks = get_networks( $args );

foreach ( $networks as $network ) {
	$id      = (int) $network->id;
	$details = $network;

	if ( ! $details ) {
		continue;
	}

	printf( "%d: %s%s\n", $id, esc_html( $details->domain ), esc_html( $details->path ) );
}

Output is valid and updates as you type.

Most multisite installs have exactly one network. On those, get_networks() returns one row and there is no reason to call it: is_multisite() and get_current_network_id() already answer the question.

It earns its place on a multi-network install, where one WordPress codebase serves several independent networks, each with its own sites, users table sharing and settings. That is rare, it is usually a hosting arrangement rather than a product decision, and when you are in one this is the query you need.

The related thing worth knowing either way: network options are not site options. They live in sitemeta, they are per network, and inside a loop you want get_network_option() with an explicit ID rather than get_site_option(), which silently reads the current network’s.

How to use

  1. Filter by domain or path if you know them, or leave everything empty to list all networks.
  2. Tick the site count if you want how many sites each network has, which is an extra query each.
  3. Tick the option example to see the per-network read.

Example

if ( ! is_multisite() ) {
	return;
}

$args = array(
	'orderby'        => 'domain',
	'order'          => 'ASC',
	'number'         => 20,
);

$networks = get_networks( $args );

foreach ( $networks as $network ) {
	$id      = (int) $network->id;
	$details = $network;

	printf( "%d: %s%s\n", $id, esc_html( $details->domain ), esc_html( $details->path ) );
}

With site counts turned on, each iteration runs a get_sites() with count => true, which returns an integer and builds no site objects.

Pitfalls

One network is the normal case. If you are reaching for this on an ordinary multisite, the answer you want is probably get_current_network_id() or get_sites().

Network options live in sitemeta. get_site_option() reads the current network’s; get_option() reads the current site’s. The naming is historical and it is the most confusing pair in multisite. Inside a loop over networks, use get_network_option( $id, ... ) so there is no ambiguity.

A subdirectory network has one domain for all its sites. So filtering networks by domain on a subdirectory install matches the one network, and filtering sites by domain matches all of them. The two tables store different things.

Paths carry both slashes. A root network’s path is /. Passing an empty string matches nothing.

search covers the domain and path columns only. There is nothing else in the networks table to search.

Counting sites per network is N+1 queries. Fine for a handful of networks, not for a page that runs on every request. Cache the result if it is being displayed.

Users may or may not be shared. On a multi-network install the users table is shared by default across all of it, which means a user list is network-wide and capabilities are per site. That surprises people more than the queries do.

Compatibility

Everything runs in the browser: nothing is uploaded and nothing is stored.

WP_Network_Query and get_networks() arrived in WordPress 4.6. orderby accepts domain, path, id, network__in, and the two length variants, which are useful when you need the longest matching domain, as the request routing itself does.

count => true returns an integer. fields => 'ids' returns integers rather than WP_Network objects, and get_network() then reads from the cache the query primed.

Every string is escaped for a PHP single-quoted string. The hostile fixture includes an apostrophe, */, ?> and ${x}, and the output passes php -l with the same token structure as the default file.

Multi-network installs are not configurable from the admin: they need SUBDOMAIN_INSTALL, a sunrise.php drop-in and a domain-mapping arrangement, which is why the code here reads networks rather than creating them.

Frequently asked questions

What is a multi-network install?
One WordPress codebase serving several networks, each with its own sites and settings. It requires a sunrise.php drop-in and manual configuration, and plugins like WP Multi Network exist to manage it.
How do I get the current network?
get_current_network_id(), or get_network() with no argument for the object. No query needed.
get_site_option or get_network_option?
get_site_option() reads the current network’s option, despite the name. get_network_option() takes an explicit network ID. Use the second one anywhere the network is not obviously the current one.
Can I add a network programmatically?
There is no core API worth relying on. It is a database and configuration job, and the plugins that do it exist because core deliberately does not.
Does this work on single site?
No, and the generated code returns early. Networks are a multisite concept and the tables do not exist otherwise.

From the people who built this tool

WP Adminify

The WordPress admin, rebuilt: a dashboard worth looking at, menu and column control, a real file manager and the login page your client sees.

See WP Adminify Free version on WordPress.org

Weekly drops

New tools, when there are new tools

One email when something worth using ships. No schedule to fill, so no filler.

Your address goes nowhere else, and one click unsubscribes.