Plugin.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | 海豚PHP框架 [ DolphinPHP ]
  4. // +----------------------------------------------------------------------
  5. // | 版权所有 2016~2019 广东卓锐软件有限公司 [ http://www.zrthink.com ]
  6. // +----------------------------------------------------------------------
  7. // | 官方网站: http://dolphinphp.com
  8. // +----------------------------------------------------------------------
  9. namespace app\admin\model;
  10. use think\Model;
  11. /**
  12. * 插件模型
  13. * @package app\admin\model
  14. */
  15. class Plugin extends Model
  16. {
  17. // 设置当前模型对应的完整数据表名称
  18. protected $name = 'admin_plugin';
  19. // 自动写入时间戳
  20. protected $autoWriteTimestamp = true;
  21. // 写入时处理config
  22. public function setConfigAttr($value)
  23. {
  24. return !empty($value) ? json_encode($value) : '';
  25. }
  26. /**
  27. * 获取所有插件信息
  28. * @param string $keyword 查找关键词
  29. * @param string $status 查找状态
  30. * @author 蔡伟明 <314013107@qq.com>
  31. * @return array|bool
  32. */
  33. public function getAll($keyword = '', $status = '')
  34. {
  35. $result = cache('plugin_all');
  36. if (!$result) {
  37. // 获取插件目录下的所有插件目录
  38. $dirs = array_map('basename', glob(config('plugin_path').'*', GLOB_ONLYDIR));
  39. if ($dirs === false || !file_exists(config('plugin_path'))) {
  40. $this->error = '插件目录不可读或者不存在';
  41. return false;
  42. }
  43. // 读取数据库插件表
  44. $plugins = $this->order('sort asc,id desc')->column(true, 'name');
  45. // 读取未安装的插件
  46. foreach ($dirs as $plugin) {
  47. if (!isset($plugins[$plugin])) {
  48. $plugins[$plugin]['name'] = $plugin;
  49. // 获取插件类名
  50. $class = get_plugin_class($plugin);
  51. // 插件类不存在则跳过实例化
  52. if (!class_exists($class)) {
  53. // 插件的入口文件不存在!
  54. $plugins[$plugin]['status'] = '-2';
  55. continue;
  56. }
  57. // 实例化插件
  58. $obj = new $class;
  59. // 插件插件信息缺失
  60. if (!isset($obj->info) || empty($obj->info)) {
  61. // 插件信息缺失!
  62. $plugins[$plugin]['status'] = '-3';
  63. continue;
  64. }
  65. // 插件插件信息不完整
  66. if (!$this->checkInfo($obj->info)) {
  67. $plugins[$plugin]['status'] = '-4';
  68. continue;
  69. }
  70. // 插件未安装
  71. $plugins[$plugin] = $obj->info;
  72. $plugins[$plugin]['status'] = '-1';
  73. }
  74. }
  75. // 数量统计
  76. $total = [
  77. 'all' => count($plugins), // 所有插件数量
  78. '-2' => 0, // 错误插件数量
  79. '-1' => 0, // 未安装数量
  80. '0' => 0, // 未启用数量
  81. '1' => 0, // 已启用数量
  82. ];
  83. // 过滤查询结果和统计数量
  84. foreach ($plugins as $key => $value) {
  85. // 统计数量
  86. if (in_array($value['status'], ['-2', '-3', '-4'])) {
  87. // 已损坏数量
  88. $total['-2']++;
  89. } else {
  90. $total[(string)$value['status']]++;
  91. }
  92. // 过滤查询
  93. if ($status != '') {
  94. if ($status == '-2') {
  95. // 过滤掉非已损坏的插件
  96. if (!in_array($value['status'], ['-2', '-3', '-4'])) {
  97. unset($plugins[$key]);
  98. continue;
  99. }
  100. } else if ($value['status'] != $status) {
  101. unset($plugins[$key]);
  102. continue;
  103. }
  104. }
  105. if ($keyword != '') {
  106. if (stristr($value['name'], $keyword) === false && (!isset($value['title']) || stristr($value['title'], $keyword) === false) && (!isset($value['author']) || stristr($value['author'], $keyword) === false)) {
  107. unset($plugins[$key]);
  108. continue;
  109. }
  110. }
  111. }
  112. // 处理状态及插件按钮
  113. foreach ($plugins as &$plugin) {
  114. switch ($plugin['status']) {
  115. case '-4': // 插件信息不完整
  116. $plugin['title'] = '插件信息不完整';
  117. $plugin['bg_color'] = 'danger';
  118. $plugin['status_class'] = 'text-danger';
  119. $plugin['status_info'] = '<i class="fa fa-times"></i> 已损坏';
  120. $plugin['actions'] = '<button class="btn btn-sm btn-noborder btn-danger" type="button" disabled>不可操作</button>';
  121. break;
  122. case '-3': // 插件信息缺失
  123. $plugin['title'] = '插件信息缺失';
  124. $plugin['bg_color'] = 'danger';
  125. $plugin['status_class'] = 'text-danger';
  126. $plugin['status_info'] = '<i class="fa fa-times"></i> 已损坏';
  127. $plugin['actions'] = '<button class="btn btn-sm btn-noborder btn-danger" type="button" disabled>不可操作</button>';
  128. break;
  129. case '-2': // 入口文件不存在
  130. $plugin['title'] = '入口文件不存在';
  131. $plugin['bg_color'] = 'danger';
  132. $plugin['status_class'] = 'text-danger';
  133. $plugin['status_info'] = '<i class="fa fa-times"></i> 已损坏';
  134. $plugin['actions'] = '<button class="btn btn-sm btn-noborder btn-danger" type="button" disabled>不可操作</button>';
  135. break;
  136. case '-1': // 未安装
  137. $plugin['bg_color'] = 'info';
  138. $plugin['actions'] = '<a class="btn btn-sm btn-noborder btn-success ajax-get confirm" href="'.url('install', ['name' => $plugin['name']]).'">安装</a>';
  139. $plugin['status_class'] = 'text-info';
  140. $plugin['status_info'] = '<i class="fa fa-fw fa-th-large"></i> 未安装';
  141. break;
  142. case '0': // 禁用
  143. $plugin['bg_color'] = 'warning';
  144. $plugin['actions'] = '<a class="btn btn-sm btn-noborder btn-success ajax-get confirm" href="'.url('enable', ['ids' => $plugin['id']]).'">启用</a> ';
  145. $plugin['actions'] .= '<a class="btn btn-sm btn-noborder btn-danger ajax-get confirm" data-tips="如果包括数据库,将同时删除数据库!" href="'.url('uninstall', ['name' => $plugin['name']]).'">卸载</a> ';
  146. if (isset($plugin['config']) && $plugin['config'] != '') {
  147. $plugin['actions'] .= '<a class="btn btn-sm btn-noborder btn-info" href="'.url('config', ['name' => $plugin['name']]).'">设置</a> ';
  148. }
  149. if ($plugin['admin'] != '0') {
  150. $plugin['actions'] .= '<a class="btn btn-sm btn-noborder btn-primary" href="'.url('manage', ['name' => $plugin['name']]).'">管理</a> ';
  151. }
  152. $plugin['status_class'] = 'text-warning';
  153. $plugin['status_info'] = '<i class="fa fa-ban"></i> 已禁用';
  154. break;
  155. case '1': // 启用
  156. $plugin['bg_color'] = 'success';
  157. $plugin['actions'] = '<a class="btn btn-sm btn-noborder btn-warning ajax-get confirm" href="'.url('disable', ['ids' => $plugin['id']]).'">禁用</a> ';
  158. $plugin['actions'] .= '<a class="btn btn-sm btn-noborder btn-danger ajax-get confirm" data-tips="如果包括数据库,将同时删除数据库!" href="'.url('uninstall', ['name' => $plugin['name']]).'">卸载</a> ';
  159. if (isset($plugin['config']) && $plugin['config'] != '') {
  160. $plugin['actions'] .= '<a class="btn btn-sm btn-noborder btn-info" href="'.url('config', ['name' => $plugin['name']]).'">设置</a> ';
  161. }
  162. if ($plugin['admin'] != '0') {
  163. $plugin['actions'] .= '<a class="btn btn-sm btn-noborder btn-primary" href="'.url('manage', ['name' => $plugin['name']]).'">管理</a> ';
  164. }
  165. $plugin['status_class'] = 'text-success';
  166. $plugin['status_info'] = '<i class="fa fa-check"></i> 已启用';
  167. break;
  168. default: // 未知
  169. $plugin['title'] = '未知';
  170. break;
  171. }
  172. }
  173. $result = ['total' => $total, 'plugins' => $plugins];
  174. // 非开发模式,缓存数据
  175. if (config('develop_mode') == 0) {
  176. cache('plugin_all', $result);
  177. }
  178. }
  179. return $result;
  180. }
  181. /**
  182. * 检查插件插件信息是否完整
  183. * @param string $info 插件插件信息
  184. * @author 蔡伟明 <314013107@qq.com>
  185. * @return bool
  186. */
  187. private function checkInfo($info = '')
  188. {
  189. $default_item = ['name','title','author','version'];
  190. foreach ($default_item as $item) {
  191. if (!isset($info[$item]) || $info[$item] == '') {
  192. return false;
  193. }
  194. }
  195. return true;
  196. }
  197. /**
  198. * 获取插件配置
  199. * @param string $name 插件名称
  200. * @param string $item 指定返回的插件配置项
  201. * @author 蔡伟明 <314013107@qq.com>
  202. * @return array|mixed
  203. */
  204. public function getConfig($name = '', $item = '')
  205. {
  206. $config = cache('plugin_config_'.$name);
  207. if (!$config) {
  208. $config = $this->where('name', $name)->value('config');
  209. if (!$config) {
  210. return [];
  211. }
  212. $config = json_decode($config, true);
  213. // 非开发模式,缓存数据
  214. if (config('develop_mode') == 0) {
  215. cache('plugin_config_'.$name, $config);
  216. }
  217. }
  218. if (!empty($item)) {
  219. $items = explode(',', $item);
  220. if (count($items) == 1) {
  221. return isset($config[$item]) ? $config[$item] : '';
  222. }
  223. $result = [];
  224. foreach ($items as $item) {
  225. $result[$item] = isset($config[$item]) ? $config[$item] : '';
  226. }
  227. return $result;
  228. }
  229. return $config;
  230. }
  231. /**
  232. * 设置插件配置
  233. * @param string $name 插件名.配置名
  234. * @param string $value 配置值
  235. * @author caiweiming <314013107@qq.com>
  236. * @return bool
  237. */
  238. public function setConfig($name = '', $value = '')
  239. {
  240. $item = '';
  241. if (strpos($name, '.')) {
  242. list($name, $item) = explode('.', $name);
  243. }
  244. // 获取缓存
  245. $config = cache('plugin_config_'.$name);
  246. if (!$config) {
  247. $config = $this->where('name', $name)->value('config');
  248. if (!$config) {
  249. return false;
  250. }
  251. $config = json_decode($config, true);
  252. }
  253. if ($item === '') {
  254. // 批量更新
  255. if (!is_array($value) || empty($value)) {
  256. // 值的格式错误,必须为数组
  257. return false;
  258. }
  259. $config = array_merge($config, $value);
  260. } else {
  261. // 更新单个值
  262. $config[$item] = $value;
  263. }
  264. if (false === $this->where('name', $name)->setField('config', json_encode($config))) {
  265. return false;
  266. }
  267. // 非开发模式,缓存数据
  268. if (config('develop_mode') == 0) {
  269. cache('plugin_config_'.$name, $config);
  270. }
  271. return true;
  272. }
  273. }