This commit is contained in:
2024-11-05 12:10:06 +08:00
commit 6b687feee2
3995 changed files with 418583 additions and 0 deletions
@@ -0,0 +1,5 @@
composer.lock
vendor
vendor/
.idea
.idea/
+5
View File
@@ -0,0 +1,5 @@
# webman-framework
Note: This repository is the core code of the webman framework. If you want to build an application using webman, visit the main [webman](https://github.com/walkor/webman) repository.
## LICENSE
MIT
+47
View File
@@ -0,0 +1,47 @@
{
"name": "workerman/webman-framework",
"type": "library",
"keywords": [
"high performance",
"http service"
],
"homepage": "https://www.workerman.net",
"license": "MIT",
"description": "High performance HTTP Service Framework.",
"authors": [
{
"name": "walkor",
"email": "walkor@workerman.net",
"homepage": "https://www.workerman.net",
"role": "Developer"
}
],
"support": {
"email": "walkor@workerman.net",
"issues": "https://github.com/walkor/webman/issues",
"forum": "https://wenda.workerman.net/",
"wiki": "https://doc.workerman.net/",
"source": "https://github.com/walkor/webman-framework"
},
"require": {
"php": ">=7.2",
"ext-json": "*",
"workerman/workerman": "^4.0.4 || ^5.0.0",
"nikic/fast-route": "^1.3",
"psr/container": ">=1.0"
},
"suggest": {
"ext-event": "For better performance. "
},
"autoload": {
"psr-4": {
"Webman\\": "./src",
"support\\": "./src/support",
"Support\\": "./src/support",
"Support\\Bootstrap\\": "./src/support/bootstrap",
"Support\\Exception\\": "./src/support/exception",
"Support\\View\\": "./src/support/view"
}
},
"minimum-stability": "dev"
}
+939
View File
@@ -0,0 +1,939 @@
<?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;
use Closure;
use Exception;
use FastRoute\Dispatcher;
use InvalidArgumentException;
use Monolog\Logger;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;
use ReflectionClass;
use ReflectionException;
use ReflectionFunction;
use ReflectionFunctionAbstract;
use ReflectionMethod;
use Throwable;
use Webman\Exception\ExceptionHandler;
use Webman\Exception\ExceptionHandlerInterface;
use Webman\Http\Request;
use Webman\Http\Response;
use Webman\Route\Route as RouteObject;
use Workerman\Connection\TcpConnection;
use Workerman\Protocols\Http;
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;
use function clearstatcache;
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_string;
use function key;
use function method_exists;
use function next;
use function ob_get_clean;
use function ob_start;
use function pathinfo;
use function scandir;
use function str_replace;
use function strpos;
use function strtolower;
use function substr;
use function trim;
/**
* Class App
* @package Webman
*/
class App
{
/**
* @var callable[]
*/
protected static $callbacks = [];
/**
* @var Worker
*/
protected static $worker = null;
/**
* @var Logger
*/
protected static $logger = null;
/**
* @var string
*/
protected static $appPath = '';
/**
* @var string
*/
protected static $publicPath = '';
/**
* @var string
*/
protected static $requestClass = '';
/**
* App constructor.
* @param string $requestClass
* @param Logger $logger
* @param string $appPath
* @param string $publicPath
*/
public function __construct(string $requestClass, Logger $logger, string $appPath, string $publicPath)
{
static::$requestClass = $requestClass;
static::$logger = $logger;
static::$publicPath = $publicPath;
static::$appPath = $appPath;
}
/**
* OnMessage.
* @param TcpConnection|mixed $connection
* @param Request|mixed $request
* @return null
*/
public function onMessage($connection, $request)
{
try {
Context::set(Request::class, $request);
$path = $request->path();
$key = $request->method() . $path;
if (isset(static::$callbacks[$key])) {
[$callback, $request->plugin, $request->app, $request->controller, $request->action, $request->route] = static::$callbacks[$key];
static::send($connection, $callback($request), $request);
return null;
}
if (
static::unsafeUri($connection, $path, $request) ||
static::findFile($connection, $path, $key, $request) ||
static::findRoute($connection, $path, $key, $request)
) {
return null;
}
$controllerAndAction = static::parseControllerAction($path);
$plugin = $controllerAndAction['plugin'] ?? static::getPluginByPath($path);
if (!$controllerAndAction || Route::hasDisableDefaultRoute($plugin)) {
$request->plugin = $plugin;
$callback = static::getFallback($plugin);
$request->app = $request->controller = $request->action = '';
static::send($connection, $callback($request), $request);
return null;
}
$app = $controllerAndAction['app'];
$controller = $controllerAndAction['controller'];
$action = $controllerAndAction['action'];
$callback = static::getCallback($plugin, $app, [$controller, $action]);
static::collectCallbacks($key, [$callback, $plugin, $app, $controller, $action, null]);
[$callback, $request->plugin, $request->app, $request->controller, $request->action, $request->route] = static::$callbacks[$key];
static::send($connection, $callback($request), $request);
} catch (Throwable $e) {
static::send($connection, static::exceptionResponse($e, $request), $request);
}
return null;
}
/**
* OnWorkerStart.
* @param $worker
* @return void
*/
public function onWorkerStart($worker)
{
static::$worker = $worker;
Http::requestClass(static::$requestClass);
}
/**
* CollectCallbacks.
* @param string $key
* @param array $data
* @return void
*/
protected static function collectCallbacks(string $key, array $data)
{
static::$callbacks[$key] = $data;
if (count(static::$callbacks) >= 1024) {
unset(static::$callbacks[key(static::$callbacks)]);
}
}
/**
* UnsafeUri.
* @param TcpConnection $connection
* @param string $path
* @param $request
* @return bool
*/
protected static function unsafeUri(TcpConnection $connection, string $path, $request): bool
{
if (
!$path ||
strpos($path, '..') !== false ||
strpos($path, "\\") !== false ||
strpos($path, "\0") !== false
) {
$callback = static::getFallback();
$request->plugin = $request->app = $request->controller = $request->action = '';
static::send($connection, $callback($request), $request);
return true;
}
return false;
}
/**
* GetFallback.
* @param string $plugin
* @return Closure
*/
protected static function getFallback(string $plugin = ''): 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);
};
}
/**
* ExceptionResponse.
* @param Throwable $e
* @param $request
* @return Response
*/
protected static function exceptionResponse(Throwable $e, $request): Response
{
try {
$app = $request->app ?: '';
$plugin = $request->plugin ?: '';
$exceptionConfig = static::config($plugin, 'exception');
$defaultException = $exceptionConfig[''] ?? ExceptionHandler::class;
$exceptionHandlerClass = $exceptionConfig[$app] ?? $defaultException;
/** @var ExceptionHandlerInterface $exceptionHandler */
$exceptionHandler = static::container($plugin)->make($exceptionHandlerClass, [
'logger' => static::$logger,
'debug' => static::config($plugin, 'app.debug')
]);
$exceptionHandler->report($e);
$response = $exceptionHandler->render($request, $e);
$response->exception($e);
return $response;
} catch (Throwable $e) {
$response = new Response(500, [], static::config($plugin ?? '', 'app.debug') ? (string)$e : $e->getMessage());
$response->exception($e);
return $response;
}
}
/**
* GetCallback.
* @param string $plugin
* @param string $app
* @param $call
* @param array|null $args
* @param bool $withGlobalMiddleware
* @param RouteObject|null $route
* @return callable
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws ReflectionException
*/
protected static function getCallback(string $plugin, string $app, $call, array $args = null, bool $withGlobalMiddleware = true, RouteObject $route = null)
{
$args = $args === null ? null : array_values($args);
$middlewares = [];
if ($route) {
$routeMiddlewares = $route->getMiddleware();
foreach ($routeMiddlewares as $className) {
$middlewares[] = [$className, 'process'];
}
}
$middlewares = array_merge($middlewares, Middleware::getMiddleware($plugin, $app, $withGlobalMiddleware));
foreach ($middlewares as $key => $item) {
$middleware = $item[0];
if (is_string($middleware)) {
$middleware = static::container($plugin)->get($middleware);
} elseif ($middleware instanceof Closure) {
$middleware = call_user_func($middleware, static::container($plugin));
}
if (!$middleware instanceof MiddlewareInterface) {
throw new InvalidArgumentException('Not support middleware type');
}
$middlewares[$key][0] = $middleware;
}
$needInject = static::isNeedInject($call, $args);
if (is_array($call) && is_string($call[0])) {
$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]);
$reflector = static::getReflector($call);
$args = static::resolveMethodDependencies($plugin, $request, $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);
};
}
} else {
$call[0] = static::container($plugin)->get($call[0]);
}
}
if ($needInject) {
$call = static::resolveInject($plugin, $call);
}
if ($middlewares) {
$callback = array_reduce($middlewares, function ($carry, $pipe) {
return function ($request) use ($carry, $pipe) {
try {
return $pipe($request, $carry);
} catch (Throwable $e) {
return static::exceptionResponse($e, $request);
}
};
}, function ($request) use ($call, $args) {
try {
if ($args === null) {
$response = $call($request);
} else {
$response = $call($request, ...$args);
}
} catch (Throwable $e) {
return static::exceptionResponse($e, $request);
}
if (!$response instanceof Response) {
if (!is_string($response)) {
$response = static::stringify($response);
}
$response = new Response(200, [], $response);
}
return $response;
});
} else {
if ($args === null) {
$callback = $call;
} else {
$callback = function ($request) use ($call, $args) {
return $call($request, ...$args);
};
}
}
return $callback;
}
/**
* ResolveInject.
* @param string $plugin
* @param array|Closure $call
* @return Closure
* @see Dependency injection through reflection information
*/
protected static function resolveInject(string $plugin, $call): Closure
{
return function (Request $request, ...$args) use ($plugin, $call) {
$reflector = static::getReflector($call);
$args = static::resolveMethodDependencies($plugin, $request, $args, $reflector);
return $call(...$args);
};
}
/**
* Check whether inject is required.
* @param $call
* @param $args
* @return bool
* @throws ReflectionException
*/
protected static function isNeedInject($call, $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) {
return false;
}
$firstParameter = current($reflectionParameters);
unset($reflectionParameters[key($reflectionParameters)]);
$adaptersList = ['int', 'string', 'bool', 'array', 'object', 'float', 'mixed', 'resource'];
foreach ($reflectionParameters as $parameter) {
if ($parameter->hasType() && !in_array($parameter->getType()->getName(), $adaptersList)) {
return true;
}
}
if (!$firstParameter->hasType()) {
return count($args) > count($reflectionParameters);
}
if (!is_a(static::$requestClass, $firstParameter->getType()->getName())) {
return true;
}
return false;
}
/**
* Get reflector.
* @param $call
* @return ReflectionFunction|ReflectionMethod
* @throws ReflectionException
*/
protected static function getReflector($call)
{
if ($call instanceof Closure || is_string($call)) {
return new ReflectionFunction($call);
}
return new ReflectionMethod($call[0], $call[1]);
}
/**
* Return dependent parameters
* @param string $plugin
* @param Request $request
* @param array $args
* @param ReflectionFunctionAbstract $reflector
* @return array
*/
protected static function resolveMethodDependencies(string $plugin, Request $request, array $args, 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);
} else {
// Indicates whether the current parameter has a default value. If yes, return true
$parameters[] = $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null;
}
// Quota of consumption variables
next($args);
}
}
// Returns the result of parameters replacement
return $parameters;
}
/**
* Container.
* @param string $plugin
* @return ContainerInterface
*/
public static function container(string $plugin = '')
{
return static::config($plugin, 'container');
}
/**
* Get request.
* @return Request|\support\Request
*/
public static function request()
{
return Context::get(Request::class);
}
/**
* Get worker.
* @return Worker
*/
public static function worker(): ?Worker
{
return static::$worker;
}
/**
* Find Route.
* @param TcpConnection $connection
* @param string $path
* @param string $key
* @param Request|mixed $request
* @return bool
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws ReflectionException
*/
protected static function findRoute(TcpConnection $connection, string $path, string $key, $request): bool
{
$routeInfo = Route::dispatch($request->method(), $path);
if ($routeInfo[0] === Dispatcher::FOUND) {
$routeInfo[0] = 'route';
$callback = $routeInfo[1]['callback'];
$route = clone $routeInfo[1]['route'];
$app = $controller = $action = '';
$args = !empty($routeInfo[2]) ? $routeInfo[2] : null;
if ($args) {
$route->setParams($args);
}
if (is_array($callback)) {
$controller = $callback[0];
$plugin = static::getPluginByClass($controller);
$app = static::getAppByController($controller);
$action = static::getRealMethod($controller, $callback[1]) ?? '';
} else {
$plugin = static::getPluginByPath($path);
}
$callback = static::getCallback($plugin, $app, $callback, $args, true, $route);
static::collectCallbacks($key, [$callback, $plugin, $app, $controller ?: '', $action, $route]);
[$callback, $request->plugin, $request->app, $request->controller, $request->action, $request->route] = static::$callbacks[$key];
static::send($connection, $callback($request), $request);
return true;
}
return false;
}
/**
* Find File.
* @param TcpConnection $connection
* @param string $path
* @param string $key
* @param Request|mixed $request
* @return bool
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws ReflectionException
*/
protected static function findFile(TcpConnection $connection, string $path, string $key, $request): bool
{
if (preg_match('/%[0-9a-f]{2}/i', $path)) {
$path = urldecode($path);
if (static::unsafeUri($connection, $path, $request)) {
return true;
}
}
$pathExplodes = explode('/', trim($path, '/'));
$plugin = '';
if (isset($pathExplodes[1]) && $pathExplodes[0] === 'app') {
$plugin = $pathExplodes[1];
$publicDir = static::config($plugin, 'app.public_path') ?: BASE_PATH . "/plugin/$pathExplodes[1]/public";
$path = substr($path, strlen("/app/$pathExplodes[1]/"));
} else {
$publicDir = static::$publicPath;
}
$file = "$publicDir/$path";
if (!is_file($file)) {
return false;
}
if (pathinfo($file, PATHINFO_EXTENSION) === 'php') {
if (!static::config($plugin, 'app.support_php_files', false)) {
return false;
}
static::collectCallbacks($key, [function () use ($file) {
return static::execPhpFile($file);
}, '', '', '', '', null]);
[, $request->plugin, $request->app, $request->controller, $request->action, $request->route] = static::$callbacks[$key];
static::send($connection, static::execPhpFile($file), $request);
return true;
}
if (!static::config($plugin, 'static.enable', false)) {
return false;
}
static::collectCallbacks($key, [static::getCallback($plugin, '__static__', function ($request) use ($file, $plugin) {
clearstatcache(true, $file);
if (!is_file($file)) {
$callback = static::getFallback($plugin);
return $callback($request);
}
return (new Response())->file($file);
}, 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;
}
/**
* Send.
* @param TcpConnection|mixed $connection
* @param mixed $response
* @param Request|mixed $request
* @return void
*/
protected static function send($connection, $response, $request)
{
$keepAlive = $request->header('connection');
Context::destroy();
if (($keepAlive === null && $request->protocolVersion() === '1.1')
|| $keepAlive === 'keep-alive' || $keepAlive === 'Keep-Alive'
) {
$connection->send($response);
return;
}
$connection->close($response);
}
/**
* ParseControllerAction.
* @param string $path
* @return array|false
* @throws ReflectionException
*/
protected static function parseControllerAction(string $path)
{
$path = str_replace(['-', '//'], ['', '/'], $path);
static $cache = [];
if (isset($cache[$path])) {
return $cache[$path];
}
$pathExplode = explode('/', trim($path, '/'));
$isPlugin = isset($pathExplode[1]) && $pathExplode[0] === 'app';
$configPrefix = $isPlugin ? "plugin.$pathExplode[1]." : '';
$pathPrefix = $isPlugin ? "/app/$pathExplode[1]" : '';
$classPrefix = $isPlugin ? "plugin\\$pathExplode[1]" : '';
$suffix = Config::get("{$configPrefix}app.controller_suffix", '');
$relativePath = trim(substr($path, strlen($pathPrefix)), '/');
$pathExplode = $relativePath ? explode('/', $relativePath) : [];
$action = 'index';
if (!$controllerAction = static::guessControllerAction($pathExplode, $action, $suffix, $classPrefix)) {
if (count($pathExplode) <= 1) {
return false;
}
$action = end($pathExplode);
unset($pathExplode[count($pathExplode) - 1]);
$controllerAction = static::guessControllerAction($pathExplode, $action, $suffix, $classPrefix);
}
if ($controllerAction && !isset($path[256])) {
$cache[$path] = $controllerAction;
if (count($cache) > 1024) {
unset($cache[key($cache)]);
}
}
return $controllerAction;
}
/**
* GuessControllerAction.
* @param $pathExplode
* @param $action
* @param $suffix
* @param $classPrefix
* @return array|false
* @throws ReflectionException
*/
protected static function guessControllerAction($pathExplode, $action, $suffix, $classPrefix)
{
$map[] = trim("$classPrefix\\app\\controller\\" . implode('\\', $pathExplode), '\\');
foreach ($pathExplode as $index => $section) {
$tmp = $pathExplode;
array_splice($tmp, $index, 1, [$section, 'controller']);
$map[] = trim("$classPrefix\\" . implode('\\', array_merge(['app'], $tmp)), '\\');
}
foreach ($map as $item) {
$map[] = $item . '\\index';
}
foreach ($map as $controllerClass) {
// Remove xx\xx\controller
if (substr($controllerClass, -11) === '\\controller') {
continue;
}
$controllerClass .= $suffix;
if ($controllerAction = static::getControllerAction($controllerClass, $action)) {
return $controllerAction;
}
}
return false;
}
/**
* GetControllerAction.
* @param string $controllerClass
* @param string $action
* @return array|false
* @throws ReflectionException
*/
protected static function getControllerAction(string $controllerClass, string $action)
{
// Disable calling magic methods
if (strpos($action, '__') === 0) {
return false;
}
if (($controllerClass = static::getController($controllerClass)) && ($action = static::getAction($controllerClass, $action))) {
return [
'plugin' => static::getPluginByClass($controllerClass),
'app' => static::getAppByController($controllerClass),
'controller' => $controllerClass,
'action' => $action
];
}
return false;
}
/**
* GetController.
* @param string $controllerClass
* @return string|false
* @throws ReflectionException
*/
protected static function getController(string $controllerClass)
{
if (class_exists($controllerClass)) {
return (new ReflectionClass($controllerClass))->name;
}
$explodes = explode('\\', strtolower(ltrim($controllerClass, '\\')));
$basePath = $explodes[0] === 'plugin' ? BASE_PATH . '/plugin' : static::$appPath;
unset($explodes[0]);
$fileName = array_pop($explodes) . '.php';
$found = true;
foreach ($explodes as $pathSection) {
if (!$found) {
break;
}
$dirs = Util::scanDir($basePath, false);
$found = false;
foreach ($dirs as $name) {
$path = "$basePath/$name";
if (is_dir($path) && strtolower($name) === $pathSection) {
$basePath = $path;
$found = true;
break;
}
}
}
if (!$found) {
return false;
}
foreach (scandir($basePath) ?: [] as $name) {
if (strtolower($name) === $fileName) {
require_once "$basePath/$name";
if (class_exists($controllerClass, false)) {
return (new ReflectionClass($controllerClass))->name;
}
}
}
return false;
}
/**
* GetAction.
* @param string $controllerClass
* @param string $action
* @return string|false
*/
protected static function getAction(string $controllerClass, string $action)
{
$methods = get_class_methods($controllerClass);
$lowerAction = strtolower($action);
$found = false;
foreach ($methods as $candidate) {
if (strtolower($candidate) === $lowerAction) {
$action = $candidate;
$found = true;
break;
}
}
if ($found) {
return $action;
}
// Action is not public method
if (method_exists($controllerClass, $action)) {
return false;
}
if (method_exists($controllerClass, '__call')) {
return $action;
}
return false;
}
/**
* GetPluginByClass.
* @param string $controllerClass
* @return mixed|string
*/
public static function getPluginByClass(string $controllerClass)
{
$controllerClass = trim($controllerClass, '\\');
$tmp = explode('\\', $controllerClass, 3);
if ($tmp[0] !== 'plugin') {
return '';
}
return $tmp[1] ?? '';
}
/**
* GetPluginByPath.
* @param string $path
* @return mixed|string
*/
public static function getPluginByPath(string $path)
{
$path = trim($path, '/');
$tmp = explode('/', $path, 3);
if ($tmp[0] !== 'app') {
return '';
}
return $tmp[1] ?? '';
}
/**
* GetAppByController.
* @param string $controllerClass
* @return mixed|string
*/
protected static function getAppByController(string $controllerClass)
{
$controllerClass = trim($controllerClass, '\\');
$tmp = explode('\\', $controllerClass, 5);
$pos = $tmp[0] === 'plugin' ? 3 : 1;
if (!isset($tmp[$pos])) {
return '';
}
return strtolower($tmp[$pos]) === 'controller' ? '' : $tmp[$pos];
}
/**
* ExecPhpFile.
* @param string $file
* @return false|string
*/
public static function execPhpFile(string $file)
{
ob_start();
// Try to include php file.
try {
include $file;
} catch (Exception $e) {
echo $e;
}
return ob_get_clean();
}
/**
* GetRealMethod.
* @param string $class
* @param string $method
* @return string
*/
protected static function getRealMethod(string $class, string $method): string
{
$method = strtolower($method);
$methods = get_class_methods($class);
foreach ($methods as $candidate) {
if (strtolower($candidate) === $method) {
return $candidate;
}
}
return $method;
}
/**
* Config.
* @param string $plugin
* @param string $key
* @param $default
* @return array|mixed|null
*/
protected static function config(string $plugin, string $key, $default = null)
{
return Config::get($plugin ? "plugin.$plugin.$key" : $key, $default);
}
/**
* @param mixed $data
* @return string
*/
protected static function stringify($data): string
{
$type = gettype($data);
switch ($type) {
case 'boolean':
return $data ? 'true' : 'false';
case 'NULL':
return 'NULL';
case 'array':
return 'Array';
case 'object':
if (!method_exists($data, '__toString')) {
return 'Object';
}
default:
return (string)$data;
}
}
}
+28
View File
@@ -0,0 +1,28 @@
<?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;
use Workerman\Worker;
interface Bootstrap
{
/**
* onWorkerStart
*
* @param Worker|null $worker
* @return mixed
*/
public static function start(?Worker $worker);
}
+296
View File
@@ -0,0 +1,296 @@
<?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;
use FilesystemIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use function array_replace_recursive;
use function array_reverse;
use function count;
use function explode;
use function in_array;
use function is_array;
use function is_dir;
use function is_file;
use function key;
use function str_replace;
class Config
{
/**
* @var array
*/
protected static $config = [];
/**
* @var string
*/
protected static $configPath = '';
/**
* @var bool
*/
protected static $loaded = false;
/**
* Load.
* @param string $configPath
* @param array $excludeFile
* @param string|null $key
* @return void
*/
public static function load(string $configPath, array $excludeFile = [], string $key = null)
{
static::$configPath = $configPath;
if (!$configPath) {
return;
}
static::$loaded = false;
$config = static::loadFromDir($configPath, $excludeFile);
if (!$config) {
static::$loaded = true;
return;
}
if ($key !== null) {
foreach (array_reverse(explode('.', $key)) as $k) {
$config = [$k => $config];
}
}
static::$config = array_replace_recursive(static::$config, $config);
static::formatConfig();
static::$loaded = true;
}
/**
* This deprecated method will certainly be removed in the future.
* @param string $configPath
* @param array $excludeFile
* @return void
* @deprecated
*/
public static function reload(string $configPath, array $excludeFile = [])
{
static::load($configPath, $excludeFile);
}
/**
* Clear.
* @return void
*/
public static function clear()
{
static::$config = [];
}
/**
* FormatConfig.
* @return void
*/
protected static function formatConfig()
{
$config = static::$config;
// Merge log config
foreach ($config['plugin'] ?? [] as $firm => $projects) {
if (isset($projects['app'])) {
foreach ($projects['log'] ?? [] as $key => $item) {
$config['log']["plugin.$firm.$key"] = $item;
}
}
foreach ($projects as $name => $project) {
if (!is_array($project)) {
continue;
}
foreach ($project['log'] ?? [] as $key => $item) {
$config['log']["plugin.$firm.$name.$key"] = $item;
}
}
}
// Merge database config
foreach ($config['plugin'] ?? [] as $firm => $projects) {
if (isset($projects['app'])) {
foreach ($projects['database']['connections'] ?? [] as $key => $connection) {
$config['database']['connections']["plugin.$firm.$key"] = $connection;
}
}
foreach ($projects as $name => $project) {
if (!is_array($project)) {
continue;
}
foreach ($project['database']['connections'] ?? [] as $key => $connection) {
$config['database']['connections']["plugin.$firm.$name.$key"] = $connection;
}
}
}
if (!empty($config['database']['connections'])) {
$config['database']['default'] = $config['database']['default'] ?? key($config['database']['connections']);
}
// Merge thinkorm config
foreach ($config['plugin'] ?? [] as $firm => $projects) {
if (isset($projects['app'])) {
foreach ($projects['thinkorm']['connections'] ?? [] as $key => $connection) {
$config['thinkorm']['connections']["plugin.$firm.$key"] = $connection;
}
}
foreach ($projects as $name => $project) {
if (!is_array($project)) {
continue;
}
foreach ($project['thinkorm']['connections'] ?? [] as $key => $connection) {
$config['thinkorm']['connections']["plugin.$firm.$name.$key"] = $connection;
}
}
}
if (!empty($config['thinkorm']['connections'])) {
$config['thinkorm']['default'] = $config['thinkorm']['default'] ?? key($config['thinkorm']['connections']);
}
// Merge redis config
foreach ($config['plugin'] ?? [] as $firm => $projects) {
if (isset($projects['app'])) {
foreach ($projects['redis'] ?? [] as $key => $connection) {
$config['redis']["plugin.$firm.$key"] = $connection;
}
}
foreach ($projects as $name => $project) {
if (!is_array($project)) {
continue;
}
foreach ($project['redis'] ?? [] as $key => $connection) {
$config['redis']["plugin.$firm.$name.$key"] = $connection;
}
}
}
static::$config = $config;
}
/**
* LoadFromDir.
* @param string $configPath
* @param array $excludeFile
* @return array
*/
public static function loadFromDir(string $configPath, array $excludeFile = []): array
{
$allConfig = [];
$dirIterator = new RecursiveDirectoryIterator($configPath, FilesystemIterator::FOLLOW_SYMLINKS);
$iterator = new RecursiveIteratorIterator($dirIterator);
foreach ($iterator as $file) {
/** var SplFileInfo $file */
if (is_dir($file) || $file->getExtension() != 'php' || in_array($file->getBaseName('.php'), $excludeFile)) {
continue;
}
$appConfigFile = $file->getPath() . '/app.php';
if (!is_file($appConfigFile)) {
continue;
}
$relativePath = str_replace($configPath . DIRECTORY_SEPARATOR, '', substr($file, 0, -4));
$explode = array_reverse(explode(DIRECTORY_SEPARATOR, $relativePath));
if (count($explode) >= 2) {
$appConfig = include $appConfigFile;
if (empty($appConfig['enable'])) {
continue;
}
}
$config = include $file;
foreach ($explode as $section) {
$tmp = [];
$tmp[$section] = $config;
$config = $tmp;
}
$allConfig = array_replace_recursive($allConfig, $config);
}
return $allConfig;
}
/**
* Get.
* @param string|null $key
* @param mixed $default
* @return array|mixed|void|null
*/
public static function get(string $key = null, $default = null)
{
if ($key === null) {
return static::$config;
}
$keyArray = explode('.', $key);
$value = static::$config;
$found = true;
foreach ($keyArray as $index) {
if (!isset($value[$index])) {
if (static::$loaded) {
return $default;
}
$found = false;
break;
}
$value = $value[$index];
}
if ($found) {
return $value;
}
return static::read($key, $default);
}
/**
* Read.
* @param string $key
* @param mixed $default
* @return array|mixed|null
*/
protected static function read(string $key, $default = null)
{
$path = static::$configPath;
if ($path === '') {
return $default;
}
$keys = $keyArray = explode('.', $key);
foreach ($keyArray as $index => $section) {
unset($keys[$index]);
if (is_file($file = "$path/$section.php")) {
$config = include $file;
return static::find($keys, $config, $default);
}
if (!is_dir($path = "$path/$section")) {
return $default;
}
}
return $default;
}
/**
* Find.
* @param array $keyArray
* @param mixed $stack
* @param mixed $default
* @return array|mixed
*/
protected static function find(array $keyArray, $stack, $default)
{
if (!is_array($stack)) {
return $default;
}
$value = $stack;
foreach ($keyArray as $index) {
if (!isset($value[$index])) {
return $default;
}
$value = $value[$index];
}
return $value;
}
}
+84
View File
@@ -0,0 +1,84 @@
<?php
namespace Webman;
use Psr\Container\ContainerInterface;
use Webman\Exception\NotFoundException;
use function array_key_exists;
use function class_exists;
/**
* Class Container
* @package Webman
*/
class Container implements ContainerInterface
{
/**
* @var array
*/
protected $instances = [];
/**
* @var array
*/
protected $definitions = [];
/**
* Get.
* @param string $name
* @return mixed
* @throws NotFoundException
*/
public function get(string $name)
{
if (!isset($this->instances[$name])) {
if (isset($this->definitions[$name])) {
$this->instances[$name] = call_user_func($this->definitions[$name], $this);
} else {
if (!class_exists($name)) {
throw new NotFoundException("Class '$name' not found");
}
$this->instances[$name] = new $name();
}
}
return $this->instances[$name];
}
/**
* Has.
* @param string $name
* @return bool
*/
public function has(string $name): bool
{
return array_key_exists($name, $this->instances)
|| array_key_exists($name, $this->definitions);
}
/**
* Make.
* @param string $name
* @param array $constructor
* @return mixed
* @throws NotFoundException
*/
public function make(string $name, array $constructor = [])
{
if (!class_exists($name)) {
throw new NotFoundException("Class '$name' not found");
}
return new $name(... array_values($constructor));
}
/**
* AddDefinitions.
* @param array $definitions
* @return $this
*/
public function addDefinitions(array $definitions): Container
{
$this->definitions = array_merge($this->definitions, $definitions);
return $this;
}
}
+129
View File
@@ -0,0 +1,129 @@
<?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;
use Fiber;
use SplObjectStorage;
use StdClass;
use Swow\Coroutine;
use WeakMap;
use Workerman\Events\Revolt;
use Workerman\Events\Swoole;
use Workerman\Events\Swow;
use Workerman\Worker;
use function property_exists;
/**
* Class Context
* @package Webman
*/
class Context
{
/**
* @var SplObjectStorage|WeakMap
*/
protected static $objectStorage;
/**
* @var StdClass
*/
protected static $object;
/**
* @return StdClass
*/
protected static function getObject(): StdClass
{
if (!static::$objectStorage) {
static::$objectStorage = class_exists(WeakMap::class) ? new WeakMap() : new SplObjectStorage();
static::$object = new StdClass;
}
$key = static::getKey();
if (!isset(static::$objectStorage[$key])) {
static::$objectStorage[$key] = new StdClass;
}
return static::$objectStorage[$key];
}
/**
* @return mixed
*/
protected static function getKey()
{
switch (Worker::$eventLoopClass) {
case Revolt::class:
return Fiber::getCurrent();
case Swoole::class:
return \Swoole\Coroutine::getContext();
case Swow::class:
return Coroutine::getCurrent();
}
return static::$object;
}
/**
* @param string|null $key
* @return mixed
*/
public static function get(string $key = null)
{
$obj = static::getObject();
if ($key === null) {
return $obj;
}
return $obj->$key ?? null;
}
/**
* @param string $key
* @param $value
* @return void
*/
public static function set(string $key, $value): void
{
$obj = static::getObject();
$obj->$key = $value;
}
/**
* @param string $key
* @return void
*/
public static function delete(string $key): void
{
$obj = static::getObject();
unset($obj->$key);
}
/**
* @param string $key
* @return bool
*/
public static function has(string $key): bool
{
$obj = static::getObject();
return property_exists($obj, $key);
}
/**
* @return void
*/
public static function destroy(): void
{
unset(static::$objectStorage[static::getKey()]);
}
}
@@ -0,0 +1,118 @@
<?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 Psr\Log\LoggerInterface;
use Throwable;
use Webman\Http\Request;
use Webman\Http\Response;
use function json_encode;
use function nl2br;
use function trim;
/**
* Class Handler
* @package support\exception
*/
class ExceptionHandler implements ExceptionHandlerInterface
{
/**
* @var LoggerInterface
*/
protected $logger = null;
/**
* @var bool
*/
protected $debug = false;
/**
* @var array
*/
public $dontReport = [];
/**
* ExceptionHandler constructor.
* @param $logger
* @param $debug
*/
public function __construct($logger, $debug)
{
$this->logger = $logger;
$this->debug = $debug;
}
/**
* @param Throwable $exception
* @return void
*/
public function report(Throwable $exception)
{
if ($this->shouldntReport($exception)) {
return;
}
$logs = '';
if ($request = \request()) {
$logs = $request->getRealIp() . ' ' . $request->method() . ' ' . trim($request->fullUrl(), '/');
}
$this->logger->error($logs . PHP_EOL . $exception);
}
/**
* @param Request $request
* @param Throwable $exception
* @return Response
*/
public function render(Request $request, Throwable $exception): Response
{
$code = $exception->getCode();
if ($request->expectsJson()) {
$json = ['code' => $code ?: 500, 'msg' => $this->debug ? $exception->getMessage() : 'Server internal error'];
$this->debug && $json['traces'] = (string)$exception;
return new Response(200, ['Content-Type' => 'application/json'],
json_encode($json, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
}
$error = $this->debug ? nl2br((string)$exception) : 'Server internal error';
return new Response(500, [], $error);
}
/**
* @param Throwable $e
* @return bool
*/
protected function shouldntReport(Throwable $e): bool
{
foreach ($this->dontReport as $type) {
if ($e instanceof $type) {
return true;
}
}
return false;
}
/**
* Compatible $this->_debug
*
* @param string $name
* @return bool|null
*/
public function __get(string $name)
{
if ($name === '_debug') {
return $this->debug;
}
return null;
}
}
@@ -0,0 +1,35 @@
<?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 Throwable;
use Webman\Http\Request;
use Webman\Http\Response;
interface ExceptionHandlerInterface
{
/**
* @param Throwable $exception
* @return mixed
*/
public function report(Throwable $exception);
/**
* @param Request $request
* @param Throwable $exception
* @return Response
*/
public function render(Request $request, Throwable $exception): Response;
}
@@ -0,0 +1,25 @@
<?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;
/**
* Class FileException
* @package Webman\Exception
*/
class FileException extends RuntimeException
{
}
@@ -0,0 +1,25 @@
<?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 Psr\Container\NotFoundExceptionInterface;
/**
* Class NotFoundException
* @package Webman\Exception
*/
class NotFoundException extends \Exception implements NotFoundExceptionInterface
{
}
+56
View File
@@ -0,0 +1,56 @@
<?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;
use SplFileInfo;
use Webman\Exception\FileException;
use function chmod;
use function is_dir;
use function mkdir;
use function pathinfo;
use function restore_error_handler;
use function set_error_handler;
use function sprintf;
use function strip_tags;
use function umask;
class File extends SplFileInfo
{
/**
* Move.
* @param string $destination
* @return File
*/
public function move(string $destination): File
{
set_error_handler(function ($type, $msg) use (&$error) {
$error = $msg;
});
$path = pathinfo($destination, PATHINFO_DIRNAME);
if (!is_dir($path) && !mkdir($path, 0777, true)) {
restore_error_handler();
throw new FileException(sprintf('Unable to create the "%s" directory (%s)', $path, strip_tags($error)));
}
if (!rename($this->getPathname(), $destination)) {
restore_error_handler();
throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $destination, strip_tags($error)));
}
restore_error_handler();
@chmod($destination, 0666 & ~umask());
return new self($destination);
}
}
@@ -0,0 +1,25 @@
<?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;
/**
* This deprecated class will certainly be removed in the future.
* Please use Webman\Session\FileSessionHandler
* @deprecated
* @package Webman
*/
class FileSessionHandler extends Session\FileSessionHandler
{
}
+321
View File
@@ -0,0 +1,321 @@
<?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\Http;
use Webman\Route\Route;
use function current;
use function filter_var;
use function ip2long;
use function is_array;
use function strpos;
use const FILTER_FLAG_IPV4;
use const FILTER_FLAG_NO_PRIV_RANGE;
use const FILTER_FLAG_NO_RES_RANGE;
use const FILTER_VALIDATE_IP;
/**
* Class Request
* @package Webman\Http
*/
class Request extends \Workerman\Protocols\Http\Request
{
/**
* @var string
*/
public $plugin = null;
/**
* @var string
*/
public $app = null;
/**
* @var string
*/
public $controller = null;
/**
* @var string
*/
public $action = null;
/**
* @var Route
*/
public $route = null;
/**
* @return mixed|null
*/
public function all()
{
return $this->post() + $this->get();
}
/**
* Input
* @param string $name
* @param mixed $default
* @return mixed|null
*/
public function input(string $name, $default = null)
{
$post = $this->post();
if (isset($post[$name])) {
return $post[$name];
}
$get = $this->get();
return $get[$name] ?? $default;
}
/**
* Only
* @param array $keys
* @return array
*/
public function only(array $keys): array
{
$all = $this->all();
$result = [];
foreach ($keys as $key) {
if (isset($all[$key])) {
$result[$key] = $all[$key];
}
}
return $result;
}
/**
* Except
* @param array $keys
* @return mixed|null
*/
public function except(array $keys)
{
$all = $this->all();
foreach ($keys as $key) {
unset($all[$key]);
}
return $all;
}
/**
* File
* @param string|null $name
* @return null|UploadFile[]|UploadFile
*/
public function file($name = null)
{
$files = parent::file($name);
if (null === $files) {
return $name === null ? [] : null;
}
if ($name !== null) {
// Multi files
if (is_array(current($files))) {
return $this->parseFiles($files);
}
return $this->parseFile($files);
}
$uploadFiles = [];
foreach ($files as $name => $file) {
// Multi files
if (is_array(current($file))) {
$uploadFiles[$name] = $this->parseFiles($file);
} else {
$uploadFiles[$name] = $this->parseFile($file);
}
}
return $uploadFiles;
}
/**
* ParseFile
* @param array $file
* @return UploadFile
*/
protected function parseFile(array $file): UploadFile
{
return new UploadFile($file['tmp_name'], $file['name'], $file['type'], $file['error']);
}
/**
* ParseFiles
* @param array $files
* @return array
*/
protected function parseFiles(array $files): array
{
$uploadFiles = [];
foreach ($files as $key => $file) {
if (is_array(current($file))) {
$uploadFiles[$key] = $this->parseFiles($file);
} else {
$uploadFiles[$key] = $this->parseFile($file);
}
}
return $uploadFiles;
}
/**
* GetRemoteIp
* @return string
*/
public function getRemoteIp(): string
{
return $this->connection->getRemoteIp();
}
/**
* GetRemotePort
* @return int
*/
public function getRemotePort(): int
{
return $this->connection->getRemotePort();
}
/**
* GetLocalIp
* @return string
*/
public function getLocalIp(): string
{
return $this->connection->getLocalIp();
}
/**
* GetLocalPort
* @return int
*/
public function getLocalPort(): int
{
return $this->connection->getLocalPort();
}
/**
* GetRealIp
* @param bool $safeMode
* @return string
*/
public function getRealIp(bool $safeMode = true): string
{
$remoteIp = $this->getRemoteIp();
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)))));
if (is_string($ip)) {
$ip = current(explode(',', $ip));
}
return filter_var($ip, FILTER_VALIDATE_IP) ? $ip : $remoteIp;
}
/**
* Url
* @return string
*/
public function url(): string
{
return '//' . $this->host() . $this->path();
}
/**
* FullUrl
* @return string
*/
public function fullUrl(): string
{
return '//' . $this->host() . $this->uri();
}
/**
* IsAjax
* @return bool
*/
public function isAjax(): bool
{
return $this->header('X-Requested-With') === 'XMLHttpRequest';
}
/**
* IsPjax
* @return bool
*/
public function isPjax(): bool
{
return (bool)$this->header('X-PJAX');
}
/**
* ExpectsJson
* @return bool
*/
public function expectsJson(): bool
{
return ($this->isAjax() && !$this->isPjax()) || $this->acceptJson();
}
/**
* AcceptJson
* @return bool
*/
public function acceptJson(): bool
{
return false !== strpos($this->header('accept', ''), 'json');
}
/**
* IsIntranetIp
* @param string $ip
* @return bool
*/
public static function isIntranetIp(string $ip): bool
{
// Not validate ip .
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
return false;
}
// Is intranet ip ? For IPv4, the result of false may not be accurate, so we need to check it manually later .
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
return true;
}
// Manual check only for IPv4 .
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
return false;
}
// Manual check .
$reservedIps = [
1681915904 => 1686110207, // 100.64.0.0 - 100.127.255.255
3221225472 => 3221225727, // 192.0.0.0 - 192.0.0.255
3221225984 => 3221226239, // 192.0.2.0 - 192.0.2.255
3227017984 => 3227018239, // 192.88.99.0 - 192.88.99.255
3323068416 => 3323199487, // 198.18.0.0 - 198.19.255.255
3325256704 => 3325256959, // 198.51.100.0 - 198.51.100.255
3405803776 => 3405804031, // 203.0.113.0 - 203.0.113.255
3758096384 => 4026531839, // 224.0.0.0 - 239.255.255.255
];
$ipLong = ip2long($ip);
foreach ($reservedIps as $ipStart => $ipEnd) {
if (($ipLong >= $ipStart) && ($ipLong <= $ipEnd)) {
return true;
}
}
return false;
}
}
+87
View File
@@ -0,0 +1,87 @@
<?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\Http;
use Throwable;
use Webman\App;
use function filemtime;
use function gmdate;
/**
* Class Response
* @package Webman\Http
*/
class Response extends \Workerman\Protocols\Http\Response
{
/**
* @var Throwable
*/
protected $exception = null;
/**
* File
* @param string $file
* @return $this
*/
public function file(string $file): Response
{
if ($this->notModifiedSince($file)) {
return $this->withStatus(304);
}
return $this->withFile($file);
}
/**
* Download
* @param string $file
* @param string $downloadName
* @return $this
*/
public function download(string $file, string $downloadName = ''): Response
{
$this->withFile($file);
if ($downloadName) {
$this->header('Content-Disposition', "attachment; filename=\"$downloadName\"");
}
return $this;
}
/**
* NotModifiedSince
* @param string $file
* @return bool
*/
protected function notModifiedSince(string $file): bool
{
$ifModifiedSince = App::request()->header('if-modified-since');
if ($ifModifiedSince === null || !is_file($file) || !($mtime = filemtime($file))) {
return false;
}
return $ifModifiedSince === gmdate('D, d M Y H:i:s', $mtime) . ' GMT';
}
/**
* Exception
* @param Throwable|null $exception
* @return Throwable|null
*/
public function exception(Throwable $exception = null): ?Throwable
{
if ($exception) {
$this->exception = $exception;
}
return $this->exception;
}
}
@@ -0,0 +1,111 @@
<?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\Http;
use Webman\File;
use function pathinfo;
/**
* Class UploadFile
* @package Webman\Http
*/
class UploadFile extends File
{
/**
* @var string
*/
protected $uploadName = null;
/**
* @var string
*/
protected $uploadMimeType = null;
/**
* @var int
*/
protected $uploadErrorCode = null;
/**
* UploadFile constructor.
*
* @param string $fileName
* @param string $uploadName
* @param string $uploadMimeType
* @param int $uploadErrorCode
*/
public function __construct(string $fileName, string $uploadName, string $uploadMimeType, int $uploadErrorCode)
{
$this->uploadName = $uploadName;
$this->uploadMimeType = $uploadMimeType;
$this->uploadErrorCode = $uploadErrorCode;
parent::__construct($fileName);
}
/**
* GetUploadName
* @return string
*/
public function getUploadName(): ?string
{
return $this->uploadName;
}
/**
* GetUploadMimeType
* @return string
*/
public function getUploadMimeType(): ?string
{
return $this->uploadMimeType;
}
/**
* GetUploadExtension
* @return string
*/
public function getUploadExtension(): string
{
return pathinfo($this->uploadName, PATHINFO_EXTENSION);
}
/**
* GetUploadErrorCode
* @return int
*/
public function getUploadErrorCode(): ?int
{
return $this->uploadErrorCode;
}
/**
* IsValid
* @return bool
*/
public function isValid(): bool
{
return $this->uploadErrorCode === UPLOAD_ERR_OK;
}
/**
* GetUploadMineType
* @return string
* @deprecated
*/
public function getUploadMineType(): ?string
{
return $this->uploadMimeType;
}
}
+59
View File
@@ -0,0 +1,59 @@
<?php
namespace Webman;
class Install
{
const WEBMAN_PLUGIN = true;
/**
* @var array
*/
protected static $pathRelation = [
'start.php' => 'start.php',
'windows.php' => 'windows.php',
'support/bootstrap.php' => 'support/bootstrap.php',
'support/helpers.php' => 'support/helpers.php',
];
/**
* Install
* @return void
*/
public static function install()
{
static::installByRelation();
}
/**
* Uninstall
* @return void
*/
public static function uninstall()
{
}
/**
* InstallByRelation
* @return void
*/
public static function installByRelation()
{
foreach (static::$pathRelation as $source => $dest) {
if ($pos = strrpos($dest, '/')) {
$parentDir = base_path() . '/' . substr($dest, 0, $pos);
if (!is_dir($parentDir)) {
mkdir($parentDir, 0777, true);
}
}
$sourceFile = __DIR__ . "/$source";
copy_dir($sourceFile, base_path() . "/$dest", true);
echo "Create $dest\r\n";
if (is_file($sourceFile)) {
@unlink($sourceFile);
}
}
}
}
+90
View File
@@ -0,0 +1,90 @@
<?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;
use RuntimeException;
use function array_merge;
use function array_reverse;
use function is_array;
use function method_exists;
class Middleware
{
/**
* @var array
*/
protected static $instances = [];
/**
* @param mixed $allMiddlewares
* @param string $plugin
* @return void
*/
public static function load($allMiddlewares, string $plugin = '')
{
if (!is_array($allMiddlewares)) {
return;
}
foreach ($allMiddlewares as $appName => $middlewares) {
if (!is_array($middlewares)) {
throw new RuntimeException('Bad middleware config');
}
if ($appName === '@') {
$plugin = '';
}
if (strpos($appName, 'plugin.') !== false) {
$explode = explode('.', $appName, 4);
$plugin = $explode[1];
$appName = $explode[2] ?? '';
}
foreach ($middlewares as $className) {
if (method_exists($className, 'process')) {
static::$instances[$plugin][$appName][] = [$className, 'process'];
} else {
// @todo Log
echo "middleware $className::process not exsits\n";
}
}
}
}
/**
* @param string $plugin
* @param string $appName
* @param bool $withGlobalMiddleware
* @return array|mixed
*/
public static function getMiddleware(string $plugin, string $appName, bool $withGlobalMiddleware = true)
{
$globalMiddleware = static::$instances['']['@'] ?? [];
$appGlobalMiddleware = $withGlobalMiddleware && isset(static::$instances[$plugin]['']) ? static::$instances[$plugin][''] : [];
if ($appName === '') {
return array_reverse(array_merge($globalMiddleware, $appGlobalMiddleware));
}
$appMiddleware = static::$instances[$plugin][$appName] ?? [];
return array_reverse(array_merge($globalMiddleware, $appGlobalMiddleware, $appMiddleware));
}
/**
* @return void
* @deprecated
*/
public static function container($_)
{
}
}
@@ -0,0 +1,30 @@
<?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;
use Webman\Http\Request;
use Webman\Http\Response;
interface MiddlewareInterface
{
/**
* Process an incoming server request.
*
* Processes an incoming server request in order to produce a response.
* If unable to produce the response itself, it may delegate to the provided
* request handler to do so.
*/
public function process(Request $request, callable $handler): Response;
}
+472
View File
@@ -0,0 +1,472 @@
<?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;
use FastRoute\Dispatcher\GroupCountBased;
use FastRoute\RouteCollector;
use FilesystemIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use Webman\Route\Route as RouteObject;
use function array_diff;
use function array_values;
use function class_exists;
use function explode;
use function FastRoute\simpleDispatcher;
use function in_array;
use function is_array;
use function is_callable;
use function is_file;
use function is_scalar;
use function is_string;
use function json_encode;
use function method_exists;
use function strpos;
/**
* Class Route
* @package Webman
*/
class Route
{
/**
* @var Route
*/
protected static $instance = null;
/**
* @var GroupCountBased
*/
protected static $dispatcher = null;
/**
* @var RouteCollector
*/
protected static $collector = null;
/**
* @var null|callable
*/
protected static $fallback = [];
/**
* @var array
*/
protected static $nameList = [];
/**
* @var string
*/
protected static $groupPrefix = '';
/**
* @var bool
*/
protected static $disableDefaultRoute = [];
/**
* @var RouteObject[]
*/
protected static $allRoutes = [];
/**
* @var RouteObject[]
*/
protected $routes = [];
/**
* @var Route[]
*/
protected $children = [];
/**
* @param string $path
* @param callable|mixed $callback
* @return RouteObject
*/
public static function get(string $path, $callback): RouteObject
{
return static::addRoute('GET', $path, $callback);
}
/**
* @param string $path
* @param callable|mixed $callback
* @return RouteObject
*/
public static function post(string $path, $callback): RouteObject
{
return static::addRoute('POST', $path, $callback);
}
/**
* @param string $path
* @param callable|mixed $callback
* @return RouteObject
*/
public static function put(string $path, $callback): RouteObject
{
return static::addRoute('PUT', $path, $callback);
}
/**
* @param string $path
* @param callable|mixed $callback
* @return RouteObject
*/
public static function patch(string $path, $callback): RouteObject
{
return static::addRoute('PATCH', $path, $callback);
}
/**
* @param string $path
* @param callable|mixed $callback
* @return RouteObject
*/
public static function delete(string $path, $callback): RouteObject
{
return static::addRoute('DELETE', $path, $callback);
}
/**
* @param string $path
* @param callable|mixed $callback
* @return RouteObject
*/
public static function head(string $path, $callback): RouteObject
{
return static::addRoute('HEAD', $path, $callback);
}
/**
* @param string $path
* @param callable|mixed $callback
* @return RouteObject
*/
public static function options(string $path, $callback): RouteObject
{
return static::addRoute('OPTIONS', $path, $callback);
}
/**
* @param string $path
* @param callable|mixed $callback
* @return RouteObject
*/
public static function any(string $path, $callback): RouteObject
{
return static::addRoute(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'], $path, $callback);
}
/**
* @param $method
* @param string $path
* @param callable|mixed $callback
* @return RouteObject
*/
public static function add($method, string $path, $callback): RouteObject
{
return static::addRoute($method, $path, $callback);
}
/**
* @param string|callable $path
* @param callable|null $callback
* @return static
*/
public static function group($path, callable $callback = null): Route
{
if ($callback === null) {
$callback = $path;
$path = '';
}
$previousGroupPrefix = static::$groupPrefix;
static::$groupPrefix = $previousGroupPrefix . $path;
$previousInstance = static::$instance;
$instance = static::$instance = new static;
static::$collector->addGroup($path, $callback);
static::$groupPrefix = $previousGroupPrefix;
static::$instance = $previousInstance;
if ($previousInstance) {
$previousInstance->addChild($instance);
}
return $instance;
}
/**
* @param string $name
* @param string $controller
* @param array $options
* @return void
*/
public static function resource(string $name, string $controller, array $options = [])
{
$name = trim($name, '/');
if (is_array($options) && !empty($options)) {
$diffOptions = array_diff($options, ['index', 'create', 'store', 'update', 'show', 'edit', 'destroy', 'recovery']);
if (!empty($diffOptions)) {
foreach ($diffOptions as $action) {
static::any("/$name/{$action}[/{id}]", [$controller, $action])->name("$name.{$action}");
}
}
// 注册路由 由于顺序不同会导致路由无效 因此不适用循环注册
if (in_array('index', $options)) static::get("/$name", [$controller, 'index'])->name("$name.index");
if (in_array('create', $options)) static::get("/$name/create", [$controller, 'create'])->name("$name.create");
if (in_array('store', $options)) static::post("/$name", [$controller, 'store'])->name("$name.store");
if (in_array('update', $options)) static::put("/$name/{id}", [$controller, 'update'])->name("$name.update");
if (in_array('patch', $options)) static::patch("/$name/{id}", [$controller, 'patch'])->name("$name.patch");
if (in_array('show', $options)) static::get("/$name/{id}", [$controller, 'show'])->name("$name.show");
if (in_array('edit', $options)) static::get("/$name/{id}/edit", [$controller, 'edit'])->name("$name.edit");
if (in_array('destroy', $options)) static::delete("/$name/{id}", [$controller, 'destroy'])->name("$name.destroy");
if (in_array('recovery', $options)) static::put("/$name/{id}/recovery", [$controller, 'recovery'])->name("$name.recovery");
} else {
//为空时自动注册所有常用路由
if (method_exists($controller, 'index')) static::get("/$name", [$controller, 'index'])->name("$name.index");
if (method_exists($controller, 'create')) static::get("/$name/create", [$controller, 'create'])->name("$name.create");
if (method_exists($controller, 'store')) static::post("/$name", [$controller, 'store'])->name("$name.store");
if (method_exists($controller, 'update')) static::put("/$name/{id}", [$controller, 'update'])->name("$name.update");
if (method_exists($controller, 'patch')) static::patch("/$name/{id}", [$controller, 'patch'])->name("$name.patch");
if (method_exists($controller, 'show')) static::get("/$name/{id}", [$controller, 'show'])->name("$name.show");
if (method_exists($controller, 'edit')) static::get("/$name/{id}/edit", [$controller, 'edit'])->name("$name.edit");
if (method_exists($controller, 'destroy')) static::delete("/$name/{id}", [$controller, 'destroy'])->name("$name.destroy");
if (method_exists($controller, 'recovery')) static::put("/$name/{id}/recovery", [$controller, 'recovery'])->name("$name.recovery");
}
}
/**
* @return RouteObject[]
*/
public static function getRoutes(): array
{
return static::$allRoutes;
}
/**
* disableDefaultRoute.
*
* @return void
*/
public static function disableDefaultRoute($plugin = '')
{
static::$disableDefaultRoute[$plugin] = true;
}
/**
* @param string $plugin
* @return bool
*/
public static function hasDisableDefaultRoute(string $plugin = ''): bool
{
return static::$disableDefaultRoute[$plugin] ?? false;
}
/**
* @param $middleware
* @return $this
*/
public function middleware($middleware): Route
{
foreach ($this->routes as $route) {
$route->middleware($middleware);
}
foreach ($this->getChildren() as $child) {
$child->middleware($middleware);
}
return $this;
}
/**
* @param RouteObject $route
*/
public function collect(RouteObject $route)
{
$this->routes[] = $route;
}
/**
* @param string $name
* @param RouteObject $instance
*/
public static function setByName(string $name, RouteObject $instance)
{
static::$nameList[$name] = $instance;
}
/**
* @param string $name
* @return null|RouteObject
*/
public static function getByName(string $name): ?RouteObject
{
return static::$nameList[$name] ?? null;
}
/**
* @param Route $route
* @return void
*/
public function addChild(Route $route)
{
$this->children[] = $route;
}
/**
* @return Route[]
*/
public function getChildren()
{
return $this->children;
}
/**
* @param string $method
* @param string $path
* @return array
*/
public static function dispatch(string $method, string $path): array
{
return static::$dispatcher->dispatch($method, $path);
}
/**
* @param string $path
* @param callable|mixed $callback
* @return callable|false|string[]
*/
public static function convertToCallable(string $path, $callback)
{
if (is_string($callback) && strpos($callback, '@')) {
$callback = explode('@', $callback, 2);
}
if (!is_array($callback)) {
if (!is_callable($callback)) {
$callStr = is_scalar($callback) ? $callback : 'Closure';
echo "Route $path $callStr is not callable\n";
return false;
}
} else {
$callback = array_values($callback);
if (!isset($callback[1]) || !class_exists($callback[0]) || !method_exists($callback[0], $callback[1])) {
echo "Route $path " . json_encode($callback) . " is not callable\n";
return false;
}
}
return $callback;
}
/**
* @param array|string $methods
* @param string $path
* @param callable|mixed $callback
* @return RouteObject
*/
protected static function addRoute($methods, string $path, $callback): RouteObject
{
$route = new RouteObject($methods, static::$groupPrefix . $path, $callback);
static::$allRoutes[] = $route;
if ($callback = static::convertToCallable($path, $callback)) {
static::$collector->addRoute($methods, $path, ['callback' => $callback, 'route' => $route]);
}
if (static::$instance) {
static::$instance->collect($route);
}
return $route;
}
/**
* Load.
* @param mixed $paths
* @return void
*/
public static function load($paths)
{
if (!is_array($paths)) {
return;
}
static::$dispatcher = simpleDispatcher(function (RouteCollector $route) use ($paths) {
Route::setCollector($route);
foreach ($paths as $configPath) {
$routeConfigFile = $configPath . '/route.php';
if (is_file($routeConfigFile)) {
require_once $routeConfigFile;
}
if (!is_dir($pluginConfigPath = $configPath . '/plugin')) {
continue;
}
$dirIterator = new RecursiveDirectoryIterator($pluginConfigPath, FilesystemIterator::FOLLOW_SYMLINKS);
$iterator = new RecursiveIteratorIterator($dirIterator);
foreach ($iterator as $file) {
if ($file->getBaseName('.php') !== 'route') {
continue;
}
$appConfigFile = pathinfo($file, PATHINFO_DIRNAME) . '/app.php';
if (!is_file($appConfigFile)) {
continue;
}
$appConfig = include $appConfigFile;
if (empty($appConfig['enable'])) {
continue;
}
require_once $file;
}
}
});
}
/**
* SetCollector.
* @param RouteCollector $route
* @return void
*/
public static function setCollector(RouteCollector $route)
{
static::$collector = $route;
}
/**
* Fallback.
* @param callable|mixed $callback
* @param string $plugin
* @return void
*/
public static function fallback(callable $callback, string $plugin = '')
{
static::$fallback[$plugin] = $callback;
}
/**
* GetFallBack.
* @param string $plugin
* @return callable|null
*/
public static function getFallback(string $plugin = ''): ?callable
{
return static::$fallback[$plugin] ?? null;
}
/**
* @return void
* @deprecated
*/
public static function container()
{
}
}
+199
View File
@@ -0,0 +1,199 @@
<?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\Route;
use Webman\Route as Router;
use function array_merge;
use function count;
use function preg_replace_callback;
use function str_replace;
/**
* Class Route
* @package Webman
*/
class Route
{
/**
* @var string|null
*/
protected $name = null;
/**
* @var array
*/
protected $methods = [];
/**
* @var string
*/
protected $path = '';
/**
* @var callable
*/
protected $callback = null;
/**
* @var array
*/
protected $middlewares = [];
/**
* @var array
*/
protected $params = [];
/**
* Route constructor.
* @param array $methods
* @param string $path
* @param callable $callback
*/
public function __construct($methods, string $path, $callback)
{
$this->methods = (array)$methods;
$this->path = $path;
$this->callback = $callback;
}
/**
* Get name.
* @return string|null
*/
public function getName(): ?string
{
return $this->name ?? null;
}
/**
* Name.
* @param string $name
* @return $this
*/
public function name(string $name): Route
{
$this->name = $name;
Router::setByName($name, $this);
return $this;
}
/**
* Middleware.
* @param mixed $middleware
* @return $this|array
*/
public function middleware($middleware = null)
{
if ($middleware === null) {
return $this->middlewares;
}
$this->middlewares = array_merge($this->middlewares, is_array($middleware) ? array_reverse($middleware) : [$middleware]);
return $this;
}
/**
* GetPath.
* @return string
*/
public function getPath(): string
{
return $this->path;
}
/**
* GetMethods.
* @return array
*/
public function getMethods(): array
{
return $this->methods;
}
/**
* GetCallback.
* @return callable|null
*/
public function getCallback()
{
return $this->callback;
}
/**
* GetMiddleware.
* @return array
*/
public function getMiddleware(): array
{
return $this->middlewares;
}
/**
* Param.
* @param string|null $name
* @param $default
* @return array|mixed|null
*/
public function param(string $name = null, $default = null)
{
if ($name === null) {
return $this->params;
}
return $this->params[$name] ?? $default;
}
/**
* SetParams.
* @param array $params
* @return $this
*/
public function setParams(array $params): Route
{
$this->params = array_merge($this->params, $params);
return $this;
}
/**
* Url.
* @param array $parameters
* @return string
*/
public function url(array $parameters = []): string
{
if (empty($parameters)) {
return $this->path;
}
$path = str_replace(['[', ']'], '', $this->path);
$path = preg_replace_callback('/\{(.*?)(?:\:[^\}]*?)*?\}/', function ($matches) use (&$parameters) {
if (!$parameters) {
return $matches[0];
}
if (isset($parameters[$matches[1]])) {
$value = $parameters[$matches[1]];
unset($parameters[$matches[1]]);
return $value;
}
$key = key($parameters);
if (is_int($key)) {
$value = $parameters[$key];
unset($parameters[$key]);
return $value;
}
return $matches[0];
}, $path);
return count($parameters) > 0 ? $path . '?' . http_build_query($parameters) : $path;
}
}
@@ -0,0 +1,26 @@
<?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\Session;
use Workerman\Protocols\Http\Session\FileSessionHandler as FileHandler;
/**
* Class FileSessionHandler
* @package Webman
*/
class FileSessionHandler extends FileHandler
{
}
@@ -0,0 +1,22 @@
<?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\Session;
use Workerman\Protocols\Http\Session\RedisClusterSessionHandler as RedisClusterHandler;
class RedisClusterSessionHandler extends RedisClusterHandler
{
}
@@ -0,0 +1,26 @@
<?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\Session;
use Workerman\Protocols\Http\Session\RedisSessionHandler as RedisHandler;
/**
* Class FileSessionHandler
* @package Webman
*/
class RedisSessionHandler extends RedisHandler
{
}
+44
View File
@@ -0,0 +1,44 @@
<?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;
use function array_diff;
use function array_map;
use function scandir;
/**
* Class Util
* @package Webman
*/
class Util
{
/**
* ScanDir.
* @param string $basePath
* @param bool $withBasePath
* @return array
*/
public static function scanDir(string $basePath, bool $withBasePath = true): array
{
if (!is_dir($basePath)) {
return [];
}
$paths = array_diff(scandir($basePath), array('.', '..')) ?: [];
return $withBasePath ? array_map(static function ($path) use ($basePath) {
return $basePath . DIRECTORY_SEPARATOR . $path;
}, $paths) : $paths;
}
}
+27
View File
@@ -0,0 +1,27 @@
<?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;
interface View
{
/**
* Render.
* @param string $template
* @param array $vars
* @param string|null $app
* @return string
*/
public static function render(string $template, array $vars, string $app = null): string;
}
+151
View File
@@ -0,0 +1,151 @@
<?php
namespace support;
use Dotenv\Dotenv;
use RuntimeException;
use Webman\Config;
use Webman\Util;
use Workerman\Connection\TcpConnection;
use Workerman\Worker;
use function base_path;
use function call_user_func;
use function is_dir;
use function opcache_get_status;
use function opcache_invalidate;
use const DIRECTORY_SEPARATOR;
class App
{
/**
* Run.
* @return void
*/
public static function run()
{
ini_set('display_errors', 'on');
error_reporting(E_ALL);
if (class_exists(Dotenv::class) && file_exists(run_path('.env'))) {
if (method_exists(Dotenv::class, 'createUnsafeImmutable')) {
Dotenv::createUnsafeImmutable(run_path())->load();
} else {
Dotenv::createMutable(run_path())->load();
}
}
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)) {
if (!mkdir($runtimeLogsPath, 0777, true)) {
throw new RuntimeException("Failed to create runtime logs directory. Please check the permission.");
}
}
$runtimeViewsPath = runtime_path() . DIRECTORY_SEPARATOR . 'views';
if (!file_exists($runtimeViewsPath) || !is_dir($runtimeViewsPath)) {
if (!mkdir($runtimeViewsPath, 0777, true)) {
throw new RuntimeException("Failed to create runtime views directory. Please check the permission.");
}
}
Worker::$onMasterReload = function () {
if (function_exists('opcache_get_status')) {
if ($status = opcache_get_status()) {
if (isset($status['scripts']) && $scripts = $status['scripts']) {
foreach (array_keys($scripts) as $file) {
opcache_invalidate($file, true);
}
}
}
}
};
$config = config('server');
Worker::$pidFile = $config['pid_file'];
Worker::$stdoutFile = $config['stdout_file'];
Worker::$logFile = $config['log_file'];
Worker::$eventLoopClass = $config['event_loop'] ?? '';
TcpConnection::$defaultMaxPackageSize = $config['max_package_size'] ?? 10 * 1024 * 1024;
if (property_exists(Worker::class, 'statusFile')) {
Worker::$statusFile = $config['status_file'] ?? '';
}
if (property_exists(Worker::class, 'stopTimeout')) {
Worker::$stopTimeout = $config['stop_timeout'] ?? 2;
}
if ($config['listen']) {
$worker = new Worker($config['listen'], $config['context']);
$propertyMap = [
'name',
'count',
'user',
'group',
'reusePort',
'transport',
'protocol'
];
foreach ($propertyMap as $property) {
if (isset($config[$property])) {
$worker->$property = $config[$property];
}
}
$worker->onWorkerStart = function ($worker) {
require_once base_path() . '/support/bootstrap.php';
$app = new \Webman\App(config('app.request_class', Request::class), Log::channel('default'), app_path(), public_path());
$worker->onMessage = [$app, 'onMessage'];
call_user_func([$app, 'onWorkerStart'], $worker);
};
}
// Windows does not support custom processes.
if (DIRECTORY_SEPARATOR === '/') {
foreach (config('process', []) as $processName => $config) {
worker_start($processName, $config);
}
foreach (config('plugin', []) as $firm => $projects) {
foreach ($projects as $name => $project) {
if (!is_array($project)) {
continue;
}
foreach ($project['process'] ?? [] as $processName => $config) {
worker_start("plugin.$firm.$name.$processName", $config);
}
}
foreach ($projects['process'] ?? [] as $processName => $config) {
worker_start("plugin.$firm.$processName", $config);
}
}
}
Worker::runAll();
}
/**
* LoadAllConfig.
* @param array $excludes
* @return void
*/
public static function loadAllConfig(array $excludes = [])
{
Config::load(config_path(), $excludes);
$directory = base_path() . '/plugin';
foreach (Util::scanDir($directory, false) as $name) {
$dir = "$directory/$name/config";
if (is_dir($dir)) {
Config::load($dir, $excludes, "plugin.$name");
}
}
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
namespace support;
use Symfony\Component\Cache\Adapter\RedisAdapter;
use Symfony\Component\Cache\Psr16Cache;
/**
* Class Cache
* @package support\bootstrap
*
* Strings methods
* @method static mixed get($key, $default = null)
* @method static bool set($key, $value, $ttl = null)
* @method static bool delete($key)
* @method static bool clear()
* @method static iterable getMultiple($keys, $default = null)
* @method static bool setMultiple($values, $ttl = null)
* @method static bool deleteMultiple($keys)
* @method static bool has($key)
*/
class Cache
{
/**
* @var Psr16Cache
*/
public static $instance = null;
/**
* @return Psr16Cache
*/
public static function instance()
{
if (!static::$instance) {
$adapter = new RedisAdapter(Redis::connection()->client());
self::$instance = new Psr16Cache($adapter);
}
return static::$instance;
}
/**
* @param $name
* @param $arguments
* @return mixed
*/
public static function __callStatic($name, $arguments)
{
return static::instance()->{$name}(... $arguments);
}
}
@@ -0,0 +1,48 @@
<?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;
use Webman\Config;
/**
* Class Container
* @package support
* @method static mixed get($name)
* @method static mixed make($name, array $parameters)
* @method static bool has($name)
*/
class Container
{
/**
* Instance
* @param string $plugin
* @return array|mixed|void|null
*/
public static function instance(string $plugin = '')
{
return Config::get($plugin ? "plugin.$plugin.container" : 'container');
}
/**
* @param string $name
* @param array $arguments
* @return mixed
*/
public static function __callStatic(string $name, array $arguments)
{
$plugin = \Webman\App::getPluginByClass($name);
return static::instance($plugin)->{$name}(... $arguments);
}
}
@@ -0,0 +1,25 @@
<?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;
/**
* Class Context
* @package Webman
*/
class Context extends \Webman\Context
{
}
+36
View File
@@ -0,0 +1,36 @@
<?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;
use Closure;
use Illuminate\Database\Capsule\Manager;
/**
* Class Db
* @package support
* @method static array select(string $query, $bindings = [], $useReadPdo = true)
* @method static int insert(string $query, $bindings = [])
* @method static int update(string $query, $bindings = [])
* @method static int delete(string $query, $bindings = [])
* @method static bool statement(string $query, $bindings = [])
* @method static mixed transaction(Closure $callback, $attempts = 1)
* @method static void beginTransaction()
* @method static void rollBack($toLevel = null)
* @method static void commit()
*/
class Db extends Manager
{
}
+139
View File
@@ -0,0 +1,139 @@
<?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;
use Monolog\Formatter\FormatterInterface;
use Monolog\Handler\FormattableHandlerInterface;
use Monolog\Handler\HandlerInterface;
use Monolog\Logger;
use function array_values;
use function config;
use function is_array;
/**
* Class Log
* @package support
*
* @method static void log($level, $message, array $context = [])
* @method static void debug($message, array $context = [])
* @method static void info($message, array $context = [])
* @method static void notice($message, array $context = [])
* @method static void warning($message, array $context = [])
* @method static void error($message, array $context = [])
* @method static void critical($message, array $context = [])
* @method static void alert($message, array $context = [])
* @method static void emergency($message, array $context = [])
*/
class Log
{
/**
* @var array
*/
protected static $instance = [];
/**
* Channel.
* @param string $name
* @return Logger
*/
public static function channel(string $name = 'default'): Logger
{
if (!isset(static::$instance[$name])) {
$config = config('log', [])[$name];
$handlers = self::handlers($config);
$processors = self::processors($config);
static::$instance[$name] = new Logger($name, $handlers, $processors);
}
return static::$instance[$name];
}
/**
* Handlers.
* @param array $config
* @return array
*/
protected static function handlers(array $config): array
{
$handlerConfigs = $config['handlers'] ?? [[]];
$handlers = [];
foreach ($handlerConfigs as $value) {
$class = $value['class'] ?? [];
$constructor = $value['constructor'] ?? [];
$formatterConfig = $value['formatter'] ?? [];
$class && $handlers[] = self::handler($class, $constructor, $formatterConfig);
}
return $handlers;
}
/**
* Handler.
* @param string $class
* @param array $constructor
* @param array $formatterConfig
* @return HandlerInterface
*/
protected static function handler(string $class, array $constructor, array $formatterConfig): HandlerInterface
{
/** @var HandlerInterface $handler */
$handler = new $class(... array_values($constructor));
if ($handler instanceof FormattableHandlerInterface && $formatterConfig) {
$formatterClass = $formatterConfig['class'];
$formatterConstructor = $formatterConfig['constructor'];
/** @var FormatterInterface $formatter */
$formatter = new $formatterClass(... array_values($formatterConstructor));
$handler->setFormatter($formatter);
}
return $handler;
}
/**
* Processors.
* @param array $config
* @return array
*/
protected static function processors(array $config): array
{
$result = [];
if (!isset($config['processors']) && isset($config['processor'])) {
$config['processors'] = [$config['processor']];
}
foreach ($config['processors'] ?? [] as $value) {
if (is_array($value) && isset($value['class'])) {
$value = new $value['class'](... array_values($value['constructor'] ?? []));
}
$result[] = $value;
}
return $result;
}
/**
* @param string $name
* @param array $arguments
* @return mixed
*/
public static function __callStatic(string $name, array $arguments)
{
return static::channel()->{$name}(... $arguments);
}
}
+262
View File
@@ -0,0 +1,262 @@
<?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;
use Closure;
use Illuminate\Contracts\Pagination\CursorPaginator;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Database\Eloquent\Model as BaseModel;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Query\Expression;
use Illuminate\Database\Query\Grammars\Grammar;
use Illuminate\Database\Query\Processors\Processor;
use Illuminate\Support\Collection;
use Illuminate\Support\LazyCollection;
/**
* @method static BaseModel make($attributes = [])
* @method static \Illuminate\Database\Eloquent\Builder|static withGlobalScope($identifier, $scope)
* @method static \Illuminate\Database\Eloquent\Builder|static withoutGlobalScope($scope)
* @method static \Illuminate\Database\Eloquent\Builder|static withoutGlobalScopes($scopes = null)
* @method static array removedScopes()
* @method static \Illuminate\Database\Eloquent\Builder|static whereKey($id)
* @method static \Illuminate\Database\Eloquent\Builder|static whereKeyNot($id)
* @method static \Illuminate\Database\Eloquent\Builder|static where($column, $operator = null, $value = null, $boolean = 'and')
* @method static BaseModel|null firstWhere($column, $operator = null, $value = null, $boolean = 'and')
* @method static \Illuminate\Database\Eloquent\Builder|static orWhere($column, $operator = null, $value = null)
* @method static \Illuminate\Database\Eloquent\Builder|static latest($column = null)
* @method static \Illuminate\Database\Eloquent\Builder|static oldest($column = null)
* @method static \Illuminate\Database\Eloquent\Collection|static hydrate($items)
* @method static \Illuminate\Database\Eloquent\Collection|static fromQuery($query, $bindings = [])
* @method static BaseModel|\Illuminate\Database\Eloquent\Collection|static[]|static|null find($id, $columns = [])
* @method static \Illuminate\Database\Eloquent\Collection|static findMany($ids, $columns = [])
* @method static BaseModel|\Illuminate\Database\Eloquent\Collection|static|static[] findOrFail($id, $columns = [])
* @method static BaseModel|static findOrNew($id, $columns = [])
* @method static BaseModel|static firstOrNew($attributes = [], $values = [])
* @method static BaseModel|static firstOrCreate($attributes = [], $values = [])
* @method static BaseModel|static updateOrCreate($attributes, $values = [])
* @method static BaseModel|static firstOrFail($columns = [])
* @method static BaseModel|static|mixed firstOr($columns = [], $callback = null)
* @method static BaseModel sole($columns = [])
* @method static mixed value($column)
* @method static \Illuminate\Database\Eloquent\Collection[]|static[] get($columns = [])
* @method static BaseModel[]|static[] getModels($columns = [])
* @method static array eagerLoadRelations($models)
* @method static LazyCollection cursor()
* @method static Collection pluck($column, $key = null)
* @method static LengthAwarePaginator paginate($perPage = null, $columns = [], $pageName = 'page', $page = null)
* @method static Paginator simplePaginate($perPage = null, $columns = [], $pageName = 'page', $page = null)
* @method static CursorPaginator cursorPaginate($perPage = null, $columns = [], $cursorName = 'cursor', $cursor = null)
* @method static BaseModel|$this create($attributes = [])
* @method static BaseModel|$this forceCreate($attributes)
* @method static int upsert($values, $uniqueBy, $update = null)
* @method static void onDelete($callback)
* @method static static|mixed scopes($scopes)
* @method static static applyScopes()
* @method static \Illuminate\Database\Eloquent\Builder|static without($relations)
* @method static \Illuminate\Database\Eloquent\Builder|static withOnly($relations)
* @method static BaseModel newModelInstance($attributes = [])
* @method static \Illuminate\Database\Eloquent\Builder|static withCasts($casts)
* @method static Builder getQuery()
* @method static \Illuminate\Database\Eloquent\Builder|static setQuery($query)
* @method static Builder toBase()
* @method static array getEagerLoads()
* @method static \Illuminate\Database\Eloquent\Builder|static setEagerLoads($eagerLoad)
* @method static BaseModel getModel()
* @method static \Illuminate\Database\Eloquent\Builder|static setModel($model)
* @method static Closure getMacro($name)
* @method static bool hasMacro($name)
* @method static Closure getGlobalMacro($name)
* @method static bool hasGlobalMacro($name)
* @method static static clone ()
* @method static \Illuminate\Database\Eloquent\Builder|static has($relation, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
* @method static \Illuminate\Database\Eloquent\Builder|static orHas($relation, $operator = '>=', $count = 1)
* @method static \Illuminate\Database\Eloquent\Builder|static doesntHave($relation, $boolean = 'and', $callback = null)
* @method static \Illuminate\Database\Eloquent\Builder|static orDoesntHave($relation)
* @method static \Illuminate\Database\Eloquent\Builder|static whereHas($relation, $callback = null, $operator = '>=', $count = 1)
* @method static \Illuminate\Database\Eloquent\Builder|static orWhereHas($relation, $callback = null, $operator = '>=', $count = 1)
* @method static \Illuminate\Database\Eloquent\Builder|static whereDoesntHave($relation, $callback = null)
* @method static \Illuminate\Database\Eloquent\Builder|static orWhereDoesntHave($relation, $callback = null)
* @method static \Illuminate\Database\Eloquent\Builder|static hasMorph($relation, $types, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
* @method static \Illuminate\Database\Eloquent\Builder|static orHasMorph($relation, $types, $operator = '>=', $count = 1)
* @method static \Illuminate\Database\Eloquent\Builder|static doesntHaveMorph($relation, $types, $boolean = 'and', $callback = null)
* @method static \Illuminate\Database\Eloquent\Builder|static orDoesntHaveMorph($relation, $types)
* @method static \Illuminate\Database\Eloquent\Builder|static whereHasMorph($relation, $types, $callback = null, $operator = '>=', $count = 1)
* @method static \Illuminate\Database\Eloquent\Builder|static orWhereHasMorph($relation, $types, $callback = null, $operator = '>=', $count = 1)
* @method static \Illuminate\Database\Eloquent\Builder|static whereDoesntHaveMorph($relation, $types, $callback = null)
* @method static \Illuminate\Database\Eloquent\Builder|static orWhereDoesntHaveMorph($relation, $types, $callback = null)
* @method static \Illuminate\Database\Eloquent\Builder|static withAggregate($relations, $column, $function = null)
* @method static \Illuminate\Database\Eloquent\Builder|static withCount($relations)
* @method static \Illuminate\Database\Eloquent\Builder|static withMax($relation, $column)
* @method static \Illuminate\Database\Eloquent\Builder|static withMin($relation, $column)
* @method static \Illuminate\Database\Eloquent\Builder|static withSum($relation, $column)
* @method static \Illuminate\Database\Eloquent\Builder|static withAvg($relation, $column)
* @method static \Illuminate\Database\Eloquent\Builder|static withExists($relation)
* @method static \Illuminate\Database\Eloquent\Builder|static mergeConstraintsFrom($from)
* @method static Collection explain()
* @method static bool chunk($count, $callback)
* @method static Collection chunkMap($callback, $count = 1000)
* @method static bool each($callback, $count = 1000)
* @method static bool chunkById($count, $callback, $column = null, $alias = null)
* @method static bool eachById($callback, $count = 1000, $column = null, $alias = null)
* @method static LazyCollection lazy($chunkSize = 1000)
* @method static LazyCollection lazyById($chunkSize = 1000, $column = null, $alias = null)
* @method static BaseModel|object|static|null first($columns = [])
* @method static BaseModel|object|null baseSole($columns = [])
* @method static \Illuminate\Database\Eloquent\Builder|static tap($callback)
* @method static mixed when($value, $callback, $default = null)
* @method static mixed unless($value, $callback, $default = null)
* @method static Builder select($columns = [])
* @method static Builder selectSub($query, $as)
* @method static Builder selectRaw($expression, $bindings = [])
* @method static Builder fromSub($query, $as)
* @method static Builder fromRaw($expression, $bindings = [])
* @method static Builder addSelect($column)
* @method static Builder distinct()
* @method static Builder from($table, $as = null)
* @method static Builder join($table, $first, $operator = null, $second = null, $type = 'inner', $where = false)
* @method static Builder joinWhere($table, $first, $operator, $second, $type = 'inner')
* @method static Builder joinSub($query, $as, $first, $operator = null, $second = null, $type = 'inner', $where = false)
* @method static Builder leftJoin($table, $first, $operator = null, $second = null)
* @method static Builder leftJoinWhere($table, $first, $operator, $second)
* @method static Builder leftJoinSub($query, $as, $first, $operator = null, $second = null)
* @method static Builder rightJoin($table, $first, $operator = null, $second = null)
* @method static Builder rightJoinWhere($table, $first, $operator, $second)
* @method static Builder rightJoinSub($query, $as, $first, $operator = null, $second = null)
* @method static Builder crossJoin($table, $first = null, $operator = null, $second = null)
* @method static Builder crossJoinSub($query, $as)
* @method static void mergeWheres($wheres, $bindings)
* @method static array prepareValueAndOperator($value, $operator, $useDefault = false)
* @method static Builder whereColumn($first, $operator = null, $second = null, $boolean = 'and')
* @method static Builder orWhereColumn($first, $operator = null, $second = null)
* @method static Builder whereRaw($sql, $bindings = [], $boolean = 'and')
* @method static Builder orWhereRaw($sql, $bindings = [])
* @method static Builder whereIn($column, $values, $boolean = 'and', $not = false)
* @method static Builder orWhereIn($column, $values)
* @method static Builder whereNotIn($column, $values, $boolean = 'and')
* @method static Builder orWhereNotIn($column, $values)
* @method static Builder whereIntegerInRaw($column, $values, $boolean = 'and', $not = false)
* @method static Builder orWhereIntegerInRaw($column, $values)
* @method static Builder whereIntegerNotInRaw($column, $values, $boolean = 'and')
* @method static Builder orWhereIntegerNotInRaw($column, $values)
* @method static Builder whereNull($columns, $boolean = 'and', $not = false)
* @method static Builder orWhereNull($column)
* @method static Builder whereNotNull($columns, $boolean = 'and')
* @method static Builder whereBetween($column, $values, $boolean = 'and', $not = false)
* @method static Builder whereBetweenColumns($column, $values, $boolean = 'and', $not = false)
* @method static Builder orWhereBetween($column, $values)
* @method static Builder orWhereBetweenColumns($column, $values)
* @method static Builder whereNotBetween($column, $values, $boolean = 'and')
* @method static Builder whereNotBetweenColumns($column, $values, $boolean = 'and')
* @method static Builder orWhereNotBetween($column, $values)
* @method static Builder orWhereNotBetweenColumns($column, $values)
* @method static Builder orWhereNotNull($column)
* @method static Builder whereDate($column, $operator, $value = null, $boolean = 'and')
* @method static Builder orWhereDate($column, $operator, $value = null)
* @method static Builder whereTime($column, $operator, $value = null, $boolean = 'and')
* @method static Builder orWhereTime($column, $operator, $value = null)
* @method static Builder whereDay($column, $operator, $value = null, $boolean = 'and')
* @method static Builder orWhereDay($column, $operator, $value = null)
* @method static Builder whereMonth($column, $operator, $value = null, $boolean = 'and')
* @method static Builder orWhereMonth($column, $operator, $value = null)
* @method static Builder whereYear($column, $operator, $value = null, $boolean = 'and')
* @method static Builder orWhereYear($column, $operator, $value = null)
* @method static Builder whereNested($callback, $boolean = 'and')
* @method static Builder forNestedWhere()
* @method static Builder addNestedWhereQuery($query, $boolean = 'and')
* @method static Builder whereExists($callback, $boolean = 'and', $not = false)
* @method static Builder orWhereExists($callback, $not = false)
* @method static Builder whereNotExists($callback, $boolean = 'and')
* @method static Builder orWhereNotExists($callback)
* @method static Builder addWhereExistsQuery($query, $boolean = 'and', $not = false)
* @method static Builder whereRowValues($columns, $operator, $values, $boolean = 'and')
* @method static Builder orWhereRowValues($columns, $operator, $values)
* @method static Builder whereJsonContains($column, $value, $boolean = 'and', $not = false)
* @method static Builder orWhereJsonContains($column, $value)
* @method static Builder whereJsonDoesntContain($column, $value, $boolean = 'and')
* @method static Builder orWhereJsonDoesntContain($column, $value)
* @method static Builder whereJsonLength($column, $operator, $value = null, $boolean = 'and')
* @method static Builder orWhereJsonLength($column, $operator, $value = null)
* @method static Builder dynamicWhere($method, $parameters)
* @method static Builder groupBy(...$groups)
* @method static Builder groupByRaw($sql, $bindings = [])
* @method static Builder having($column, $operator = null, $value = null, $boolean = 'and')
* @method static Builder orHaving($column, $operator = null, $value = null)
* @method static Builder havingBetween($column, $values, $boolean = 'and', $not = false)
* @method static Builder havingRaw($sql, $bindings = [], $boolean = 'and')
* @method static Builder orHavingRaw($sql, $bindings = [])
* @method static Builder orderBy($column, $direction = 'asc')
* @method static Builder orderByDesc($column)
* @method static Builder inRandomOrder($seed = '')
* @method static Builder orderByRaw($sql, $bindings = [])
* @method static Builder skip($value)
* @method static Builder offset($value)
* @method static Builder take($value)
* @method static Builder limit($value)
* @method static Builder forPage($page, $perPage = 15)
* @method static Builder forPageBeforeId($perPage = 15, $lastId = 0, $column = 'id')
* @method static Builder forPageAfterId($perPage = 15, $lastId = 0, $column = 'id')
* @method static Builder reorder($column = null, $direction = 'asc')
* @method static Builder union($query, $all = false)
* @method static Builder unionAll($query)
* @method static Builder lock($value = true)
* @method static Builder lockForUpdate()
* @method static Builder sharedLock()
* @method static Builder beforeQuery($callback)
* @method static void applyBeforeQueryCallbacks()
* @method static string toSql()
* @method static int getCountForPagination($columns = [])
* @method static string implode($column, $glue = '')
* @method static bool exists()
* @method static bool doesntExist()
* @method static mixed existsOr($callback)
* @method static mixed doesntExistOr($callback)
* @method static int count($columns = '*')
* @method static mixed min($column)
* @method static mixed max($column)
* @method static mixed sum($column)
* @method static mixed avg($column)
* @method static mixed average($column)
* @method static mixed aggregate($function, $columns = [])
* @method static float|int numericAggregate($function, $columns = [])
* @method static bool insert($values)
* @method static int insertOrIgnore($values)
* @method static int insertGetId($values, $sequence = null)
* @method static int insertUsing($columns, $query)
* @method static bool updateOrInsert($attributes, $values = [])
* @method static void truncate()
* @method static Expression raw($value)
* @method static array getBindings()
* @method static array getRawBindings()
* @method static Builder setBindings($bindings, $type = 'where')
* @method static Builder addBinding($value, $type = 'where')
* @method static Builder mergeBindings($query)
* @method static array cleanBindings($bindings)
* @method static Processor getProcessor()
* @method static Grammar getGrammar()
* @method static Builder useWritePdo()
* @method static static cloneWithout($properties)
* @method static static cloneWithoutBindings($except)
* @method static Builder dump()
* @method static void dd()
* @method static void macro($name, $macro)
* @method static void mixin($mixin, $replace = true)
* @method static mixed macroCall($method, $parameters)
*/
class Model extends BaseModel
{
}
+108
View File
@@ -0,0 +1,108 @@
<?php
namespace support;
use function defined;
use function is_callable;
use function is_file;
use function method_exists;
class Plugin
{
/**
* Install.
* @param mixed $event
* @return void
*/
public static function install($event)
{
static::findHelper();
$psr4 = static::getPsr4($event);
foreach ($psr4 as $namespace => $path) {
$pluginConst = "\\{$namespace}Install::WEBMAN_PLUGIN";
if (!defined($pluginConst)) {
continue;
}
$installFunction = "\\{$namespace}Install::install";
if (is_callable($installFunction)) {
$installFunction(true);
}
}
}
/**
* Update.
* @param mixed $event
* @return void
*/
public static function update($event)
{
static::findHelper();
$psr4 = static::getPsr4($event);
foreach ($psr4 as $namespace => $path) {
$pluginConst = "\\{$namespace}Install::WEBMAN_PLUGIN";
if (!defined($pluginConst)) {
continue;
}
$updateFunction = "\\{$namespace}Install::update";
if (is_callable($updateFunction)) {
$updateFunction();
continue;
}
$installFunction = "\\{$namespace}Install::install";
if (is_callable($installFunction)) {
$installFunction(false);
}
}
}
/**
* Uninstall.
* @param mixed $event
* @return void
*/
public static function uninstall($event)
{
static::findHelper();
$psr4 = static::getPsr4($event);
foreach ($psr4 as $namespace => $path) {
$pluginConst = "\\{$namespace}Install::WEBMAN_PLUGIN";
if (!defined($pluginConst)) {
continue;
}
$uninstallFunction = "\\{$namespace}Install::uninstall";
if (is_callable($uninstallFunction)) {
$uninstallFunction();
}
}
}
/**
* Get psr-4 info
*
* @param mixed $event
* @return array
*/
protected static function getPsr4($event)
{
$operation = $event->getOperation();
$autoload = method_exists($operation, 'getPackage') ? $operation->getPackage()->getAutoload() : $operation->getTargetPackage()->getAutoload();
return $autoload['psr-4'] ?? [];
}
/**
* FindHelper.
* @return void
*/
protected static function findHelper()
{
// Plugin.php in vendor
$file = __DIR__ . '/../../../../../support/helpers.php';
if (is_file($file)) {
require_once $file;
return;
}
// Plugin.php in webman
require_once __DIR__ . '/helpers.php';
}
}
+283
View File
@@ -0,0 +1,283 @@
<?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;
use Illuminate\Events\Dispatcher;
use Illuminate\Redis\Connections\Connection;
use Illuminate\Redis\RedisManager;
use Workerman\Timer;
use Workerman\Worker;
use function class_exists;
use function config;
use function in_array;
/**
* Class Redis
* @package support
*
* Strings methods
* @method static int append($key, $value)
* @method static int bitCount($key)
* @method static int decr($key, $value = 1)
* @method static int decrBy($key, $value)
* @method static string|bool get($key)
* @method static int getBit($key, $offset)
* @method static string getRange($key, $start, $end)
* @method static string getSet($key, $value)
* @method static int incr($key, $value = 1)
* @method static int incrBy($key, $value)
* @method static float incrByFloat($key, $value)
* @method static array mGet(array $keys)
* @method static array getMultiple(array $keys)
* @method static bool mSet($pairs)
* @method static bool mSetNx($pairs)
* @method static bool set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
* @method static bool setBit($key, $offset, $value)
* @method static bool setEx($key, $ttl, $value)
* @method static bool pSetEx($key, $ttl, $value)
* @method static bool setNx($key, $value)
* @method static string setRange($key, $offset, $value)
* @method static int strLen($key)
* Keys methods
* @method static int del(...$keys)
* @method static int unlink(...$keys)
* @method static false|string dump($key)
* @method static int exists(...$keys)
* @method static bool expire($key, $ttl)
* @method static bool pexpire($key, $ttl)
* @method static bool expireAt($key, $timestamp)
* @method static bool pexpireAt($key, $timestamp)
* @method static array keys($pattern)
* @method static bool|array scan($it)
* @method static void migrate($host, $port, $keys, $dbIndex, $timeout, $copy = false, $replace = false)
* @method static bool select($dbIndex)
* @method static bool move($key, $dbIndex)
* @method static string|int|bool object($information, $key)
* @method static bool persist($key)
* @method static string randomKey()
* @method static bool rename($srcKey, $dstKey)
* @method static bool renameNx($srcKey, $dstKey)
* @method static string type($key)
* @method static int|array sort($key, $options = [])
* @method static int ttl($key)
* @method static int pttl($key)
* @method static void restore($key, $ttl, $value)
* Hashes methods
* @method static false|int hSet($key, $hashKey, $value)
* @method static bool hSetNx($key, $hashKey, $value)
* @method static false|string hGet($key, $hashKey)
* @method static false|int hLen($key)
* @method static false|int hDel($key, ...$hashKeys)
* @method static array hKeys($key)
* @method static array hVals($key)
* @method static array hGetAll($key)
* @method static bool hExists($key, $hashKey)
* @method static int hIncrBy($key, $hashKey, $value)
* @method static float hIncrByFloat($key, $hashKey, $value)
* @method static bool hMSet($key, $members)
* @method static array hMGet($key, $memberKeys)
* @method static array hScan($key, $iterator, $pattern = '', $count = 0)
* @method static int hStrLen($key, $hashKey)
* Lists methods
* @method static array blPop($keys, $timeout)
* @method static array brPop($keys, $timeout)
* @method static false|string bRPopLPush($srcKey, $dstKey, $timeout)
* @method static false|string lIndex($key, $index)
* @method static int lInsert($key, $position, $pivot, $value)
* @method static false|string lPop($key)
* @method static false|int lPush($key, ...$entries)
* @method static false|int lPushx($key, $value)
* @method static array lRange($key, $start, $end)
* @method static false|int lRem($key, $count, $value)
* @method static bool lSet($key, $index, $value)
* @method static false|array lTrim($key, $start, $end)
* @method static false|string rPop($key)
* @method static false|string rPopLPush($srcKey, $dstKey)
* @method static false|int rPush($key, ...$entries)
* @method static false|int rPushX($key, $value)
* @method static false|int lLen($key)
* Sets methods
* @method static int sAdd($key, $value)
* @method static int sCard($key)
* @method static array sDiff($keys)
* @method static false|int sDiffStore($dst, $keys)
* @method static false|array sInter($keys)
* @method static false|int sInterStore($dst, $keys)
* @method static bool sIsMember($key, $member)
* @method static array sMembers($key)
* @method static bool sMove($src, $dst, $member)
* @method static false|string|array sPop($key, $count = 0)
* @method static false|string|array sRandMember($key, $count = 0)
* @method static int sRem($key, ...$members)
* @method static array sUnion(...$keys)
* @method static false|int sUnionStore($dst, ...$keys)
* @method static false|array sScan($key, $iterator, $pattern = '', $count = 0)
* Sorted sets methods
* @method static array bzPopMin($keys, $timeout)
* @method static array bzPopMax($keys, $timeout)
* @method static int zAdd($key, $score, $value)
* @method static int zCard($key)
* @method static int zCount($key, $start, $end)
* @method static double zIncrBy($key, $value, $member)
* @method static int zinterstore($keyOutput, $arrayZSetKeys, $arrayWeights = [], $aggregateFunction = '')
* @method static array zPopMin($key, $count)
* @method static array zPopMax($key, $count)
* @method static array zRange($key, $start, $end, $withScores = false)
* @method static array zRangeByScore($key, $start, $end, $options = [])
* @method static array zRevRangeByScore($key, $start, $end, $options = [])
* @method static array zRangeByLex($key, $min, $max, $offset = 0, $limit = 0)
* @method static int zRank($key, $member)
* @method static int zRevRank($key, $member)
* @method static int zRem($key, ...$members)
* @method static int zRemRangeByRank($key, $start, $end)
* @method static int zRemRangeByScore($key, $start, $end)
* @method static array zRevRange($key, $start, $end, $withScores = false)
* @method static double zScore($key, $member)
* @method static int zunionstore($keyOutput, $arrayZSetKeys, $arrayWeights = [], $aggregateFunction = '')
* @method static false|array zScan($key, $iterator, $pattern = '', $count = 0)
* HyperLogLogs methods
* @method static int pfAdd($key, $values)
* @method static int pfCount($keys)
* @method static bool pfMerge($dstKey, $srcKeys)
* Geocoding methods
* @method static int geoAdd($key, $longitude, $latitude, $member, ...$items)
* @method static array geoHash($key, ...$members)
* @method static array geoPos($key, ...$members)
* @method static double geoDist($key, $members, $unit = '')
* @method static int|array geoRadius($key, $longitude, $latitude, $radius, $unit, $options = [])
* @method static array geoRadiusByMember($key, $member, $radius, $units, $options = [])
* Streams methods
* @method static int xAck($stream, $group, $arrMessages)
* @method static string xAdd($strKey, $strId, $arrMessage, $iMaxLen = 0, $booApproximate = false)
* @method static array xClaim($strKey, $strGroup, $strConsumer, $minIdleTime, $arrIds, $arrOptions = [])
* @method static int xDel($strKey, $arrIds)
* @method static mixed xGroup($command, $strKey, $strGroup, $strMsgId, $booMKStream = null)
* @method static mixed xInfo($command, $strStream, $strGroup = null)
* @method static int xLen($stream)
* @method static array xPending($strStream, $strGroup, $strStart = 0, $strEnd = 0, $iCount = 0, $strConsumer = null)
* @method static array xRange($strStream, $strStart, $strEnd, $iCount = 0)
* @method static array xRead($arrStreams, $iCount = 0, $iBlock = null)
* @method static array xReadGroup($strGroup, $strConsumer, $arrStreams, $iCount = 0, $iBlock = null)
* @method static array xRevRange($strStream, $strEnd, $strStart, $iCount = 0)
* @method static int xTrim($strStream, $iMaxLen, $booApproximate = null)
* Pub/sub methods
* @method static mixed pSubscribe($patterns, $callback)
* @method static mixed publish($channel, $message)
* @method static mixed subscribe($channels, $callback)
* @method static mixed pubSub($keyword, $argument = null)
* Generic methods
* @method static mixed rawCommand(...$commandAndArgs)
* Transactions methods
* @method static \Redis multi()
* @method static mixed exec()
* @method static mixed discard()
* @method static mixed watch($keys)
* @method static mixed unwatch($keys)
* Scripting methods
* @method static mixed eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
* @method static mixed evalSha($scriptSha, $numkeys, ...$arguments)
* @method static mixed script($command, ...$scripts)
* @method static mixed client(...$args)
* @method static null|string getLastError()
* @method static bool clearLastError()
* @method static mixed _prefix($value)
* @method static mixed _serialize($value)
* @method static mixed _unserialize($value)
* Introspection methods
* @method static bool isConnected()
* @method static mixed getHost()
* @method static mixed getPort()
* @method static false|int getDbNum()
* @method static false|double getTimeout()
* @method static mixed getReadTimeout()
* @method static mixed getPersistentID()
* @method static mixed getAuth()
*/
class Redis
{
/**
* @var RedisManager
*/
protected static $instance = null;
/**
* need to install phpredis extension
*/
const PHPREDIS_CLIENT = 'phpredis';
/**
* need to install the 'predis/predis' packgage.
* cmd: composer install predis/predis
*/
const PREDIS_CLIENT = 'predis';
/**
* Support client collection
*/
static $allowClient = [
self::PHPREDIS_CLIENT,
self::PREDIS_CLIENT
];
/**
* @return RedisManager
*/
public static function instance(): ?RedisManager
{
if (!static::$instance) {
$config = config('redis');
$client = $config['client'] ?? self::PHPREDIS_CLIENT;
if (!in_array($client, static::$allowClient)) {
$client = self::PHPREDIS_CLIENT;
}
static::$instance = new RedisManager('', $client, $config);
}
return static::$instance;
}
/**
* Connection.
* @param string $name
* @return Connection
*/
public static function connection(string $name = 'default'): Connection
{
static $timers = [];
$connection = static::instance()->connection($name);
if (!isset($timers[$name])) {
$timers[$name] = Worker::getAllWorkers() ? Timer::add(55, function () use ($connection) {
$connection->get('ping');
}) : 1;
if (class_exists(Dispatcher::class)) {
$connection->setEventDispatcher(new Dispatcher());
}
}
return $connection;
}
/**
* @param string $name
* @param array $arguments
* @return mixed
*/
public static function __callStatic(string $name, array $arguments)
{
return static::connection()->{$name}(... $arguments);
}
}
@@ -0,0 +1,24 @@
<?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;
/**
* Class Request
* @package support
*/
class Request extends \Webman\Http\Request
{
}
@@ -0,0 +1,24 @@
<?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;
/**
* Class Response
* @package support
*/
class Response extends \Webman\Http\Response
{
}
@@ -0,0 +1,107 @@
<?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;
use FilesystemIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RegexIterator;
use Symfony\Component\Translation\Translator;
use Webman\Exception\NotFoundException;
use function basename;
use function config;
use function get_realpath;
use function pathinfo;
use function request;
use function substr;
/**
* Class Translation
* @package support
* @method static string trans(?string $id, array $parameters = [], string $domain = null, string $locale = null)
* @method static void setLocale(string $locale)
* @method static string getLocale()
*/
class Translation
{
/**
* @var Translator[]
*/
protected static $instance = [];
/**
* Instance.
* @param string $plugin
* @return Translator
* @throws NotFoundException
*/
public static function instance(string $plugin = ''): Translator
{
if (!isset(static::$instance[$plugin])) {
$config = config($plugin ? "plugin.$plugin.translation" : 'translation', []);
$paths = (array)($config['path'] ?? []);
static::$instance[$plugin] = $translator = new Translator($config['locale']);
$translator->setFallbackLocales($config['fallback_locale']);
$classes = [
'Symfony\Component\Translation\Loader\PhpFileLoader' => [
'extension' => '.php',
'format' => 'phpfile'
],
'Symfony\Component\Translation\Loader\PoFileLoader' => [
'extension' => '.po',
'format' => 'pofile'
]
];
foreach ($paths as $path) {
// Phar support. Compatible with the 'realpath' function in the phar file.
if (!$translationsPath = get_realpath($path)) {
throw new NotFoundException("File {$path} not found");
}
foreach ($classes as $class => $opts) {
$translator->addLoader($opts['format'], new $class);
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($translationsPath, FilesystemIterator::SKIP_DOTS));
$files = new RegexIterator($iterator, '/^.+' . preg_quote($opts['extension']) . '$/i', RegexIterator::GET_MATCH);
foreach ($files as $file) {
$file = $file[0];
$domain = basename($file, $opts['extension']);
$dirName = pathinfo($file, PATHINFO_DIRNAME);
$locale = substr(strrchr($dirName, DIRECTORY_SEPARATOR), 1);
if ($domain && $locale) {
$translator->addResource($opts['format'], $file, $locale, $domain);
}
}
}
}
}
return static::$instance[$plugin];
}
/**
* @param string $name
* @param array $arguments
* @return mixed
* @throws NotFoundException
*/
public static function __callStatic(string $name, array $arguments)
{
$request = request();
$plugin = $request->plugin ?? '';
return static::instance($plugin)->{$name}(... $arguments);
}
}
+35
View File
@@ -0,0 +1,35 @@
<?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;
use function config;
use function request;
class View
{
/**
* Assign.
* @param mixed $name
* @param mixed $value
* @return void
*/
public static function assign($name, $value = null)
{
$request = request();
$plugin = $request->plugin ?? '';
$handler = config($plugin ? "plugin.$plugin.view.handler" : 'view.handler');
$handler::assign($name, $value);
}
}
@@ -0,0 +1,125 @@
<?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\bootstrap;
use Illuminate\Container\Container as IlluminateContainer;
use Illuminate\Database\Capsule\Manager as Capsule;
use Illuminate\Database\MySqlConnection;
use Illuminate\Events\Dispatcher;
use Illuminate\Pagination\Paginator;
use Illuminate\Pagination\CursorPaginator;
use Illuminate\Pagination\Cursor;
use Jenssegers\Mongodb\Connection as JenssegersMongodbConnection;
use MongoDB\Laravel\Connection as LaravelMongodbConnection;
use support\Container;
use Throwable;
use Webman\Bootstrap;
use Workerman\Timer;
use Workerman\Worker;
use function class_exists;
use function config;
/**
* Class Laravel
* @package support\Bootstrap
*/
class LaravelDb implements Bootstrap
{
/**
* @param Worker|null $worker
*
* @return void
*/
public static function start(?Worker $worker)
{
if (!class_exists(Capsule::class)) {
return;
}
$config = config('database', []);
$connections = $config['connections'] ?? [];
if (!$connections) {
return;
}
$capsule = new Capsule(IlluminateContainer::getInstance());
$capsule->getDatabaseManager()->extend('mongodb', function ($config, $name) {
$config['name'] = $name;
return class_exists(LaravelMongodbConnection::class) ? new LaravelMongodbConnection($config) : new JenssegersMongodbConnection($config);
});
$default = $config['default'] ?? false;
if ($default) {
$defaultConfig = $connections[$config['default']] ?? false;
if ($defaultConfig) {
$capsule->addConnection($defaultConfig);
}
}
foreach ($connections as $name => $config) {
$capsule->addConnection($config, $name);
}
if (class_exists(Dispatcher::class) && !$capsule->getEventDispatcher()) {
$capsule->setEventDispatcher(Container::make(Dispatcher::class, [IlluminateContainer::getInstance()]));
}
$capsule->setAsGlobal();
$capsule->bootEloquent();
// Heartbeat
if ($worker) {
Timer::add(55, function () use ($default, $connections, $capsule) {
foreach ($capsule->getDatabaseManager()->getConnections() as $connection) {
/* @var MySqlConnection $connection **/
if ($connection->getConfig('driver') == 'mysql') {
try {
$connection->select('select 1');
} catch (Throwable $e) {}
}
}
});
}
// Paginator
if (class_exists(Paginator::class)) {
if (method_exists(Paginator::class, 'queryStringResolver')) {
Paginator::queryStringResolver(function () {
$request = request();
return $request ? $request->queryString() : null;
});
}
Paginator::currentPathResolver(function () {
$request = request();
return $request ? $request->path(): '/';
});
Paginator::currentPageResolver(function ($pageName = 'page') {
$request = request();
if (!$request) {
return 1;
}
$page = (int)($request->input($pageName, 1));
return $page > 0 ? $page : 1;
});
if (class_exists(CursorPaginator::class)) {
CursorPaginator::currentCursorResolver(function ($cursorName = 'cursor') {
return Cursor::fromEncoded(request()->input($cursorName));
});
}
}
}
}
@@ -0,0 +1,61 @@
<?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\bootstrap;
use Webman\Bootstrap;
use Workerman\Protocols\Http;
use Workerman\Protocols\Http\Session as SessionBase;
use Workerman\Worker;
use function config;
use function property_exists;
/**
* Class Session
* @package support
*/
class Session implements Bootstrap
{
/**
* @param Worker|null $worker
* @return void
*/
public static function start(?Worker $worker)
{
$config = config('session');
if (property_exists(SessionBase::class, 'name')) {
SessionBase::$name = $config['session_name'];
} else {
Http::sessionName($config['session_name']);
}
SessionBase::handlerClass($config['handler'], $config['config'][$config['type']]);
$map = [
'auto_update_timestamp' => 'autoUpdateTimestamp',
'cookie_lifetime' => 'cookieLifetime',
'gc_probability' => 'gcProbability',
'cookie_path' => 'cookiePath',
'http_only' => 'httpOnly',
'same_site' => 'sameSite',
'lifetime' => 'lifetime',
'domain' => 'domain',
'secure' => 'secure',
];
foreach ($map as $key => $name) {
if (isset($config[$key]) && property_exists(SessionBase::class, $name)) {
SessionBase::${$name} = $config[$key];
}
}
}
}
@@ -0,0 +1,38 @@
<?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 Exception;
use Webman\Http\Request;
use Webman\Http\Response;
use function json_encode;
/**
* Class BusinessException
* @package support\exception
*/
class BusinessException extends Exception
{
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());
}
}
@@ -0,0 +1,47 @@
<?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\Exception\ExceptionHandler;
use Webman\Http\Request;
use Webman\Http\Response;
/**
* Class Handler
* @package support\exception
*/
class Handler extends ExceptionHandler
{
public $dontReport = [
BusinessException::class,
];
public function report(Throwable $exception)
{
parent::report($exception);
}
public function render(Request $request, Throwable $exception): Response
{
if(($exception instanceof BusinessException) && ($response = $exception->render($request)))
{
return $response;
}
return parent::render($request, $exception);
}
}
@@ -0,0 +1,73 @@
<?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\view;
use Jenssegers\Blade\Blade as BladeView;
use Webman\View;
use function app_path;
use function array_merge;
use function base_path;
use function config;
use function is_array;
use function request;
use function runtime_path;
/**
* Class Blade
* composer require jenssegers/blade
* @package support\view
*/
class Blade implements View
{
/**
* Assign.
* @param string|array $name
* @param mixed $value
*/
public static function assign($name, $value = null)
{
$request = request();
$request->_view_vars = array_merge((array) $request->_view_vars, is_array($name) ? $name : [$name => $value]);
}
/**
* Render.
* @param string $template
* @param array $vars
* @param string|null $app
* @param string|null $plugin
* @return string
*/
public static function render(string $template, array $vars, string $app = null, string $plugin = null): string
{
static $views = [];
$request = request();
$plugin = $plugin === null ? ($request->plugin ?? '') : $plugin;
$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])) {
$viewPath = $app === '' ? "$baseViewPath/view" : "$baseViewPath/$app/view";
$views[$key] = new BladeView($viewPath, runtime_path() . '/views');
$extension = config("{$configPrefix}view.extension");
if ($extension) {
$extension($views[$key]);
}
}
$vars = array_merge((array) $request->_view_vars, $vars);
return $views[$key]->render($template, $vars);
}
}
@@ -0,0 +1,78 @@
<?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\view;
use Throwable;
use Webman\View;
use function app_path;
use function array_merge;
use function base_path;
use function config;
use function extract;
use function is_array;
use function ob_end_clean;
use function ob_get_clean;
use function ob_start;
use function request;
/**
* Class Raw
* @package support\view
*/
class Raw implements View
{
/**
* Assign.
* @param string|array $name
* @param mixed $value
*/
public static function assign($name, $value = null)
{
$request = request();
$request->_view_vars = array_merge((array) $request->_view_vars, is_array($name) ? $name : [$name => $value]);
}
/**
* Render.
* @param string $template
* @param array $vars
* @param string|null $app
* @param string|null $plugin
* @return false|string
*/
public static function render(string $template, array $vars, string $app = null, string $plugin = null): string
{
$request = request();
$plugin = $plugin === null ? ($request->plugin ?? '') : $plugin;
$configPrefix = $plugin ? "plugin.$plugin." : '';
$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";
extract((array) $request->_view_vars);
extract($vars);
ob_start();
// Try to include php file.
try {
include $__template_path__;
} catch (Throwable $e) {
ob_end_clean();
throw $e;
}
return ob_get_clean();
}
}
@@ -0,0 +1,75 @@
<?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\view;
use think\Template;
use Webman\View;
use function app_path;
use function array_merge;
use function base_path;
use function config;
use function is_array;
use function ob_get_clean;
use function ob_start;
use function request;
use function runtime_path;
/**
* Class Blade
* @package support\view
*/
class ThinkPHP implements View
{
/**
* Assign.
* @param string|array $name
* @param mixed $value
*/
public static function assign($name, $value = null)
{
$request = request();
$request->_view_vars = array_merge((array) $request->_view_vars, is_array($name) ? $name : [$name => $value]);
}
/**
* Render.
* @param string $template
* @param array $vars
* @param string|null $app
* @param string|null $plugin
* @return false|string
*/
public static function render(string $template, array $vars, string $app = null, string $plugin = null): string
{
$request = request();
$plugin = $plugin === null ? ($request->plugin ?? '') : $plugin;
$app = $app === null ? $request->app : $app;
$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/";
$defaultOptions = [
'view_path' => $viewPath,
'cache_path' => runtime_path() . '/views/',
'view_suffix' => $viewSuffix
];
$options = array_merge($defaultOptions, config("{$configPrefix}view.options", []));
$views = new Template($options);
ob_start();
$vars = array_merge((array) $request->_view_vars, $vars);
$views->fetch($template, $vars);
return ob_get_clean();
}
}
@@ -0,0 +1,76 @@
<?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\view;
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
use Twig\Loader\FilesystemLoader;
use Webman\View;
use function app_path;
use function array_merge;
use function base_path;
use function config;
use function is_array;
use function request;
/**
* Class Blade
* @package support\view
*/
class Twig implements View
{
/**
* Assign.
* @param string|array $name
* @param mixed $value
*/
public static function assign($name, $value = null)
{
$request = request();
$request->_view_vars = array_merge((array) $request->_view_vars, is_array($name) ? $name : [$name => $value]);
}
/**
* Render.
* @param string $template
* @param array $vars
* @param string|null $app
* @param string|null $plugin
* @return string
*/
public static function render(string $template, array $vars, string $app = null, string $plugin = null): string
{
static $views = [];
$request = request();
$plugin = $plugin === null ? ($request->plugin ?? '') : $plugin;
$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();
$viewPath = $app === '' ? "$baseViewPath/view/" : "$baseViewPath/$app/view/";
$views[$key] = new Environment(new FilesystemLoader($viewPath), config("{$configPrefix}view.options", []));
$extension = config("{$configPrefix}view.extension");
if ($extension) {
$extension($views[$key]);
}
}
$vars = array_merge((array) $request->_view_vars, $vars);
return $views[$key]->render("$template.$viewSuffix", $vars);
}
}