feat(admin): 优化订单创建逻辑

- 将订单创建时的参数从 $order 变为 $params,提高代码可读性
- 更新 composer.json,将 webman-framework 版本升级到 1.6
- 修改 worker_start 函数,增加自定义 worker 类功能
- 优化 view 函数,增加对模板输入的统一处理
- 更新 cpu_count 函数,增加错误处理机制
This commit is contained in:
mkm 2024-11-19 12:11:23 +08:00
parent aabc53ffea
commit b7951806b8
36 changed files with 1092 additions and 318 deletions

View File

@ -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'),

View File

@ -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",

52
composer.lock generated
View File

@ -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",

View File

@ -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);

View File

@ -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;
}
// featcustom 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('/(?<!^)[A-Z]/', '_$0', substr(strrchr($controllerName, '\\'), 1)));
$actionFileBaseName = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $request->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

View File

@ -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": {

View File

@ -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(),

View File

@ -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
}

View File

@ -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;
}
/**

View File

@ -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;

View File

@ -0,0 +1,95 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @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;
}
}

View File

@ -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'];

View File

@ -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']);
}
}
}

View File

@ -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));
}
/**

View File

@ -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];
}
/**

View File

@ -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',

View File

@ -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);
}
}

View File

@ -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());
}

View File

@ -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 **/

View File

@ -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());
}
}

View File

@ -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);
}

View File

@ -0,0 +1,24 @@
<?php
namespace support\exception;
use Throwable;
class InputTypeException extends PageNotFoundException
{
/**
* @var string
*/
protected $template = '/app/view/400';
/**
* InputTypeException constructor.
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(string $message = 'Input :parameter must be of type :exceptType, :actualType given', int $code = 400, Throwable $previous = null) {
parent::__construct($message, $code, $previous);
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace support\exception;
use Throwable;
use Webman\Http\Request;
use Webman\Http\Response;
class MissingInputException extends PageNotFoundException
{
/**
* @var string
*/
protected $template = '/app/view/400';
/**
* MissingInputException constructor.
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(string $message = 'Missing input parameter :parameter', int $code = 400, 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;
$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));
}
}

View File

@ -0,0 +1,20 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace support\exception;
class NotFoundException extends BusinessException
{
}

View File

@ -0,0 +1,93 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @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 <<<EOF
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>$message</title>
<style>
.center {
text-align: center;
}
</style>
</head>
<body>
<h1 class="center">$message</h1>
<hr>
<div class="center">webman</div>
</body>
</html>
EOF;
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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/',

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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);
}
/**

View File

@ -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<div style=\"text-align:center\"><h1>WebSocket</h1><hr>workerman/" . Worker::VERSION . "</div>",
true);
$connection->close("HTTP/1.0 400 Bad Request\r\nServer: workerman\r\n\r\n<div style=\"text-align:center\"><h1>WebSocket</h1><hr>workerman</div>", 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<div style=\"text-align:center\"><h1>WebSocket</h1><hr>workerman/" . Worker::VERSION . "</div>",
true);
$connection->close("HTTP/1.0 400 Bad Request\r\nServer: workerman\r\n\r\n<div style=\"text-align:center\"><h1>400 Bad Request</h1><hr>workerman</div>", true);
return 0;
}

View File

@ -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;

View File

@ -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 = '<n>' . \str_pad('<w> WORKERMAN </w>', $total_length + \strlen('<w></w>'), '-', \STR_PAD_BOTH) . '</n>'. \PHP_EOL;
$line_two = \str_pad('<w> WORKERS </w>' , $total_length + \strlen('<w></w>'), '-', \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.= "<w>{$column_name}</w>" . \str_pad('', static::$$key + static::UI_SAFE_LENGTH - \strlen($column_name));
$title.= "<w>{$column_name}</w>" . \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>|<\/n>|<w>|<\/w>|<g>|<\/g>)/is", (string) $worker->{$prop}, $matches);
\preg_match_all("/(<n>|<\/n>|<w>|<\/w>|<g>|<\/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;
}
}

View File

@ -24,7 +24,7 @@
"source": "https://github.com/walkor/workerman"
},
"require": {
"php": ">=7.0"
"php": ">=8.0"
},
"suggest": {
"ext-event": "For better performance. "

View File

@ -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);