Pico::loadPlugins(): Load composer-plugins first and skip conflicting plugins in the plugins/ dir

pico-3.0-alpha
Daniel Rudolf 8 years ago
parent fc76d37dbc
commit 7087573aed
No known key found for this signature in database
GPG Key ID: A061F02CD8DE4538
  1. 155
      lib/Pico.php

@ -444,9 +444,13 @@ class Pico
/** /**
* Loads plugins from vendor/pico-plugin.php and Pico::$pluginsDir * Loads plugins from vendor/pico-plugin.php and Pico::$pluginsDir
* *
* See {@see Pico::loadLocalPlugins()} for details about plugins installed * See {@see Pico::loadComposerPlugins()} for details about plugins loaded
* to {@see Pico::$pluginsDir}, and {@see Pico::loadComposerPlugins()} for * from `vendor/pico-plugin.php` (i.e. plugins that were installed using
* details about plugins installed using `composer`. * `composer`), and {@see Pico::loadLocalPlugins()} for details about
* plugins installed to {@see Pico::$pluginsDir}.
*
* Pico always loads plugins from `vendor/pico-plugin.php` first and
* ignores conflicting plugins in {@see Pico::$pluginsDir}.
* *
* Please note that Pico will change the processing order when needed to * Please note that Pico will change the processing order when needed to
* incorporate plugin dependencies. See {@see Pico::sortPlugins()} for * incorporate plugin dependencies. See {@see Pico::sortPlugins()} for
@ -460,8 +464,55 @@ class Pico
*/ */
protected function loadPlugins() protected function loadPlugins()
{ {
$this->loadLocalPlugins();
$this->loadComposerPlugins(); $this->loadComposerPlugins();
$this->loadLocalPlugins();
}
/**
* Loads plugins from vendor/pico-plugin.php
*
* This method loads all plugins installed using `composer` and Pico's
* `picocms/pico-installer` installer by reading the `pico-plugin.php` in
* composer's `vendor` dir.
*
* @see Pico::loadPlugins()
* @see Pico::loadLocalPlugins()
* @return void
*/
protected function loadComposerPlugins()
{
$composerPlugins = array();
if (file_exists($this->getVendorDir() . 'vendor/pico-plugin.php')) {
// composer root package
$composerPlugins = require($this->getVendorDir() . 'vendor/pico-plugin.php') ?: array();
} elseif (file_exists($this->getVendorDir() . '../../../vendor/pico-plugin.php')) {
// composer dependency package
$composerPlugins = require($this->getVendorDir() . '../../../vendor/pico-plugin.php') ?: array();
}
foreach ($composerPlugins as $package => $classNames) {
foreach ($classNames as $className) {
$plugin = new $className($this);
$className = get_class($plugin);
if (isset($this->plugins[$className])) {
continue;
}
if (!($plugin instanceof PicoPluginInterface)) {
throw new RuntimeException(
"Unable to load plugin '" . $className . "' via 'vendor/pico-plugin.php': "
. "Plugins installed by composer must implement 'PicoPluginInterface'"
);
}
$this->plugins[$className] = $plugin;
if (defined($className . '::API_VERSION') && ($className::API_VERSION >= static::API_VERSION)) {
$this->nativePlugins[$className] = $plugin;
}
}
}
} }
/** /**
@ -471,7 +522,7 @@ class Pico
* `<plugin name>.php` only. Plugin names are treated case insensitive. * `<plugin name>.php` only. Plugin names are treated case insensitive.
* Pico will throw a RuntimeException if it can't load a plugin. * Pico will throw a RuntimeException if it can't load a plugin.
* *
* Plugin files MAY be prefixed by a number (e.g. 00-PicoDeprecated.php) * Plugin files MAY be prefixed by a number (e.g. `00-PicoDeprecated.php`)
* to indicate their processing order. Plugins without a prefix will be * to indicate their processing order. Plugins without a prefix will be
* loaded last. If you want to use a prefix, you MUST NOT use the reserved * loaded last. If you want to use a prefix, you MUST NOT use the reserved
* prefixes `00` to `09`. Prefixes are completely optional, however, you * prefixes `00` to `09`. Prefixes are completely optional, however, you
@ -489,35 +540,53 @@ class Pico
*/ */
protected function loadLocalPlugins() protected function loadLocalPlugins()
{ {
$pluginsLowered = array_change_key_case($this->plugins, CASE_LOWER);
$pluginFiles = array(); $pluginFiles = array();
$files = scandir($this->getPluginsDir()); $files = scandir($this->getPluginsDir()) ?: array();
if ($files !== false) { foreach ($files as $file) {
foreach ($files as $file) { if ($file[0] === '.') {
if ($file[0] === '.') { continue;
}
if (is_dir($this->getPluginsDir() . $file)) {
$className = preg_replace('/^[0-9]+-/', '', $file);
$classNameLowered = strtolower($className);
if (isset($pluginsLowered[$classNameLowered])) {
continue; continue;
} }
if (is_dir($this->getPluginsDir() . $file)) { if (file_exists($this->getPluginsDir() . $file . '/' . $className . '.php')) {
$className = preg_replace('/^[0-9]+-/', '', $file); $pluginFiles[$className] = $file . '/' . $className . '.php';
} else {
$subdirFiles = $this->getFilesGlob($this->getPluginsDir() . $file . '/?*.php', self::SORT_NONE); $subdirFiles = $this->getFilesGlob($this->getPluginsDir() . $file . '/?*.php', self::SORT_NONE);
foreach ($subdirFiles as $subdirFile) { foreach ($subdirFiles as $subdirFile) {
$subdirFile = basename($subdirFile, '.php'); $subdirFile = basename($subdirFile, '.php');
if (strcasecmp($className, $subdirFile) === 0) { if ($classNameLowered === strtolower($subdirFile)) {
$pluginFiles[$className] = $file . '/' . $subdirFile . '.php'; $pluginFiles[$className] = $file . '/' . $subdirFile . '.php';
break;
} }
} }
}
if (!isset($pluginFiles[$className])) { if (!isset($pluginFiles[$className])) {
throw new RuntimeException( throw new RuntimeException(
"Unable to load plugin '" . $className . "' from " "Unable to load plugin '" . $className . "' from "
. "'" . $file . "/" . $className . ".php': File not found" . "'" . $file . "/" . $className . ".php': File not found"
); );
} }
} elseif (substr($file, -4) === '.php') { } elseif (substr($file, -4) === '.php') {
$className = preg_replace('/^[0-9]+-/', '', substr($file, 0, -4)); $className = preg_replace('/^[0-9]+-/', '', substr($file, 0, -4));
$pluginFiles[$className] = $file; $classNameLowered = strtolower($className);
if (isset($pluginsLowered[$classNameLowered])) {
continue;
} }
$pluginFiles[$className] = $file;
} else {
throw new RuntimeException("Unable to load plugin from '" . $file . "': Not a valid plugin file");
} }
} }
@ -537,10 +606,6 @@ class Pico
$plugin = new $className($this); $plugin = new $className($this);
$className = get_class($plugin); $className = get_class($plugin);
if (isset($this->plugins[$className])) {
continue;
}
$this->plugins[$className] = $plugin; $this->plugins[$className] = $plugin;
if ($plugin instanceof PicoPluginInterface) { if ($plugin instanceof PicoPluginInterface) {
@ -554,44 +619,6 @@ class Pico
} }
} }
/**
* Loads plugins from vendor/pico-plugin.php
*
* This method loads all plugins installed using `composer` and Pico's
* `picocms/pico-composer` installer by reading the `pico-plugin.php` in
* composer's `vendor` dir. Using composer enables plugin developers to
* load multiple plugins and their dependencies using a single composer
* package.
*
* @see Pico::loadPlugins()
* @see Pico::loadLocalPlugins()
* @return void
*/
protected function loadComposerPlugins()
{
$composerPlugins = array();
if (file_exists($this->getVendorDir() . 'vendor/pico-plugin.php')) {
// composer root package
$composerPlugins = require($this->getVendorDir() . 'vendor/pico-plugin.php') ?: array();
} elseif (file_exists($this->getVendorDir() . '../../../vendor/pico-plugin.php')) {
// composer dependency package
$composerPlugins = require($this->getVendorDir() . '../../../vendor/pico-plugin.php') ?: array();
}
foreach ($composerPlugins as $package => $classNames) {
foreach ($classNames as $className) {
$plugin = new $className($this);
$this->plugins[$className] = $plugin;
if ($plugin instanceof PicoPluginInterface) {
if (defined($className . '::API_VERSION') && ($className::API_VERSION >= static::API_VERSION)) {
$this->nativePlugins[$className] = $plugin;
}
}
}
}
}
/** /**
* Manually loads a plugin * Manually loads a plugin
* *

Loading…
Cancel
Save