first commit

This commit is contained in:
Mr.Qin
2022-08-19 19:48:37 +08:00
commit afdd648b65
3275 changed files with 631084 additions and 0 deletions

57
support/Plugin.php Normal file
View File

@@ -0,0 +1,57 @@
<?php
namespace support;
class Plugin
{
public static function install($event)
{
static::findHepler();
$operation = $event->getOperation();
$autoload = method_exists($operation, 'getPackage') ? $operation->getPackage()->getAutoload() : $operation->getTargetPackage()->getAutoload();
if (!isset($autoload['psr-4'])) {
return;
}
foreach ($autoload['psr-4'] as $namespace => $path) {
$install_function = "\\{$namespace}Install::install";
$plugin_const = "\\{$namespace}Install::WEBMAN_PLUGIN";
if (defined($plugin_const) && is_callable($install_function)) {
$install_function();
}
}
}
public static function update($event)
{
static::install($event);
}
public static function uninstall($event)
{
static::findHepler();
$autoload = $event->getOperation()->getPackage()->getAutoload();
if (!isset($autoload['psr-4'])) {
return;
}
foreach ($autoload['psr-4'] as $namespace => $path) {
$uninstall_function = "\\{$namespace}Install::uninstall";
$plugin_const = "\\{$namespace}Install::WEBMAN_PLUGIN";
if (defined($plugin_const) && is_callable($uninstall_function)) {
$uninstall_function();
}
}
}
protected static function findHepler()
{
// 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';
}
}

261
support/Request.php Normal file
View File

