123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- <?php
- /**
- * This file is part of Pico. It's copyrighted by the contributors recorded
- * in the version control history of the file, available from the following
- * original location:
- *
- * <https://github.com/picocms/Pico/blob/master/lib/AbstractPicoPlugin.php>
- *
- * SPDX-License-Identifier: MIT
- * License-Filename: LICENSE
- */
- /**
- * Abstract class to extend from when implementing a Pico plugin
- *
- * Please refer to {@see PicoPluginInterface} for more information about how
- * to develop a plugin for Pico.
- *
- * @see PicoPluginInterface
- *
- * @author Daniel Rudolf
- * @link http://picocms.org
- * @license http://opensource.org/licenses/MIT The MIT License
- * @version 2.0
- */
- abstract class AbstractPicoPlugin implements PicoPluginInterface
- {
- /**
- * Current instance of Pico
- *
- * @see PicoPluginInterface::getPico()
- * @var Pico
- */
- protected $pico;
- /**
- * Boolean indicating if this plugin is enabled (TRUE) or disabled (FALSE)
- *
- * @see PicoPluginInterface::isEnabled()
- * @see PicoPluginInterface::setEnabled()
- * @var bool|null
- */
- protected $enabled;
- /**
- * Boolean indicating if this plugin was ever enabled/disabled manually
- *
- * @see PicoPluginInterface::isStatusChanged()
- * @var bool
- */
- protected $statusChanged = false;
- /**
- * Boolean indicating whether this plugin matches Pico's API version
- *
- * @see AbstractPicoPlugin::checkCompatibility()
- * @var bool|null
- */
- protected $nativePlugin;
- /**
- * List of plugins which this plugin depends on
- *
- * @see AbstractPicoPlugin::checkDependencies()
- * @see PicoPluginInterface::getDependencies()
- * @var string[]
- */
- protected $dependsOn = array();
- /**
- * List of plugin which depend on this plugin
- *
- * @see AbstractPicoPlugin::checkDependants()
- * @see PicoPluginInterface::getDependants()
- * @var object[]|null
- */
- private $dependants;
- /**
- * Constructs a new instance of a Pico plugin
- *
- * @param Pico $pico current instance of Pico
- */
- public function __construct(Pico $pico)
- {
- $this->pico = $pico;
- }
- /**
- * {@inheritDoc}
- */
- public function handleEvent($eventName, array $params)
- {
- // plugins can be enabled/disabled using the config
- if ($eventName === 'onConfigLoaded') {
- $this->configEnabled();
- }
- if ($this->isEnabled() || ($eventName === 'onPluginsLoaded')) {
- if (method_exists($this, $eventName)) {
- call_user_func_array(array($this, $eventName), $params);
- }
- }
- }
- /**
- * Enables or disables this plugin depending on Pico's config
- */
- protected function configEnabled()
- {
- $pluginEnabled = $this->getPico()->getConfig(get_called_class() . '.enabled');
- if ($pluginEnabled !== null) {
- $this->setEnabled($pluginEnabled);
- } else {
- $pluginEnabled = $this->getPluginConfig('enabled');
- if ($pluginEnabled !== null) {
- $this->setEnabled($pluginEnabled);
- } elseif ($this->enabled) {
- $this->setEnabled(true, true, true);
- } elseif ($this->enabled === null) {
- // make sure dependencies are already fulfilled,
- // otherwise the plugin needs to be enabled manually
- try {
- $this->setEnabled(true, false, true);
- } catch (RuntimeException $e) {
- $this->enabled = false;
- }
- }
- }
- }
- /**
- * {@inheritDoc}
- */
- public function setEnabled($enabled, $recursive = true, $auto = false)
- {
- $this->statusChanged = (!$this->statusChanged) ? !$auto : true;
- $this->enabled = (bool) $enabled;
- if ($enabled) {
- $this->checkCompatibility();
- $this->checkDependencies($recursive);
- } else {
- $this->checkDependants($recursive);
- }
- }
- /**
- * {@inheritDoc}
- */
- public function isEnabled()
- {
- return $this->enabled;
- }
- /**
- * {@inheritDoc}
- */
- public function isStatusChanged()
- {
- return $this->statusChanged;
- }
- /**
- * {@inheritDoc}
- */
- public function getPico()
- {
- return $this->pico;
- }
- /**
- * Returns either the value of the specified plugin config variable or
- * the config array
- *
- * @param string $configName optional name of a config variable
- * @param mixed $default optional default value to return when the
- * named config variable doesn't exist
- *
- * @return mixed if no name of a config variable has been supplied, the
- * plugin's config array is returned; otherwise it returns either the
- * value of the named config variable, or, if the named config variable
- * doesn't exist, the provided default value or NULL
- */
- public function getPluginConfig($configName = null, $default = null)
- {
- $pluginConfig = $this->getPico()->getConfig(get_called_class(), array());
- if ($configName === null) {
- return $pluginConfig;
- }
- return isset($pluginConfig[$configName]) ? $pluginConfig[$configName] : $default;
- }
- /**
- * Passes all not satisfiable method calls to Pico
- *
- * @see PicoPluginInterface::getPico()
- *
- * @deprecated 3.0.0
- *
- * @param string $methodName name of the method to call
- * @param array $params parameters to pass
- *
- * @return mixed return value of the called method
- */
- public function __call($methodName, array $params)
- {
- if (method_exists($this->getPico(), $methodName)) {
- return call_user_func_array(array($this->getPico(), $methodName), $params);
- }
- throw new BadMethodCallException(
- 'Call to undefined method ' . get_class($this->getPico()) . '::' . $methodName . '() '
- . 'through ' . get_called_class() . '::__call()'
- );
- }
- /**
- * Enables all plugins which this plugin depends on
- *
- * @see PicoPluginInterface::getDependencies()
- *
- * @param bool $recursive enable required plugins automatically
- *
- * @throws RuntimeException thrown when a dependency fails
- */
- protected function checkDependencies($recursive)
- {
- foreach ($this->getDependencies() as $pluginName) {
- try {
- $plugin = $this->getPico()->getPlugin($pluginName);
- } catch (RuntimeException $e) {
- throw new RuntimeException(
- "Unable to enable plugin '" . get_called_class() . "': "
- . "Required plugin '" . $pluginName . "' not found"
- );
- }
- // plugins which don't implement PicoPluginInterface are always enabled
- if (($plugin instanceof PicoPluginInterface) && !$plugin->isEnabled()) {
- if ($recursive) {
- if (!$plugin->isStatusChanged()) {
- $plugin->setEnabled(true, true, true);
- } else {
- throw new RuntimeException(
- "Unable to enable plugin '" . get_called_class() . "': "
- . "Required plugin '" . $pluginName . "' was disabled manually"
- );
- }
- } else {
- throw new RuntimeException(
- "Unable to enable plugin '" . get_called_class() . "': "
- . "Required plugin '" . $pluginName . "' is disabled"
- );
- }
- }
- }
- }
- /**
- * {@inheritDoc}
- */
- public function getDependencies()
- {
- return (array) $this->dependsOn;
- }
- /**
- * Disables all plugins which depend on this plugin
- *
- * @see PicoPluginInterface::getDependants()
- *
- * @param bool $recursive disabled dependant plugins automatically
- *
- * @throws RuntimeException thrown when a dependency fails
- */
- protected function checkDependants($recursive)
- {
- $dependants = $this->getDependants();
- if ($dependants) {
- if ($recursive) {
- foreach ($this->getDependants() as $pluginName => $plugin) {
- if ($plugin->isEnabled()) {
- if (!$plugin->isStatusChanged()) {
- $plugin->setEnabled(false, true, true);
- } else {
- throw new RuntimeException(
- "Unable to disable plugin '" . get_called_class() . "': "
- . "Required by manually enabled plugin '" . $pluginName . "'"
- );
- }
- }
- }
- } else {
- $dependantsList = 'plugin' . ((count($dependants) > 1) ? 's' : '') . ' '
- . "'" . implode("', '", array_keys($dependants)) . "'";
- throw new RuntimeException(
- "Unable to disable plugin '" . get_called_class() . "': "
- . "Required by " . $dependantsList
- );
- }
- }
- }
- /**
- * {@inheritDoc}
- */
- public function getDependants()
- {
- if ($this->dependants === null) {
- $this->dependants = array();
- foreach ($this->getPico()->getPlugins() as $pluginName => $plugin) {
- // only plugins which implement PicoPluginInterface support dependencies
- if ($plugin instanceof PicoPluginInterface) {
- $dependencies = $plugin->getDependencies();
- if (in_array(get_called_class(), $dependencies)) {
- $this->dependants[$pluginName] = $plugin;
- }
- }
- }
- }
- return $this->dependants;
- }
- /**
- * Checks compatibility with Pico's API version
- *
- * Pico automatically adds a dependency to {@see PicoDeprecated} when the
- * plugin's API is older than Pico's API. {@see PicoDeprecated} furthermore
- * throws a exception when it can't provide compatibility in such cases.
- * However, we still have to decide whether this plugin is compatible to
- * newer API versions, what requires some special (version specific)
- * precaution and is therefore usually not the case.
- *
- * @throws RuntimeException thrown when the plugin's and Pico's API aren't
- * compatible
- */
- protected function checkCompatibility()
- {
- if ($this->nativePlugin === null) {
- $picoClassName = get_class($this->pico);
- $picoApiVersion = defined($picoClassName . '::API_VERSION') ? $picoClassName::API_VERSION : 1;
- $pluginApiVersion = defined('static::API_VERSION') ? static::API_VERSION : 1;
- $this->nativePlugin = ($pluginApiVersion === $picoApiVersion);
- if (!$this->nativePlugin && ($pluginApiVersion > $picoApiVersion)) {
- throw new RuntimeException(
- "Unable to enable plugin '" . get_called_class() . "': The plugin's API (version "
- . $pluginApiVersion . ") isn't compatible with Pico's API (version " . $picoApiVersion . ")"
- );
- }
- }
- }
- }
|