From f68a9308126f075f0fc6515a66b1fc91190ec282 Mon Sep 17 00:00:00 2001 From: zhao Date: Tue, 18 Feb 2020 19:28:14 +0800 Subject: [PATCH] install addons --- addons/README.md | 274 ++++++++++++++++++ composer.json | 3 +- composer.lock | 60 +++- config/addons.php | 17 ++ vendor/composer/autoload_files.php | 1 + vendor/composer/autoload_psr4.php | 2 +- vendor/composer/autoload_static.php | 2 + vendor/composer/installed.json | 60 ++++ vendor/services.php | 3 +- vendor/zzstudio/think-addons/README.md | 274 ++++++++++++++++++ vendor/zzstudio/think-addons/composer.json | 40 +++ vendor/zzstudio/think-addons/src/Addons.php | 194 +++++++++++++ .../think-addons/src/addons/Route.php | 93 ++++++ .../think-addons/src/addons/Service.php | 232 +++++++++++++++ .../src/addons/command/SendConfig.php | 67 +++++ .../src/addons/middleware/Addons.php | 48 +++ vendor/zzstudio/think-addons/src/config.php | 17 ++ vendor/zzstudio/think-addons/src/helper.php | 177 +++++++++++ .../zzstudio/think-addons/src/lang/zh-cn.php | 97 +++++++ 19 files changed, 1657 insertions(+), 4 deletions(-) create mode 100644 addons/README.md create mode 100644 config/addons.php create mode 100644 vendor/zzstudio/think-addons/README.md create mode 100644 vendor/zzstudio/think-addons/composer.json create mode 100644 vendor/zzstudio/think-addons/src/Addons.php create mode 100644 vendor/zzstudio/think-addons/src/addons/Route.php create mode 100644 vendor/zzstudio/think-addons/src/addons/Service.php create mode 100644 vendor/zzstudio/think-addons/src/addons/command/SendConfig.php create mode 100644 vendor/zzstudio/think-addons/src/addons/middleware/Addons.php create mode 100644 vendor/zzstudio/think-addons/src/config.php create mode 100644 vendor/zzstudio/think-addons/src/helper.php create mode 100644 vendor/zzstudio/think-addons/src/lang/zh-cn.php 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 秒后自动跳转', +];