@@ -0,0 +1,261 @@
<?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 system\Random;
/**
* Class Request
* @package support
*/
class Request extends \Webman\Http\Request
{
/**
* 用户ID
* @var int
*/
public $userId = 0;
/**
* 用户信息
*/
public $userInfo = [];
/**
* 生成请求令牌
* @access public
* @param string $name 令牌名称
* @param mixed $type 令牌生成方法
* @return string
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
public function buildToken(string $name = '__token__', $type = 'md5'): string
{
$type = is_callable($type) ? $type : 'md5';
$token = call_user_func($type, Random::alpha(32));
request()->session()->set($name, $token);
return $token;
}
/**
* 检查请求令牌
* @access public
* @param string $token 令牌名称
* @param array $data 表单数据
* @return bool
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
public function checkToken(string $token = '__token__', array $data = []): bool
{
if (in_array($this->method(), ['GET', 'HEAD', 'OPTIONS'], true)) {
return true;
}
if (!request()->session()->has($token)) {
// 令牌数据无效
return false;
}
// Header验证
if (request()->header('X-CSRF-TOKEN') && request()->session()->get($token) === request()->header('X-CSRF-TOKEN')) {
// 防止重复提交
request()->session()->delete($token); // 验证完成销毁session
return true;
}
if (empty($data)) {
$data = request()->all();
}
// 令牌验证
if (isset($data[$token]) && request()->session()->get($token) === $data[$token]) {
// 防止重复提交
request()->session()->delete($token); // 验证完成销毁session
return true;
}
// 开启TOKEN重置
request()->session()->delete($token);
return false;
}
/**
* 获取token
* @return array|string|null
*/
public function getToken($token = '__token__')
{
return request()->header($token, input($token, request()->cookie($token)));
}
/**
* 是否为GET请求
* @access public
* @return bool
*/
public function isGet(): bool
{
return $this->method() == 'GET';
}
/**
* 是否为POST请求
* @access public
* @return bool
*/
public function isPost(): bool
{
return $this->method() == 'POST';
}
/**
* 是否为PUT请求
* @access public
* @return bool
*/
public function isPut(): bool
{
return $this->method() == 'PUT';
}
public function server($name = null)
{
return $name ? ($_SERVER[$name] ?? null) : $_SERVER;
}
/**
* 获取应用
* @return string
*/
public function getApp(): string
{
return '/' . request()->app;
}
/**
* 获取控制器
* @return string|string[]|null
*/
public function getController(bool $lower = false)
{
$controller = str_replace(["app\\$this->app\\controller\\", '\\'], ['', '/'], request()->controller);
return $lower ? strtolower($controller) : $controller;
}
/**
* 获取控制器方法
* @return string
*/
public function getAction(): string
{
return request()->action ?? 'null';
}
/**
* 检测是否使用手机访问
* @access public
* @return bool
*/
public function isMobile(): bool
{
if (request()->header('HTTP_VIA') && stristr(request()->header('HTTP_VIA'), "wap")) {
return true;
}
if (request()->header('accept') && strpos(strtoupper(request()->header('accept')), "VND.WAP.WML")) {
return true;
}
if (request()->header('HTTP_X_WAP_PROFILE') || request()->header('HTTP_PROFILE')) {
return true;
}
if (request()->header('user-agent') && preg_match('/(blackberry|configuration\/cldc|hp |hp-|htc |htc_|htc-|iemobile|kindle|midp|mmp|motorola|mobile|nokia|opera mini|opera |Googlebot-Mobile|YahooSeeker\/M1A1-R2D2|android|iphone|ipod|mobi|palm|palmos|pocket|portalmmm|ppc;|smartphone|sonyericsson|sqh|spv|symbian|treo|up.browser|up.link|vodafone|windows ce|xda |xda_)/i', request()->header('user-agent'))) {
return true;
}
return false;
}
/**
* 当前是否ssl
* @access public
* @return bool
*/
public function isSsl(): bool
{
if ($this->server('HTTPS') && ('1' == $this->server('HTTPS') || 'on' == strtolower($this->server('HTTPS')))) {
return true;
} elseif ('https' == $this->server('REQUEST_SCHEME')) {
return true;
} elseif ('443' == $this->server('SERVER_PORT')) {
return true;
} elseif ('https' == $this->server('HTTP_X_FORWARDED_PROTO')) {
return true;
}
return false;
}
/**
* 当前URL地址中的scheme参数
* @access public
* @return string
*/
public function scheme(): string
{
return $this->isSsl() ? 'https' : 'http';
}
/**
* 获取当前包含协议的域名
* @access public
* @param bool $port 是否需要去除端口号
* @return string
*/
public function domain(bool $port = false): string
{
return $this->scheme() . '://' . $this->host($port);
}
/**
* 获取当前根域名
* @access public
* @return string
*/
public function rootDomain(): string
{
$item = explode('.', request()->host());
$count = count($item);
return $count > 1 ? $item[$count - 2] . '.' . $item[$count - 1] : $item[0];
}
/**
* 获取当前子域名
* @access public
* @return string
*/
public function subDomain(): string
{
$rootDomain = \request()->rootDomain();
if ($rootDomain) {
$sub = stristr(\request()->host(), $rootDomain, true);
$subDomain = $sub ? rtrim($sub, '.') : '';
} else {
$subDomain = '';
}
return $subDomain;
}
}

24
support/Response.php Normal file
View File

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

132
support/bootstrap.php Normal file
View File

