Files
YwxAppThink/ywxapp/AddonBase.php
T

463 lines
13 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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';
}
}