chore: 重写初始提交(清空历史,整理后全量提交)
This commit is contained in:
@@ -0,0 +1,462 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | YwxApp [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: ywxapp<admin@ywxapp.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace ywxapp;
|
||||
|
||||
use think\facade\Config;
|
||||
use think\View;
|
||||
use ywxapp\service\AddonService;
|
||||
|
||||
/**
|
||||
* 插件基类
|
||||
* Class addon
|
||||
* @author Ywxapp <ywxapp@qq.com>
|
||||
* @package think\addon
|
||||
*/
|
||||
abstract class AddonBase
|
||||
{
|
||||
protected $result;
|
||||
// 视图实例对象
|
||||
protected $view = null;
|
||||
// 当前错误信息
|
||||
protected $error;
|
||||
// 插件目录
|
||||
public $addon_path = '';
|
||||
public $addonPath = '';
|
||||
|
||||
// 插件标识
|
||||
protected $addonName = '';
|
||||
// 插件配置作用域
|
||||
protected $configRange = 'addonconfig';
|
||||
// 插件信息作用域
|
||||
protected $infoRange = 'addoninfo';
|
||||
/**
|
||||
* 插件菜单
|
||||
*/
|
||||
protected $AddonMenu = [
|
||||
'frontend' => [[
|
||||
'name' => 'mydemo', //权限规则标识,首个菜单标识必须和插件标识相同
|
||||
'title' => 'Mydemo管理', //菜单标题
|
||||
'icon' => 'fa fa-map-marker', //菜单按钮,可使用Font-Awesome的图标
|
||||
'ismenu' => 1, //是否为菜单
|
||||
'weigh' => 1, //权重,值越大越靠前
|
||||
'remark' => 'Demo管理描述内容', //菜单描述内容,在列表处显示
|
||||
'sublist' => [ //子菜单配置,子菜单name必须以 插件标识/ 开始,这里如 mydemo/
|
||||
["name" => "mydemo/signin/index", "title" => "查看"],
|
||||
["name" => "mydemo/signin/add", "title" => "添加"],
|
||||
["name" => "mydemo/signin/edit", "title" => "编辑"],
|
||||
["name" => "mydemo/signin/del", "title" => "删除"],
|
||||
["name" => "mydemo/signin/multi", "title" => "批量更新"],
|
||||
]
|
||||
]],
|
||||
'member' => [],
|
||||
'backend' => [],
|
||||
|
||||
];
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
*/
|
||||
|
||||
public function __construct($name = null)
|
||||
{
|
||||
$this->result = app()->result;
|
||||
$name = is_null($name) ? $this->getName() : $name;
|
||||
$this->addonName = $name;
|
||||
$this->addonPath = ADDON_PATH . $name . DIRECTORY_SEPARATOR;
|
||||
$this->addon_path = $this->addonPath;
|
||||
$config = ['view_path' => $this->addonPath];
|
||||
$config = array_merge(Config::get('template'), $config);
|
||||
// 必须创建独立的 View 实例,绝不能复用全局单例 app()->view:
|
||||
// 否则会把全局视图的 view_path 改成 addon/<name>/,污染后续所有页面的模板解析。
|
||||
$this->view = new View(app());
|
||||
$this->view->config($config);
|
||||
if (method_exists($this, 'initialize')) {
|
||||
$this->initialize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取基础配置信息
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
|
||||
final public function getInfo($name = '', $force = false)
|
||||
{
|
||||
if (empty($name)) {
|
||||
$name = $this->getName();
|
||||
}
|
||||
if (! $force) {
|
||||
$info = Config::get($name, $this->infoRange);
|
||||
if ($info) {
|
||||
return $info;
|
||||
}
|
||||
}
|
||||
$info = [];
|
||||
$infoFile = $this->addonPath . 'info.php';
|
||||
if (is_file($infoFile)) {
|
||||
$info = (array) include $infoFile;
|
||||
$info['url'] = addon_url($name);
|
||||
}
|
||||
Config::set($info, $name, $this->infoRange);
|
||||
return $info ? $info : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件的配置数组
|
||||
* @param string $name 可选模块名
|
||||
* @return array
|
||||
*/
|
||||
|
||||
/**
|
||||
* 读取插件合并配置(静态、无实例)。
|
||||
* 供运行时 getConfig 与 AppInit 预热共用,避免在预热阶段为每个启用插件实例化 Addon
|
||||
* (其构造函数会 new View)带来的每请求开销。
|
||||
* 合并顺序与历史 getConfig 完全一致:静态 config/<name>.php → 表单 config.php → 后台保存值。
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
public static function readConfig(string $name): array
|
||||
{
|
||||
$addonPath = (defined('ADDON_PATH') ? ADDON_PATH : app()->getRootPath() . 'addon' . DIRECTORY_SEPARATOR)
|
||||
. $name . DIRECTORY_SEPARATOR;
|
||||
$config = [];
|
||||
// ① 静态代码级配置:addon/<name>/config/<name>.php(随插件分发、支持 env() 覆盖)。
|
||||
// 与表单 config.php 分工:此处放「运行期/部署相关、需在代码中用 env() 取默认」的项
|
||||
// (如令牌、远程开关、SSL 校验);表单 config.php 放「后台可编辑」的项。
|
||||
$staticFile = $addonPath . 'config' . DIRECTORY_SEPARATOR . $name . '.php';
|
||||
if (is_file($staticFile)) {
|
||||
$staticArr = include $staticFile;
|
||||
if (is_array($staticArr)) {
|
||||
$config = array_merge($config, $staticArr);
|
||||
}
|
||||
}
|
||||
// ② 表单配置:addon/<name>/config.php(后台可编辑字段,静态 value 为默认值)
|
||||
$configFile = $addonPath . 'config.php';
|
||||
if (is_file($configFile)) {
|
||||
$configArr = include $configFile;
|
||||
if (is_array($configArr)) {
|
||||
foreach ($configArr as $key => $value) {
|
||||
// 兼容两种 config.php 格式:
|
||||
// ① 标准表单格式 [['name'=>'x','value'=>'y'], ...]
|
||||
// ② 关联数组格式(代码级配置)['key'=>'val', ...]
|
||||
if (is_array($value) && array_key_exists('name', $value) && array_key_exists('value', $value)) {
|
||||
$config[$value['name']] = $value['value'];
|
||||
} else {
|
||||
$config[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 统一配置源:用后台保存的值覆盖上面的默认值,
|
||||
// 避免插件运行期只能读到默认值、忽略后台配置保存(修复配置"双入口"不一致)
|
||||
$saved = AddonService::config($name);
|
||||
if (!empty($saved) && is_array($saved)) {
|
||||
$config = array_merge($config, $saved);
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
final public function getConfig($name = '', $force = false)
|
||||
{
|
||||
if (empty($name)) {
|
||||
$name = $this->getName();
|
||||
}
|
||||
if (! $force) {
|
||||
$config = Config::get($name);
|
||||
if ($config) {
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
$config = self::readConfig($name);
|
||||
Config::set($config, $name);
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置数据
|
||||
* @param $name
|
||||
* @param array $value
|
||||
* @return array
|
||||
*/
|
||||
|
||||
final public function setConfig($name = '', $value = [])
|
||||
{
|
||||
if (empty($name)) {
|
||||
$name = $this->getName();
|
||||
}
|
||||
$config = $this->getConfig($name);
|
||||
$config = array_merge($config, $value);
|
||||
Config::set($config, $name);
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置插件信息数据
|
||||
* @param $name
|
||||
* @param array $value
|
||||
* @return array
|
||||
*/
|
||||
|
||||
final public function setInfo($name = '', $value = [])
|
||||
{
|
||||
if (empty($name)) {
|
||||
$name = $this->getName();
|
||||
}
|
||||
$info = $this->getInfo($name);
|
||||
$info = array_merge($info, $value);
|
||||
Config::set($info, $name, $this->infoRange);
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取完整配置列表
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
|
||||
final public function getFullConfig($name = '')
|
||||
{
|
||||
$fullConfigArr = [];
|
||||
if (empty($name)) {
|
||||
$name = $this->getName();
|
||||
}
|
||||
$configFile = $this->addonPath . 'info.php';
|
||||
if (is_file($configFile)) {
|
||||
$fullConfigArr = include $configFile;
|
||||
}
|
||||
return $fullConfigArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前模块名
|
||||
* @return string
|
||||
*/
|
||||
|
||||
final public function getName()
|
||||
{
|
||||
if ($this->addonName) {
|
||||
return $this->addonName;
|
||||
}
|
||||
// 插件引导类固定命名为 addon\<标识>\Addon(如 addon\appmall\Addon)。
|
||||
// 取「类名末段(Addon)之前」的命名空间末段作为插件标识,
|
||||
// 不要直接取类名末段(会得到 'addon' 导致 addonPath/view_path 全部错乱)。
|
||||
$data = explode('\\', get_class($this));
|
||||
array_pop($data);
|
||||
return strtolower(array_pop($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置插件标识
|
||||
* @param $name
|
||||
*/
|
||||
|
||||
final public function setName($name)
|
||||
{
|
||||
$this->addonName = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查基础配置信息是否完整
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
final public function checkInfo()
|
||||
{
|
||||
$info = $this->getInfo();
|
||||
$info_check_keys = ['name', 'title', 'intro', 'author', 'version', 'state'];
|
||||
foreach ($info_check_keys as $value) {
|
||||
if (! array_key_exists($value, $info)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载模板和页面输出 可以返回输出内容
|
||||
* @access public
|
||||
* @param string $template 模板文件名或者内容
|
||||
* @param array $vars 模板输出变量
|
||||
* @param array $replace 替换内容
|
||||
* @param array $config 模板参数
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
|
||||
public function fetch($template = '', $vars = [], $replace = [], $config = [])
|
||||
{
|
||||
if (! is_file($template)) {
|
||||
$template = '/' . $template;
|
||||
}
|
||||
// 关闭模板布局
|
||||
$this->view->engine->layout(false);
|
||||
|
||||
echo $this->view->fetch($template, $vars, $replace, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染内容输出
|
||||
* @access public
|
||||
* @param string $content 内容
|
||||
* @param array $vars 模板输出变量
|
||||
* @param array $replace 替换内容
|
||||
* @param array $config 模板参数
|
||||
* @return mixed
|
||||
*/
|
||||
|
||||
public function display($content, $vars = [], $replace = [], $config = [])
|
||||
{
|
||||
// 关闭模板布局
|
||||
$this->view->engine->layout(false);
|
||||
|
||||
echo $this->view->display($content, $vars, $replace, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染内容输出
|
||||
* @access public
|
||||
* @param string $content 内容
|
||||
* @param array $vars 模板输出变量
|
||||
* @return mixed
|
||||
*/
|
||||
|
||||
public function show($content, $vars = [])
|
||||
{
|
||||
// 关闭模板布局
|
||||
$this->view->engine->layout(false);
|
||||
|
||||
echo $this->view->fetch($content, $vars, [], [], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板变量赋值
|
||||
* @access protected
|
||||
* @param mixed $name 要显示的模板变量
|
||||
* @param mixed $value 变量的值
|
||||
* @return void
|
||||
*/
|
||||
|
||||
public function assign($name, $value = '')
|
||||
{
|
||||
$this->view->assign($name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前错误信息
|
||||
* @return mixed
|
||||
*/
|
||||
|
||||
public function getError()
|
||||
{
|
||||
return $this->result->error;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件安装方法
|
||||
*/
|
||||
|
||||
abstract public function install();
|
||||
|
||||
/**
|
||||
* 插件卸载方法
|
||||
*/
|
||||
|
||||
abstract public function uninstall();
|
||||
|
||||
/**
|
||||
* 启用插件时回调(可重写,返回 false 表示阻断启用)
|
||||
* 由 AddonService::enable() 通过 callAddonHook 调用
|
||||
*/
|
||||
|
||||
public function enable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用插件时回调(可重写,返回 false 表示阻断禁用)
|
||||
* 由 AddonService::disable() 通过 callAddonHook 调用
|
||||
*/
|
||||
|
||||
public function disable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 升级插件时回调(可重写)
|
||||
* 由 AddonService::upgrade() 通过 callAddonHook 调用
|
||||
* @param string $currentVersion 已安装版本号
|
||||
*/
|
||||
|
||||
public function upgrade($currentVersion = '')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册钩子(在 loadAddonRelevant 时可被调用,默认空实现)
|
||||
* @param mixed $hookService
|
||||
* @return $this
|
||||
*/
|
||||
|
||||
public function registerHooks($hookService = null)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册路由(默认空实现)
|
||||
*/
|
||||
|
||||
public function registerRoutes()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册服务(默认空实现)
|
||||
*/
|
||||
|
||||
public function registerServices()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 声明本插件依赖的其它插件标识(用于安装前置校验)
|
||||
* @return array
|
||||
*/
|
||||
|
||||
public function getDependencies(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 声明本插件依赖的 PHP 扩展(用于安装前置校验)
|
||||
* @return array
|
||||
*/
|
||||
|
||||
public function getExtensions(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 要求的最低 ThinkPHP 版本
|
||||
* @return string
|
||||
*/
|
||||
|
||||
public function getThinkVersion(): string
|
||||
{
|
||||
return '>=8.0';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user