diff --git a/addons/README.md b/addons/README.md
new file mode 100644
index 0000000..457acb8
--- /dev/null
+++ b/addons/README.md
@@ -0,0 +1,274 @@
+# think-addons
+The ThinkPHP 6 Addons Package
+
+## 安装
+> composer require zzstudio/think-addons
+
+## 配置
+
+### 生成配置
+
+系统安装后会自动在 config 目录中生成 addons.php 的配置文件,
+如果系统未生成可在命令行执行
+
+```php
+php think addons:config
+```
+
+快速生成配置文件
+
+### 公共配置
+```php
+'addons' => [
+ // 是否自动读取取插件钩子配置信息(默认是开启)
+ 'autoload' => true,
+ // 当关闭自动获取配置时需要手动配置hooks信息
+ 'hooks' => [
+ // 可以定义多个钩子
+ 'testhook'=>'test' // 键为钩子名称,用于在业务中自定义钩子处理,值为实现该钩子的插件,
+ // 多个插件可以用数组也可以用逗号分割
+ ],
+ 'route' => [],
+ 'service' => [],
+];
+```
+或者在\config目录中新建`addons.php`,内容为:
+```php
+ false,
+ // 当关闭自动获取配置时需要手动配置hooks信息
+ 'hooks' => [
+ // 可以定义多个钩子
+ 'testhook'=>'test' // 键为钩子名称,用于在业务中自定义钩子处理,值为实现该钩子的插件,
+ // 多个插件可以用数组也可以用逗号分割
+ ],
+ 'route' => [],
+ 'service' => [],
+];
+```
+
+## 创建插件
+> 创建的插件可以在view视图中使用,也可以在php业务中使用
+
+安装完成后访问系统时会在项目根目录生成名为`addons`的目录,在该目录中创建需要的插件。
+
+下面写一个例子:
+
+### 创建test插件
+> 在addons目录中创建test目录
+
+### 创建钩子实现类
+> 在test目录中创建 Plugin.php 类文件。注意:类文件首字母需大写
+
+```php
+ 'test', // 插件标识
+ 'title' => '插件测试', // 插件名称
+ 'description' => 'thinkph6插件测试', // 插件简介
+ 'status' => 0, // 状态
+ 'author' => 'byron sampson',
+ 'version' => '0.1'
+ ];
+
+ /**
+ * 插件安装方法
+ * @return bool
+ */
+ public function install()
+ {
+ return true;
+ }
+
+ /**
+ * 插件卸载方法
+ * @return bool
+ */
+ public function uninstall()
+ {
+ return true;
+ }
+
+ /**
+ * 实现的testhook钩子方法
+ * @return mixed
+ */
+ public function testhook($param)
+ {
+ // 调用钩子时候的参数信息
+ print_r($param);
+ // 当前插件的配置信息,配置信息存在当前目录的config.php文件中,见下方
+ print_r($this->getConfig());
+ // 可以返回模板,模板文件默认读取的为插件目录中的文件。模板名不能为空!
+ return $this->fetch('info');
+ }
+
+}
+```
+
+### 创建插件配置文件
+> 在test目录中创建config.php类文件,插件配置文件可以省略。
+
+```php
+ [
+ 'title' => '是否显示:',
+ 'type' => 'radio',
+ 'options' => [
+ '1' => '显示',
+ '0' => '不显示'
+ ],
+ 'value' => '1'
+ ]
+];
+```
+
+### 创建钩子模板文件
+> 在test->view目录中创建info.html模板文件,钩子在使用fetch方法时对应的模板文件。
+
+```html
+
hello tpl
+
+如果插件中需要有链接或提交数据的业务,可以在插件中创建controller业务文件,
+要访问插件中的controller时使用addon_url生成url链接。
+如下:
+link test
+或
+link test
+格式为:
+test为插件名,Action为controller中的类名[多级控制器可以用.分割],link为controller中的方法
+```
+
+### 创建插件的controller文件
+> 在test目录中创建controller目录,在controller目录中创建Index.php文件
+> controller类的用法与tp6中的controller一致
+
+```php
+ 创建好插件后就可以在正常业务中使用该插件中的钩子了
+> 使用钩子的时候第二个参数可以省略
+
+### 模板中使用钩子
+
+```html
+{:hook('testhook', ['id'=>1])}
+```
+
+### php业务中使用
+> 只要是thinkphp6正常流程中的任意位置均可以使用
+
+```php
+hook('testhook', ['id'=>1])
+```
+
+### 插件公共方法
+```php
+/**
+ * 处理插件钩子
+ * @param string $event 钩子名称
+ * @param array|null $params 传入参数
+ * @param bool $once 是否只返回一个结果
+ * @return mixed
+ */
+function hook($event, $params = null, bool $once = false);
+
+/**
+ * 读取插件的基础信息
+ * @param string $name 插件名
+ * @return array
+ */
+function get_addons_info($name);
+
+/**
+ * 获取插件Plugin的单例
+ * @param string $name 插件名
+ * @return mixed|null
+ */
+function get_addons_instance($name);
+
+/**
+ * 插件显示内容里生成访问插件的url
+ * @param $url 在插件控制器中可忽略插件名,在非插件中生成时需指定插件名。例:插件名://控制器/方法
+ * @param array $param
+ * @param bool|string $suffix 生成的URL后缀
+ * @param bool|string $domain 域名
+ * @return bool|string
+ */
+function addons_url($url = '', $param = [], $suffix = true, $domain = false);
+
+```
+
+## 插件目录结构
+### 最终生成的目录结构为
+
+```html
+www WEB部署目录(或者子目录)
+├─addons 插件目录
+├─app 应用目录
+│ ├─controller 控制器目录
+│ ├─model 模型目录
+│ ├─ ... 更多类库目录
+│ │
+│ ├─common.php 公共函数文件
+│ └─event.php 事件定义文件
+│
+├─config 配置目录
+│ ├─app.php 应用配置
+│ ├─cache.php 缓存配置
+│ ├─console.php 控制台配置
+│ ├─cookie.php Cookie配置
+│ ├─database.php 数据库配置
+│ ├─filesystem.php 文件磁盘配置
+│ ├─lang.php 多语言配置
+│ ├─log.php 日志配置
+│ ├─middleware.php 中间件配置
+│ ├─route.php URL和路由配置
+│ ├─session.php Session配置
+│ ├─trace.php Trace配置
+│ └─view.php 视图配置
+│
+├─view 视图目录
+├─route 路由定义目录
+│ ├─route.php 路由定义文件
+│ └─ ...
+│
+├─public WEB目录(对外访问目录)
+│ ├─index.php 入口文件
+│ ├─router.php 快速测试文件
+│ └─.htaccess 用于apache的重写
+│
+├─extend 扩展类库目录
+├─runtime 应用的运行时目录(可写,可定制)
+├─vendor Composer类库目录
+├─.example.env 环境变量示例文件
+├─composer.json composer 定义文件
+├─LICENSE.txt 授权说明文件
+├─README.md README 文件
+├─think 命令行入口文件
+```
diff --git a/composer.json b/composer.json
index ee84756..4013e10 100644
--- a/composer.json
+++ b/composer.json
@@ -27,7 +27,8 @@
"taoser/think-auth": "^1.0",
"topthink/think-multi-app": "^1.0",
"topthink/think-captcha": "^3.0",
- "topthink/think-view": "^1.0"
+ "topthink/think-view": "^1.0",
+ "zzstudio/think-addons": "^2.0"
},
"require-dev": {
"symfony/var-dumper": "^4.2",
diff --git a/composer.lock b/composer.lock
index a066b82..9b842d0 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "6ebd3a4578a0960080346336d4970bfd",
+ "content-hash": "6228bf65a800388ad9f6b06b8ac6ba72",
"packages": [
{
"name": "league/flysystem",
@@ -907,6 +907,64 @@
],
"description": "thinkphp template driver",
"time": "2019-11-06T11:40:13+00:00"
+ },
+ {
+ "name": "zzstudio/think-addons",
+ "version": "2.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/zz-studio/think-addons.git",
+ "reference": "7eb740cb219a111d593a05ad88248a74f640fe5c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/zz-studio/think-addons/zipball/7eb740cb219a111d593a05ad88248a74f640fe5c",
+ "reference": "7eb740cb219a111d593a05ad88248a74f640fe5c",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1.0",
+ "topthink/framework": "^6.0",
+ "topthink/think-helper": "^3.0.0",
+ "topthink/think-view": "^1.0"
+ },
+ "type": "library",
+ "extra": {
+ "think": {
+ "services": [
+ "think\\addons\\Service"
+ ],
+ "config": {
+ "addons": "src/config.php"
+ }
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "think\\": "src/"
+ },
+ "files": [
+ "src/helper.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "byron",
+ "email": "xiaobo.sun@qq.com"
+ }
+ ],
+ "description": "The ThinkPHP6 Addons Package",
+ "time": "2020-01-06T06:42:39+00:00"
}
],
"packages-dev": [
diff --git a/config/addons.php b/config/addons.php
new file mode 100644
index 0000000..b040d07
--- /dev/null
+++ b/config/addons.php
@@ -0,0 +1,17 @@
+
+// +----------------------------------------------------------------------
+
+return [
+ 'autoload' => true,
+ 'hooks' => [],
+ 'route' => [],
+ 'service' => [],
+];
\ No newline at end of file
diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php
index e92cb92..08a29d2 100644
--- a/vendor/composer/autoload_files.php
+++ b/vendor/composer/autoload_files.php
@@ -12,4 +12,5 @@ return array(
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
'1cfd2761b63b0a29ed23657ea394cb2d' => $vendorDir . '/topthink/think-captcha/src/helper.php',
+ '39594db8502267d6df2fe2dca5f3914d' => $vendorDir . '/zzstudio/think-addons/src/helper.php',
);
diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php
index 3de2257..5f9ed19 100644
--- a/vendor/composer/autoload_psr4.php
+++ b/vendor/composer/autoload_psr4.php
@@ -10,7 +10,7 @@ return array(
'think\\trace\\' => array($vendorDir . '/topthink/think-trace/src'),
'think\\captcha\\' => array($vendorDir . '/topthink/think-captcha/src'),
'think\\app\\' => array($vendorDir . '/topthink/think-multi-app/src'),
- 'think\\' => array($vendorDir . '/topthink/framework/src/think', $vendorDir . '/topthink/think-helper/src', $vendorDir . '/topthink/think-orm/src', $vendorDir . '/topthink/think-template/src'),
+ 'think\\' => array($vendorDir . '/topthink/framework/src/think', $vendorDir . '/topthink/think-helper/src', $vendorDir . '/topthink/think-orm/src', $vendorDir . '/topthink/think-template/src', $vendorDir . '/zzstudio/think-addons/src'),
'taoser\\think\\' => array($vendorDir . '/taoser/think-auth/src'),
'app\\' => array($baseDir . '/app'),
'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
index 9a5eb2f..9c5dcad 100644
--- a/vendor/composer/autoload_static.php
+++ b/vendor/composer/autoload_static.php
@@ -13,6 +13,7 @@ class ComposerStaticInit9b80d9a7bd440d07cac42880e0942921
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
'1cfd2761b63b0a29ed23657ea394cb2d' => __DIR__ . '/..' . '/topthink/think-captcha/src/helper.php',
+ '39594db8502267d6df2fe2dca5f3914d' => __DIR__ . '/..' . '/zzstudio/think-addons/src/helper.php',
);
public static $prefixLengthsPsr4 = array (
@@ -77,6 +78,7 @@ class ComposerStaticInit9b80d9a7bd440d07cac42880e0942921
1 => __DIR__ . '/..' . '/topthink/think-helper/src',
2 => __DIR__ . '/..' . '/topthink/think-orm/src',
3 => __DIR__ . '/..' . '/topthink/think-template/src',
+ 4 => __DIR__ . '/..' . '/zzstudio/think-addons/src',
),
'taoser\\think\\' =>
array (
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index bccbe1b..0622ade 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -1201,5 +1201,65 @@
}
],
"description": "thinkphp template driver"
+ },
+ {
+ "name": "zzstudio/think-addons",
+ "version": "2.0.5",
+ "version_normalized": "2.0.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/zz-studio/think-addons.git",
+ "reference": "7eb740cb219a111d593a05ad88248a74f640fe5c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/zz-studio/think-addons/zipball/7eb740cb219a111d593a05ad88248a74f640fe5c",
+ "reference": "7eb740cb219a111d593a05ad88248a74f640fe5c",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1.0",
+ "topthink/framework": "^6.0",
+ "topthink/think-helper": "^3.0.0",
+ "topthink/think-view": "^1.0"
+ },
+ "time": "2020-01-06T06:42:39+00:00",
+ "type": "library",
+ "extra": {
+ "think": {
+ "services": [
+ "think\\addons\\Service"
+ ],
+ "config": {
+ "addons": "src/config.php"
+ }
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "think\\": "src/"
+ },
+ "files": [
+ "src/helper.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "byron",
+ "email": "xiaobo.sun@qq.com"
+ }
+ ],
+ "description": "The ThinkPHP6 Addons Package"
}
]
diff --git a/vendor/services.php b/vendor/services.php
index 6784ee9..5b99449 100644
--- a/vendor/services.php
+++ b/vendor/services.php
@@ -1,8 +1,9 @@
'think\\captcha\\CaptchaService',
1 => 'think\\app\\Service',
2 => 'think\\trace\\Service',
+ 3 => 'think\\addons\\Service',
);
\ No newline at end of file
diff --git a/vendor/zzstudio/think-addons/README.md b/vendor/zzstudio/think-addons/README.md
new file mode 100644
index 0000000..457acb8
--- /dev/null
+++ b/vendor/zzstudio/think-addons/README.md
@@ -0,0 +1,274 @@
+# think-addons
+The ThinkPHP 6 Addons Package
+
+## 安装
+> composer require zzstudio/think-addons
+
+## 配置
+
+### 生成配置
+
+系统安装后会自动在 config 目录中生成 addons.php 的配置文件,
+如果系统未生成可在命令行执行
+
+```php
+php think addons:config
+```
+
+快速生成配置文件
+
+### 公共配置
+```php
+'addons' => [
+ // 是否自动读取取插件钩子配置信息(默认是开启)
+ 'autoload' => true,
+ // 当关闭自动获取配置时需要手动配置hooks信息
+ 'hooks' => [
+ // 可以定义多个钩子
+ 'testhook'=>'test' // 键为钩子名称,用于在业务中自定义钩子处理,值为实现该钩子的插件,
+ // 多个插件可以用数组也可以用逗号分割
+ ],
+ 'route' => [],
+ 'service' => [],
+];
+```
+或者在\config目录中新建`addons.php`,内容为:
+```php
+ false,
+ // 当关闭自动获取配置时需要手动配置hooks信息
+ 'hooks' => [
+ // 可以定义多个钩子
+ 'testhook'=>'test' // 键为钩子名称,用于在业务中自定义钩子处理,值为实现该钩子的插件,
+ // 多个插件可以用数组也可以用逗号分割
+ ],
+ 'route' => [],
+ 'service' => [],
+];
+```
+
+## 创建插件
+> 创建的插件可以在view视图中使用,也可以在php业务中使用
+
+安装完成后访问系统时会在项目根目录生成名为`addons`的目录,在该目录中创建需要的插件。
+
+下面写一个例子:
+
+### 创建test插件
+> 在addons目录中创建test目录
+
+### 创建钩子实现类
+> 在test目录中创建 Plugin.php 类文件。注意:类文件首字母需大写
+
+```php
+ 'test', // 插件标识
+ 'title' => '插件测试', // 插件名称
+ 'description' => 'thinkph6插件测试', // 插件简介
+ 'status' => 0, // 状态
+ 'author' => 'byron sampson',
+ 'version' => '0.1'
+ ];
+
+ /**
+ * 插件安装方法
+ * @return bool
+ */
+ public function install()
+ {
+ return true;
+ }
+
+ /**
+ * 插件卸载方法
+ * @return bool
+ */
+ public function uninstall()
+ {
+ return true;
+ }
+
+ /**
+ * 实现的testhook钩子方法
+ * @return mixed
+ */
+ public function testhook($param)
+ {
+ // 调用钩子时候的参数信息
+ print_r($param);
+ // 当前插件的配置信息,配置信息存在当前目录的config.php文件中,见下方
+ print_r($this->getConfig());
+ // 可以返回模板,模板文件默认读取的为插件目录中的文件。模板名不能为空!
+ return $this->fetch('info');
+ }
+
+}
+```
+
+### 创建插件配置文件
+> 在test目录中创建config.php类文件,插件配置文件可以省略。
+
+```php
+ [
+ 'title' => '是否显示:',
+ 'type' => 'radio',
+ 'options' => [
+ '1' => '显示',
+ '0' => '不显示'
+ ],
+ 'value' => '1'
+ ]
+];
+```
+
+### 创建钩子模板文件
+> 在test->view目录中创建info.html模板文件,钩子在使用fetch方法时对应的模板文件。
+
+```html
+hello tpl
+
+如果插件中需要有链接或提交数据的业务,可以在插件中创建controller业务文件,
+要访问插件中的controller时使用addon_url生成url链接。
+如下:
+link test
+或
+link test
+格式为:
+test为插件名,Action为controller中的类名[多级控制器可以用.分割],link为controller中的方法
+```
+
+### 创建插件的controller文件
+> 在test目录中创建controller目录,在controller目录中创建Index.php文件
+> controller类的用法与tp6中的controller一致
+
+```php
+ 创建好插件后就可以在正常业务中使用该插件中的钩子了
+> 使用钩子的时候第二个参数可以省略
+
+### 模板中使用钩子
+
+```html
+{:hook('testhook', ['id'=>1])}
+```
+
+### php业务中使用
+> 只要是thinkphp6正常流程中的任意位置均可以使用
+
+```php
+hook('testhook', ['id'=>1])
+```
+
+### 插件公共方法
+```php
+/**
+ * 处理插件钩子
+ * @param string $event 钩子名称
+ * @param array|null $params 传入参数
+ * @param bool $once 是否只返回一个结果
+ * @return mixed
+ */
+function hook($event, $params = null, bool $once = false);
+
+/**
+ * 读取插件的基础信息
+ * @param string $name 插件名
+ * @return array
+ */
+function get_addons_info($name);
+
+/**
+ * 获取插件Plugin的单例
+ * @param string $name 插件名
+ * @return mixed|null
+ */
+function get_addons_instance($name);
+
+/**
+ * 插件显示内容里生成访问插件的url
+ * @param $url 在插件控制器中可忽略插件名,在非插件中生成时需指定插件名。例:插件名://控制器/方法
+ * @param array $param
+ * @param bool|string $suffix 生成的URL后缀
+ * @param bool|string $domain 域名
+ * @return bool|string
+ */
+function addons_url($url = '', $param = [], $suffix = true, $domain = false);
+
+```
+
+## 插件目录结构
+### 最终生成的目录结构为
+
+```html
+www WEB部署目录(或者子目录)
+├─addons 插件目录
+├─app 应用目录
+│ ├─controller 控制器目录
+│ ├─model 模型目录
+│ ├─ ... 更多类库目录
+│ │
+│ ├─common.php 公共函数文件
+│ └─event.php 事件定义文件
+│
+├─config 配置目录
+│ ├─app.php 应用配置
+│ ├─cache.php 缓存配置
+│ ├─console.php 控制台配置
+│ ├─cookie.php Cookie配置
+│ ├─database.php 数据库配置
+│ ├─filesystem.php 文件磁盘配置
+│ ├─lang.php 多语言配置
+│ ├─log.php 日志配置
+│ ├─middleware.php 中间件配置
+│ ├─route.php URL和路由配置
+│ ├─session.php Session配置
+│ ├─trace.php Trace配置
+│ └─view.php 视图配置
+│
+├─view 视图目录
+├─route 路由定义目录
+│ ├─route.php 路由定义文件
+│ └─ ...
+│
+├─public WEB目录(对外访问目录)
+│ ├─index.php 入口文件
+│ ├─router.php 快速测试文件
+│ └─.htaccess 用于apache的重写
+│
+├─extend 扩展类库目录
+├─runtime 应用的运行时目录(可写,可定制)
+├─vendor Composer类库目录
+├─.example.env 环境变量示例文件
+├─composer.json composer 定义文件
+├─LICENSE.txt 授权说明文件
+├─README.md README 文件
+├─think 命令行入口文件
+```
diff --git a/vendor/zzstudio/think-addons/composer.json b/vendor/zzstudio/think-addons/composer.json
new file mode 100644
index 0000000..8c36067
--- /dev/null
+++ b/vendor/zzstudio/think-addons/composer.json
@@ -0,0 +1,40 @@
+{
+ "name": "zz-studio/think-addons",
+ "description": "The ThinkPHP6 Addons Package",
+ "license": "Apache-2.0",
+ "authors": [
+ {
+ "name": "byron",
+ "email": "xiaobo.sun@qq.com"
+ }
+ ],
+ "require": {
+ "php": ">=7.1.0",
+ "topthink/framework": "^6.0",
+ "topthink/think-view": "^1.0",
+ "topthink/think-helper": "^3.0.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "think\\": "src/"
+ },
+ "files": [
+ "src/helper.php"
+ ]
+ },
+ "extra": {
+ "think": {
+ "services": [
+ "think\\addons\\Service"
+ ],
+ "config":{
+ "addons": "src/config.php"
+ }
+ }
+ },
+ "scripts" : {
+ "post-install-cmd": [
+ "php think addons:config"
+ ]
+ }
+}
diff --git a/vendor/zzstudio/think-addons/src/Addons.php b/vendor/zzstudio/think-addons/src/Addons.php
new file mode 100644
index 0000000..1a00954
--- /dev/null
+++ b/vendor/zzstudio/think-addons/src/Addons.php
@@ -0,0 +1,194 @@
+app = $app;
+ $this->request = $app->request;
+ $this->name = $this->getName();
+ $this->addon_path = $app->addons->getAddonsPath() . $this->name . DIRECTORY_SEPARATOR;
+ $this->addon_config = "addon_{$this->name}_config";
+ $this->addon_info = "addon_{$this->name}_info";
+ $this->view = clone View::engine('Think');
+ $this->view->config([
+ 'view_path' => $this->addon_path . 'view' . DIRECTORY_SEPARATOR
+ ]);
+
+ // 控制器初始化
+ $this->initialize();
+ }
+
+ // 初始化
+ protected function initialize()
+ {}
+
+ /**
+ * 获取插件标识
+ * @return mixed|null
+ */
+ final protected function getName()
+ {
+ $class = get_class($this);
+ list(, $name, ) = explode('\\', $class);
+ $this->request->addon = $name;
+
+ return $name;
+ }
+
+ /**
+ * 加载模板输出
+ * @param string $template
+ * @param array $vars 模板文件名
+ * @return false|mixed|string 模板输出变量
+ * @throws \think\Exception
+ */
+ protected function fetch($template = '', $vars = [])
+ {
+ return $this->view->fetch($template, $vars);
+ }
+
+ /**
+ * 渲染内容输出
+ * @access protected
+ * @param string $content 模板内容
+ * @param array $vars 模板输出变量
+ * @return mixed
+ */
+ protected function display($content = '', $vars = [])
+ {
+ return $this->view->display($content, $vars);
+ }
+
+ /**
+ * 模板变量赋值
+ * @access protected
+ * @param mixed $name 要显示的模板变量
+ * @param mixed $value 变量的值
+ * @return $this
+ */
+ protected function assign($name, $value = '')
+ {
+ $this->view->assign([$name => $value]);
+
+ return $this;
+ }
+
+ /**
+ * 初始化模板引擎
+ * @access protected
+ * @param array|string $engine 引擎参数
+ * @return $this
+ */
+ protected function engine($engine)
+ {
+ $this->view->engine($engine);
+
+ return $this;
+ }
+
+ /**
+ * 插件基础信息
+ * @return array
+ */
+ final public function getInfo()
+ {
+ $info = Config::get($this->addon_info, []);
+ if ($info) {
+ return $info;
+ }
+
+ // 文件属性
+ $info = $this->info ?? [];
+ // 文件配置
+ $info_file = $this->addon_path . 'info.ini';
+ if (is_file($info_file)) {
+ $_info = parse_ini_file($info_file, true, INI_SCANNER_TYPED) ?: [];
+ $_info['url'] = addons_url();
+ $info = array_merge($_info, $info);
+ }
+ Config::set($info, $this->addon_info);
+
+ return isset($info) ? $info : [];
+ }
+
+ /**
+ * 获取配置信息
+ * @param bool $type 是否获取完整配置
+ * @return array|mixed
+ */
+ final public function getConfig($type = false)
+ {
+ $config = Config::get($this->addon_config, []);
+ if ($config) {
+ return $config;
+ }
+ $config_file = $this->addon_path . 'config.php';
+ if (is_file($config_file)) {
+ $temp_arr = (array)include $config_file;
+ if ($type) {
+ return $temp_arr;
+ }
+ foreach ($temp_arr as $key => $value) {
+ $config[$key] = $value['value'];
+ }
+ unset($temp_arr);
+ }
+ Config::set($config, $this->addon_config);
+
+ return $config;
+ }
+
+ //必须实现安装
+ abstract public function install();
+
+ //必须卸载插件方法
+ abstract public function uninstall();
+}
diff --git a/vendor/zzstudio/think-addons/src/addons/Route.php b/vendor/zzstudio/think-addons/src/addons/Route.php
new file mode 100644
index 0000000..d81923d
--- /dev/null
+++ b/vendor/zzstudio/think-addons/src/addons/Route.php
@@ -0,0 +1,93 @@
+request;
+
+ Event::trigger('addons_begin', $request);
+
+ if (empty($addon) || empty($controller) || empty($action)) {
+ throw new HttpException(500, lang('addon can not be empty'));
+ }
+
+ $request->addon = $addon;
+ // 设置当前请求的控制器、操作
+ $request->setController($controller)->setAction($action);
+
+ // 获取插件基础信息
+ $info = get_addons_info($addon);
+ if (!$info) {
+ throw new HttpException(404, lang('addon %s not found', [$addon]));
+ }
+ if (!$info['status']) {
+ throw new HttpException(500, lang('addon %s is disabled', [$addon]));
+ }
+
+ // 监听addon_module_init
+ Event::trigger('addon_module_init', $request);
+ $class = get_addons_class($addon, 'controller', $controller);
+ if (!$class) {
+ throw new HttpException(404, lang('addon controller %s not found', [Str::studly($controller)]));
+ }
+
+ // 重写视图基础路径
+ $config = Config::get('view');
+ $config['view_path'] = $app->addons->getAddonsPath() . $addon . DIRECTORY_SEPARATOR . 'view' . DIRECTORY_SEPARATOR;
+ Config::set($config, 'view');
+
+ // 生成控制器对象
+ $instance = new $class($app);
+ $vars = [];
+ if (is_callable([$instance, $action])) {
+ // 执行操作方法
+ $call = [$instance, $action];
+ } elseif (is_callable([$instance, '_empty'])) {
+ // 空操作
+ $call = [$instance, '_empty'];
+ $vars = [$action];
+ } else {
+ // 操作不存在
+ throw new HttpException(404, lang('addon action %s not found', [get_class($instance).'->'.$action.'()']));
+ }
+ Event::trigger('addons_action_begin', $call);
+
+ return call_user_func_array($call, $vars);
+ }
+}
\ No newline at end of file
diff --git a/vendor/zzstudio/think-addons/src/addons/Service.php b/vendor/zzstudio/think-addons/src/addons/Service.php
new file mode 100644
index 0000000..0d288da
--- /dev/null
+++ b/vendor/zzstudio/think-addons/src/addons/Service.php
@@ -0,0 +1,232 @@
+addons_path = $this->getAddonsPath();
+ // 加载系统语言包
+ Lang::load([
+ $this->app->getRootPath() . '/vendor/zzstudio/think-addons/src/lang/zh-cn.php'
+ ]);
+ // 自动载入插件
+ $this->autoload();
+ // 加载插件事件
+ $this->loadEvent();
+ // 加载插件系统服务
+ $this->loadService();
+ // 绑定插件容器
+ $this->app->bind('addons', Service::class);
+ }
+
+ public function boot()
+ {
+ $this->registerRoutes(function (Route $route) {
+ // 路由脚本
+ $execute = '\\think\\addons\\Route::execute';
+
+ // 注册插件公共中间件
+ if (is_file($this->app->addons->getAddonsPath() . 'middleware.php')) {
+ $this->app->middleware->import(include $this->app->addons->getAddonsPath() . 'middleware.php', 'route');
+ }
+
+ // 注册控制器路由
+ $route->rule("addons/:addon/[:controller]/[:action]", $execute)->middleware(Addons::class);
+ // 自定义路由
+ $routes = (array) Config::get('addons.route', []);
+ foreach ($routes as $key => $val) {
+ if (!$val) {
+ continue;
+ }
+ if (is_array($val)) {
+ $domain = $val['domain'];
+ $rules = [];
+ foreach ($val['rule'] as $k => $rule) {
+ [$addon, $controller, $action] = explode('/', $rule);
+ $rules[$k] = [
+ 'addons' => $addon,
+ 'controller' => $controller,
+ 'action' => $action,
+ 'indomain' => 1,
+ ];
+ }
+ $route->domain($domain, function () use ($rules, $route, $execute) {
+ // 动态注册域名的路由规则
+ foreach ($rules as $k => $rule) {
+ $route->rule($k, $execute)
+ ->name($k)
+ ->completeMatch(true)
+ ->append($rule);
+ }
+ });
+ } else {
+ list($addon, $controller, $action) = explode('/', $val);
+ $route->rule($key, $execute)
+ ->name($key)
+ ->completeMatch(true)
+ ->append([
+ 'addons' => $addon,
+ 'controller' => $controller,
+ 'action' => $action
+ ]);
+ }
+ }
+ });
+ }
+
+ /**
+ * 插件事件
+ */
+ private function loadEvent()
+ {
+ $hooks = $this->app->isDebug() ? [] : Cache::get('hooks', []);
+ if (empty($hooks)) {
+ $hooks = (array) Config::get('addons.hooks', []);
+ // 初始化钩子
+ foreach ($hooks as $key => $values) {
+ if (is_string($values)) {
+ $values = explode(',', $values);
+ } else {
+ $values = (array) $values;
+ }
+ $hooks[$key] = array_filter(array_map(function ($v) use ($key) {
+ return [get_addons_class($v), $key];
+ }, $values));
+ }
+ Cache::set('hooks', $hooks);
+ }
+ //如果在插件中有定义 AddonsInit,则直接执行
+ if (isset($hooks['AddonsInit'])) {
+ foreach ($hooks['AddonsInit'] as $k => $v) {
+ Event::trigger('AddonsInit', $v);
+ }
+ }
+ Event::listenEvents($hooks);
+ }
+
+ /**
+ * 挂载插件服务
+ */
+ private function loadService()
+ {
+ $results = scandir($this->addons_path);
+ $bind = [];
+ foreach ($results as $name) {
+ if ($name === '.' or $name === '..') {
+ continue;
+ }
+ if (is_file($this->addons_path . $name)) {
+ continue;
+ }
+ $addonDir = $this->addons_path . $name . DIRECTORY_SEPARATOR;
+ if (!is_dir($addonDir)) {
+ continue;
+ }
+
+ if (!is_file($addonDir . ucfirst($name) . '.php')) {
+ continue;
+ }
+
+ $service_file = $addonDir . 'service.ini';
+ if (!is_file($service_file)) {
+ continue;
+ }
+ $info = parse_ini_file($service_file, true, INI_SCANNER_TYPED) ?: [];
+ $bind = array_merge($bind, $info);
+ }
+ $this->app->bind($bind);
+ }
+
+ /**
+ * 自动载入插件
+ * @return bool
+ */
+ private function autoload()
+ {
+ // 是否处理自动载入
+ if (!Config::get('addons.autoload', true)) {
+ return true;
+ }
+ $config = Config::get('addons');
+ // 读取插件目录及钩子列表
+ $base = get_class_methods("\\think\\Addons");
+ // 读取插件目录中的php文件
+ foreach (glob($this->getAddonsPath() . '*/*.php') as $addons_file) {
+ // 格式化路径信息
+ $info = pathinfo($addons_file);
+ // 获取插件目录名
+ $name = pathinfo($info['dirname'], PATHINFO_FILENAME);
+ // 找到插件入口文件
+ if (strtolower($info['filename']) === 'plugin') {
+ // 读取出所有公共方法
+ $methods = (array)get_class_methods("\\addons\\" . $name . "\\" . $info['filename']);
+ // 跟插件基类方法做比对,得到差异结果
+ $hooks = array_diff($methods, $base);
+ // 循环将钩子方法写入配置中
+ foreach ($hooks as $hook) {
+ if (!isset($config['hooks'][$hook])) {
+ $config['hooks'][$hook] = [];
+ }
+ // 兼容手动配置项
+ if (is_string($config['hooks'][$hook])) {
+ $config['hooks'][$hook] = explode(',', $config['hooks'][$hook]);
+ }
+ if (!in_array($name, $config['hooks'][$hook])) {
+ $config['hooks'][$hook][] = $name;
+ }
+ }
+ }
+ }
+ Config::set($config, 'addons');
+ }
+
+ /**
+ * 获取 addons 路径
+ * @return string
+ */
+ public function getAddonsPath()
+ {
+ // 初始化插件目录
+ $addons_path = $this->app->getRootPath() . 'addons' . DIRECTORY_SEPARATOR;
+ // 如果插件目录不存在则创建
+ if (!is_dir($addons_path)) {
+ @mkdir($addons_path, 0755, true);
+ }
+
+ return $addons_path;
+ }
+
+ /**
+ * 获取插件的配置信息
+ * @param string $name
+ * @return array
+ */
+ public function getAddonsConfig()
+ {
+ $name = $this->app->request->addon;
+ $addon = get_addons_instance($name);
+ if (!$addon) {
+ return [];
+ }
+
+ return $addon->getConfig();
+ }
+}
diff --git a/vendor/zzstudio/think-addons/src/addons/command/SendConfig.php b/vendor/zzstudio/think-addons/src/addons/command/SendConfig.php
new file mode 100644
index 0000000..223be6f
--- /dev/null
+++ b/vendor/zzstudio/think-addons/src/addons/command/SendConfig.php
@@ -0,0 +1,67 @@
+ - < | WECHAT: wx5ini99
+ * / \ | DATETIME: 2019/10/29
+ * // \\ |
+ * //| . |\\ |
+ * "'\ /'"_.-~^`'-. |
+ * \ _ /--' ` |
+ * ___)( )(___ |-----------------------------------------
+ * (((__) (__))) | 高山仰止,景行行止.虽不能至,心向往之。
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 2019 http://www.zzstudio.net All rights reserved.
+ * +----------------------------------------------------------------------
+ */
+
+namespace think\addons\command;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Argument;
+use think\console\input\Option;
+use think\console\Output;
+use think\facade\Env;
+
+class SendConfig extends Command
+{
+
+ public function configure()
+ {
+ $this->setName('addons:config')
+ ->setDescription('send config to config folder');
+ }
+
+ public function execute(Input $input, Output $output)
+ {
+ //获取默认配置文件
+ $content = file_get_contents(root_path() . 'vendor/zzstudio/think-addons/src/config.php');
+
+ $configPath = config_path() . '/';
+ $configFile = $configPath . 'addons.php';
+
+
+ //判断目录是否存在
+ if (!file_exists($configPath)) {
+ mkdir($configPath, 0755, true);
+ }
+
+ //判断文件是否存在
+ if (is_file($configFile)) {
+ throw new \InvalidArgumentException(sprintf('The config file "%s" already exists', $configFile));
+ }
+
+ if (false === file_put_contents($configFile, $content)) {
+ throw new \RuntimeException(sprintf('The config file "%s" could not be written to "%s"', $configFile,$configPath));
+ }
+
+ $output->writeln('create addons config ok');
+ }
+
+}
\ No newline at end of file
diff --git a/vendor/zzstudio/think-addons/src/addons/middleware/Addons.php b/vendor/zzstudio/think-addons/src/addons/middleware/Addons.php
new file mode 100644
index 0000000..0c708e3
--- /dev/null
+++ b/vendor/zzstudio/think-addons/src/addons/middleware/Addons.php
@@ -0,0 +1,48 @@
+app = $app;
+ }
+
+ /**
+ * 插件中间件
+ * @param $request
+ * @param \Closure $next
+ * @return mixed
+ */
+ public function handle($request, \Closure $next)
+ {
+ hook('addon_middleware', $request);
+
+ return $next($request);
+ }
+}
\ No newline at end of file
diff --git a/vendor/zzstudio/think-addons/src/config.php b/vendor/zzstudio/think-addons/src/config.php
new file mode 100644
index 0000000..b040d07
--- /dev/null
+++ b/vendor/zzstudio/think-addons/src/config.php
@@ -0,0 +1,17 @@
+
+// +----------------------------------------------------------------------
+
+return [
+ 'autoload' => true,
+ 'hooks' => [],
+ 'route' => [],
+ 'service' => [],
+];
\ No newline at end of file
diff --git a/vendor/zzstudio/think-addons/src/helper.php b/vendor/zzstudio/think-addons/src/helper.php
new file mode 100644
index 0000000..f7e1951
--- /dev/null
+++ b/vendor/zzstudio/think-addons/src/helper.php
@@ -0,0 +1,177 @@
+addCommands([
+ 'addons:config' => '\\think\\addons\\command\\SendConfig'
+ ]);
+});
+
+// 插件类库自动载入
+spl_autoload_register(function ($class) {
+
+ $class = ltrim($class, '\\');
+
+ $dir = app()->getRootPath();
+ $namespace = 'addons';
+
+ if (strpos($class, $namespace) === 0) {
+ $class = substr($class, strlen($namespace));
+ $path = '';
+ if (($pos = strripos($class, '\\')) !== false) {
+ $path = str_replace('\\', '/', substr($class, 0, $pos)) . '/';
+ $class = substr($class, $pos + 1);
+ }
+ $path .= str_replace('_', '/', $class) . '.php';
+ $dir .= $namespace . $path;
+
+ if (file_exists($dir)) {
+ include $dir;
+ return true;
+ }
+
+ return false;
+ }
+
+ return false;
+
+});
+
+if (!function_exists('hook')) {
+ /**
+ * 处理插件钩子
+ * @param string $event 钩子名称
+ * @param array|null $params 传入参数
+ * @param bool $once 是否只返回一个结果
+ * @return mixed
+ */
+ function hook($event, $params = null, bool $once = false)
+ {
+ $result = Event::trigger($event, $params, $once);
+
+ return join('', $result);
+ }
+}
+
+if (!function_exists('get_addons_info')) {
+ /**
+ * 读取插件的基础信息
+ * @param string $name 插件名
+ * @return array
+ */
+ function get_addons_info($name)
+ {
+ $addon = get_addons_instance($name);
+ if (!$addon) {
+ return [];
+ }
+
+ return $addon->getInfo();
+ }
+}
+
+if (!function_exists('get_addons_instance')) {
+ /**
+ * 获取插件的单例
+ * @param string $name 插件名
+ * @return mixed|null
+ */
+ function get_addons_instance($name)
+ {
+ static $_addons = [];
+ if (isset($_addons[$name])) {
+ return $_addons[$name];
+ }
+ $class = get_addons_class($name);
+ if (class_exists($class)) {
+ $_addons[$name] = new $class(app());
+
+ return $_addons[$name];
+ } else {
+ return null;
+ }
+ }
+}
+
+if (!function_exists('get_addons_class')) {
+ /**
+ * 获取插件类的类名
+ * @param string $name 插件名
+ * @param string $type 返回命名空间类型
+ * @param string $class 当前类名
+ * @return string
+ */
+ function get_addons_class($name, $type = 'hook', $class = null)
+ {
+ $name = trim($name);
+ // 处理多级控制器情况
+ if (!is_null($class) && strpos($class, '.')) {
+ $class = explode('.', $class);
+
+ $class[count($class) - 1] = Str::studly(end($class));
+ $class = implode('\\', $class);
+ } else {
+ $class = Str::studly(is_null($class) ? $name : $class);
+ }
+ switch ($type) {
+ case 'controller':
+ $namespace = '\\addons\\' . $name . '\\controller\\' . $class;
+ break;
+ default:
+ $namespace = '\\addons\\' . $name . '\\Plugin';
+ }
+
+ return class_exists($namespace) ? $namespace : '';
+ }
+}
+
+if (!function_exists('addons_url')) {
+ /**
+ * 插件显示内容里生成访问插件的url
+ * @param $url
+ * @param array $param
+ * @param bool|string $suffix 生成的URL后缀
+ * @param bool|string $domain 域名
+ * @return bool|string
+ */
+ function addons_url($url = '', $param = [], $suffix = true, $domain = false)
+ {
+ $request = app('request');
+ if (empty($url)) {
+ // 生成 url 模板变量
+ $addons = $request->addon;
+ $controller = $request->controller();
+ $controller = str_replace('/', '.', $controller);
+ $action = $request->action();
+ } else {
+ $url = Str::studly($url);
+ $url = parse_url($url);
+ if (isset($url['scheme'])) {
+ $addons = strtolower($url['scheme']);
+ $controller = $url['host'];
+ $action = trim($url['path'], '/');
+ } else {
+ $route = explode('/', $url['path']);
+ $addons = $request->addon;
+ $action = array_pop($route);
+ $controller = array_pop($route) ?: $request->controller();
+ }
+ $controller = Str::snake((string)$controller);
+
+ /* 解析URL带的参数 */
+ if (isset($url['query'])) {
+ parse_str($url['query'], $query);
+ $param = array_merge($query, $param);
+ }
+ }
+
+ return Route::buildUrl("@addons/{$addons}/{$controller}/{$action}", $param)->suffix($suffix)->domain($domain);
+ }
+}
+
diff --git a/vendor/zzstudio/think-addons/src/lang/zh-cn.php b/vendor/zzstudio/think-addons/src/lang/zh-cn.php
new file mode 100644
index 0000000..f665c23
--- /dev/null
+++ b/vendor/zzstudio/think-addons/src/lang/zh-cn.php
@@ -0,0 +1,97 @@
+ '插件 %s 未找到',
+ 'addon %s is disabled' => '插件 %s 已禁用',
+ 'addon controller %s not found' => '插件控制器 %s 未找到',
+ 'addon action %s not found' => '插件控制器方法 %s 未找到',
+ 'addon can not be empty' => '插件不能为空',
+ 'Keep login' => '保持会话',
+ 'Forgot password' => '忘记密码?',
+ 'Username' => '用户名',
+ 'User id' => '会员ID',
+ 'Nickname' => '昵称',
+ 'Password' => '密码',
+ 'Sign up' => '注 册',
+ 'Sign in' => '登 录',
+ 'Sign out' => '注 销',
+ 'Guest' => '游客',
+ 'Welcome' => '%s,你好!',
+ 'Add' => '添加',
+ 'Edit' => '编辑',
+ 'Delete' => '删除',
+ 'Move' => '移动',
+ 'Name' => '名称',
+ 'Status' => '状态',
+ 'Weigh' => '权重',
+ 'Operate' => '操作',
+ 'Warning' => '温馨提示',
+ 'Default' => '默认',
+ 'Article' => '文章',
+ 'Page' => '单页',
+ 'OK' => '确定',
+ 'Cancel' => '取消',
+ 'Loading' => '加载中',
+ 'More' => '更多',
+ 'Normal' => '正常',
+ 'Hidden' => '隐藏',
+ 'Submit' => '提交',
+ 'Reset' => '重置',
+ 'Execute' => '执行',
+ 'Close' => '关闭',
+ 'Search' => '搜索',
+ 'Refresh' => '刷新',
+ 'First' => '首页',
+ 'Previous' => '上一页',
+ 'Next' => '下一页',
+ 'Last' => '末页',
+ 'None' => '无',
+ 'Online' => '在线',
+ 'Logout' => '注销',
+ 'Profile' => '个人资料',
+ 'Index' => '首页',
+ 'Hot' => '热门',
+ 'Recommend' => '推荐',
+ 'Dashboard' => '控制台',
+ 'Code' => '编号',
+ 'Message' => '内容',
+ 'Line' => '行号',
+ 'File' => '文件',
+ 'Menu' => '菜单',
+ 'Type' => '类型',
+ 'Title' => '标题',
+ 'Content' => '内容',
+ 'Append' => '追加',
+ 'Memo' => '备注',
+ 'Parent' => '父级',
+ 'Params' => '参数',
+ 'Permission' => '权限',
+ 'Begin time' => '开始时间',
+ 'End time' => '结束时间',
+ 'Create time' => '创建时间',
+ 'Flag' => '标志',
+ 'Home' => '首页',
+ 'Store' => '插件市场',
+ 'Services' => '服务',
+ 'Download' => '下载',
+ 'Demo' => '演示',
+ 'Donation' => '捐赠',
+ 'Forum' => '社区',
+ 'Docs' => '文档',
+ 'Go back' => '返回首页',
+ 'Jump now' => '立即跳转',
+ 'Please login first' => '请登录后再操作',
+ 'Send verification code' => '发送验证码',
+ 'Redirect now' => '立即跳转',
+ 'Operation completed' => '操作成功!',
+ 'Operation failed' => '操作失败!',
+ 'Unknown data format' => '未知的数据格式!',
+ 'Network error' => '网络错误!',
+ 'Advanced search' => '高级搜索',
+ 'Invalid parameters' => '未知参数',
+ 'No results were found' => '记录未找到',
+ 'Parameter %s can not be empty' => '参数 %s 不能为空',
+ 'You have no permission' => '你没有权限访问',
+ 'An unexpected error occurred' => '发生了一个意外错误,程序猿正在紧急处理中',
+ 'This page will be re-directed in %s seconds' => '页面将在 %s 秒后自动跳转',
+];