<?php
+
namespace Shaarli\Plugin;
use Shaarli\Config\ConfigManager;
use Shaarli\Plugin\Exception\PluginFileNotFoundException;
+use Shaarli\Plugin\Exception\PluginInvalidRouteException;
/**
* Class PluginManager
*
* @var array $loadedPlugins
*/
- private $loadedPlugins = array();
+ private $loadedPlugins = [];
+
+ /** @var array List of registered routes. Contains keys:
+ * - `method`: HTTP method, GET/POST/PUT/PATCH/DELETE
+ * - `route` (path): without prefix, e.g. `/up/{variable}`
+ * It will be later prefixed by `/plugin/<plugin name>/`.
+ * - `callable` string, function name or FQN class's method, e.g. `demo_plugin_custom_controller`.
+ */
+ protected $registeredRoutes = [];
/**
* @var ConfigManager Configuration Manager instance.
public function __construct(&$conf)
{
$this->conf = $conf;
- $this->errors = array();
+ $this->errors = [];
}
/**
$this->loadPlugin($dirs[$index], $plugin);
} catch (PluginFileNotFoundException $e) {
error_log($e->getMessage());
+ } catch (\Throwable $e) {
+ $error = $plugin . t(' [plugin incompatibility]: ') . $e->getMessage();
+ $this->errors = array_unique(array_merge($this->errors, [$error]));
}
}
}
*
* @return void
*/
- public function executeHooks($hook, &$data, $params = array())
+ public function executeHooks($hook, &$data, $params = [])
{
- if (!empty($params['target'])) {
- $data['_PAGE_'] = $params['target'];
- }
+ $metadataParameters = [
+ 'target' => '_PAGE_',
+ 'loggedin' => '_LOGGEDIN_',
+ 'basePath' => '_BASE_PATH_',
+ 'rootPath' => '_ROOT_PATH_',
+ 'bookmarkService' => '_BOOKMARK_SERVICE_',
+ ];
- if (isset($params['loggedin'])) {
- $data['_LOGGEDIN_'] = $params['loggedin'];
- }
-
- if (isset($params['basePath'])) {
- $data['_BASE_PATH_'] = $params['basePath'];
+ foreach ($metadataParameters as $parameter => $metaKey) {
+ if (array_key_exists($parameter, $params)) {
+ $data[$metaKey] = $params[$parameter];
+ }
}
foreach ($this->loadedPlugins as $plugin) {
}
}
}
+
+ foreach ($metadataParameters as $metaKey) {
+ unset($data[$metaKey]);
+ }
}
/**
}
}
+ $registerRouteFunction = $pluginName . '_register_routes';
+ $routes = null;
+ if (function_exists($registerRouteFunction)) {
+ $routes = call_user_func($registerRouteFunction);
+ }
+
+ if ($routes !== null) {
+ foreach ($routes as $route) {
+ if (static::validateRouteRegistration($route)) {
+ $this->registeredRoutes[$pluginName][] = $route;
+ } else {
+ throw new PluginInvalidRouteException($pluginName);
+ }
+ }
+ }
+
$this->loadedPlugins[] = $pluginName;
}
*/
public function getPluginsMeta()
{
- $metaData = array();
+ $metaData = [];
$dirs = glob(self::$PLUGINS_PATH . '/*', GLOB_ONLYDIR | GLOB_MARK);
// Browse all plugin directories.
if (isset($metaData[$plugin]['parameters'])) {
$params = explode(';', $metaData[$plugin]['parameters']);
} else {
- $params = array();
+ $params = [];
}
- $metaData[$plugin]['parameters'] = array();
+ $metaData[$plugin]['parameters'] = [];
foreach ($params as $param) {
if (empty($param)) {
continue;
return $metaData;
}
+ /**
+ * @return array List of registered custom routes by plugins.
+ */
+ public function getRegisteredRoutes(): array
+ {
+ return $this->registeredRoutes;
+ }
+
/**
* Return the list of encountered errors.
*
{
return $this->errors;
}
+
+ /**
+ * Checks whether provided input is valid to register a new route.
+ * It must contain keys `method`, `route`, `callable` (all strings).
+ *
+ * @param string[] $input
+ *
+ * @return bool
+ */
+ protected static function validateRouteRegistration(array $input): bool
+ {
+ if (
+ !array_key_exists('method', $input)
+ || !in_array(strtoupper($input['method']), ['GET', 'PUT', 'PATCH', 'POST', 'DELETE'])
+ ) {
+ return false;
+ }
+
+ if (!array_key_exists('route', $input) || !preg_match('#^[a-z\d/\.\-_]+$#', $input['route'])) {
+ return false;
+ }
+
+ if (!array_key_exists('callable', $input)) {
+ return false;
+ }
+
+ return true;
+ }
}