123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452 |
- <?php
- namespace Typecho;
- use Typecho\Plugin\Exception as PluginException;
- /**
- * 插件处理类
- *
- * @category typecho
- * @package Plugin
- * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org)
- * @license GNU General Public License 2.0
- */
- class Plugin
- {
- /**
- * 所有启用的插件
- *
- * @var array
- */
- private static $plugin = [];
- /**
- * 实例化的插件对象
- *
- * @var array
- */
- private static $instances;
- /**
- * 临时存储变量
- *
- * @var array
- */
- private static $tmp = [];
- /**
- * 唯一句柄
- *
- * @var string
- */
- private $handle;
- /**
- * 组件
- *
- * @var string
- */
- private $component;
- /**
- * 是否触发插件的信号
- *
- * @var boolean
- */
- private $signal;
- /**
- * 插件初始化
- *
- * @param string $handle 插件
- */
- public function __construct(string $handle)
- {
- if (defined('__TYPECHO_CLASS_ALIASES__')) {
- $alias = array_search('\\' . ltrim($handle, '\\'), __TYPECHO_CLASS_ALIASES__);
- $handle = $alias ?: $handle;
- }
- $this->handle = Common::nativeClassName($handle);
- }
- /**
- * 插件初始化
- *
- * @param array $plugins 插件列表
- */
- public static function init(array $plugins)
- {
- $plugins['activated'] = array_key_exists('activated', $plugins) ? $plugins['activated'] : [];
- $plugins['handles'] = array_key_exists('handles', $plugins) ? $plugins['handles'] : [];
- /** 初始化变量 */
- self::$plugin = $plugins;
- }
- /**
- * 获取实例化插件对象
- *
- * @param string $handle 插件
- * @return Plugin
- */
- public static function factory(string $handle): Plugin
- {
- return self::$instances[$handle] ?? (self::$instances[$handle] = new self($handle));
- }
- /**
- * 启用插件
- *
- * @param string $pluginName 插件名称
- */
- public static function activate(string $pluginName)
- {
- self::$plugin['activated'][$pluginName] = self::$tmp;
- self::$tmp = [];
- }
- /**
- * 禁用插件
- *
- * @param string $pluginName 插件名称
- */
- public static function deactivate(string $pluginName)
- {
- /** 去掉所有相关回调函数 */
- if (
- isset(self::$plugin['activated'][$pluginName]['handles'])
- && is_array(self::$plugin['activated'][$pluginName]['handles'])
- ) {
- foreach (self::$plugin['activated'][$pluginName]['handles'] as $handle => $handles) {
- self::$plugin['handles'][$handle] = self::pluginHandlesDiff(
- empty(self::$plugin['handles'][$handle]) ? [] : self::$plugin['handles'][$handle],
- empty($handles) ? [] : $handles
- );
- if (empty(self::$plugin['handles'][$handle])) {
- unset(self::$plugin['handles'][$handle]);
- }
- }
- }
- /** 禁用当前插件 */
- unset(self::$plugin['activated'][$pluginName]);
- }
- /**
- * 插件handle比对
- *
- * @param array $pluginHandles
- * @param array $otherPluginHandles
- * @return array
- */
- private static function pluginHandlesDiff(array $pluginHandles, array $otherPluginHandles): array
- {
- foreach ($otherPluginHandles as $handle) {
- while (false !== ($index = array_search($handle, $pluginHandles))) {
- unset($pluginHandles[$index]);
- }
- }
- return $pluginHandles;
- }
- /**
- * 导出当前插件设置
- *
- * @return array
- */
- public static function export(): array
- {
- return self::$plugin;
- }
- /**
- * 获取插件文件的头信息
- *
- * @param string $pluginFile 插件文件路径
- * @return array
- */
- public static function parseInfo(string $pluginFile): array
- {
- $tokens = token_get_all(file_get_contents($pluginFile));
- $isDoc = false;
- $isFunction = false;
- $isClass = false;
- $isInClass = false;
- $isInFunction = false;
- $isDefined = false;
- $current = null;
- /** 初始信息 */
- $info = [
- 'description' => '',
- 'title' => '',
- 'author' => '',
- 'homepage' => '',
- 'version' => '',
- 'since' => '',
- 'activate' => false,
- 'deactivate' => false,
- 'config' => false,
- 'personalConfig' => false
- ];
- $map = [
- 'package' => 'title',
- 'author' => 'author',
- 'link' => 'homepage',
- 'since' => 'since',
- 'version' => 'version'
- ];
- foreach ($tokens as $token) {
- /** 获取doc comment */
- if (!$isDoc && is_array($token) && T_DOC_COMMENT == $token[0]) {
- /** 分行读取 */
- $described = false;
- $lines = preg_split("(\r|\n)", $token[1]);
- foreach ($lines as $line) {
- $line = trim($line);
- if (!empty($line) && '*' == $line[0]) {
- $line = trim(substr($line, 1));
- if (!$described && !empty($line) && '@' == $line[0]) {
- $described = true;
- }
- if (!$described && !empty($line)) {
- $info['description'] .= $line . "\n";
- } elseif ($described && !empty($line) && '@' == $line[0]) {
- $info['description'] = trim($info['description']);
- $line = trim(substr($line, 1));
- $args = explode(' ', $line);
- $key = array_shift($args);
- if (isset($map[$key])) {
- $info[$map[$key]] = trim(implode(' ', $args));
- }
- }
- }
- }
- $isDoc = true;
- }
- if (is_array($token)) {
- switch ($token[0]) {
- case T_FUNCTION:
- $isFunction = true;
- break;
- case T_IMPLEMENTS:
- $isClass = true;
- break;
- case T_WHITESPACE:
- case T_COMMENT:
- case T_DOC_COMMENT:
- break;
- case T_STRING:
- $string = strtolower($token[1]);
- switch ($string) {
- case 'typecho_plugin_interface':
- case 'plugininterface':
- $isInClass = $isClass;
- break;
- case 'activate':
- case 'deactivate':
- case 'config':
- case 'personalconfig':
- if ($isFunction) {
- $current = ('personalconfig' == $string ? 'personalConfig' : $string);
- }
- break;
- default:
- if (!empty($current) && $isInFunction && $isInClass) {
- $info[$current] = true;
- }
- break;
- }
- break;
- default:
- if (!empty($current) && $isInFunction && $isInClass) {
- $info[$current] = true;
- }
- break;
- }
- } else {
- $token = strtolower($token);
- switch ($token) {
- case '{':
- if ($isDefined) {
- $isInFunction = true;
- }
- break;
- case '(':
- if ($isFunction && !$isDefined) {
- $isDefined = true;
- }
- break;
- case '}':
- case ';':
- $isDefined = false;
- $isFunction = false;
- $isInFunction = false;
- $current = null;
- break;
- default:
- if (!empty($current) && $isInFunction && $isInClass) {
- $info[$current] = true;
- }
- break;
- }
- }
- }
- return $info;
- }
- /**
- * 获取插件路径和类名
- * 返回值为一个数组
- * 第一项为插件路径,第二项为类名
- *
- * @param string $pluginName 插件名
- * @param string $path 插件目录
- * @return array
- * @throws PluginException
- */
- public static function portal(string $pluginName, string $path): array
- {
- switch (true) {
- case file_exists($pluginFileName = $path . '/' . $pluginName . '/Plugin.php'):
- $className = "\\" . PLUGIN_NAMESPACE . "\\{$pluginName}\\Plugin";
- break;
- case file_exists($pluginFileName = $path . '/' . $pluginName . '.php'):
- $className = "\\" . PLUGIN_NAMESPACE . "\\" . $pluginName;
- break;
- default:
- throw new PluginException('Missing Plugin ' . $pluginName, 404);
- }
- return [$pluginFileName, $className];
- }
- /**
- * 版本依赖性检测
- *
- * @param string|null $version 插件版本
- * @return boolean
- */
- public static function checkDependence(?string $version): bool
- {
- //如果没有检测规则,直接掠过
- if (empty($version)) {
- return true;
- }
- return version_compare(Common::VERSION, $version, '>=');
- }
- /**
- * 判断插件是否存在
- *
- * @param string $pluginName 插件名称
- * @return mixed
- */
- public static function exists(string $pluginName)
- {
- return array_key_exists($pluginName, self::$plugin['activated']);
- }
- /**
- * 插件调用后的触发器
- *
- * @param boolean|null $signal 触发器
- * @return Plugin
- */
- public function trigger(?bool &$signal): Plugin
- {
- $signal = false;
- $this->signal = &$signal;
- return $this;
- }
- /**
- * 通过魔术函数设置当前组件位置
- *
- * @param string $component 当前组件
- * @return Plugin
- */
- public function __get(string $component)
- {
- $this->component = $component;
- return $this;
- }
- /**
- * 设置回调函数
- *
- * @param string $component 当前组件
- * @param callable $value 回调函数
- */
- public function __set(string $component, callable $value)
- {
- $weight = 0;
- if (strpos($component, '_') > 0) {
- $parts = explode('_', $component, 2);
- [$component, $weight] = $parts;
- $weight = intval($weight) - 10;
- }
- $component = $this->handle . ':' . $component;
- if (!isset(self::$plugin['handles'][$component])) {
- self::$plugin['handles'][$component] = [];
- }
- if (!isset(self::$tmp['handles'][$component])) {
- self::$tmp['handles'][$component] = [];
- }
- foreach (self::$plugin['handles'][$component] as $key => $val) {
- $key = floatval($key);
- if ($weight > $key) {
- break;
- } elseif ($weight == $key) {
- $weight += 0.001;
- }
- }
- self::$plugin['handles'][$component][strval($weight)] = $value;
- self::$tmp['handles'][$component][] = $value;
- ksort(self::$plugin['handles'][$component], SORT_NUMERIC);
- }
- /**
- * 回调处理函数
- *
- * @param string $component 当前组件
- * @param array $args 参数
- * @return mixed
- */
- public function __call(string $component, array $args)
- {
- $component = $this->handle . ':' . $component;
- $last = count($args);
- $args[$last] = $last > 0 ? $args[0] : false;
- if (isset(self::$plugin['handles'][$component])) {
- $args[$last] = null;
- $this->signal = true;
- foreach (self::$plugin['handles'][$component] as $callback) {
- $args[$last] = call_user_func_array($callback, $args);
- }
- }
- return $args[$last];
- }
- }
|