Skip to content

Commit

Permalink
Autoload mu-plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
Foxaii authored and swalkinshaw committed Sep 6, 2014
1 parent 10667f4 commit 82a9c43
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ web/app/upgrade
web/app/uploads/*
!web/app/uploads/.gitkeep
!web/app/mu-plugins/register-theme-directory.php
!web/app/mu-plugins/bedrock-autoloader.php

# Vendor (e.g. Composer)
vendor/*
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Run `composer create-project roots/bedrock <path>` (see [Installation/Usage](#in
* Easy WordPress configuration with environment specific files
* Environment variables with [Dotenv](https://github.com/vlucas/phpdotenv)
* Easy server environments with [Vagrant](http://www.vagrantup.com/) and [Ansible](http://www.ansible.com/home) - [bedrock-ansible](https://github.com/roots/bedrock-ansible) on GitHub
* Autoloader for mu-plugins (let's you use regular plugins as mu-plugins)

Bedrock is meant as a base for you to fork and modify to fit your needs. It is delete-key friendly and you can strip out or modify any part of it. You'll also want to customize Bedrock with settings specific to your sites/company.

Expand Down Expand Up @@ -270,6 +271,13 @@ Vagrant and Ansible integration with Bedrock can now be found in the separate [b

Note that using Ansible you no longer need to manually create/edit a `.env` file (or use `composer create-project` to generate one). Ansible will generate a `.env` based on its config and automatically generate salts/keys.

## mu-plugins autoloader

Bedrock includes an autoloader that enables standard plugins to be required just like must-use plugins.
The autoloaded plugins are included after all mu-plugins and standard plugins have been loaded.
An asterisk (*) next to the name of the plugin designates the plugins that have been autoloaded.
To remove this functionality, just delete `web/app/mu-plugins/bedrock-autoloader.php`.

## Todo

* Solution for basic database syncing/copying
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@
"generate-salts": true
},
"autoload": {
"psr-0": {"Bedrock\\Installer": "scripts"}
"psr-0": {"Roots\\Bedrock\\Installer": "scripts"}
},
"scripts": {
"post-root-package-install": ["Bedrock\\Installer::addSalts"]
"post-root-package-install": ["Roots\\Bedrock\\Installer::addSalts"]
},
"repositories": [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Bedrock;
namespace Roots\Bedrock;

use Composer\Script\Event;

Expand All @@ -17,7 +17,7 @@ class Installer {
);

public static function addSalts(Event $event) {
$root = dirname(dirname(__DIR__));
$root = dirname(dirname(dirname(__DIR__)));
$composer = $event->getComposer();
$io = $event->getIO();

Expand Down
151 changes: 151 additions & 0 deletions web/app/mu-plugins/bedrock-autoloader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php
/**
* Plugin Name: Bedrock Autoloader
* Plugin URI: https://github.com/roots/bedrock/
* Description: An autoloader that enables standard plugins to be required just like must-use plugins. The autoloaded plugins are included after all mu-plugins and standard plugins have been loaded. An asterisk (*) next to the name of the plugin designates the plugins that have been autoloaded.
* Version: 1.0.0
* Author: Roots
* Author URI: http://roots.io/
* License: MIT License
*/

namespace Roots\Bedrock;

if (!is_blog_installed()) { return; }

class Autoloader {
private static $cache; // Stores our plugin cache and site option.
private static $auto_plugins; // Contains the autoloaded plugins (only when needed).
private static $mu_plugins; // Contains the mu plugins (only when needed).
private static $count; // Contains the plugin count.
private static $activated; // Newly activated plugins.
private static $relative_path; // Relative path to the mu-plugins dir.
private static $_single; // Let's make this a singleton.

function __construct() {
if (isset(self::$_single)) { return; }

self::$_single = $this; // Singleton set.
self::$relative_path = '/../' . basename(__DIR__); // Rel path set.

add_action('plugins_loaded', array($this, 'load_plugins'), 0); // Always add filter to autoload.

if (is_admin()) {
add_filter('show_advanced_plugins', array($this, 'show_in_admin'), 0, 2); // Admin only filter.
}
}

/**
* Run some checks then autoload our plugins.
*/
public function load_plugins() {
$this->check_cache();
$this->validate_plugins();
$this->count_plugins();

foreach (self::$cache['plugins'] as $plugin_file => $plugin_info) {
include_once(WPMU_PLUGIN_DIR . '/' . $plugin_file);
}

$this->plugin_hooks();
}

/**
* Filter show_advanced_plugins to display the autoloaded plugins.
*/
public function show_in_admin($bool, $type) {
$screen = get_current_screen();

if ($screen->{'base'} != 'plugins' || $type != 'mustuse' || !current_user_can('activate_plugins')) {
return $bool;
}

global $plugins;

$this->update_cache(); // May as well update the transient cache whilst here.

self::$auto_plugins = array_map(function ($auto_plugin) {
$auto_plugin['Name'] .= ' *';
return $auto_plugin;
}, self::$auto_plugins);

$plugins['mustuse'] = array_unique(array_merge(self::$auto_plugins, self::$mu_plugins), SORT_REGULAR);

return false; // Prevent WordPress overriding our work.
}

/**
* This sets the cache or calls for an update
*/
private function check_cache() {
$cache = get_site_option('bedrock_autoloader');

if ($cache == false) {
return $this->update_cache();
}

self::$cache = $cache;
}

/**
* Get the plugins and mu-plugins from the mu-plugin path and remove duplicates.
* Check cache against current plugins for newly activated plugins.
* After that, we can update the cache.
*/
private function update_cache() {
require_once(ABSPATH . 'wp-admin/includes/plugin.php');

self::$auto_plugins = get_plugins(self::$relative_path);
self::$mu_plugins = get_mu_plugins(self::$relative_path);
$plugins = array_diff_key(self::$auto_plugins, self::$mu_plugins);
$rebuild = !is_array(self::$cache['plugins']);
self::$activated = ($rebuild) ? $plugins : array_diff_key($plugins, self::$cache['plugins']);
self::$cache = array('plugins' => $plugins, 'count' => $this->count_plugins());

update_site_option('bedrock_autoloader', self::$cache);
}

/**
* This accounts for the plugin hooks that would run if the plugins were
* loaded as usual. Plugins are removed by deletion, so there's no way
* to deactivate or uninstall.
*/
private function plugin_hooks() {
if (!is_array(self::$activated)) { return; }

foreach (self::$activated as $plugin_file => $plugin_info) {
do_action('activate_' . $plugin_file);
}
}

/**
* Check that the plugin file exists, if it doesn't update the cache.
*/
private function validate_plugins() {
foreach (self::$cache['plugins'] as $plugin_file => $plugin_info) {
if (!file_exists(WPMU_PLUGIN_DIR . '/' . $plugin_file)) {
$this->update_cache();
break;
}
}
}

/**
* Count our plugins (but only once) by counting the top level folders in the
* mu-plugins dir. If it's more or less than last time, update the cache.
*/
private function count_plugins() {
if (isset(self::$count)) { return self::$count; }

$count = count(glob(WPMU_PLUGIN_DIR . '/*/', GLOB_ONLYDIR | GLOB_NOSORT));

if (!isset(self::$cache['count']) || $count != self::$cache['count']) {
self::$count = $count;
$this->update_cache();
}

return self::$count;
}
}

new Autoloader();

0 comments on commit 82a9c43

Please sign in to comment.