Skip to content

Instantly share code, notes, and snippets.

@felixarntz
Last active August 10, 2017 10:21
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save felixarntz/9b3bed66099641a45ce437b067fea547 to your computer and use it in GitHub Desktop.
Save felixarntz/9b3bed66099641a45ce437b067fea547 to your computer and use it in GitHub Desktop.
WP Scoped MU Plugin Loader
<?php
/*
Plugin Name: WP Scoped MU Plugin Loader
Plugin URI: https://gist.github.com/felixarntz/9b3bed66099641a45ce437b067fea547
Description: Loads scoped MU plugins for specific sites or networks for an improved organization of those more specific tweaks.
Version: 1.0.0
Author: Felix Arntz
Author URI: https://leaves-and-love.net
License: GNU General Public License v2
License URI: http://www.gnu.org/licenses/gpl-2.0.html
*/
/**
* Class used for loading scoped MU plugins
*
* @since 1.0.0
*/
class WP_Scoped_MU_Plugin_Loader {
/**
* Directory name for network-wide MU plugins.
*
* @since 1.0.0
* @var string
*/
const NETWORKWIDE_DIR = 'network-wide';
/**
* Directory name for site-wide MU plugins.
*
* @since 1.0.0
* @var string
*/
const SITEWIDE_DIR = 'site-wide';
/**
* Base directory to look for scoped MU plugins.
*
* @since 1.0.0
* @var string
*/
private $basedir;
/**
* Constructor.
*
* Sets the base directory property.
*
* @since 1.0.0
*
* @param string $basedir Base directory to look for scoped MU plugins.
*/
public function __construct( $basedir ) {
$this->basedir = trailingslashit( $basedir );
}
/**
* Loads all scoped MU plugins for the current network and site.
*
* @since 1.0.0
*/
public function load() {
$network = $this->get_current_network();
$site = $this->get_current_site();
$this->load_networkwide( $network );
$this->load_sitewide( $site );
}
/**
* Loads network-wide MU plugins for a given network.
*
* @since 1.0.0
*
* @param WP_Network $network Network object.
*/
public function load_networkwide( $network ) {
$dirs = $this->get_search_directories( $this->basedir . self::NETWORKWIDE_DIR, $network );
foreach ( $dirs as $dir ) {
foreach ( $this->get_mu_plugins( $dir ) as $mu_plugin ) {
require_once $mu_plugin;
}
}
}
/**
* Loads site-wide MU plugins for a given site.
*
* @since 1.0.0
*
* @param WP_Site $site Site object.
*/
public function load_sitewide( $site ) {
$dirs = $this->get_search_directories( $this->basedir . self::SITEWIDE_DIR, $site );
foreach ( $dirs as $dir ) {
foreach ( $this->get_mu_plugins( $dir ) as $mu_plugin ) {
require_once $mu_plugin;
}
}
}
/**
* Returns the current network.
*
* @since 1.0.0
*
* @return WP_Network Current network object.
*/
private function get_current_network() {
return get_network();
}
/**
* Returns the current site.
*
* @since 1.0.0
*
* @return WP_Site Current site object.
*/
private function get_current_site() {
return get_site();
}
/**
* Returns directories to search for MU plugins for a given site or network.
*
* @since 1.0.0
*
* @param string $basedir Basedir to look in for the respective kind of object.
* @param WP_Network|WP_Site $obj Network or site object.
* @return array Array of directories.
*/
private function get_search_directories( $basedir, $obj ) {
$basedir = trailingslashit( $basedir );
return array(
$basedir . $obj->id,
$basedir . str_replace( '/', '+', $obj->domain . $obj->path ),
);
}
/**
* Returns MU plugin file names found in a given directory.
*
* @since 1.0.0
*
* @param string $dir Directory to look for MU plugins.
* @return array MU plugin files in that directory.
*/
private function get_mu_plugins( $dir ) {
$mu_plugins = array();
if ( ! is_dir( $dir ) ) {
return $mu_plugins;
}
$handle = opendir( $dir );
if ( ! $handle ) {
return $mu_plugins;
}
$dir = trailingslashit( $dir );
while ( ( $mu_plugin = readdir( $handle ) ) !== false ) {
if ( substr( $mu_plugin, -4 ) === '.php' ) {
$mu_plugins[] = $dir . $mu_plugin;
}
}
closedir( $handle );
sort( $mu_plugins );
return $mu_plugins;
}
}
/**
* Initializes the loader to load scoped MU plugins.
*
* @since 1.0.0
*/
function wp_scoped_mu_plugin_loader_run() {
$loader = new WP_Scoped_MU_Plugin_Loader( plugin_dir_path( __FILE__ ) );
$loader->load();
}
// Load all scoped MU plugins.
wp_scoped_mu_plugin_loader_run();
@felixarntz
Copy link
Author

felixarntz commented Aug 5, 2017

WP Scoped MU Plugin Loader

Loads scoped MU plugins for specific sites or networks for an improved organization of those more specific tweaks.

Background Info

We usually use MU plugins for small tweaks in our WordPress setups. Sometimes more, sometimes less. In a multisite or multinetwork setup, some of these tweaks however should not be applied globally as they only affect a specific site or network. Of course this can be achieved with a regular MU plugin already, but as you get more and more of those tweaks in place, overview might get lost as you may have a hard time figuring out to which site you applied which tweaks and where to find them. This simple loader aims at solving this problem by allowing you to create site- or network-specific MU plugins.

Of course you could also use regular plugins for that and activate them per site or network, but in some cases MU plugins are easier to maintain since they usually are only plain PHP files and, more importantly, cannot be deactivated through WP Admin in any way.

Requirements

  • WordPress 4.6+
  • Multisite

Setup

  • Move the file into your wp-content/mu-plugins directory. Create the directory if it does not exist.
  • To create an MU plugin for a specific site, put it into a directory wp-content/mu-plugins/site-wide/{$site_id} (where {$site_id} is the ID of the site) or wp-content/mu-plugins/site-wide/{$site_domain_and_path} (where {$site_domain_and_path} is the concatenated site domain and path with slashes replaced with + characters).
  • To create an MU plugin for a specific network, put it into a directory wp-content/mu-plugins/network-wide/{$network_id} (where {$network_id} is the ID of the network) or wp-content/mu-plugins/network-wide/{$network_domain_and_path} (where {$network_domain_and_path} is the concatenated network domain and path with slashes replaced with + characters).

That's it! You're all set. It may seem weird to replace the slashes in the domain and path combinations with + characters, however this needs to happen in order not to conflict with the directory separators of the server.

Happy coding!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment