mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-22 16:15:34 +08:00
Merge pull request #124 from zhamao-robot/refactor-zmconfig
重构 ZMConfig
This commit is contained in:
@@ -27,7 +27,6 @@
|
|||||||
"symfony/polyfill-mbstring": "^1.19",
|
"symfony/polyfill-mbstring": "^1.19",
|
||||||
"symfony/polyfill-php80": "^1.16",
|
"symfony/polyfill-php80": "^1.16",
|
||||||
"symfony/routing": "~6.0 || ~5.0 || ~4.0 || ~3.0",
|
"symfony/routing": "~6.0 || ~5.0 || ~4.0 || ~3.0",
|
||||||
"zhamao/config": "^1.0",
|
|
||||||
"zhamao/connection-manager": "^1.0",
|
"zhamao/connection-manager": "^1.0",
|
||||||
"zhamao/console": "^1.0",
|
"zhamao/console": "^1.0",
|
||||||
"zhamao/request": "^1.1"
|
"zhamao/request": "^1.1"
|
||||||
|
|||||||
@@ -79,4 +79,6 @@
|
|||||||
| E00075 | Cron表达式非法 | 检查 @Cron 中的表达式是否书写格式正确 |
|
| E00075 | Cron表达式非法 | 检查 @Cron 中的表达式是否书写格式正确 |
|
||||||
| E00076 | Cron检查间隔非法 | 检查 @Cron 中的检查间隔(毫秒)是否在1000-60000之间 |
|
| E00076 | Cron检查间隔非法 | 检查 @Cron 中的检查间隔(毫秒)是否在1000-60000之间 |
|
||||||
| E00077 | 输入了非法的消息匹配参数类型 | 检查传入 `@CommandArgument` 或 `checkArguments()` 方法有没有传入非法的 `type` 参数 |
|
| E00077 | 输入了非法的消息匹配参数类型 | 检查传入 `@CommandArgument` 或 `checkArguments()` 方法有没有传入非法的 `type` 参数 |
|
||||||
|
| E00078 | 配置文件读取失败,未找到 base 配置文件 | 检查配置文件是否存在(一般是由于没找到基类配置文件产生的此错误) |
|
||||||
|
| E00079 | 配置文件读取失败 | 根据报错信息修复配置文件(或将 debug 日志发给框架开发者或反馈 Issue) |
|
||||||
| E99999 | 未知错误 | |
|
| E99999 | 未知错误 | |
|
||||||
|
|||||||
18
src/ZM/Config/ConfigMetadata.php
Normal file
18
src/ZM/Config/ConfigMetadata.php
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace ZM\Config;
|
||||||
|
|
||||||
|
class ConfigMetadata
|
||||||
|
{
|
||||||
|
public $is_patch = false;
|
||||||
|
|
||||||
|
public $is_env = false;
|
||||||
|
|
||||||
|
public $path = '';
|
||||||
|
|
||||||
|
public $extension = '';
|
||||||
|
|
||||||
|
public $data = [];
|
||||||
|
}
|
||||||
285
src/ZM/Config/ZMConfig.php
Normal file
285
src/ZM/Config/ZMConfig.php
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace ZM\Config;
|
||||||
|
|
||||||
|
use ZM\Console\Console;
|
||||||
|
use ZM\Exception\ConfigException;
|
||||||
|
use ZM\Utils\DataProvider;
|
||||||
|
|
||||||
|
class ZMConfig
|
||||||
|
{
|
||||||
|
public const SUPPORTED_EXTENSIONS = ['php', 'json'];
|
||||||
|
|
||||||
|
public const SUPPORTED_ENVIRONMENTS = ['development', 'production', 'staging'];
|
||||||
|
|
||||||
|
private const DEFAULT_PATH = __DIR__ . '/../../../config';
|
||||||
|
|
||||||
|
/** @var string 上次报错 */
|
||||||
|
public static $last_error = '';
|
||||||
|
|
||||||
|
/** @var array 配置文件 */
|
||||||
|
public static $config = [];
|
||||||
|
|
||||||
|
/** @var string 配置文件 */
|
||||||
|
private static $path = '.';
|
||||||
|
|
||||||
|
/** @var string 上次的路径 */
|
||||||
|
private static $last_path = '.';
|
||||||
|
|
||||||
|
/** @var string 配置文件环境变量 */
|
||||||
|
private static $env = 'development';
|
||||||
|
|
||||||
|
/** @var array 配置文件元数据 */
|
||||||
|
private static $config_meta_list = [];
|
||||||
|
|
||||||
|
public static function setDirectory($path)
|
||||||
|
{
|
||||||
|
self::$last_path = self::$path;
|
||||||
|
return self::$path = $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public static function restoreDirectory()
|
||||||
|
{
|
||||||
|
self::$path = self::$last_path;
|
||||||
|
self::$last_path = '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getDirectory(): string
|
||||||
|
{
|
||||||
|
return self::$path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function setEnv($env = 'development'): bool
|
||||||
|
{
|
||||||
|
if (!in_array($env, self::SUPPORTED_ENVIRONMENTS)) {
|
||||||
|
throw new ConfigException('E00079', 'Unsupported environment: ' . $env);
|
||||||
|
}
|
||||||
|
self::$env = $env;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getEnv(): string
|
||||||
|
{
|
||||||
|
return self::$env;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $additional_key
|
||||||
|
* @throws ConfigException
|
||||||
|
* @return null|array|false|mixed
|
||||||
|
*/
|
||||||
|
public static function get(string $name, $additional_key = '')
|
||||||
|
{
|
||||||
|
$separated = explode('.', $name);
|
||||||
|
if ($additional_key !== '') {
|
||||||
|
$separated = array_merge($separated, explode('.', $additional_key));
|
||||||
|
}
|
||||||
|
$head_name = array_shift($separated);
|
||||||
|
// 首先判断有没有初始化这个配置文件,因为是只读,所以是懒加载,加载第一次后缓存起来
|
||||||
|
if (!isset(self::$config[$head_name])) {
|
||||||
|
Console::debug('配置文件' . $name . ' ' . $additional_key . '没读取过,正在从文件加载 ...');
|
||||||
|
self::$config[$head_name] = self::loadConfig($head_name);
|
||||||
|
}
|
||||||
|
// global.remote_terminal
|
||||||
|
// 根据切分来寻找子配置
|
||||||
|
$obj = self::$config[$head_name];
|
||||||
|
foreach ($separated as $key) {
|
||||||
|
if (isset($obj[$key])) {
|
||||||
|
$obj = $obj[$key];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function trace(string $name)
|
||||||
|
{
|
||||||
|
// TODO: 调试配置文件搜寻路径
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function reload()
|
||||||
|
{
|
||||||
|
self::$config = [];
|
||||||
|
self::$config_meta_list = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function smartPatch($data, $patch)
|
||||||
|
{
|
||||||
|
/* patch 样例:
|
||||||
|
[patch]
|
||||||
|
runtime:
|
||||||
|
annotation_reader_ignore: ["牛逼"]
|
||||||
|
custom: "非常酷的patch模式"
|
||||||
|
|
||||||
|
[base]
|
||||||
|
runtime:
|
||||||
|
annotation_reader_ignore: []
|
||||||
|
reload_delay_time: 800
|
||||||
|
|
||||||
|
[result]
|
||||||
|
runtime:
|
||||||
|
annotation_reader_ignore: ["牛逼"]
|
||||||
|
reload_delay_time: 800
|
||||||
|
custom: "非常酷的patch模式"
|
||||||
|
*/
|
||||||
|
if (is_array($data) && is_array($patch)) { // 两者必须是数组才行
|
||||||
|
if (is_assoc_array($patch) && is_assoc_array($data)) { // 两者必须都是kv数组才能递归merge,如果是顺序数组,则直接覆盖
|
||||||
|
foreach ($patch as $k => $v) {
|
||||||
|
if (!isset($data[$k])) { // 如果项目不在基类存在,则直接写入
|
||||||
|
$data[$k] = $v;
|
||||||
|
} else { // 如果base存在的话,则递归patch覆盖
|
||||||
|
$data[$k] = self::smartPatch($data[$k], $v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $patch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ConfigException
|
||||||
|
* @return array|int|string
|
||||||
|
*/
|
||||||
|
private static function loadConfig(string $name)
|
||||||
|
{
|
||||||
|
// 首先获取此名称的所有配置文件的路径
|
||||||
|
self::parseList($name);
|
||||||
|
|
||||||
|
$env1_patch0 = null;
|
||||||
|
$env1_patch1 = null;
|
||||||
|
$env0_patch0 = null;
|
||||||
|
$env0_patch1 = null;
|
||||||
|
foreach (self::$config_meta_list[$name] as $v) {
|
||||||
|
/** @var ConfigMetadata $v */
|
||||||
|
if ($v->is_env && !$v->is_patch) {
|
||||||
|
$env1_patch0 = $v->data;
|
||||||
|
} elseif ($v->is_env && $v->is_patch) {
|
||||||
|
$env1_patch1 = $v->data;
|
||||||
|
} elseif (!$v->is_env && !$v->is_patch) {
|
||||||
|
$env0_patch0 = $v->data;
|
||||||
|
} else {
|
||||||
|
$env0_patch1 = $v->data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 优先级:无env无patch < 无env有patch < 有env无patch < 有env有patch
|
||||||
|
// 但是无patch的版本必须有一个,否则会报错
|
||||||
|
if ($env1_patch0 === null && $env0_patch0 === null) {
|
||||||
|
throw new ConfigException('E00078', '未找到配置文件 ' . $name . ' !');
|
||||||
|
}
|
||||||
|
$data = $env1_patch0 ?? $env0_patch0;
|
||||||
|
if (is_array($patch = $env1_patch1 ?? $env0_patch1) && is_assoc_array($patch)) {
|
||||||
|
$data = self::smartPatch($data, $patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过名称将所有该名称的配置文件路径和信息读取到列表中
|
||||||
|
* @throws ConfigException
|
||||||
|
*/
|
||||||
|
private static function parseList(string $name): void
|
||||||
|
{
|
||||||
|
$list = [];
|
||||||
|
$files = DataProvider::scanDirFiles(self::$path, true, true);
|
||||||
|
foreach ($files as $file) {
|
||||||
|
Console::debug('正在从目录' . self::$path . '读取配置文件 ' . $file);
|
||||||
|
$info = pathinfo($file);
|
||||||
|
$info['extension'] = $info['extension'] ?? '';
|
||||||
|
|
||||||
|
// 排除子文件夹名字带点的文件
|
||||||
|
if ($info['dirname'] !== '.' && strpos($info['dirname'], '.') !== false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断文件名是否为配置文件
|
||||||
|
if (!in_array($info['extension'], self::SUPPORTED_EXTENSIONS)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ext = $info['extension'];
|
||||||
|
$dot_separated = explode('.', $info['filename']);
|
||||||
|
|
||||||
|
// 将配置文件加进来
|
||||||
|
$obj = new ConfigMetadata();
|
||||||
|
if ($dot_separated[0] === $name) { // 如果文件名与配置文件名一致
|
||||||
|
// 首先检测该文件是否为补丁版本儿
|
||||||
|
if (str_ends_with($info['filename'], '.patch')) {
|
||||||
|
$obj->is_patch = true;
|
||||||
|
$info['filename'] = substr($info['filename'], 0, -6);
|
||||||
|
} else {
|
||||||
|
$obj->is_patch = false;
|
||||||
|
}
|
||||||
|
// 其次检测该文件是不是带有环境参数的版本儿
|
||||||
|
if (str_ends_with($info['filename'], '.' . self::$env)) {
|
||||||
|
$obj->is_env = true;
|
||||||
|
$info['filename'] = substr($info['filename'], 0, -(strlen(self::$env) + 1));
|
||||||
|
} else {
|
||||||
|
$obj->is_env = false;
|
||||||
|
}
|
||||||
|
if (mb_strpos($info['filename'], '.') !== false) {
|
||||||
|
Console::warning('文件名 ' . $info['filename'] . ' 不合法(含有"."),请检查文件名是否合法。');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$obj->path = realpath(self::$path . '/' . $info['dirname'] . '/' . $info['basename']);
|
||||||
|
$obj->extension = $ext;
|
||||||
|
$obj->data = self::readConfigFromFile(realpath(self::$path . '/' . $info['dirname'] . '/' . $info['basename']), $info['extension']);
|
||||||
|
$list[] = $obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果是源码模式,config目录和default目录相同,所以不需要继续采摘default目录下的文件
|
||||||
|
if (realpath(self::$path) !== realpath(self::DEFAULT_PATH)) {
|
||||||
|
$files = DataProvider::scanDirFiles(self::DEFAULT_PATH, true, true);
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$info = pathinfo($file);
|
||||||
|
$info['extension'] = $info['extension'] ?? '';
|
||||||
|
// 判断文件名是否为配置文件
|
||||||
|
if (!in_array($info['extension'], self::SUPPORTED_EXTENSIONS)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($info['filename'] === $name) { // 如果文件名与配置文件名一致
|
||||||
|
$obj = new ConfigMetadata();
|
||||||
|
$obj->is_patch = false;
|
||||||
|
$obj->is_env = false;
|
||||||
|
$obj->path = realpath(self::DEFAULT_PATH . '/' . $info['dirname'] . '/' . $info['basename']);
|
||||||
|
$obj->extension = $info['extension'];
|
||||||
|
$obj->data = self::readConfigFromFile(realpath(self::DEFAULT_PATH . '/' . $info['dirname'] . '/' . $info['basename']), $info['extension']);
|
||||||
|
$list[] = $obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self::$config_meta_list[$name] = $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $filename
|
||||||
|
* @param mixed $ext_name
|
||||||
|
* @throws ConfigException
|
||||||
|
*/
|
||||||
|
private static function readConfigFromFile($filename, $ext_name)
|
||||||
|
{
|
||||||
|
Console::debug('正加载配置文件 ' . $filename);
|
||||||
|
switch ($ext_name) {
|
||||||
|
case 'php':
|
||||||
|
$r = require $filename;
|
||||||
|
if (is_array($r)) {
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
throw new ConfigException('E00079', 'php配置文件include失败,请检查终端warning错误');
|
||||||
|
case 'json':
|
||||||
|
default:
|
||||||
|
$r = json_decode(file_get_contents($filename), true);
|
||||||
|
if (is_array($r)) {
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
throw new ConfigException('E00079', 'json反序列化失败,请检查文件内容');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@ class OnWorkerStop implements SwooleEvent
|
|||||||
{
|
{
|
||||||
WorkerContainer::getInstance()->flush();
|
WorkerContainer::getInstance()->flush();
|
||||||
|
|
||||||
if ($worker_id == (ZMConfig::get('worker_cache')['worker'] ?? 0)) {
|
if ($worker_id == (ZMConfig::get('global.worker_cache.worker') ?? 0)) {
|
||||||
LightCache::savePersistence();
|
LightCache::savePersistence();
|
||||||
}
|
}
|
||||||
Console::verbose(($server->taskworker ? 'Task' : '') . "Worker #{$worker_id} 已停止 (Worker 状态码: " . $server->getWorkerStatus($worker_id) . ')');
|
Console::verbose(($server->taskworker ? 'Task' : '') . "Worker #{$worker_id} 已停止 (Worker 状态码: " . $server->getWorkerStatus($worker_id) . ')');
|
||||||
|
|||||||
15
src/ZM/Exception/ConfigException.php
Normal file
15
src/ZM/Exception/ConfigException.php
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace ZM\Exception;
|
||||||
|
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class ConfigException extends ZMException
|
||||||
|
{
|
||||||
|
public function __construct($err_code, $message = '', $code = 0, Throwable $previous = null)
|
||||||
|
{
|
||||||
|
parent::__construct(zm_internal_errcode($err_code) . $message, $code, $previous);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ use ZM\Config\ZMConfig;
|
|||||||
use ZM\ConnectionManager\ManagerGM;
|
use ZM\ConnectionManager\ManagerGM;
|
||||||
use ZM\Console\Console;
|
use ZM\Console\Console;
|
||||||
use ZM\Console\TermColor;
|
use ZM\Console\TermColor;
|
||||||
|
use ZM\Exception\ConfigException;
|
||||||
use ZM\Exception\ZMKnownException;
|
use ZM\Exception\ZMKnownException;
|
||||||
use ZM\Store\LightCache;
|
use ZM\Store\LightCache;
|
||||||
use ZM\Store\LightCacheInside;
|
use ZM\Store\LightCacheInside;
|
||||||
@@ -77,8 +78,9 @@ class Framework
|
|||||||
/**
|
/**
|
||||||
* 创建一个新的框架实例
|
* 创建一个新的框架实例
|
||||||
*
|
*
|
||||||
* @param array $args 运行参数
|
* @param array $args 运行参数
|
||||||
* @param bool $instant_mode 是否为单文件模式
|
* @param bool $instant_mode 是否为单文件模式
|
||||||
|
* @throws ConfigException
|
||||||
*/
|
*/
|
||||||
public function __construct(array $args = [], bool $instant_mode = false)
|
public function __construct(array $args = [], bool $instant_mode = false)
|
||||||
{
|
{
|
||||||
@@ -88,7 +90,7 @@ class Framework
|
|||||||
|
|
||||||
// 初始化配置
|
// 初始化配置
|
||||||
ZMConfig::setDirectory(DataProvider::getSourceRootDir() . '/config');
|
ZMConfig::setDirectory(DataProvider::getSourceRootDir() . '/config');
|
||||||
ZMConfig::setEnv($args['env'] ?? '');
|
ZMConfig::setEnv($args['env'] ?? 'development');
|
||||||
if (ZMConfig::get('global') === false) {
|
if (ZMConfig::get('global') === false) {
|
||||||
echo zm_internal_errcode('E00007') . 'Global config load failed: ' . ZMConfig::$last_error . "\nError path: " . DataProvider::getSourceRootDir() . "\nPlease init first!\nSee: https://github.com/zhamao-robot/zhamao-framework/issues/37\n";
|
echo zm_internal_errcode('E00007') . 'Global config load failed: ' . ZMConfig::$last_error . "\nError path: " . DataProvider::getSourceRootDir() . "\nPlease init first!\nSee: https://github.com/zhamao-robot/zhamao-framework/issues/37\n";
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -686,8 +688,9 @@ class Framework
|
|||||||
/**
|
/**
|
||||||
* 解析命令行的 $argv 参数们
|
* 解析命令行的 $argv 参数们
|
||||||
*
|
*
|
||||||
* @param array $args 命令行参数
|
* @param array $args 命令行参数
|
||||||
* @param bool|string $add_port 是否添加端口号
|
* @param bool|string $add_port 是否添加端口号
|
||||||
|
* @throws ConfigException
|
||||||
*/
|
*/
|
||||||
private function parseCliArgs(array $args, &$add_port)
|
private function parseCliArgs(array $args, &$add_port)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use RuntimeException;
|
|||||||
use Traversable;
|
use Traversable;
|
||||||
use ZM\Config\ZMConfig;
|
use ZM\Config\ZMConfig;
|
||||||
use ZM\Console\Console;
|
use ZM\Console\Console;
|
||||||
|
use ZM\Exception\ConfigException;
|
||||||
|
|
||||||
class DataProvider
|
class DataProvider
|
||||||
{
|
{
|
||||||
@@ -57,6 +58,7 @@ class DataProvider
|
|||||||
/**
|
/**
|
||||||
* 获取框架反代链接
|
* 获取框架反代链接
|
||||||
*
|
*
|
||||||
|
* @throws ConfigException
|
||||||
* @return null|array|false|mixed
|
* @return null|array|false|mixed
|
||||||
*/
|
*/
|
||||||
public static function getFrameworkLink()
|
public static function getFrameworkLink()
|
||||||
@@ -88,6 +90,7 @@ class DataProvider
|
|||||||
*
|
*
|
||||||
* @param string $filename 文件名
|
* @param string $filename 文件名
|
||||||
* @param array|int|Iterator|JsonSerializable|string|Traversable $file_array 文件内容数组
|
* @param array|int|Iterator|JsonSerializable|string|Traversable $file_array 文件内容数组
|
||||||
|
* @throws ConfigException
|
||||||
* @return false|int 返回文件大小或false
|
* @return false|int 返回文件大小或false
|
||||||
*/
|
*/
|
||||||
public static function saveToJson(string $filename, $file_array)
|
public static function saveToJson(string $filename, $file_array)
|
||||||
@@ -112,8 +115,9 @@ class DataProvider
|
|||||||
/**
|
/**
|
||||||
* 从json加载变量到内存
|
* 从json加载变量到内存
|
||||||
*
|
*
|
||||||
* @param string $filename 文件名
|
* @param string $filename 文件名
|
||||||
* @return null|mixed 返回文件内容数据或null
|
* @throws ConfigException
|
||||||
|
* @return null|mixed 返回文件内容数据或null
|
||||||
*/
|
*/
|
||||||
public static function loadFromJson(string $filename)
|
public static function loadFromJson(string $filename)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -776,6 +776,11 @@ function compare_object_and_array_by_keys(object $object, array $array, array $k
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function is_assoc_array(array $array): bool
|
||||||
|
{
|
||||||
|
return !empty($array) && array_keys($array) !== range(0, count($array) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 以下为废弃的函数,将于未来移除
|
* 以下为废弃的函数,将于未来移除
|
||||||
*/
|
*/
|
||||||
|
|||||||
163
tests/ZM/Config/ZMConfigTest.php
Normal file
163
tests/ZM/Config/ZMConfigTest.php
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Tests\ZM\Config;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use ZM\Config\ZMConfig;
|
||||||
|
use ZM\Console\Console;
|
||||||
|
use ZM\Exception\ConfigException;
|
||||||
|
use ZM\Utils\DataProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class ZMConfigTest extends TestCase
|
||||||
|
{
|
||||||
|
public static function setUpBeforeClass(): void
|
||||||
|
{
|
||||||
|
$mock_dir = __DIR__ . '/config_mock';
|
||||||
|
ZMConfig::reload();
|
||||||
|
ZMConfig::setDirectory(__DIR__ . '/config_mock');
|
||||||
|
if (!is_dir($mock_dir)) {
|
||||||
|
mkdir($mock_dir, 0755, true);
|
||||||
|
}
|
||||||
|
// 下方测试需要临时写入的文件
|
||||||
|
file_put_contents($mock_dir . '/global.patch.php', '<?php return ["port" => 30055];');
|
||||||
|
file_put_contents($mock_dir . '/php_exception.php', '<?php return true;');
|
||||||
|
file_put_contents($mock_dir . '/json_exception.json', '"string"');
|
||||||
|
file_put_contents($mock_dir . '/global.development.patch.php', '<?php return ["port" => 30055];');
|
||||||
|
file_put_contents($mock_dir . '/global.invalid.development.php', '<?php return ["port" => 30055];');
|
||||||
|
file_put_contents($mock_dir . '/fake.development.json', '{"multi":{"level":"test"}}');
|
||||||
|
file_put_contents($mock_dir . '/no_main_only_patch.patch.json', '{"multi":{"level":"test"}}');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function tearDownAfterClass(): void
|
||||||
|
{
|
||||||
|
ZMConfig::reload();
|
||||||
|
ZMConfig::restoreDirectory();
|
||||||
|
foreach (DataProvider::scanDirFiles(__DIR__ . '/config_mock', true, false) as $file) {
|
||||||
|
unlink($file);
|
||||||
|
}
|
||||||
|
rmdir(__DIR__ . '/config_mock');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ConfigException
|
||||||
|
*/
|
||||||
|
public function testReload()
|
||||||
|
{
|
||||||
|
$this->expectOutputRegex('/没读取过,正在从文件加载/');
|
||||||
|
$this->assertEquals('0.0.0.0', ZMConfig::get('global.host'));
|
||||||
|
ZMConfig::reload();
|
||||||
|
Console::setLevel(4);
|
||||||
|
$this->assertEquals('0.0.0.0', ZMConfig::get('global.host'));
|
||||||
|
Console::setLevel(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetAndRestoreDirectory()
|
||||||
|
{
|
||||||
|
$origin = ZMConfig::getDirectory();
|
||||||
|
ZMConfig::setDirectory('.');
|
||||||
|
$this->assertEquals('.', ZMConfig::getDirectory());
|
||||||
|
ZMConfig::restoreDirectory();
|
||||||
|
$this->assertEquals($origin, ZMConfig::getDirectory());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetAndGetEnv()
|
||||||
|
{
|
||||||
|
$this->expectException(ConfigException::class);
|
||||||
|
ZMConfig::setEnv('production');
|
||||||
|
$this->assertEquals('production', ZMConfig::getEnv());
|
||||||
|
ZMConfig::setEnv();
|
||||||
|
ZMConfig::setEnv('reee');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider providerTestGet
|
||||||
|
* @param mixed $expected
|
||||||
|
* @throws ConfigException
|
||||||
|
*/
|
||||||
|
public function testGet(array $data_params, $expected)
|
||||||
|
{
|
||||||
|
$this->assertEquals($expected, ZMConfig::get(...$data_params));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function providerTestGet(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'get port' => [['global.port'], 30055],
|
||||||
|
'get port key 2' => [['global', 'port'], 30055],
|
||||||
|
'get invalid key' => [['global', 'invalid'], null],
|
||||||
|
'get another environment' => [['fake.multi.level'], 'test'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetPhpException()
|
||||||
|
{
|
||||||
|
$this->expectException(ConfigException::class);
|
||||||
|
ZMConfig::get('php_exception');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetJsonException()
|
||||||
|
{
|
||||||
|
$this->expectException(ConfigException::class);
|
||||||
|
ZMConfig::get('json_exception');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnlyPatchException()
|
||||||
|
{
|
||||||
|
$this->expectException(ConfigException::class);
|
||||||
|
ZMConfig::get('no_main_only_patch.test');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSmartPatch()
|
||||||
|
{
|
||||||
|
$array = [
|
||||||
|
'key-1-1' => 'value-1-1',
|
||||||
|
'key-1-2' => [
|
||||||
|
'key-2-1' => [
|
||||||
|
'key-3-1' => [
|
||||||
|
'value-3-1',
|
||||||
|
'value-3-2',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'key-1-3' => [
|
||||||
|
'key-4-1' => 'value-4-1',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$patch = [
|
||||||
|
'key-1-2' => [
|
||||||
|
'key-2-1' => [
|
||||||
|
'key-3-1' => [
|
||||||
|
'value-3-3',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'key-1-3' => [
|
||||||
|
'key-4-2' => [
|
||||||
|
'key-5-1' => 'value-5-1',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$expected = [
|
||||||
|
'key-1-1' => 'value-1-1',
|
||||||
|
'key-1-2' => [
|
||||||
|
'key-2-1' => [
|
||||||
|
'key-3-1' => [
|
||||||
|
'value-3-3',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'key-1-3' => [
|
||||||
|
'key-4-1' => 'value-4-1',
|
||||||
|
'key-4-2' => [
|
||||||
|
'key-5-1' => 'value-5-1',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$this->assertEquals($expected, ZMConfig::smartPatch($array, $patch));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ use Swoole\Http\Response;
|
|||||||
use Symfony\Component\Routing\RouteCollection;
|
use Symfony\Component\Routing\RouteCollection;
|
||||||
use ZM\Annotation\Http\RequestMapping;
|
use ZM\Annotation\Http\RequestMapping;
|
||||||
use ZM\Annotation\Http\RequestMethod;
|
use ZM\Annotation\Http\RequestMethod;
|
||||||
|
use ZM\Config\ZMConfig;
|
||||||
use ZM\Utils\HttpUtil;
|
use ZM\Utils\HttpUtil;
|
||||||
use ZM\Utils\Manager\RouteManager;
|
use ZM\Utils\Manager\RouteManager;
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ class HttpUtilTest extends TestCase
|
|||||||
{
|
{
|
||||||
$swoole_response = $this->getMockClass(Response::class);
|
$swoole_response = $this->getMockClass(Response::class);
|
||||||
$r = new \ZM\Http\Response(new $swoole_response());
|
$r = new \ZM\Http\Response(new $swoole_response());
|
||||||
HttpUtil::handleStaticPage($page, $r);
|
HttpUtil::handleStaticPage($page, $r, ZMConfig::get('global', 'static_file_server'));
|
||||||
$this->assertEquals($expected, $r->getStatusCode() === 200);
|
$this->assertEquals($expected, $r->getStatusCode() === 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Tests\ZM\Utils;
|
namespace Tests\ZM\Utils;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use ZM\Annotation\CQ\CQCommand;
|
use ZM\Annotation\CQ\CQCommand;
|
||||||
use ZM\API\CQ;
|
|
||||||
use ZM\Event\EventManager;
|
use ZM\Event\EventManager;
|
||||||
use ZM\Utils\DataProvider;
|
use ZM\Utils\DataProvider;
|
||||||
use ZM\Utils\MessageUtil;
|
use ZM\Utils\MessageUtil;
|
||||||
@@ -17,6 +17,9 @@ use ZM\Utils\MessageUtil;
|
|||||||
*/
|
*/
|
||||||
class MessageUtilTest extends TestCase
|
class MessageUtilTest extends TestCase
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
public function testAddShortCommand(): void
|
public function testAddShortCommand(): void
|
||||||
{
|
{
|
||||||
EventManager::$events[CQCommand::class] = [];
|
EventManager::$events[CQCommand::class] = [];
|
||||||
@@ -152,17 +155,17 @@ class MessageUtilTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testDownloadCQImage(): void
|
public function testDownloadCQImage(): void
|
||||||
{
|
{
|
||||||
if (file_exists(DataProvider::getDataFolder('images') . '/test.jpg')) {
|
if (file_exists(DataProvider::getDataFolder('images') . '/test.png')) {
|
||||||
unlink(DataProvider::getDataFolder('images') . '/test.jpg');
|
unlink(DataProvider::getDataFolder('images') . '/test.png');
|
||||||
}
|
}
|
||||||
$msg = '[CQ:image,file=test.jpg,url=https://zhamao.xin/file/hello.jpg]';
|
$msg = '[CQ:image,file=test.png,url=https://zhamao.xin/file/hello.png]';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$result = MessageUtil::downloadCQImage($msg);
|
$result = MessageUtil::downloadCQImage($msg);
|
||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
$this->assertCount(1, $result);
|
$this->assertCount(1, $result);
|
||||||
$this->assertFileExists(DataProvider::getDataFolder('images') . '/test.jpg');
|
$this->assertFileExists(DataProvider::getDataFolder('images') . '/test.png');
|
||||||
unlink(DataProvider::getDataFolder('images') . '/test.jpg');
|
unlink(DataProvider::getDataFolder('images') . '/test.png');
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
if (strpos($e->getMessage(), 'enable-openssl') !== false) {
|
if (strpos($e->getMessage(), 'enable-openssl') !== false) {
|
||||||
$this->markTestSkipped('OpenSSL is not enabled');
|
$this->markTestSkipped('OpenSSL is not enabled');
|
||||||
|
|||||||
Reference in New Issue
Block a user