From b7951806b85aafe47bd3b0d23999603e0da82961 Mon Sep 17 00:00:00 2001 From: mkm <727897186@qq.com> Date: Tue, 19 Nov 2024 12:11:23 +0800 Subject: [PATCH] =?UTF-8?q?feat(admin):=20=E4=BC=98=E5=8C=96=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E5=88=9B=E5=BB=BA=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将订单创建时的参数从 $order 变为 $params,提高代码可读性 - 更新 composer.json,将 webman-framework 版本升级到 1.6 - 修改 worker_start 函数,增加自定义 worker 类功能 - 优化 view 函数,增加对模板输入的统一处理 - 更新 cpu_count 函数,增加错误处理机制 --- .../beforehand_order/BeforehandOrderLogic.php | 24 +- composer.json | 2 +- composer.lock | 52 +-- support/bootstrap.php | 6 + support/helpers.php | 83 +++-- vendor/composer/installed.json | 30 +- vendor/composer/installed.php | 12 +- .../workerman/webman-framework/composer.json | 7 +- vendor/workerman/webman-framework/src/App.php | 299 ++++++++++++------ .../webman-framework/src/Context.php | 12 +- .../src/Exception/BusinessException.php | 95 ++++++ .../src/Exception/ExceptionHandler.php | 3 + .../webman-framework/src/Http/Request.php | 80 ++++- .../webman-framework/src/Middleware.php | 25 +- .../workerman/webman-framework/src/Route.php | 97 +++++- .../webman-framework/src/support/App.php | 16 +- .../webman-framework/src/support/Cache.php | 54 +++- .../webman-framework/src/support/Redis.php | 41 ++- .../src/support/bootstrap/LaravelDb.php | 3 +- .../support/exception/BusinessException.php | 18 +- .../src/support/exception/Handler.php | 5 - .../support/exception/InputTypeException.php | 24 ++ .../exception/MissingInputException.php | 46 +++ .../support/exception/NotFoundException.php | 20 ++ .../exception/PageNotFoundException.php | 93 ++++++ .../src/support/view/Blade.php | 14 +- .../webman-framework/src/support/view/Raw.php | 3 +- .../src/support/view/ThinkPHP.php | 7 +- .../src/support/view/Twig.php | 16 +- .../workerman/Connection/TcpConnection.php | 7 +- vendor/workerman/workerman/Events/Swoole.php | 61 +++- .../workerman/Protocols/Websocket.php | 10 +- vendor/workerman/workerman/Protocols/Ws.php | 2 +- vendor/workerman/workerman/Worker.php | 124 ++++++-- vendor/workerman/workerman/composer.json | 2 +- windows.php | 17 +- 36 files changed, 1092 insertions(+), 318 deletions(-) create mode 100644 vendor/workerman/webman-framework/src/Exception/BusinessException.php create mode 100644 vendor/workerman/webman-framework/src/support/exception/InputTypeException.php create mode 100644 vendor/workerman/webman-framework/src/support/exception/MissingInputException.php create mode 100644 vendor/workerman/webman-framework/src/support/exception/NotFoundException.php create mode 100644 vendor/workerman/webman-framework/src/support/exception/PageNotFoundException.php diff --git a/app/admin/logic/beforehand_order/BeforehandOrderLogic.php b/app/admin/logic/beforehand_order/BeforehandOrderLogic.php index 84868cc0e..bde344d9e 100644 --- a/app/admin/logic/beforehand_order/BeforehandOrderLogic.php +++ b/app/admin/logic/beforehand_order/BeforehandOrderLogic.php @@ -447,18 +447,18 @@ class BeforehandOrderLogic extends BaseLogic 'nickname' => $order['real_name'] ?? '', 'phone' => $order['user_phone'] ?? '', 'address' => $order['user_address'] ?? '', - 'arrival_time' => $order['arrival_time'] ?? '', - 'purpose' => $order['purpose'] ?? '', - 'tables' => $order['tables'] ?? '', - 'days' => $order['days'] ?? '', - 'chef' => $order['chef'] ?? '', - 'chef_phone' => $order['chef_phone'] ?? '', - 'splitting_officer' => $order['splitting_officer'] ?? '', - 'merchandiser' => $order['merchandiser'] ?? '', - 'distribution_personnel' => $order['distribution_personnel'] ?? '', - 'transporter' => $order['transporter'] ?? '', - 'system_store_name' => $order['system_store_name'] ?? '', - 'regional_manager' => $order['regional_manager'] ?? '', + 'arrival_time' => $params['arrival_time'] ?? '', + 'purpose' => $params['purpose'] ?? '', + 'tables' => $params['tables'] ?? '', + 'days' => $params['days'] ?? '', + 'chef' => $params['chef'] ?? '', + 'chef_phone' => $params['chef_phone'] ?? '', + 'splitting_officer' => $params['splitting_officer'] ?? '', + 'merchandiser' => $params['merchandiser'] ?? '', + 'distribution_personnel' => $params['distribution_personnel'] ?? '', + 'transporter' => $params['transporter'] ?? '', + 'system_store_name' => $params['system_store_name'] ?? '', + 'regional_manager' => $params['regional_manager'] ?? '', ]; $order = BeforehandOrder::create([ 'order_id' => getNewOrderId('YG'), diff --git a/composer.json b/composer.json index 981d45cf5..4bb6cc7ca 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ }, "require": { "php": ">=8.1", - "workerman/webman-framework": "^1.5.22", + "workerman/webman-framework": "^1.6", "monolog/monolog": "^2.2", "webman/think-orm": "v1.1.3", "vlucas/phpdotenv": "^5.4", diff --git a/composer.lock b/composer.lock index 3d7154e8c..e5a87f041 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": "60cad00a35881913fa82cc0d9a2d9e13", + "content-hash": "e624460001f6646c62934c275bd79785", "packages": [ { "name": "aliyuncs/oss-sdk-php", @@ -2807,13 +2807,7 @@ "type": "zip", "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812", "reference": "181d480e08d9476e61381e04a71b34dc0432e812", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.4.0" @@ -3974,13 +3968,7 @@ "type": "zip", "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=7.4.0" @@ -7718,24 +7706,24 @@ }, { "name": "workerman/webman-framework", - "version": "v1.5.22", + "version": "v1.6.4", "source": { "type": "git", "url": "https://github.com/walkor/webman-framework.git", - "reference": "f52d9739a264d99d49427081c8a85303c02a770e" + "reference": "b0db16acf942322d0eb2ad2803300775b19111b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/walkor/webman-framework/zipball/f52d9739a264d99d49427081c8a85303c02a770e", - "reference": "f52d9739a264d99d49427081c8a85303c02a770e", + "url": "https://api.github.com/repos/walkor/webman-framework/zipball/b0db16acf942322d0eb2ad2803300775b19111b5", + "reference": "b0db16acf942322d0eb2ad2803300775b19111b5", "shasum": "" }, "require": { "ext-json": "*", "nikic/fast-route": "^1.3", - "php": ">=7.2", + "php": ">=8.0", "psr/container": ">=1.0", - "workerman/workerman": "^4.0.4 || ^5.0.0" + "workerman/workerman": "^4.0.4 || ^5.0.0 || dev-master" }, "suggest": { "ext-event": "For better performance. " @@ -7776,30 +7764,24 @@ "source": "https://github.com/walkor/webman-framework", "wiki": "https://doc.workerman.net/" }, - "time": "2024-08-04T01:40:07+00:00" + "time": "2024-11-19T02:16:55+00:00" }, { "name": "workerman/workerman", - "version": "v4.1.15", + "version": "v4.2.0", "source": { "type": "git", "url": "https://github.com/walkor/workerman.git", - "reference": "afc8242fc769ab7cf22eb4ac22b97cb59d465e4e" + "reference": "df513f3fd274811ebb8358d05d7cec19ee8bd3e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/walkor/workerman/zipball/afc8242fc769ab7cf22eb4ac22b97cb59d465e4e", - "reference": "afc8242fc769ab7cf22eb4ac22b97cb59d465e4e", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "url": "https://api.github.com/repos/walkor/workerman/zipball/df513f3fd274811ebb8358d05d7cec19ee8bd3e1", + "reference": "df513f3fd274811ebb8358d05d7cec19ee8bd3e1", + "shasum": "" }, "require": { - "php": ">=7.0" + "php": ">=8.0" }, "suggest": { "ext-event": "For better performance. " @@ -7845,7 +7827,7 @@ "type": "patreon" } ], - "time": "2024-02-19T02:10:39+00:00" + "time": "2024-11-07T08:31:33+00:00" }, { "name": "yansongda/artful", diff --git a/support/bootstrap.php b/support/bootstrap.php index d9471e6ab..d913defd3 100644 --- a/support/bootstrap.php +++ b/support/bootstrap.php @@ -19,9 +19,15 @@ use Webman\Config; use Webman\Middleware; use Webman\Route; use Webman\Util; +use Workerman\Events\Select; +use Workerman\Worker; $worker = $worker ?? null; +if (empty(Worker::$eventLoopClass)) { + Worker::$eventLoopClass = Select::class; +} + set_error_handler(function ($level, $message, $file = '', $line = 0) { if (error_reporting() & $level) { throw new ErrorException($message, 0, $level, $file, $line); diff --git a/support/helpers.php b/support/helpers.php index 35e94f17c..19e873662 100644 --- a/support/helpers.php +++ b/support/helpers.php @@ -91,7 +91,7 @@ function public_path(string $path = '', string $plugin = null): string } $publicPaths[$plugin] = $publicPath; } - return path_combine($publicPath, $path); + return $path === '' ? $publicPath : path_combine($publicPath, $path); } /** @@ -197,67 +197,70 @@ function redirect(string $location, int $status = 302, array $headers = []): Res /** * View response - * @param string $template + * @param mixed $template * @param array $vars * @param string|null $app * @param string|null $plugin * @return Response */ -function view(string $template, array $vars = [], string $app = null, string $plugin = null): Response +function view($template = null, array $vars = [], string $app = null, string $plugin = null): Response { - $request = \request(); - $plugin = $plugin === null ? ($request->plugin ?? '') : $plugin; + [$template, $vars, $app, $plugin] = template_inputs($template, $vars, $app, $plugin); $handler = \config($plugin ? "plugin.$plugin.view.handler" : 'view.handler'); return new Response(200, [], $handler::render($template, $vars, $app, $plugin)); } /** * Raw view response - * @param string $template + * @param mixed $template * @param array $vars * @param string|null $app + * @param string|null $plugin * @return Response * @throws Throwable */ -function raw_view(string $template, array $vars = [], string $app = null): Response +function raw_view($template = null, array $vars = [], string $app = null, string $plugin = null): Response { - return new Response(200, [], Raw::render($template, $vars, $app)); + return new Response(200, [], Raw::render(...template_inputs($template, $vars, $app, $plugin))); } /** * Blade view response - * @param string $template + * @param mixed $template * @param array $vars * @param string|null $app + * @param string|null $plugin * @return Response */ -function blade_view(string $template, array $vars = [], string $app = null): Response +function blade_view($template = null, array $vars = [], string $app = null, string $plugin = null): Response { - return new Response(200, [], Blade::render($template, $vars, $app)); + return new Response(200, [], Blade::render(...template_inputs($template, $vars, $app, $plugin))); } /** * Think view response - * @param string $template + * @param mixed $template * @param array $vars * @param string|null $app + * @param string|null $plugin * @return Response */ -function think_view(string $template, array $vars = [], string $app = null): Response +function think_view($template = null, array $vars = [], string $app = null, string $plugin = null): Response { - return new Response(200, [], ThinkPHP::render($template, $vars, $app)); + return new Response(200, [], ThinkPHP::render(...template_inputs($template, $vars, $app, $plugin))); } /** * Twig view response - * @param string $template + * @param mixed $template * @param array $vars * @param string|null $app + * @param string|null $plugin * @return Response */ -function twig_view(string $template, array $vars = [], string $app = null): Response +function twig_view($template = null, array $vars = [], string $app = null, string $plugin = null): Response { - return new Response(200, [], Twig::render($template, $vars, $app)); + return new Response(200, [], Twig::render(...template_inputs($template, $vars, $app, $plugin))); } /** @@ -309,6 +312,7 @@ function route(string $name, ...$parameters): string * @param mixed $key * @param mixed $default * @return mixed|bool|Session + * @throws Exception */ function session($key = null, $default = null) { @@ -448,8 +452,13 @@ function worker_bind($worker, $class) */ function worker_start($processName, $config) { - $worker = new Worker($config['listen'] ?? null, $config['context'] ?? []); - $propertyMap = [ + if (isset($config['enable']) && !$config['enable']) { + return; + } + // feat:custom worker class [default: Workerman\Worker] + $class = is_a($class = $config['workerClass'] ?? '' , Worker::class, true) ? $class : Worker::class; + $worker = new $class($config['listen'] ?? null, $config['context'] ?? []); + $properties = [ 'count', 'user', 'group', @@ -457,9 +466,10 @@ function worker_start($processName, $config) 'reusePort', 'transport', 'protocol', + 'eventLoop', ]; $worker->name = $processName; - foreach ($propertyMap as $property) { + foreach ($properties as $property) { if (isset($config[$property])) { $worker->$property = $config[$property]; } @@ -502,6 +512,32 @@ function is_phar(): bool return class_exists(Phar::class, false) && Phar::running(); } +/** + * Get template vars + * @param mixed $template + * @param array $vars + * @param string|null $app + * @param string|null $plugin + * @return array + */ +function template_inputs($template, array $vars, ?string $app, ?string $plugin): array +{ + $request = \request(); + $plugin = $plugin === null ? ($request->plugin ?? '') : $plugin; + if (is_array($template)) { + $vars = $template; + $template = null; + } + if ($template === null && $controller = $request->controller) { + $controllerSuffix = config($plugin ? "plugin.$plugin.app.controller_suffix" : "app.controller_suffix", ''); + $controllerName = $controllerSuffix !== '' ? substr($controller, 0, -strlen($controllerSuffix)) : $controller; + $path = strtolower(preg_replace('/(?action)); + $template = "$path/$actionFileBaseName"; + } + return [$template, $vars, $app, $plugin]; +} + /** * Get cpu count * @return int @@ -517,12 +553,17 @@ function cpu_count(): int if (strtolower(PHP_OS) === 'darwin') { $count = (int)shell_exec('sysctl -n machdep.cpu.core_count'); } else { - $count = (int)shell_exec('nproc'); + try { + $count = (int)shell_exec('nproc'); + } catch (\Throwable $ex) { + // Do nothing + } } } return $count > 0 ? $count : 4; } + /** * Get request parameters, if no parameter name is passed, an array of all values is returned, default values is supported * @param string|null $param param's name diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index a85bf3910..25ca6977b 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -7670,30 +7670,30 @@ }, { "name": "workerman/webman-framework", - "version": "v1.5.22", - "version_normalized": "1.5.22.0", + "version": "v1.6.4", + "version_normalized": "1.6.4.0", "source": { "type": "git", "url": "https://github.com/walkor/webman-framework.git", - "reference": "f52d9739a264d99d49427081c8a85303c02a770e" + "reference": "b0db16acf942322d0eb2ad2803300775b19111b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/walkor/webman-framework/zipball/f52d9739a264d99d49427081c8a85303c02a770e", - "reference": "f52d9739a264d99d49427081c8a85303c02a770e", + "url": "https://api.github.com/repos/walkor/webman-framework/zipball/b0db16acf942322d0eb2ad2803300775b19111b5", + "reference": "b0db16acf942322d0eb2ad2803300775b19111b5", "shasum": "" }, "require": { "ext-json": "*", "nikic/fast-route": "^1.3", - "php": ">=7.2", + "php": ">=8.0", "psr/container": ">=1.0", - "workerman/workerman": "^4.0.4 || ^5.0.0" + "workerman/workerman": "^4.0.4 || ^5.0.0 || dev-master" }, "suggest": { "ext-event": "For better performance. " }, - "time": "2024-08-04T01:40:07+00:00", + "time": "2024-11-19T02:16:55+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -7735,26 +7735,26 @@ }, { "name": "workerman/workerman", - "version": "v4.1.15", - "version_normalized": "4.1.15.0", + "version": "v4.2.0", + "version_normalized": "4.2.0.0", "source": { "type": "git", "url": "https://github.com/walkor/workerman.git", - "reference": "afc8242fc769ab7cf22eb4ac22b97cb59d465e4e" + "reference": "df513f3fd274811ebb8358d05d7cec19ee8bd3e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/walkor/workerman/zipball/afc8242fc769ab7cf22eb4ac22b97cb59d465e4e", - "reference": "afc8242fc769ab7cf22eb4ac22b97cb59d465e4e", + "url": "https://api.github.com/repos/walkor/workerman/zipball/df513f3fd274811ebb8358d05d7cec19ee8bd3e1", + "reference": "df513f3fd274811ebb8358d05d7cec19ee8bd3e1", "shasum": "" }, "require": { - "php": ">=7.0" + "php": ">=8.0" }, "suggest": { "ext-event": "For better performance. " }, - "time": "2024-02-19T02:10:39+00:00", + "time": "2024-11-07T08:31:33+00:00", "type": "library", "installation-source": "dist", "autoload": { diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index d4e2135e9..c2edf9aa4 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -1065,18 +1065,18 @@ 'dev_requirement' => false, ), 'workerman/webman-framework' => array( - 'pretty_version' => 'v1.5.22', - 'version' => '1.5.22.0', - 'reference' => 'f52d9739a264d99d49427081c8a85303c02a770e', + 'pretty_version' => 'v1.6.4', + 'version' => '1.6.4.0', + 'reference' => 'b0db16acf942322d0eb2ad2803300775b19111b5', 'type' => 'library', 'install_path' => __DIR__ . '/../workerman/webman-framework', 'aliases' => array(), 'dev_requirement' => false, ), 'workerman/workerman' => array( - 'pretty_version' => 'v4.1.15', - 'version' => '4.1.15.0', - 'reference' => 'afc8242fc769ab7cf22eb4ac22b97cb59d465e4e', + 'pretty_version' => 'v4.2.0', + 'version' => '4.2.0.0', + 'reference' => 'df513f3fd274811ebb8358d05d7cec19ee8bd3e1', 'type' => 'library', 'install_path' => __DIR__ . '/../workerman/workerman', 'aliases' => array(), diff --git a/vendor/workerman/webman-framework/composer.json b/vendor/workerman/webman-framework/composer.json index 53c930879..5172111d8 100644 --- a/vendor/workerman/webman-framework/composer.json +++ b/vendor/workerman/webman-framework/composer.json @@ -24,9 +24,9 @@ "source": "https://github.com/walkor/webman-framework" }, "require": { - "php": ">=7.2", + "php": ">=8.0", "ext-json": "*", - "workerman/workerman": "^4.0.4 || ^5.0.0", + "workerman/workerman": "^4.0.4 || ^5.0.0 || dev-master", "nikic/fast-route": "^1.3", "psr/container": ">=1.0" }, @@ -43,5 +43,6 @@ "Support\\View\\": "./src/support/view" } }, - "minimum-stability": "dev" + "minimum-stability": "dev", + "prefer-stable": true } diff --git a/vendor/workerman/webman-framework/src/App.php b/vendor/workerman/webman-framework/src/App.php index e796fb381..db9c62524 100644 --- a/vendor/workerman/webman-framework/src/App.php +++ b/vendor/workerman/webman-framework/src/App.php @@ -18,6 +18,9 @@ namespace Webman; use Closure; use Exception; use FastRoute\Dispatcher; +use Illuminate\Database\Eloquent\Model; +use support\exception\PageNotFoundException; +use think\Model as ThinkModel; use InvalidArgumentException; use Monolog\Logger; use Psr\Container\ContainerExceptionInterface; @@ -28,6 +31,10 @@ use ReflectionException; use ReflectionFunction; use ReflectionFunctionAbstract; use ReflectionMethod; +use support\exception\BusinessException; +use support\exception\MissingInputException; +use support\exception\RecordNotFoundException; +use support\exception\InputTypeException; use Throwable; use Webman\Exception\ExceptionHandler; use Webman\Exception\ExceptionHandlerInterface; @@ -40,7 +47,6 @@ use Workerman\Worker; use function array_merge; use function array_pop; use function array_reduce; -use function array_reverse; use function array_splice; use function array_values; use function class_exists; @@ -49,19 +55,17 @@ use function count; use function current; use function end; use function explode; -use function file_get_contents; use function get_class_methods; use function gettype; use function implode; -use function in_array; use function is_a; use function is_array; use function is_dir; use function is_file; +use function is_numeric; use function is_string; use function key; use function method_exists; -use function next; use function ob_get_clean; use function ob_start; use function pathinfo; @@ -129,6 +133,7 @@ class App * @param TcpConnection|mixed $connection * @param Request|mixed $request * @return null + * @throws Throwable */ public function onMessage($connection, $request) { @@ -142,19 +147,22 @@ class App return null; } + $status = 200; if ( static::unsafeUri($connection, $path, $request) || static::findFile($connection, $path, $key, $request) || - static::findRoute($connection, $path, $key, $request) + static::findRoute($connection, $path, $key, $request, $status) ) { return null; } $controllerAndAction = static::parseControllerAction($path); $plugin = $controllerAndAction['plugin'] ?? static::getPluginByPath($path); - if (!$controllerAndAction || Route::hasDisableDefaultRoute($plugin)) { + if (!$controllerAndAction || Route::isDefaultRouteDisabled($plugin, $controllerAndAction['app'] ?: '*') || + Route::isDefaultRouteDisabled($controllerAndAction['controller']) || + Route::isDefaultRouteDisabled([$controllerAndAction['controller'], $controllerAndAction['action']])) { $request->plugin = $plugin; - $callback = static::getFallback($plugin); + $callback = static::getFallback($plugin, $status); $request->app = $request->controller = $request->action = ''; static::send($connection, $callback($request), $request); return null; @@ -181,6 +189,7 @@ class App { static::$worker = $worker; Http::requestClass(static::$requestClass); + Context::init(); } /** @@ -214,9 +223,9 @@ class App strpos($path, "\\") !== false || strpos($path, "\0") !== false ) { - $callback = static::getFallback(); + $callback = static::getFallback('', 400); $request->plugin = $request->app = $request->controller = $request->action = ''; - static::send($connection, $callback($request), $request); + static::send($connection, $callback($request, 400), $request); return true; } return false; @@ -225,18 +234,17 @@ class App /** * GetFallback. * @param string $plugin + * @param int $status * @return Closure + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws ReflectionException */ - protected static function getFallback(string $plugin = ''): Closure + protected static function getFallback(string $plugin = '', int $status = 404): Closure { // When route, controller and action not found, try to use Route::fallback - return Route::getFallback($plugin) ?: function () { - try { - $notFoundContent = file_get_contents(static::$publicPath . '/404.html'); - } catch (Throwable $e) { - $notFoundContent = '404 Not Found'; - } - return new Response(404, [], $notFoundContent); + return Route::getFallback($plugin, $status) ?: function () { + throw new PageNotFoundException(); }; } @@ -252,11 +260,17 @@ class App $app = $request->app ?: ''; $plugin = $request->plugin ?: ''; $exceptionConfig = static::config($plugin, 'exception'); - $defaultException = $exceptionConfig[''] ?? ExceptionHandler::class; + $appExceptionConfig = static::config("", 'exception'); + if (!isset($exceptionConfig['']) && isset($appExceptionConfig['@'])) { + //如果插件没有配置自己的异常处理器并且配置了全局@异常处理器 则使用全局异常处理器 + $defaultException = $appExceptionConfig['@'] ?? ExceptionHandler::class; + } else { + $defaultException = $exceptionConfig[''] ?? ExceptionHandler::class; + } $exceptionHandlerClass = $exceptionConfig[$app] ?? $defaultException; /** @var ExceptionHandlerInterface $exceptionHandler */ - $exceptionHandler = static::container($plugin)->make($exceptionHandlerClass, [ + $exceptionHandler = (static::container($plugin) ?? static::container(''))->make($exceptionHandlerClass, [ 'logger' => static::$logger, 'debug' => static::config($plugin, 'app.debug') ]); @@ -265,7 +279,7 @@ class App $response->exception($e); return $response; } catch (Throwable $e) { - $response = new Response(500, [], static::config($plugin ?? '', 'app.debug') ? (string)$e : $e->getMessage()); + $response = new Response(500, [], static::config($plugin ?? '', 'app.debug', true) ? (string)$e : $e->getMessage()); $response->exception($e); return $response; } @@ -276,7 +290,7 @@ class App * @param string $plugin * @param string $app * @param $call - * @param array|null $args + * @param array $args * @param bool $withGlobalMiddleware * @param RouteObject|null $route * @return callable @@ -284,9 +298,8 @@ class App * @throws NotFoundExceptionInterface * @throws ReflectionException */ - protected static function getCallback(string $plugin, string $app, $call, array $args = null, bool $withGlobalMiddleware = true, RouteObject $route = null) + public static function getCallback(string $plugin, string $app, $call, array $args = [], bool $withGlobalMiddleware = true, RouteObject $route = null) { - $args = $args === null ? null : array_values($args); $middlewares = []; if ($route) { $routeMiddlewares = $route->getMiddleware(); @@ -294,14 +307,16 @@ class App $middlewares[] = [$className, 'process']; } } - $middlewares = array_merge($middlewares, Middleware::getMiddleware($plugin, $app, $withGlobalMiddleware)); + $isController = is_array($call) && is_string($call[0]); + $middlewares = array_merge($middlewares, Middleware::getMiddleware($plugin, $app, $isController ? $call[0] : '', $withGlobalMiddleware)); + $container = static::container($plugin) ?? static::container(''); foreach ($middlewares as $key => $item) { $middleware = $item[0]; if (is_string($middleware)) { - $middleware = static::container($plugin)->get($middleware); + $middleware = $container->get($middleware); } elseif ($middleware instanceof Closure) { - $middleware = call_user_func($middleware, static::container($plugin)); + $middleware = call_user_func($middleware, $container); } if (!$middleware instanceof MiddlewareInterface) { throw new InvalidArgumentException('Not support middleware type'); @@ -310,30 +325,31 @@ class App } $needInject = static::isNeedInject($call, $args); - if (is_array($call) && is_string($call[0])) { + $anonymousArgs = array_values($args); + if ($isController) { $controllerReuse = static::config($plugin, 'app.controller_reuse', true); if (!$controllerReuse) { if ($needInject) { - $call = function ($request, ...$args) use ($call, $plugin) { - $call[0] = static::container($plugin)->make($call[0]); + $call = function ($request) use ($call, $plugin, $args, $container) { + $call[0] = $container->make($call[0]); $reflector = static::getReflector($call); - $args = static::resolveMethodDependencies($plugin, $request, $args, $reflector); + $args = array_values(static::resolveMethodDependencies($container, $request, array_merge($request->all(), $args), $reflector)); return $call(...$args); }; $needInject = false; } else { - $call = function ($request, ...$args) use ($call, $plugin) { - $call[0] = static::container($plugin)->make($call[0]); - return $call($request, ...$args); + $call = function ($request, ...$anonymousArgs) use ($call, $plugin, $container) { + $call[0] = $container->make($call[0]); + return $call($request, ...$anonymousArgs); }; } } else { - $call[0] = static::container($plugin)->get($call[0]); + $call[0] = $container->get($call[0]); } } if ($needInject) { - $call = static::resolveInject($plugin, $call); + $call = static::resolveInject($plugin, $call, $args); } if ($middlewares) { @@ -345,13 +361,9 @@ class App return static::exceptionResponse($e, $request); } }; - }, function ($request) use ($call, $args) { + }, function ($request) use ($call, $anonymousArgs) { try { - if ($args === null) { - $response = $call($request); - } else { - $response = $call($request, ...$args); - } + $response = $call($request, ...$anonymousArgs); } catch (Throwable $e) { return static::exceptionResponse($e, $request); } @@ -364,11 +376,11 @@ class App return $response; }); } else { - if ($args === null) { + if (!$anonymousArgs) { $callback = $call; } else { - $callback = function ($request) use ($call, $args) { - return $call($request, ...$args); + $callback = function ($request) use ($call, $anonymousArgs) { + return $call($request, ...$anonymousArgs); }; } } @@ -379,14 +391,15 @@ class App * ResolveInject. * @param string $plugin * @param array|Closure $call + * @param $args * @return Closure * @see Dependency injection through reflection information */ - protected static function resolveInject(string $plugin, $call): Closure + protected static function resolveInject(string $plugin, $call, $args): Closure { - return function (Request $request, ...$args) use ($plugin, $call) { + return function (Request $request) use ($plugin, $call, $args) { $reflector = static::getReflector($call); - $args = static::resolveMethodDependencies($plugin, $request, $args, $reflector); + $args = array_values(static::resolveMethodDependencies(static::container($plugin), $request, array_merge($request->all(), $args), $reflector)); return $call(...$args); }; } @@ -394,16 +407,15 @@ class App /** * Check whether inject is required. * @param $call - * @param $args + * @param array $args * @return bool * @throws ReflectionException */ - protected static function isNeedInject($call, $args): bool + protected static function isNeedInject($call, array &$args): bool { if (is_array($call) && !method_exists($call[0], $call[1])) { return false; } - $args = $args ?: []; $reflector = static::getReflector($call); $reflectionParameters = $reflector->getParameters(); if (!$reflectionParameters) { @@ -412,20 +424,57 @@ class App $firstParameter = current($reflectionParameters); unset($reflectionParameters[key($reflectionParameters)]); $adaptersList = ['int', 'string', 'bool', 'array', 'object', 'float', 'mixed', 'resource']; + $keys = []; + $needInject = false; foreach ($reflectionParameters as $parameter) { - if ($parameter->hasType() && !in_array($parameter->getType()->getName(), $adaptersList)) { - return true; + $parameterName = $parameter->name; + $keys[] = $parameterName; + if ($parameter->hasType()) { + $typeName = $parameter->getType()->getName(); + if (!in_array($typeName, $adaptersList)) { + $needInject = true; + continue; + } + if (!array_key_exists($parameterName, $args)) { + $needInject = true; + continue; + } + switch ($typeName) { + case 'int': + case 'float': + if (!is_numeric($args[$parameterName])) { + return true; + } + $args[$parameterName] = $typeName === 'int' ? (int)$args[$parameterName]: (float)$args[$parameterName]; + break; + case 'bool': + $args[$parameterName] = (bool)$args[$parameterName]; + break; + case 'array': + case 'object': + if (!is_array($args[$parameterName])) { + return true; + } + $args[$parameterName] = $typeName === 'array' ? $args[$parameterName] : (object)$args[$parameterName]; + break; + case 'string': + case 'mixed': + case 'resource': + break; + } } } - if (!$firstParameter->hasType()) { - return count($args) > count($reflectionParameters); + if (array_keys($args) !== $keys) { + return true; } - - if (!is_a(static::$requestClass, $firstParameter->getType()->getName())) { + if (!$firstParameter->hasType()) { + return $firstParameter->getName() !== 'request'; + } + if (!is_a(static::$requestClass, $firstParameter->getType()->getName(), true)) { return true; } - return false; + return $needInject; } /** @@ -444,56 +493,90 @@ class App /** * Return dependent parameters - * @param string $plugin + * @param ContainerInterface $container * @param Request $request - * @param array $args + * @param array $inputs * @param ReflectionFunctionAbstract $reflector * @return array + * @throws BusinessException + * @throws ReflectionException */ - protected static function resolveMethodDependencies(string $plugin, Request $request, array $args, ReflectionFunctionAbstract $reflector): array + protected static function resolveMethodDependencies(ContainerInterface $container, Request $request, array $inputs, ReflectionFunctionAbstract $reflector): array { - // Specification parameter information - $args = array_values($args); $parameters = []; - // An array of reflection classes for loop parameters, with each $parameter representing a reflection object of parameters foreach ($reflector->getParameters() as $parameter) { - // Parameter quota consumption - if ($parameter->hasType()) { - $name = $parameter->getType()->getName(); - switch ($name) { - case 'int': - case 'string': - case 'bool': - case 'array': - case 'object': - case 'float': - case 'mixed': - case 'resource': - goto _else; - default: - if (is_a($request, $name)) { - //Inject Request - $parameters[] = $request; - } else { - $parameters[] = static::container($plugin)->make($name); - } - break; - } - } else { - _else: - // The variable parameter - if (null !== key($args)) { - $parameters[] = current($args); + $parameterName = $parameter->name; + $type = $parameter->getType(); + $typeName = $type ? $type->getName() : null; + + if ($typeName && is_a($request, $typeName)) { + $parameters[$parameterName] = $request; + continue; + } + + if (!array_key_exists($parameterName, $inputs)) { + if (!$parameter->isDefaultValueAvailable()) { + if (!$typeName || !class_exists($typeName)) { + throw (new MissingInputException())->setData([ + 'parameter' => $parameterName, + ]); + } } else { - // Indicates whether the current parameter has a default value. If yes, return true - $parameters[] = $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null; + $parameters[$parameterName] = $parameter->getDefaultValue(); + continue; } - // Quota of consumption variables - next($args); + } + + switch ($typeName) { + case 'int': + case 'float': + if (!is_numeric($inputs[$parameterName])) { + throw (new InputTypeException())->setData([ + 'parameter' => $parameterName, + 'exceptType' => $typeName, + 'actualType' => gettype($inputs[$parameterName]), + ]); + } + $parameters[$parameterName] = $typeName === 'float' ? (float)$inputs[$parameterName] : (int)$inputs[$parameterName]; + break; + case 'bool': + $parameters[$parameterName] = (bool)$inputs[$parameterName]; + break; + case 'array': + case 'object': + if (!is_array($inputs[$parameterName])) { + throw (new InputTypeException())->setData([ + 'parameter' => $parameterName, + 'exceptType' => $typeName, + 'actualType' => gettype($inputs[$parameterName]), + ]); + } + $parameters[$parameterName] = $typeName === 'object' ? (object)$inputs[$parameterName] : $inputs[$parameterName]; + break; + case 'string': + case 'mixed': + case 'resource': + case null: + $parameters[$parameterName] = $inputs[$parameterName]; + break; + default: + $subInputs = isset($inputs[$parameterName]) && is_array($inputs[$parameterName]) ? $inputs[$parameterName] : []; + if (is_a($typeName, Model::class, true) || is_a($typeName, ThinkModel::class, true)) { + $parameters[$parameterName] = $container->make($typeName, [ + 'attributes' => $subInputs, + 'data' => $subInputs + ]); + break; + } + if (is_array($subInputs) && $constructor = (new ReflectionClass($typeName))->getConstructor()) { + $parameters[$parameterName] = $container->make($typeName, static::resolveMethodDependencies($container, $request, $subInputs, $constructor)); + } else { + $parameters[$parameterName] = $container->make($typeName); + } + break; } } - // Returns the result of parameters replacement return $parameters; } @@ -530,21 +613,23 @@ class App * @param TcpConnection $connection * @param string $path * @param string $key - * @param Request|mixed $request + * @param $request + * @param $status * @return bool * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface - * @throws ReflectionException + * @throws ReflectionException|Throwable */ - protected static function findRoute(TcpConnection $connection, string $path, string $key, $request): bool + protected static function findRoute(TcpConnection $connection, string $path, string $key, $request, &$status): bool { $routeInfo = Route::dispatch($request->method(), $path); if ($routeInfo[0] === Dispatcher::FOUND) { + $status = 200; $routeInfo[0] = 'route'; $callback = $routeInfo[1]['callback']; $route = clone $routeInfo[1]['route']; $app = $controller = $action = ''; - $args = !empty($routeInfo[2]) ? $routeInfo[2] : null; + $args = !empty($routeInfo[2]) ? $routeInfo[2] : []; if ($args) { $route->setParams($args); } @@ -562,6 +647,7 @@ class App static::send($connection, $callback($request), $request); return true; } + $status = $routeInfo[0] === Dispatcher::METHOD_NOT_ALLOWED ? 405 : 404; return false; } @@ -570,7 +656,7 @@ class App * @param TcpConnection $connection * @param string $path * @param string $key - * @param Request|mixed $request + * @param $request * @return bool * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface @@ -622,7 +708,7 @@ class App return $callback($request); } return (new Response())->file($file); - }, null, false), '', '', '', '', null]); + }, [], false), '', '', '', '', null]); [$callback, $request->plugin, $request->app, $request->controller, $request->action, $request->route] = static::$callbacks[$key]; static::send($connection, $callback($request), $request); return true; @@ -631,7 +717,7 @@ class App /** * Send. * @param TcpConnection|mixed $connection - * @param mixed $response + * @param mixed|Response $response * @param Request|mixed $request * @return void */ @@ -641,6 +727,7 @@ class App Context::destroy(); if (($keepAlive === null && $request->protocolVersion() === '1.1') || $keepAlive === 'keep-alive' || $keepAlive === 'Keep-Alive' + || (is_a($response, Response::class) && $response->getHeader('Transfer-Encoding') === 'chunked') ) { $connection->send($response); return; @@ -708,7 +795,6 @@ class App foreach ($map as $item) { $map[] = $item . '\\index'; } - foreach ($map as $controllerClass) { // Remove xx\xx\controller if (substr($controllerClass, -11) === '\\controller') { @@ -770,6 +856,7 @@ class App $found = false; foreach ($dirs as $name) { $path = "$basePath/$name"; + if (is_dir($path) && strtolower($name) === $pathSection) { $basePath = $path; $found = true; @@ -849,7 +936,11 @@ class App if ($tmp[0] !== 'app') { return ''; } - return $tmp[1] ?? ''; + $plugin = $tmp[1] ?? ''; + if ($plugin && !static::config('', "plugin.$plugin.app")) { + return ''; + } + return $plugin; } /** diff --git a/vendor/workerman/webman-framework/src/Context.php b/vendor/workerman/webman-framework/src/Context.php index b72abf7f0..dd8e0c7ef 100644 --- a/vendor/workerman/webman-framework/src/Context.php +++ b/vendor/workerman/webman-framework/src/Context.php @@ -43,15 +43,23 @@ class Context */ protected static $object; + /** - * @return StdClass + * @return void */ - protected static function getObject(): StdClass + public static function init() { if (!static::$objectStorage) { static::$objectStorage = class_exists(WeakMap::class) ? new WeakMap() : new SplObjectStorage(); static::$object = new StdClass; } + } + + /** + * @return StdClass + */ + protected static function getObject(): StdClass + { $key = static::getKey(); if (!isset(static::$objectStorage[$key])) { static::$objectStorage[$key] = new StdClass; diff --git a/vendor/workerman/webman-framework/src/Exception/BusinessException.php b/vendor/workerman/webman-framework/src/Exception/BusinessException.php new file mode 100644 index 000000000..e396111aa --- /dev/null +++ b/vendor/workerman/webman-framework/src/Exception/BusinessException.php @@ -0,0 +1,95 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace Webman\Exception; + +use RuntimeException; +use Throwable; +use Webman\Http\Request; +use Webman\Http\Response; +use function json_encode; + +/** + * Class BusinessException + * @package support\exception + */ +class BusinessException extends RuntimeException +{ + + /** + * @var array + */ + protected $data = []; + + /** + * Render an exception into an HTTP response. + * @param Request $request + * @return Response|null + */ + public function render(Request $request): ?Response + { + if ($request->expectsJson()) { + $code = $this->getCode(); + $json = ['code' => $code ?: 500, 'msg' => $this->getMessage(), 'data' => $this->data]; + return new Response(200, ['Content-Type' => 'application/json'], + json_encode($json, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + } + return new Response(200, [], $this->getMessage()); + } + + /** + * Set data. + * @param array $data + * @return $this + */ + public function setData(array $data): BusinessException + { + $this->data = $data; + return $this; + } + + /** + * Get data. + * @return array + */ + public function getData(): array + { + return $this->data; + } + + /** + * Translate message. + * @param string $message + * @param array $parameters + * @param string|null $domain + * @param string|null $locale + * @return string + */ + protected function trans(string $message, array $parameters = [], string $domain = null, string $locale = null): string + { + $args = []; + foreach ($parameters as $key => $parameter) { + $args[":$key"] = $parameter; + } + try { + $message = trans($message, $args, $domain, $locale); + } catch (Throwable $e) { + } + foreach ($parameters as $key => $value) { + $message = str_replace(":$key", $value, $message); + } + return $message; + } + +} diff --git a/vendor/workerman/webman-framework/src/Exception/ExceptionHandler.php b/vendor/workerman/webman-framework/src/Exception/ExceptionHandler.php index b8f53e48e..ea8de8755 100644 --- a/vendor/workerman/webman-framework/src/Exception/ExceptionHandler.php +++ b/vendor/workerman/webman-framework/src/Exception/ExceptionHandler.php @@ -77,6 +77,9 @@ class ExceptionHandler implements ExceptionHandlerInterface */ public function render(Request $request, Throwable $exception): Response { + if (method_exists($exception, 'render') && ($response = $exception->render($request))) { + return $response; + } $code = $exception->getCode(); if ($request->expectsJson()) { $json = ['code' => $code ?: 500, 'msg' => $this->debug ? $exception->getMessage() : 'Server internal error']; diff --git a/vendor/workerman/webman-framework/src/Http/Request.php b/vendor/workerman/webman-framework/src/Http/Request.php index 42d0a623e..343fa3efb 100644 --- a/vendor/workerman/webman-framework/src/Http/Request.php +++ b/vendor/workerman/webman-framework/src/Http/Request.php @@ -56,12 +56,17 @@ class Request extends \Workerman\Protocols\Http\Request */ public $route = null; + /** + * @var bool + */ + protected $isDirty = false; + /** * @return mixed|null */ public function all() { - return $this->post() + $this->get(); + return $this->get() + $this->post(); } /** @@ -72,12 +77,7 @@ class Request extends \Workerman\Protocols\Http\Request */ public function input(string $name, $default = null) { - $post = $this->post(); - if (isset($post[$name])) { - return $post[$name]; - } - $get = $this->get(); - return $get[$name] ?? $default; + return $this->get($name, $this->post($name, $default)); } /** @@ -216,9 +216,12 @@ class Request extends \Workerman\Protocols\Http\Request if ($safeMode && !static::isIntranetIp($remoteIp)) { return $remoteIp; } - $ip = $this->header('x-real-ip', $this->header('x-forwarded-for', - $this->header('client-ip', $this->header('x-client-ip', - $this->header('via', $remoteIp))))); + $ip = $this->header('x-forwarded-for') + ?? $this->header('x-real-ip') + ?? $this->header('client-ip') + ?? $this->header('x-client-ip') + ?? $this->header('via') + ?? $remoteIp; if (is_string($ip)) { $ip = current(explode(',', $ip)); } @@ -318,4 +321,61 @@ class Request extends \Workerman\Protocols\Http\Request return false; } + /** + * Set get. + * @param array $get + * @return Request + */ + public function setGet(array $get): Request + { + $this->isDirty = true; + if (isset($this->data)) { + $this->data['get'] = $get; + } else { + $this->_data['get'] = $get; + } + return $this; + } + + /** + * Set post. + * @param array $post + * @return Request + */ + public function setPost(array $post): Request + { + $this->isDirty = true; + if (isset($this->data)) { + $this->data['post'] = $post; + } else { + $this->_data['post'] = $post; + } + return $this; + } + + /** + * Set headers. + * @param array $headers + * @return $this + */ + public function setHeaders(array $headers): Request + { + $this->isDirty = true; + if (isset($this->data)) { + $this->data['headers'] = $headers; + } else { + $this->_data['headers'] = $headers; + } + return $this; + } + + /** + * @return void + */ + public function __clone() + { + if ($this->isDirty) { + unset($this->data['get'], $this->data['post'], $this->data['headers']); + } + } } diff --git a/vendor/workerman/webman-framework/src/Middleware.php b/vendor/workerman/webman-framework/src/Middleware.php index 57bf133a4..f6d9e10b8 100644 --- a/vendor/workerman/webman-framework/src/Middleware.php +++ b/vendor/workerman/webman-framework/src/Middleware.php @@ -15,6 +15,7 @@ namespace Webman; +use ReflectionClass; use RuntimeException; use function array_merge; use function array_reverse; @@ -65,18 +66,32 @@ class Middleware /** * @param string $plugin * @param string $appName + * @param string $controller * @param bool $withGlobalMiddleware - * @return array|mixed + * @return array */ - public static function getMiddleware(string $plugin, string $appName, bool $withGlobalMiddleware = true) + public static function getMiddleware(string $plugin, string $appName, string $controller, bool $withGlobalMiddleware = true) { - $globalMiddleware = static::$instances['']['@'] ?? []; + $globalMiddleware = $withGlobalMiddleware ? static::$instances['']['@'] ?? [] : []; $appGlobalMiddleware = $withGlobalMiddleware && isset(static::$instances[$plugin]['']) ? static::$instances[$plugin][''] : []; + $controllerMiddleware = []; + if ($controller && class_exists($controller)) { + $reflectionClass = new ReflectionClass($controller); + if ($reflectionClass->hasProperty('middleware')) { + $defaultProperties = $reflectionClass->getDefaultProperties(); + $controllerMiddlewareClasses = $defaultProperties['middleware']; + foreach ((array)$controllerMiddlewareClasses as $className) { + if (method_exists($className, 'process')) { + $controllerMiddleware[] = [$className, 'process']; + } + } + } + } if ($appName === '') { - return array_reverse(array_merge($globalMiddleware, $appGlobalMiddleware)); + return array_reverse(array_merge($globalMiddleware, $appGlobalMiddleware, $controllerMiddleware)); } $appMiddleware = static::$instances[$plugin][$appName] ?? []; - return array_reverse(array_merge($globalMiddleware, $appGlobalMiddleware, $appMiddleware)); + return array_reverse(array_merge($globalMiddleware, $appGlobalMiddleware, $appMiddleware, $controllerMiddleware)); } /** diff --git a/vendor/workerman/webman-framework/src/Route.php b/vendor/workerman/webman-framework/src/Route.php index cf42506aa..c959ac1ef 100644 --- a/vendor/workerman/webman-framework/src/Route.php +++ b/vendor/workerman/webman-framework/src/Route.php @@ -17,8 +17,11 @@ namespace Webman; use FastRoute\Dispatcher\GroupCountBased; use FastRoute\RouteCollector; use FilesystemIterator; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; +use ReflectionException; use Webman\Route\Route as RouteObject; use function array_diff; use function array_values; @@ -57,7 +60,12 @@ class Route protected static $collector = null; /** - * @var null|callable + * @var RouteObject[] + */ + protected static $fallbackRoutes = []; + + /** + * @var array */ protected static $fallback = []; @@ -74,7 +82,17 @@ class Route /** * @var bool */ - protected static $disableDefaultRoute = []; + protected static $disabledDefaultRoutes = []; + + /** + * @var array + */ + protected static $disabledDefaultRouteControllers = []; + + /** + * @var array + */ + protected static $disabledDefaultRouteActions = []; /** * @var RouteObject[] @@ -257,20 +275,66 @@ class Route /** * disableDefaultRoute. * - * @return void + * @param string|array $plugin + * @param string|null $app + * @return bool */ - public static function disableDefaultRoute($plugin = '') + public static function disableDefaultRoute($plugin = '', string $app = null): bool { - static::$disableDefaultRoute[$plugin] = true; + // Is [controller action] + if (is_array($plugin)) { + $controllerAction = $plugin; + if (!isset($controllerAction[0]) || !is_string($controllerAction[0]) || + !isset($controllerAction[1]) || !is_string($controllerAction[1])) { + return false; + } + $controller = $controllerAction[0]; + $action = $controllerAction[1]; + static::$disabledDefaultRouteActions[$controller][$action] = $action; + return true; + } + // Is plugin + if (is_string($plugin) && (preg_match('/^[a-zA-Z0-9_]+$/', $plugin) || $plugin === '')) { + if (!isset(static::$disabledDefaultRoutes[$plugin])) { + static::$disabledDefaultRoutes[$plugin] = []; + } + $app = $app ?? '*'; + static::$disabledDefaultRoutes[$plugin][$app] = $app; + return true; + } + // Is controller + if (is_string($plugin) && class_exists($plugin)) { + static::$disabledDefaultRouteControllers[$plugin] = $plugin; + return true; + } + return false; } /** - * @param string $plugin + * @param string|array $plugin + * @param string|null $app * @return bool */ - public static function hasDisableDefaultRoute(string $plugin = ''): bool + public static function isDefaultRouteDisabled($plugin = '', string $app = null): bool { - return static::$disableDefaultRoute[$plugin] ?? false; + // Is [controller action] + if (is_array($plugin)) { + if (!isset($plugin[0]) || !is_string($plugin[0]) || + !isset($plugin[1]) || !is_string($plugin[1])) { + return false; + } + return isset(static::$disabledDefaultRouteActions[$plugin[0]][$plugin[1]]); + } + // Is plugin + if (is_string($plugin) && (preg_match('/^[a-zA-Z0-9_]+$/', $plugin) || $plugin === '')) { + $app = $app ?? '*'; + return isset(static::$disabledDefaultRoutes[$plugin]['*']) || isset(static::$disabledDefaultRoutes[$plugin][$app]); + } + // Is controller + if (is_string($plugin) && class_exists($plugin)) { + return isset(static::$disabledDefaultRouteControllers[$plugin]); + } + return false; } /** @@ -447,17 +511,28 @@ class Route */ public static function fallback(callable $callback, string $plugin = '') { - static::$fallback[$plugin] = $callback; + $route = new RouteObject([], '', $callback); + static::$fallbackRoutes[$plugin] = $route; + return $route; } /** * GetFallBack. * @param string $plugin + * @param int $status * @return callable|null + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws ReflectionException */ - public static function getFallback(string $plugin = ''): ?callable + public static function getFallback(string $plugin = '', int $status = 404) { - return static::$fallback[$plugin] ?? null; + if (!isset(static::$fallback[$plugin])) { + $callback = null; + $route = static::$fallbackRoutes[$plugin] ?? null; + static::$fallback[$plugin] = $route ? App::getCallback($plugin, 'NOT_FOUND', $route->getCallback(), ['status' => $status], false, $route) : null; + } + return static::$fallback[$plugin]; } /** diff --git a/vendor/workerman/webman-framework/src/support/App.php b/vendor/workerman/webman-framework/src/support/App.php index 53619ddf1..344869ff0 100644 --- a/vendor/workerman/webman-framework/src/support/App.php +++ b/vendor/workerman/webman-framework/src/support/App.php @@ -4,6 +4,7 @@ namespace support; use Dotenv\Dotenv; use RuntimeException; +use Throwable; use Webman\Config; use Webman\Util; use Workerman\Connection\TcpConnection; @@ -20,6 +21,7 @@ class App /** * Run. * @return void + * @throws Throwable */ public static function run() { @@ -34,15 +36,19 @@ class App } } - static::loadAllConfig(['route', 'container']); + if (!$appConfigFile = config_path('app.php')) { + throw new RuntimeException('Config file not found: app.php'); + } + $appConfig = require $appConfigFile; + if ($timezone = $appConfig['default_timezone'] ?? '') { + date_default_timezone_set($timezone); + } + static::loadAllConfig(['route', 'container']); $errorReporting = config('app.error_reporting'); if (isset($errorReporting)) { error_reporting($errorReporting); } - if ($timezone = config('app.default_timezone')) { - date_default_timezone_set($timezone); - } $runtimeLogsPath = runtime_path() . DIRECTORY_SEPARATOR . 'logs'; if (!file_exists($runtimeLogsPath) || !is_dir($runtimeLogsPath)) { @@ -83,7 +89,7 @@ class App Worker::$stopTimeout = $config['stop_timeout'] ?? 2; } - if ($config['listen']) { + if ($config['listen'] ?? false) { $worker = new Worker($config['listen'], $config['context']); $propertyMap = [ 'name', diff --git a/vendor/workerman/webman-framework/src/support/Cache.php b/vendor/workerman/webman-framework/src/support/Cache.php index 8cf5a4490..332c23f57 100644 --- a/vendor/workerman/webman-framework/src/support/Cache.php +++ b/vendor/workerman/webman-framework/src/support/Cache.php @@ -3,7 +3,11 @@ namespace support; use Symfony\Component\Cache\Adapter\RedisAdapter; +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\Adapter\PdoAdapter; use Symfony\Component\Cache\Psr16Cache; +use InvalidArgumentException; /** * Class Cache @@ -22,20 +26,52 @@ use Symfony\Component\Cache\Psr16Cache; class Cache { /** - * @var Psr16Cache + * @var Psr16Cache[] */ - public static $instance = null; + public static $instances = []; - /** + /*** + * @param string|null $name * @return Psr16Cache */ - public static function instance() + public static function store(?string $name = null): Psr16Cache { - if (!static::$instance) { - $adapter = new RedisAdapter(Redis::connection()->client()); - self::$instance = new Psr16Cache($adapter); + $name = $name ?: config('cache.default', 'redis'); + $stores = !config('cache') ? [ + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default' + ], + ] : config('cache.stores', []); + if (!isset($stores[$name])) { + throw new InvalidArgumentException("cache.store.$name is not defined. Please check config/cache.php"); } - return static::$instance; + if (!isset(static::$instances[$name])) { + $driver = $stores[$name]['driver']; + switch ($driver) { + case 'redis': + $client = Redis::connection($stores[$name]['connection'])->client(); + $adapter = new RedisAdapter($client); + break; + case 'file': + $adapter = new FilesystemAdapter('', 0, $stores[$name]['path']); + break; + case 'array': + $adapter = new ArrayAdapter(0, $stores[$name]['serialize'] ?? false, 0, 0); + break; + /** + * Pdo can not reconnect when the connection is lost. So we can not use pdo as cache. + */ + /*case 'database': + $adapter = new PdoAdapter(Db::connection($stores[$name]['connection'])->getPdo()); + break;*/ + default: + throw new InvalidArgumentException("cache.store.$name.driver=$driver is not supported."); + } + static::$instances[$name] = new Psr16Cache($adapter); + } + + return static::$instances[$name]; } /** @@ -45,6 +81,6 @@ class Cache */ public static function __callStatic($name, $arguments) { - return static::instance()->{$name}(... $arguments); + return static::store()->{$name}(... $arguments); } } diff --git a/vendor/workerman/webman-framework/src/support/Redis.php b/vendor/workerman/webman-framework/src/support/Redis.php index de566c53c..912a01134 100644 --- a/vendor/workerman/webman-framework/src/support/Redis.php +++ b/vendor/workerman/webman-framework/src/support/Redis.php @@ -233,6 +233,27 @@ class Redis self::PREDIS_CLIENT ]; + /** + * The Redis server configurations. + * + * @var array + */ + protected static $config = []; + + /** + * Static timers facilitate deletion during callbacks. + * + * @var array + */ + protected static $timers = []; + + /** + * The number of seconds an idle connection will be terminated. + * + * @var int + */ + protected static $idle_time = 0; + /** * @return RedisManager */ @@ -246,6 +267,7 @@ class Redis $client = self::PHPREDIS_CLIENT; } + static::$config = $config; static::$instance = new RedisManager('', $client, $config); } return static::$instance; @@ -254,16 +276,27 @@ class Redis /** * Connection. * @param string $name - * @return Connection + * @return Connection|\Redis */ public static function connection(string $name = 'default'): Connection { - static $timers = []; + if (!empty(static::$config[$name]['idle_timeout'])) { + static::$idle_time = time(); + } + $connection = static::instance()->connection($name); - if (!isset($timers[$name])) { - $timers[$name] = Worker::getAllWorkers() ? Timer::add(55, function () use ($connection) { + if (!isset(static::$timers[$name])) { + static::$timers[$name] = Worker::getAllWorkers() ? Timer::add(55, function () use ($connection, $name) { + if (!empty(static::$config[$name]['idle_timeout']) + && time() - static::$idle_time > static::$config[$name]['idle_timeout']) { + Timer::del(static::$timers[$name]); + unset(static::$timers[$name]); + return $connection->client()->close(); + } + $connection->get('ping'); }) : 1; + if (class_exists(Dispatcher::class)) { $connection->setEventDispatcher(new Dispatcher()); } diff --git a/vendor/workerman/webman-framework/src/support/bootstrap/LaravelDb.php b/vendor/workerman/webman-framework/src/support/bootstrap/LaravelDb.php index 2ec3ed63d..201541235 100644 --- a/vendor/workerman/webman-framework/src/support/bootstrap/LaravelDb.php +++ b/vendor/workerman/webman-framework/src/support/bootstrap/LaravelDb.php @@ -62,6 +62,7 @@ class LaravelDb implements Bootstrap }); $default = $config['default'] ?? false; + $persistent = $config['persistent'] ?? true; if ($default) { $defaultConfig = $connections[$config['default']] ?? false; if ($defaultConfig) { @@ -82,7 +83,7 @@ class LaravelDb implements Bootstrap $capsule->bootEloquent(); // Heartbeat - if ($worker) { + if ($worker && $persistent) { Timer::add(55, function () use ($default, $connections, $capsule) { foreach ($capsule->getDatabaseManager()->getConnections() as $connection) { /* @var MySqlConnection $connection **/ diff --git a/vendor/workerman/webman-framework/src/support/exception/BusinessException.php b/vendor/workerman/webman-framework/src/support/exception/BusinessException.php index aee787718..b72d488fb 100644 --- a/vendor/workerman/webman-framework/src/support/exception/BusinessException.php +++ b/vendor/workerman/webman-framework/src/support/exception/BusinessException.php @@ -14,25 +14,11 @@ namespace support\exception; -use Exception; -use Webman\Http\Request; -use Webman\Http\Response; -use function json_encode; - /** * Class BusinessException * @package support\exception */ -class BusinessException extends Exception +class BusinessException extends \Webman\Exception\BusinessException { - public function render(Request $request): ?Response - { - if ($request->expectsJson()) { - $code = $this->getCode(); - $json = ['code' => $code ?: 500, 'msg' => $this->getMessage()]; - return new Response(200, ['Content-Type' => 'application/json'], - json_encode($json, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); - } - return new Response(200, [], $this->getMessage()); - } + } diff --git a/vendor/workerman/webman-framework/src/support/exception/Handler.php b/vendor/workerman/webman-framework/src/support/exception/Handler.php index 98dc61800..94d08f0f9 100644 --- a/vendor/workerman/webman-framework/src/support/exception/Handler.php +++ b/vendor/workerman/webman-framework/src/support/exception/Handler.php @@ -36,11 +36,6 @@ class Handler extends ExceptionHandler public function render(Request $request, Throwable $exception): Response { - if(($exception instanceof BusinessException) && ($response = $exception->render($request))) - { - return $response; - } - return parent::render($request, $exception); } diff --git a/vendor/workerman/webman-framework/src/support/exception/InputTypeException.php b/vendor/workerman/webman-framework/src/support/exception/InputTypeException.php new file mode 100644 index 000000000..674958793 --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/exception/InputTypeException.php @@ -0,0 +1,24 @@ +getCode() ?: 404; + $debug = config($request->plugin ? "plugin.$request->plugin.app.debug" : 'app.debug'); + $data = $debug ? $this->data : ['parameter' => '']; + $message = $this->trans($this->getMessage(), $data); + if ($request->expectsJson()) { + $json = ['code' => $code, 'msg' => $message, 'data' => $data]; + return new Response(200, ['Content-Type' => 'application/json'], + json_encode($json, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + } + return new Response($code, [], $this->html($message)); + } + +} \ No newline at end of file diff --git a/vendor/workerman/webman-framework/src/support/exception/NotFoundException.php b/vendor/workerman/webman-framework/src/support/exception/NotFoundException.php new file mode 100644 index 000000000..016741d9f --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/exception/NotFoundException.php @@ -0,0 +1,20 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support\exception; + +class NotFoundException extends BusinessException +{ + +} diff --git a/vendor/workerman/webman-framework/src/support/exception/PageNotFoundException.php b/vendor/workerman/webman-framework/src/support/exception/PageNotFoundException.php new file mode 100644 index 000000000..59833123b --- /dev/null +++ b/vendor/workerman/webman-framework/src/support/exception/PageNotFoundException.php @@ -0,0 +1,93 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support\exception; + +use Throwable; +use Webman\Http\Request; +use Webman\Http\Response; + +class PageNotFoundException extends NotFoundException +{ + + /** + * @var string + */ + protected $template = '/app/view/404'; + + /** + * PageNotFoundException constructor. + * @param string $message + * @param int $code + * @param Throwable|null $previous + */ + public function __construct(string $message = '404 Not Found', int $code = 404, Throwable $previous = null) { + parent::__construct($message, $code, $previous); + } + + /** + * Render an exception into an HTTP response. + * @param Request $request + * @return Response|null + * @throws Throwable + */ + public function render(Request $request): ?Response + { + $code = $this->getCode() ?: 404; + $data = $this->data; + $message = $this->trans($this->getMessage(), $data); + if ($request->expectsJson()) { + $json = ['code' => $code, 'msg' => $message, 'data' => $data]; + return new Response(200, ['Content-Type' => 'application/json'], + json_encode($json, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + } + return new Response($code, [], $this->html($message)); + } + + /** + * Get the HTML representation of the exception. + * @param string $message + * @return string + * @throws Throwable + */ + protected function html(string $message): string + { + $message = htmlspecialchars($message); + if (is_file(base_path("$this->template.html"))) { + return raw_view($this->template, ['message' => $message])->rawBody(); + } + return << + + + + + $message + + + +

$message

+
+
webman
+ + +EOF; + + } + +} diff --git a/vendor/workerman/webman-framework/src/support/view/Blade.php b/vendor/workerman/webman-framework/src/support/view/Blade.php index f523c1976..07adc95bd 100644 --- a/vendor/workerman/webman-framework/src/support/view/Blade.php +++ b/vendor/workerman/webman-framework/src/support/view/Blade.php @@ -58,18 +58,22 @@ class Blade implements View $app = $app === null ? ($request->app ?? '') : $app; $configPrefix = $plugin ? "plugin.$plugin." : ''; $baseViewPath = $plugin ? base_path() . "/plugin/$plugin/app" : app_path(); - $key = "$plugin-$app"; - if (!isset($views[$key])) { + if ($template[0] === '/') { + $viewPath = base_path(); + $template = substr($template, 1); + } else { $viewPath = $app === '' ? "$baseViewPath/view" : "$baseViewPath/$app/view"; - $views[$key] = new BladeView($viewPath, runtime_path() . '/views'); + } + if (!isset($views[$viewPath])) { + $views[$viewPath] = new BladeView($viewPath, runtime_path() . '/views'); $extension = config("{$configPrefix}view.extension"); if ($extension) { - $extension($views[$key]); + $extension($views[$viewPath]); } } if(isset($request->_view_vars)) { $vars = array_merge((array)$request->_view_vars, $vars); } - return $views[$key]->render($template, $vars); + return $views[$viewPath]->render($template, $vars); } } diff --git a/vendor/workerman/webman-framework/src/support/view/Raw.php b/vendor/workerman/webman-framework/src/support/view/Raw.php index 2974e51d9..18027224d 100644 --- a/vendor/workerman/webman-framework/src/support/view/Raw.php +++ b/vendor/workerman/webman-framework/src/support/view/Raw.php @@ -60,8 +60,7 @@ class Raw implements View $viewSuffix = config("{$configPrefix}view.options.view_suffix", 'html'); $app = $app === null ? ($request->app ?? '') : $app; $baseViewPath = $plugin ? base_path() . "/plugin/$plugin/app" : app_path(); - $__template_path__ = $app === '' ? "$baseViewPath/view/$template.$viewSuffix" : "$baseViewPath/$app/view/$template.$viewSuffix"; - + $__template_path__ = $template[0] === '/' ? base_path() . "$template.$viewSuffix" : ($app === '' ? "$baseViewPath/view/$template.$viewSuffix" : "$baseViewPath/$app/view/$template.$viewSuffix"); if(isset($request->_view_vars)) { extract((array)$request->_view_vars); } diff --git a/vendor/workerman/webman-framework/src/support/view/ThinkPHP.php b/vendor/workerman/webman-framework/src/support/view/ThinkPHP.php index 9cc862835..e763e6c2f 100644 --- a/vendor/workerman/webman-framework/src/support/view/ThinkPHP.php +++ b/vendor/workerman/webman-framework/src/support/view/ThinkPHP.php @@ -59,7 +59,12 @@ class ThinkPHP implements View $configPrefix = $plugin ? "plugin.$plugin." : ''; $viewSuffix = config("{$configPrefix}view.options.view_suffix", 'html'); $baseViewPath = $plugin ? base_path() . "/plugin/$plugin/app" : app_path(); - $viewPath = $app === '' ? "$baseViewPath/view/" : "$baseViewPath/$app/view/"; + if ($template[0] === '/') { + $viewPath = base_path() . dirname($template) . '/'; + $template = basename($template); + } else { + $viewPath = $app === '' ? "$baseViewPath/view/" : "$baseViewPath/$app/view/"; + } $defaultOptions = [ 'view_path' => $viewPath, 'cache_path' => runtime_path() . '/views/', diff --git a/vendor/workerman/webman-framework/src/support/view/Twig.php b/vendor/workerman/webman-framework/src/support/view/Twig.php index 5ff65f45f..91e479f89 100644 --- a/vendor/workerman/webman-framework/src/support/view/Twig.php +++ b/vendor/workerman/webman-framework/src/support/view/Twig.php @@ -60,19 +60,23 @@ class Twig implements View $app = $app === null ? ($request->app ?? '') : $app; $configPrefix = $plugin ? "plugin.$plugin." : ''; $viewSuffix = config("{$configPrefix}view.options.view_suffix", 'html'); - $key = "$plugin-$app"; - if (!isset($views[$key])) { - $baseViewPath = $plugin ? base_path() . "/plugin/$plugin/app" : app_path(); + $baseViewPath = $plugin ? base_path() . "/plugin/$plugin/app" : app_path(); + if ($template[0] === '/') { + $viewPath = base_path(); + $template = substr($template, 1); + } else { $viewPath = $app === '' ? "$baseViewPath/view/" : "$baseViewPath/$app/view/"; - $views[$key] = new Environment(new FilesystemLoader($viewPath), config("{$configPrefix}view.options", [])); + } + if (!isset($views[$viewPath])) { + $views[$viewPath] = new Environment(new FilesystemLoader($viewPath), config("{$configPrefix}view.options", [])); $extension = config("{$configPrefix}view.extension"); if ($extension) { - $extension($views[$key]); + $extension($views[$viewPath]); } } if(isset($request->_view_vars)) { $vars = array_merge((array)$request->_view_vars, $vars); } - return $views[$key]->render("$template.$viewSuffix", $vars); + return $views[$viewPath]->render("$template.$viewSuffix", $vars); } } diff --git a/vendor/workerman/workerman/Connection/TcpConnection.php b/vendor/workerman/workerman/Connection/TcpConnection.php index 740f01d61..370018d09 100644 --- a/vendor/workerman/workerman/Connection/TcpConnection.php +++ b/vendor/workerman/workerman/Connection/TcpConnection.php @@ -777,13 +777,14 @@ class TcpConnection extends ConnectionInterface * This method pulls all the data out of a readable stream, and writes it to the supplied destination. * * @param self $dest + * @param bool $raw * @return void */ - public function pipe(self $dest) + public function pipe(self $dest, $raw = false) { $source = $this; - $this->onMessage = function ($source, $data) use ($dest) { - $dest->send($data); + $this->onMessage = function ($source, $data) use ($dest, $raw) { + $dest->send($data, $raw); }; $this->onClose = function ($source) use ($dest) { $dest->close(); diff --git a/vendor/workerman/workerman/Events/Swoole.php b/vendor/workerman/workerman/Events/Swoole.php index fcd747238..8f0264c34 100644 --- a/vendor/workerman/workerman/Events/Swoole.php +++ b/vendor/workerman/workerman/Events/Swoole.php @@ -16,6 +16,7 @@ namespace Workerman\Events; use Workerman\Worker; use Swoole\Event; use Swoole\Timer; +use Swoole\Coroutine; class Swoole implements EventInterface { @@ -33,6 +34,10 @@ class Swoole implements EventInterface protected $_hasSignal = false; + protected $_readEvents = array(); + + protected $_writeEvents = array(); + /** * * {@inheritdoc} @@ -61,7 +66,7 @@ class Swoole implements EventInterface $mapId = $this->mapId++; $t = (int)($fd * 1000); if ($t < 1) { - $t = 1; + $t = 1; } $timer_id = Timer::$method($t, function ($timer_id = null) use ($func, $args, $mapId) { @@ -92,9 +97,14 @@ class Swoole implements EventInterface case self::EV_READ: case self::EV_WRITE: $fd_key = (int) $fd; - if (! isset($this->_fd[$fd_key])) { + if ($flag === self::EV_READ) { + $this->_readEvents[$fd_key] = $func; + } else { + $this->_writeEvents[$fd_key] = $func; + } + if (!isset($this->_fd[$fd_key])) { if ($flag === self::EV_READ) { - $res = Event::add($fd, $func, null, SWOOLE_EVENT_READ); + $res = Event::add($fd, [$this, 'callRead'], null, SWOOLE_EVENT_READ); $fd_type = SWOOLE_EVENT_READ; } else { $res = Event::add($fd, null, $func, SWOOLE_EVENT_WRITE); @@ -124,6 +134,42 @@ class Swoole implements EventInterface } } + /** + * @param $fd + * @return void + */ + protected function callRead($stream) + { + $fd = (int) $stream; + if (isset($this->_readEvents[$fd])) { + try { + \call_user_func($this->_readEvents[$fd], $stream); + } catch (\Exception $e) { + Worker::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + } + + /** + * @param $fd + * @return void + */ + protected function callWrite($stream) + { + $fd = (int) $stream; + if (isset($this->_writeEvents[$fd])) { + try { + \call_user_func($this->_writeEvents[$fd], $stream); + } catch (\Exception $e) { + Worker::stopAll(250, $e); + } catch (\Error $e) { + Worker::stopAll(250, $e); + } + } + } + /** * * {@inheritdoc} @@ -153,6 +199,11 @@ class Swoole implements EventInterface case self::EV_READ: case self::EV_WRITE: $fd_key = (int) $fd; + if ($flag === self::EV_READ) { + unset($this->_readEvents[$fd_key]); + } elseif ($flag === self::EV_WRITE) { + unset($this->_writeEvents[$fd_key]); + } if (isset($this->_fd[$fd_key])) { $fd_val = $this->_fd[$fd_key]; if ($flag === self::EV_READ) { @@ -213,8 +264,10 @@ class Swoole implements EventInterface */ public function destroy() { + foreach (Coroutine::listCoroutines() as $coroutine) { + Coroutine::cancel($coroutine); + } Event::exit(); - posix_kill(posix_getpid(), SIGINT); } /** diff --git a/vendor/workerman/workerman/Protocols/Websocket.php b/vendor/workerman/workerman/Protocols/Websocket.php index 0f94de613..e16122851 100644 --- a/vendor/workerman/workerman/Protocols/Websocket.php +++ b/vendor/workerman/workerman/Protocols/Websocket.php @@ -363,7 +363,7 @@ class Websocket implements \Workerman\Protocols\ProtocolInterface [ 'level' => -1, 'memory' => 8, - 'window' => 9, + 'window' => 15, 'strategy' => \ZLIB_DEFAULT_STRATEGY ] ); @@ -389,7 +389,7 @@ class Websocket implements \Workerman\Protocols\ProtocolInterface [ 'level' => -1, 'memory' => 8, - 'window' => 9, + 'window' => 15, 'strategy' => \ZLIB_DEFAULT_STRATEGY ] ); @@ -420,8 +420,7 @@ class Websocket implements \Workerman\Protocols\ProtocolInterface if (\preg_match("/Sec-WebSocket-Key: *(.*?)\r\n/i", $buffer, $match)) { $Sec_WebSocket_Key = $match[1]; } else { - $connection->close("HTTP/1.1 200 WebSocket\r\nServer: workerman/" . Worker::VERSION . "\r\n\r\n

WebSocket


workerman/" . Worker::VERSION . "
", - true); + $connection->close("HTTP/1.0 400 Bad Request\r\nServer: workerman\r\n\r\n

WebSocket


workerman
", true); return 0; } // Calculation websocket key. @@ -501,8 +500,7 @@ class Websocket implements \Workerman\Protocols\ProtocolInterface return 0; } // Bad websocket handshake request. - $connection->close("HTTP/1.1 200 WebSocket\r\nServer: workerman/" . Worker::VERSION . "\r\n\r\n

WebSocket


workerman/" . Worker::VERSION . "
", - true); + $connection->close("HTTP/1.0 400 Bad Request\r\nServer: workerman\r\n\r\n

400 Bad Request


workerman
", true); return 0; } diff --git a/vendor/workerman/workerman/Protocols/Ws.php b/vendor/workerman/workerman/Protocols/Ws.php index 3db887eec..dd1bd1d78 100644 --- a/vendor/workerman/workerman/Protocols/Ws.php +++ b/vendor/workerman/workerman/Protocols/Ws.php @@ -336,7 +336,7 @@ class Ws } // Get Host. $port = $connection->getRemotePort(); - $host = $port === 80 ? $connection->getRemoteHost() : $connection->getRemoteHost() . ':' . $port; + $host = $port === 80 || $port === 443 ? $connection->getRemoteHost() : $connection->getRemoteHost() . ':' . $port; // Handshake header. $connection->context->websocketSecKey = \base64_encode(random_bytes(16)); $userHeader = $connection->headers ?? null; diff --git a/vendor/workerman/workerman/Worker.php b/vendor/workerman/workerman/Worker.php index 860d72820..8f69c4144 100644 --- a/vendor/workerman/workerman/Worker.php +++ b/vendor/workerman/workerman/Worker.php @@ -34,7 +34,7 @@ class Worker * * @var string */ - const VERSION = '4.1.15'; + const VERSION = '4.2.0'; /** * Status starting. @@ -515,7 +515,6 @@ class Worker \E_USER_ERROR => 'E_USER_ERROR', // 256 \E_USER_WARNING => 'E_USER_WARNING', // 512 \E_USER_NOTICE => 'E_USER_NOTICE', // 1024 - \E_STRICT => 'E_STRICT', // 2048 \E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', // 4096 \E_DEPRECATED => 'E_DEPRECATED', // 8192 \E_USER_DEPRECATED => 'E_USER_DEPRECATED' // 16384 @@ -540,6 +539,8 @@ class Worker */ protected static $_outputDecorated = null; + protected static $liveVersionLength = null; + /** * Run all worker instances. * @@ -689,10 +690,8 @@ class Worker // Get column mapping for UI foreach(static::getUiColumns() as $column_name => $prop){ - !isset($worker->{$prop}) && $worker->{$prop} = 'NNNN'; - $prop_length = \strlen((string) $worker->{$prop}); - $key = '_max' . \ucfirst(\strtolower($column_name)) . 'NameLength'; - static::$$key = \max(static::$$key, $prop_length); + $prop_length = \strlen((string) static::getWorkerProperty($worker, $prop)); + static::updateMaxNameLength($column_name, $prop_length); } // Listen. @@ -702,6 +701,86 @@ class Worker } } + /** + * @param Worker $worker + * @param string $prop + * @return mixed + */ + protected static function getWorkerProperty($worker, $prop) + { + switch ($prop) { + case 'transport': + return $worker->transport; + case 'user': + return $worker->user; + case 'name': + return $worker->name; + case 'socket': + return $worker->socket; + case 'count': + return $worker->count; + case 'status': + return $worker->status; + } + return null; + } + + /** + * Update specified column name length + * + * @param string $column_name + * @param int $length + * @return void + */ + protected static function updateMaxNameLength($column_name, $length) + { + switch ($column_name) { + case 'processes': + static::$_maxProcessesNameLength = max(static::$_maxProcessesNameLength, $length); + break; + case 'proto': + static::$_maxProtoNameLength = max(static::$_maxProtoNameLength, $length); + break; + case 'listen': + case 'socket': + static::$_maxSocketNameLength = max(static::$_maxSocketNameLength, $length); + break; + case 'status': + static::$_maxStatusNameLength = max(static::$_maxStatusNameLength, $length); + break; + case 'user': + static::$_maxUserNameLength = max(static::$_maxUserNameLength, $length); + break; + case 'worker': + static::$_maxWorkerNameLength = max(static::$_maxWorkerNameLength, $length); + break; + } + } + + /** + * @param string $column_name + * @return int + */ + protected static function getMaxNameLength($column_name) + { + switch ($column_name) { + case 'processes': + return static::$_maxProcessesNameLength; + case 'proto': + return static::$_maxProtoNameLength; + case 'listen': + case 'socket': + return static::$_maxSocketNameLength; + case 'status': + return static::$_maxStatusNameLength; + case 'user': + return static::$_maxUserNameLength; + case 'worker': + return static::$_maxWorkerNameLength; + } + return 0; + } + /** * Reload all worker instances. * @@ -792,7 +871,9 @@ class Worker //show version $line_version = 'Workerman version:' . static::VERSION . \str_pad('PHP version:', 22, ' ', \STR_PAD_LEFT) . \PHP_VERSION; $line_version .= \str_pad('Event-Loop:', 22, ' ', \STR_PAD_LEFT) . static::getEventLoopName() . \PHP_EOL; - !\defined('LINE_VERSIOIN_LENGTH') && \define('LINE_VERSIOIN_LENGTH', \strlen($line_version)); + if (static::$liveVersionLength === null) { + static::$liveVersionLength = \strlen($line_version); + } $total_length = static::getSingleLineTotalLength(); $line_one = '' . \str_pad(' WORKERMAN ', $total_length + \strlen(''), '-', \STR_PAD_BOTH) . ''. \PHP_EOL; $line_two = \str_pad(' WORKERS ' , $total_length + \strlen(''), '-', \STR_PAD_BOTH) . \PHP_EOL; @@ -801,10 +882,10 @@ class Worker //Show title $title = ''; foreach(static::getUiColumns() as $column_name => $prop){ - $key = '_max' . \ucfirst(\strtolower($column_name)) . 'NameLength'; + $length = static::getMaxNameLength($column_name); //just keep compatible with listen name $column_name === 'socket' && $column_name = 'listen'; - $title.= "{$column_name}" . \str_pad('', static::$$key + static::UI_SAFE_LENGTH - \strlen($column_name)); + $title.= "{$column_name}" . \str_pad('', $length + static::UI_SAFE_LENGTH - \strlen($column_name)); } $title && static::safeEcho($title . \PHP_EOL); @@ -812,10 +893,9 @@ class Worker foreach (static::$_workers as $worker) { $content = ''; foreach(static::getUiColumns() as $column_name => $prop){ - $key = '_max' . \ucfirst(\strtolower($column_name)) . 'NameLength'; - \preg_match_all("/(|<\/n>||<\/w>||<\/g>)/is", (string) $worker->{$prop}, $matches); + \preg_match_all("/(|<\/n>||<\/w>||<\/g>)/is", (string) static::getWorkerProperty($worker, $prop), $matches); $place_holder_length = !empty($matches) ? \strlen(\implode('', $matches[0])) : 0; - $content .= \str_pad((string) $worker->{$prop}, static::$$key + static::UI_SAFE_LENGTH + $place_holder_length); + $content .= \str_pad((string) static::getWorkerProperty($worker, $prop), static::getMaxNameLength($column_name) + static::UI_SAFE_LENGTH + $place_holder_length); } $content && static::safeEcho($content . \PHP_EOL); } @@ -869,13 +949,14 @@ class Worker $total_length = 0; foreach(static::getUiColumns() as $column_name => $prop){ - $key = '_max' . \ucfirst(\strtolower($column_name)) . 'NameLength'; - $total_length += static::$$key + static::UI_SAFE_LENGTH; + $total_length += static::getMaxNameLength($column_name) + static::UI_SAFE_LENGTH; } //keep beauty when show less colums - !\defined('LINE_VERSIOIN_LENGTH') && \define('LINE_VERSIOIN_LENGTH', 0); - $total_length <= LINE_VERSIOIN_LENGTH && $total_length = LINE_VERSIOIN_LENGTH; + if (static::$liveVersionLength === null) { + static::$liveVersionLength = 0; + } + $total_length <= static::$liveVersionLength && $total_length = static::$liveVersionLength; return $total_length; } @@ -2031,7 +2112,7 @@ class Worker if (static::$_masterPid === \posix_getpid()) { $all_worker_info = array(); foreach(static::$_pidMap as $worker_id => $pid_array) { - /** @var /Workerman/Worker $worker */ + /** @var Worker $worker */ $worker = static::$_workers[$worker_id]; foreach($pid_array as $pid) { $all_worker_info[$pid] = array('name' => $worker->name, 'listen' => $worker->getSocketName()); @@ -2260,8 +2341,12 @@ class Worker } elseif (!static::$_outputDecorated) { return false; } - \fwrite($stream, $msg); - \fflush($stream); + set_error_handler(function(){}); + if (!feof($stream)) { + fwrite($stream, $msg); + fflush($stream); + } + restore_error_handler(); return true; } @@ -2669,4 +2754,3 @@ class Worker return stripos($content, static::$processTitle) !== false || stripos($content, 'php') !== false; } } - diff --git a/vendor/workerman/workerman/composer.json b/vendor/workerman/workerman/composer.json index 19e298403..b9897b17d 100644 --- a/vendor/workerman/workerman/composer.json +++ b/vendor/workerman/workerman/composer.json @@ -24,7 +24,7 @@ "source": "https://github.com/walkor/workerman" }, "require": { - "php": ">=7.0" + "php": ">=8.0" }, "suggest": { "ext-event": "For better performance. " diff --git a/windows.php b/windows.php index 21fa888f3..cfece8f47 100644 --- a/windows.php +++ b/windows.php @@ -5,7 +5,7 @@ require_once __DIR__ . '/vendor/autoload.php'; use Dotenv\Dotenv; -use process\Monitor; +use app\process\Monitor; use support\App; use Workerman\Worker; @@ -31,9 +31,10 @@ $runtimeProcessPath = runtime_path() . DIRECTORY_SEPARATOR . '/windows'; if (!is_dir($runtimeProcessPath)) { mkdir($runtimeProcessPath); } -$processFiles = [ - __DIR__ . DIRECTORY_SEPARATOR . 'start.php' -]; +$processFiles = []; +if (config('server.listen')) { + $processFiles[] = __DIR__ . DIRECTORY_SEPARATOR . 'start.php'; +} foreach (config('process', []) as $processName => $config) { $processFiles[] = write_process_file($runtimeProcessPath, $processName, ''); } @@ -72,6 +73,14 @@ if (is_callable('opcache_reset')) { opcache_reset(); } +if (!\$appConfigFile = config_path('app.php')) { + throw new RuntimeException('Config file not found: app.php'); +} +\$appConfig = require \$appConfigFile; +if (\$timezone = \$appConfig['default_timezone'] ?? '') { + date_default_timezone_set(\$timezone); +} + App::loadAllConfig(['route']); worker_start('$processParam', $configParam);