@@ -0,0 +1,132 @@
<?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
*/
use Dotenv\Dotenv;
use support\Log;
use Webman\Bootstrap;
use Webman\Config;
use Webman\Route;
use Webman\Middleware;
use Webman\Util;
$worker = $worker ?? null;
if ($timezone = config('app.default_timezone')) {
date_default_timezone_set($timezone);
}
set_error_handler(function ($level, $message, $file = '', $line = 0) {
if (error_reporting() & $level) {
throw new ErrorException($message, 0, $level, $file, $line);
}
});
if ($worker) {
register_shutdown_function(function ($start_time) {
if (time() - $start_time <= 1) {
sleep(1);
}
}, time());
}
if (class_exists('Dotenv\Dotenv') && file_exists(base_path() . '/.env')) {
if (method_exists('Dotenv\Dotenv', 'createUnsafeImmutable')) {
Dotenv::createUnsafeImmutable(base_path())->load();
} else {
Dotenv::createMutable(base_path())->load();
}
}
Support\App::loadAllConfig(['route']);
foreach (config('autoload.files', []) as $file) {
include_once $file;
}
foreach (config('plugin', []) as $firm => $projects) {
foreach ($projects as $name => $project) {
if (!is_array($project)) {
continue;
}
foreach ($project['autoload']['files'] ?? [] as $file) {
include_once $file;
}
}
foreach ($projects['autoload']['files'] ?? [] as $file) {
include_once $file;
}
}
Middleware::load(config('middleware', []), '');
foreach (config('plugin', []) as $firm => $projects) {
foreach ($projects as $name => $project) {
if (!is_array($project) || $name === 'static') {
continue;
}
Middleware::load($project['middleware'] ?? [], '');
}
Middleware::load($projects['middleware'] ?? [], $firm);
if ($static_middlewares = config("plugin.$firm.static.middleware")) {
Middleware::load(['__static__' => $static_middlewares], $firm);
}
}
Middleware::load(['__static__' => config('static.middleware', [])], '');
foreach (config('bootstrap', []) as $class_name) {
if (!class_exists($class_name)) {
$log = "Warning: Class $class_name setting in config/bootstrap.php not found\r\n";
echo $log;
Log::error($log);
continue;
}
/** @var Bootstrap $class_name */
$class_name::start($worker);
}
foreach (config('plugin', []) as $firm => $projects) {
foreach ($projects as $name => $project) {
if (!is_array($project)) {
continue;
}
foreach ($project['bootstrap'] ?? [] as $class_name) {
if (!class_exists($class_name)) {
$log = "Warning: Class $class_name setting in config/plugin/$firm/$name/bootstrap.php not found\r\n";
echo $log;
Log::error($log);
continue;
}
/** @var Bootstrap $class_name */
$class_name::start($worker);
}
}
foreach ($projects['bootstrap'] ?? [] as $class_name) {
if (!class_exists($class_name)) {
$log = "Warning: Class $class_name setting in plugin/$firm/config/bootstrap.php not found\r\n";
echo $log;
Log::error($log);
continue;
}
/** @var Bootstrap $class_name */
$class_name::start($worker);
}
}
$directory = base_path() . '/plugin';
$paths = [config_path()];
foreach (Util::scanDir($directory) as $path) {
if (is_dir($path = "$path/config")) {
$paths[] = $path;
}
}
Route::load($paths);

482
support/helpers.php Normal file
View File

@@ -0,0 +1,482 @@
<?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
*/
use support\Request;
use support\Response;
use support\Translation;
use support\Container;
use support\view\Raw;
use support\view\Blade;
use support\view\ThinkPHP;
use support\view\Twig;
use Workerman\Worker;
use Webman\App;
use Webman\Config;
use Webman\Route;
// Phar support.
if (\is_phar()) {
\define('BASE_PATH', dirname(__DIR__));
} else {
\define('BASE_PATH', realpath(__DIR__ . '/../'));
}
\define('WEBMAN_VERSION', '1.4');
/**
* @param $return_phar
* @return false|string
*/
function base_path(bool $return_phar = true)
{
static $real_path = '';
if (!$real_path) {
$real_path = \is_phar() ? \dirname(Phar::running(false)) : BASE_PATH;
}
return $return_phar ? BASE_PATH : $real_path;
}
/**
* @return string
*/
function app_path()
{
return BASE_PATH . DIRECTORY_SEPARATOR . 'app';
}
/**
* @return string
*/
function public_path()
{
static $path = '';
if (!$path) {
$path = \config('app.public_path', BASE_PATH . DIRECTORY_SEPARATOR . 'public');
}
return $path;
}
/**
* @return string
*/
function config_path()
{
return BASE_PATH . DIRECTORY_SEPARATOR . 'config';
}
/**
* Phar support.
* Compatible with the 'realpath' function in the phar file.
*
* @return string
*/
function runtime_path()
{
static $path = '';
if (!$path) {
$path = \config('app.runtime_path', BASE_PATH . DIRECTORY_SEPARATOR . 'runtime');
}
return $path;
}
/**
* @param int $status
* @param array $headers
* @param string $body
* @return Response
*/
function response($body = '', $status = 200, $headers = [])
{
return new Response($status, $headers, $body);
}
/**
* @param $data
* @param int $options
* @return Response
*/
function json($data, $options = JSON_UNESCAPED_UNICODE)
{
return new Response(200, ['Content-Type' => 'application/json'], \json_encode($data, $options));
}
/**
* @param $xml
* @return Response
*/
function xml($xml)
{
if ($xml instanceof SimpleXMLElement) {
$xml = $xml->asXML();
}
return new Response(200, ['Content-Type' => 'text/xml'], $xml);
}
/**
* @param $data
* @param string $callback_name
* @return Response
*/
function jsonp($data, $callback_name = 'callback')
{
if (!\is_scalar($data) && null !== $data) {
$data = \json_encode($data);
}
return new Response(200, [], "$callback_name($data)");
}
/**
* @param string $location
* @param int $status
* @param array $headers
* @return Response
*/
function redirect(string $location, int $status = 302, array $headers = [])
{
$response = new Response($status, ['Location' => $location]);
if (!empty($headers)) {
$response->withHeaders($headers);
}
return $response;
}
/**
* @param $template
* @param array $vars
* @param null $app
* @return Response
*/
function view(string $template, array $vars = [], string $app = null)
{
$request = \request();
$plugin = $request->plugin ?? '';
$handler = \config($plugin ? "plugin.$plugin.view.handler" : 'view.handler');
return new Response(200, [], $handler::render($template, $vars, $app));
}
/**
* @param string $template
* @param array $vars
* @param string|null $app
* @return Response
* @throws Throwable
*/
function raw_view(string $template, array $vars = [], string $app = null)
{
return new Response(200, [], Raw::render($template, $vars, $app));
}
/**
* @param string $template
* @param array $vars
* @param string|null $app
* @return Response
*/
function blade_view(string $template, array $vars = [], string $app = null)
{
return new Response(200, [], Blade::render($template, $vars, $app));
}
/**
* @param string $template
* @param array $vars
* @param string|null $app
* @return Response
*/
function think_view(string $template, array $vars = [], string $app = null)
{
return new Response(200, [], ThinkPHP::render($template, $vars, $app));
}
/**
* @param string $template
* @param array $vars
* @param string|null $app
* @return Response
*/
function twig_view(string $template, array $vars = [], string $app = null)
{
return new Response(200, [], Twig::render($template, $vars, $app));
}
/**
* @return Request
*/
function request()
{
return App::request();
}
/**
* @param string|null $key
* @param $default
* @return array|mixed|null
*/
function config(string $key = null, $default = null)
{
return Config::get($key, $default);
}
/**
* @param string $name
* @param ...$parameters
* @return string
*/
function route(string $name, ...$parameters)
{
$route = Route::getByName($name);
if (!$route) {
return '';
}
if (!$parameters) {
return $route->url();
}
if (\is_array(\current($parameters))) {
$parameters = \current($parameters);
}
return $route->url($parameters);
}
/**
* @param mixed $key
* @param mixed $default
* @return mixed
*/
function session($key = null, $default = null)
{
$session = \request()->session();
if (null === $key) {
return $session;
}
if (\is_array($key)) {
$session->put($key);
return null;
}
if (\strpos($key, '.')) {
$key_array = \explode('.', $key);
$value = $session->all();
foreach ($key_array as $index) {
if (!isset($value[$index])) {
return $default;
}
$value = $value[$index];
}
return $value;
}
return $session->get($key, $default);
}
/**
* @param string $id
* @param array $parameters
* @param string|null $domain
* @param string|null $locale
* @return string
*/
function trans(string $id, array $parameters = [], string $domain = null, string $locale = null)
{
$res = Translation::trans($id, $parameters, $domain, $locale);
return $res === '' ? $id : $res;
}
/**
* @param null|string $locale
* @return string
*/
function locale(string $locale = null)
{
if (!$locale) {
return Translation::getLocale();
}
Translation::setLocale($locale);
}
/**
* 404 not found
*
* @return Response
*/
function not_found()
{
return new Response(404, [], \file_get_contents(public_path() . '/404.html'));
}
/**
* Copy dir.
*
* @param string $source
* @param string $dest
* @param bool $overwrite
* @return void
*/
function copy_dir(string $source, string $dest, bool $overwrite = false)
{
if (\is_dir($source)) {
if (!is_dir($dest)) {
\mkdir($dest);
}
$files = \scandir($source);
foreach ($files as $file) {
if ($file !== "." && $file !== "..") {
\copy_dir("$source/$file", "$dest/$file");
}
}
} else if (\file_exists($source) && ($overwrite || !\file_exists($dest))) {
\copy($source, $dest);
}
}
/**
* Remove dir.
*
* @param string $dir
* @return bool
*/
function remove_dir(string $dir)
{
if (\is_link($dir) || \is_file($dir)) {
return \unlink($dir);
}
$files = \array_diff(\scandir($dir), array('.', '..'));
foreach ($files as $file) {
(\is_dir("$dir/$file") && !\is_link($dir)) ? \remove_dir("$dir/$file") : \unlink("$dir/$file");
}
return \rmdir($dir);
}
/**
* @param $worker
* @param $class
*/
function worker_bind($worker, $class)
{
$callback_map = [
'onConnect',
'onMessage',
'onClose',
'onError',
'onBufferFull',
'onBufferDrain',
'onWorkerStop',
'onWebSocketConnect'
];
foreach ($callback_map as $name) {
if (\method_exists($class, $name)) {
$worker->$name = [$class, $name];
}
}
if (\method_exists($class, 'onWorkerStart')) {
[$class, 'onWorkerStart']($worker);
}
}
/**
* @param $process_name
* @param $config
* @return void
*/
function worker_start($process_name, $config)
{
$worker = new Worker($config['listen'] ?? null, $config['context'] ?? []);
$property_map = [
'count',
'user',
'group',
'reloadable',
'reusePort',
'transport',
'protocol',
];
$worker->name = $process_name;
foreach ($property_map as $property) {
if (isset($config[$property])) {
$worker->$property = $config[$property];
}
}
$worker->onWorkerStart = function ($worker) use ($config) {
require_once \base_path() . '/support/bootstrap.php';
foreach ($config['services'] ?? [] as $server) {
if (!\class_exists($server['handler'])) {
echo "process error: class {$server['handler']} not exists\r\n";
continue;
}
$listen = new Worker($server['listen'] ?? null, $server['context'] ?? []);
if (isset($server['listen'])) {
echo "listen: {$server['listen']}\n";
}
$instance = Container::make($server['handler'], $server['constructor'] ?? []);
\worker_bind($listen, $instance);
$listen->listen();
}
if (isset($config['handler'])) {
if (!\class_exists($config['handler'])) {
echo "process error: class {$config['handler']} not exists\r\n";
return;
}
$instance = Container::make($config['handler'], $config['constructor'] ?? []);
\worker_bind($worker, $instance);
}
};
}
/**
* Phar support.
* Compatible with the 'realpath' function in the phar file.
*
* @param string $file_path
* @return string
*/
function get_realpath(string $file_path): string
{
if (\strpos($file_path, 'phar://') === 0) {
return $file_path;
} else {
return \realpath($file_path);
}
}
/**
* @return bool
*/
function is_phar()
{
return \class_exists(\Phar::class, false) && Phar::running();
}
/**
* @return int
*/
function cpu_count()
{
// Windows does not support the number of processes setting.
if (\DIRECTORY_SEPARATOR === '\\') {
return 1;
}
$count = 4;
if (\is_callable('shell_exec')) {
if (\strtolower(PHP_OS) === 'darwin') {
$count = (int)\shell_exec('sysctl -n machdep.cpu.core_count');
} else {
$count = (int)\shell_exec('nproc');
}
}
return $count > 0 ? $count : 4;
}