mirror of
https://gitee.com/mix-php/mix.git
synced 2024-11-29 18:28:00 +08:00
增加子模块
This commit is contained in:
parent
81302c7107
commit
567594d2e8
13
.env
13
.env
@ -1,13 +0,0 @@
|
||||
# APP配置
|
||||
APP_DEBUG=true
|
||||
|
||||
# 数据库配置
|
||||
DATABASE_DSN='mysql:host=127.0.0.1;port=3306;charset=utf8;dbname=test'
|
||||
DATABASE_USERNAME=root
|
||||
DATABASE_PASSWORD=
|
||||
|
||||
# redis配置
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PORT=6379
|
||||
REDIS_DATABASE=0
|
||||
REDIS_PASSWORD=
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -22,6 +22,3 @@ composer.phar
|
||||
phpunit.phar
|
||||
# local phpunit config
|
||||
/phpunit.xml
|
||||
|
||||
# Swoole Auto Complete
|
||||
swoole-ide-helper.phar
|
||||
|
60
.gitmodules
vendored
Normal file
60
.gitmodules
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
[submodule "src/auth"]
|
||||
path = src/auth
|
||||
url = git@github.com:mix-php/auth.git
|
||||
[submodule "src/bean"]
|
||||
path = src/bean
|
||||
url = git@github.com:mix-php/bean.git
|
||||
[submodule "src/cache"]
|
||||
path = src/cache
|
||||
url = git@github.com:mix-php/cache.git
|
||||
[submodule "src/concurrent"]
|
||||
path = src/concurrent
|
||||
url = git@github.com:mix-php/concurrent.git
|
||||
[submodule "src/console"]
|
||||
path = src/console
|
||||
url = git@github.com:mix-php/console.git
|
||||
[submodule "src/database"]
|
||||
path = src/database
|
||||
url = git@github.com:mix-php/database.git
|
||||
[submodule "src/event"]
|
||||
path = src/event
|
||||
url = git@github.com:mix-php/event.git
|
||||
[submodule "src/helper"]
|
||||
path = src/helper
|
||||
url = git@github.com:mix-php/helper.git
|
||||
[submodule "src/http-message"]
|
||||
path = src/http-message
|
||||
url = git@github.com:mix-php/http-message.git
|
||||
[submodule "src/http-server"]
|
||||
path = src/http-server
|
||||
url = git@github.com:mix-php/http-server.git
|
||||
[submodule "src/log"]
|
||||
path = src/log
|
||||
url = git@github.com:mix-php/log.git
|
||||
[submodule "src/pool"]
|
||||
path = src/pool
|
||||
url = git@github.com:mix-php/pool.git
|
||||
[submodule "src/redis"]
|
||||
path = src/redis
|
||||
url = git@github.com:mix-php/redis.git
|
||||
[submodule "src/route"]
|
||||
path = src/route
|
||||
url = git@github.com:mix-php/route.git
|
||||
[submodule "src/server"]
|
||||
path = src/server
|
||||
url = git@github.com:mix-php/server.git
|
||||
[submodule "src/session"]
|
||||
path = src/session
|
||||
url = git@github.com:mix-php/session.git
|
||||
[submodule "src/udp-server"]
|
||||
path = src/udp-server
|
||||
url = git@github.com:mix-php/udp-server.git
|
||||
[submodule "src/validate"]
|
||||
path = src/validate
|
||||
url = git@github.com:mix-php/validate.git
|
||||
[submodule "src/view"]
|
||||
path = src/view
|
||||
url = git@github.com:mix-php/view.git
|
||||
[submodule "src/websocket"]
|
||||
path = src/websocket
|
||||
url = git@github.com:mix-php/websocket.git
|
492
app/manifest.php
492
app/manifest.php
@ -1,492 +0,0 @@
|
||||
<?php
|
||||
|
||||
// 应用清单
|
||||
return [
|
||||
|
||||
// 应用名称
|
||||
'appName' => 'mix-app',
|
||||
|
||||
// 应用版本
|
||||
'appVersion' => '0.0.0',
|
||||
|
||||
// 应用调试
|
||||
'appDebug' => getenv('APP_DEBUG'),
|
||||
|
||||
// 基础路径
|
||||
'basePath' => dirname(__DIR__),
|
||||
|
||||
// 协程配置
|
||||
'coroutine' => [
|
||||
true,
|
||||
[
|
||||
'max_coroutine' => 300000,
|
||||
'hook_flags' => SWOOLE_HOOK_ALL,
|
||||
],
|
||||
],
|
||||
|
||||
// 命令
|
||||
'commands' => [
|
||||
|
||||
/** Console */
|
||||
'he' => [
|
||||
\App\Console\Commands\HelloCommand::class,
|
||||
'description' => "\tEcho demo",
|
||||
'options' => [
|
||||
[['n', 'name'], 'description' => 'Your name'],
|
||||
['say', 'description' => "\tSay ..."],
|
||||
],
|
||||
],
|
||||
'co' => [
|
||||
\App\Console\Commands\CoroutineCommand::class,
|
||||
'description' => "\tCoroutine demo",
|
||||
],
|
||||
'wg' => [
|
||||
\App\Console\Commands\WaitGroupCommand::class,
|
||||
'description' => "\tWaitGroup demo",
|
||||
],
|
||||
'cp' => [
|
||||
\App\Console\Commands\CoroutinePoolCommand::class,
|
||||
'description' => "\tCoroutine pool demo",
|
||||
],
|
||||
'cpd' => [
|
||||
\App\Console\Commands\CoroutinePoolDaemonCommand::class,
|
||||
'description' => "\tCoroutine pool daemon demo",
|
||||
'options' => [
|
||||
[['d', 'daemon'], 'description' => 'Run in the background'],
|
||||
],
|
||||
],
|
||||
'ti' => [
|
||||
\App\Console\Commands\TimerCommand::class,
|
||||
'description' => "\tTimer demo",
|
||||
],
|
||||
|
||||
/** Http */
|
||||
'http:start' => [
|
||||
\App\Http\Commands\StartCommand::class,
|
||||
'description' => "Start service",
|
||||
'options' => [
|
||||
[['d', 'daemon'], 'description' => "Run in the background"],
|
||||
[['h', 'host'], 'description' => "Listen to the specified host"],
|
||||
[['p', 'port'], 'description' => "Listen to the specified port"],
|
||||
],
|
||||
],
|
||||
|
||||
/** WebSocket */
|
||||
'ws:start' => [
|
||||
\App\WebSocket\Commands\StartCommand::class,
|
||||
'description' => "Start service",
|
||||
'options' => [
|
||||
[['d', 'daemon'], 'description' => "Run in the background"],
|
||||
[['h', 'host'], 'description' => "Listen to the specified host"],
|
||||
[['p', 'port'], 'description' => "Listen to the specified port"],
|
||||
],
|
||||
],
|
||||
|
||||
/** Tcp */
|
||||
'tcp:start' => [
|
||||
\App\Tcp\Commands\StartCommand::class,
|
||||
'description' => "Start service",
|
||||
'options' => [
|
||||
[['d', 'daemon'], 'description' => "Run in the background"],
|
||||
[['h', 'host'], 'description' => "Listen to the specified host"],
|
||||
[['p', 'port'], 'description' => "Listen to the specified port"],
|
||||
],
|
||||
],
|
||||
|
||||
/** Udp */
|
||||
'udp:start' => [
|
||||
\App\Udp\Commands\StartCommand::class,
|
||||
'description' => "Start service",
|
||||
'options' => [
|
||||
[['d', 'daemon'], 'description' => "Run in the background"],
|
||||
[['h', 'host'], 'description' => "Listen to the specified host"],
|
||||
[['p', 'port'], 'description' => "Listen to the specified port"],
|
||||
],
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
// 依赖配置
|
||||
'beans' => [
|
||||
|
||||
// 错误
|
||||
[
|
||||
// 名称
|
||||
'name' => 'error',
|
||||
// 作用域
|
||||
'scope' => \Mix\Bean\BeanDefinition::SINGLETON,
|
||||
// 类路径
|
||||
'class' => \Mix\Console\Error::class,
|
||||
// 构造函数注入
|
||||
'constructorArgs' => [
|
||||
// 错误级别
|
||||
E_ALL,
|
||||
// 日志
|
||||
['ref' => 'log'],
|
||||
],
|
||||
],
|
||||
|
||||
// 日志
|
||||
[
|
||||
// 名称
|
||||
'name' => 'log',
|
||||
// 作用域
|
||||
'scope' => \Mix\Bean\BeanDefinition::SINGLETON,
|
||||
// 类路径
|
||||
'class' => \Mix\Log\Logger::class,
|
||||
// 属性注入
|
||||
'properties' => [
|
||||
// 日志记录级别
|
||||
'levels' => ['emergency', 'alert', 'critical', 'error', 'warning', 'notice', 'info', 'debug'],
|
||||
// 处理器
|
||||
'handler' => ['ref' => \Mix\Log\MultiHandler::class],
|
||||
],
|
||||
],
|
||||
|
||||
// 日志处理器
|
||||
[
|
||||
// 类路径
|
||||
'class' => \Mix\Log\MultiHandler::class,
|
||||
// 构造函数注入
|
||||
'constructorArgs' => [
|
||||
// 标准输出处理器
|
||||
['ref' => \Mix\Log\StdoutHandler::class],
|
||||
// 文件处理器
|
||||
['ref' => \Mix\Log\FileHandler::class],
|
||||
],
|
||||
],
|
||||
|
||||
// 日志标准输出处理器
|
||||
[
|
||||
// 类路径
|
||||
'class' => \Mix\Log\StdoutHandler::class,
|
||||
],
|
||||
|
||||
// 日志文件处理器
|
||||
[
|
||||
// 类路径
|
||||
'class' => \Mix\Log\FileHandler::class,
|
||||
// 属性注入
|
||||
'properties' => [
|
||||
// 日志目录
|
||||
'dir' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'runtime' . DIRECTORY_SEPARATOR . 'logs',
|
||||
// 日志轮转类型
|
||||
'rotate' => \Mix\Log\FileHandler::ROTATE_DAY,
|
||||
// 最大文件尺寸
|
||||
'maxFileSize' => 0,
|
||||
],
|
||||
],
|
||||
|
||||
// 事件调度器
|
||||
[
|
||||
// 作用域
|
||||
'scope' => \Mix\Bean\BeanDefinition::SINGLETON,
|
||||
// 类路径
|
||||
'class' => \Mix\Event\EventDispatcher::class,
|
||||
// 构造函数注入
|
||||
'constructorArgs' => [
|
||||
\App\Common\Listeners\DatabaseListener::class,
|
||||
\App\Common\Listeners\RedisListener::class,
|
||||
],
|
||||
],
|
||||
|
||||
// Database连接池
|
||||
[
|
||||
// 名称
|
||||
'name' => 'dbPool',
|
||||
// 作用域
|
||||
'scope' => \Mix\Bean\BeanDefinition::SINGLETON,
|
||||
// 类路径
|
||||
'class' => \Mix\Database\Pool\ConnectionPool::class,
|
||||
// 属性注入
|
||||
'properties' => [
|
||||
// 最多可空闲连接数
|
||||
'maxIdle' => 5,
|
||||
// 最大连接数
|
||||
'maxActive' => 50,
|
||||
// 拨号器
|
||||
'dialer' => ['ref' => \App\Common\Dialers\DatabaseDialer::class],
|
||||
],
|
||||
],
|
||||
|
||||
// Database连接池拨号
|
||||
[
|
||||
// 类路径
|
||||
'class' => \App\Common\Dialers\DatabaseDialer::class,
|
||||
],
|
||||
|
||||
// Redis连接池
|
||||
[
|
||||
// 名称
|
||||
'name' => 'redisPool',
|
||||
// 作用域
|
||||
'scope' => \Mix\Bean\BeanDefinition::SINGLETON,
|
||||
// 类路径
|
||||
'class' => \Mix\Redis\Pool\ConnectionPool::class,
|
||||
// 属性注入
|
||||
'properties' => [
|
||||
// 最多可空闲连接数
|
||||
'maxIdle' => 5,
|
||||
// 最大连接数
|
||||
'maxActive' => 50,
|
||||
// 拨号器
|
||||
'dialer' => ['ref' => \App\Common\Dialers\RedisDialer::class],
|
||||
],
|
||||
],
|
||||
|
||||
// Redis连接池拨号
|
||||
[
|
||||
// 类路径
|
||||
'class' => \App\Common\Dialers\RedisDialer::class,
|
||||
],
|
||||
|
||||
// Database
|
||||
[
|
||||
// 类路径
|
||||
'class' => \Mix\Database\Coroutine\Connection::class,
|
||||
// 初始化方法
|
||||
'initMethod' => 'connect',
|
||||
// 属性注入
|
||||
'properties' => [
|
||||
// 数据源格式
|
||||
'dsn' => getenv('DATABASE_DSN'),
|
||||
// 数据库用户名
|
||||
'username' => getenv('DATABASE_USERNAME'),
|
||||
// 数据库密码
|
||||
'password' => getenv('DATABASE_PASSWORD'),
|
||||
// 驱动连接选项: http://php.net/manual/zh/pdo.setattribute.php
|
||||
'attributes' => [
|
||||
// 设置默认的提取模式: \PDO::FETCH_OBJ | \PDO::FETCH_ASSOC
|
||||
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
|
||||
// 超时
|
||||
\PDO::ATTR_TIMEOUT => 5,
|
||||
],
|
||||
// 事件调度器
|
||||
'eventDispatcher' => ['ref' => \Mix\Event\EventDispatcher::class],
|
||||
],
|
||||
],
|
||||
|
||||
// Redis
|
||||
[
|
||||
// 类路径
|
||||
'class' => \Mix\Redis\Coroutine\Connection::class,
|
||||
// 初始化方法
|
||||
'initMethod' => 'connect',
|
||||
// 属性注入
|
||||
'properties' => [
|
||||
// 主机
|
||||
'host' => getenv('REDIS_HOST'),
|
||||
// 端口
|
||||
'port' => getenv('REDIS_PORT'),
|
||||
// 数据库
|
||||
'database' => getenv('REDIS_DATABASE'),
|
||||
// 密码
|
||||
'password' => getenv('REDIS_PASSWORD'),
|
||||
// 超时
|
||||
'timeout' => 5,
|
||||
// 事件调度器
|
||||
'eventDispatcher' => ['ref' => \Mix\Event\EventDispatcher::class],
|
||||
],
|
||||
],
|
||||
|
||||
// 缓存
|
||||
[
|
||||
// 名称
|
||||
'name' => 'cache',
|
||||
// 作用域
|
||||
'scope' => \Mix\Bean\BeanDefinition::SINGLETON,
|
||||
// 类路径
|
||||
'class' => \Mix\Cache\Cache::class,
|
||||
// 属性注入
|
||||
'properties' => [
|
||||
// 处理器
|
||||
'handler' => ['ref' => \Mix\Cache\FileHandler::class],
|
||||
],
|
||||
],
|
||||
|
||||
// 缓存文件处理器
|
||||
[
|
||||
// 类路径
|
||||
'class' => \Mix\Cache\FileHandler::class,
|
||||
// 属性注入
|
||||
'properties' => [
|
||||
// 缓存目录
|
||||
'dir' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'runtime' . DIRECTORY_SEPARATOR . 'cache',
|
||||
// 分区
|
||||
'partitions' => 64,
|
||||
],
|
||||
],
|
||||
|
||||
// 缓存Redis处理器
|
||||
[
|
||||
// 类路径
|
||||
'class' => \Mix\Cache\RedisHandler::class,
|
||||
// 属性注入
|
||||
'properties' => [
|
||||
// 连接池
|
||||
'pool' => ['ref' => 'redisPool'],
|
||||
// Key前缀
|
||||
'keyPrefix' => 'CACHE:',
|
||||
],
|
||||
],
|
||||
|
||||
// Http服务器
|
||||
[
|
||||
// 名称
|
||||
'name' => 'httpServer',
|
||||
// 类路径
|
||||
'class' => \Mix\Http\Server\HttpServer::class,
|
||||
// 构造函数注入
|
||||
'constructorArgs' => [
|
||||
// host
|
||||
'127.0.0.1',
|
||||
// port
|
||||
9501,
|
||||
// ssl
|
||||
false,
|
||||
],
|
||||
],
|
||||
|
||||
// 路由
|
||||
[
|
||||
// 名称
|
||||
'name' => 'route',
|
||||
// 类路径
|
||||
'class' => \Mix\Route\Router::class,
|
||||
// 初始化方法
|
||||
'initMethod' => 'parse',
|
||||
// 属性注入
|
||||
'properties' => [
|
||||
// 默认变量规则
|
||||
'defaultPattern' => '[\w-]+',
|
||||
// 路由变量规则
|
||||
'patterns' => [
|
||||
'id' => '\d+',
|
||||
],
|
||||
// 全局中间件
|
||||
'middleware' => [\App\Http\Middleware\GlobalMiddleware::class],
|
||||
// 路由规则
|
||||
'rules' => [
|
||||
// 普通路由
|
||||
'/' => [[\App\Http\Controllers\IndexController::class, 'index'], 'middleware' => [\App\Http\Middleware\ActionMiddleware::class]],
|
||||
'/profile/{id}' => [[\App\Http\Controllers\ProfileController::class, 'index'], 'middleware' => [\App\Http\Middleware\ActionMiddleware::class]],
|
||||
'POST /file/upload' => [[\App\Http\Controllers\FileController::class, 'upload'], 'middleware' => [\App\Http\Middleware\ActionMiddleware::class]],
|
||||
// 分组路由
|
||||
'/v2' => [
|
||||
// 分组中间件
|
||||
'middleware' => [\App\Http\Middleware\GroupMiddleware::class],
|
||||
// 分组路由规则
|
||||
'rules' => [
|
||||
// 分组路由
|
||||
'POST /user/create' => [[\App\Http\Controllers\UserController::class, 'create'], 'middleware' => [\App\Http\Middleware\ActionMiddleware::class]],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
// 授权
|
||||
[
|
||||
// 名称
|
||||
'name' => 'auth',
|
||||
// 作用域
|
||||
'scope' => \Mix\Bean\BeanDefinition::SINGLETON,
|
||||
// 类路径
|
||||
'class' => \Mix\Auth\Authorization::class,
|
||||
// 构造函数注入
|
||||
'constructorArgs' => [
|
||||
// JWT
|
||||
['ref' => \Mix\Auth\JWT::class],
|
||||
],
|
||||
],
|
||||
|
||||
// JWT
|
||||
[
|
||||
// 类路径
|
||||
'class' => \Mix\Auth\JWT::class,
|
||||
// 属性注入
|
||||
'properties' => [
|
||||
// 钥匙
|
||||
'key' => 'example_key',
|
||||
// 签名算法
|
||||
'algorithm' => \Mix\Auth\JWT::ALGORITHM_RS256,
|
||||
],
|
||||
],
|
||||
|
||||
// Session
|
||||
[
|
||||
// 名称
|
||||
'name' => 'session',
|
||||
// 类路径
|
||||
'class' => \Mix\Session\Session::class,
|
||||
// 初始化方法
|
||||
'initMethod' => 'start',
|
||||
// 属性注入
|
||||
'properties' => [
|
||||
// 处理器
|
||||
'handler' => ['ref' => \Mix\Session\RedisHandler::class],
|
||||
// session键名
|
||||
'name' => 'session_id',
|
||||
// 生存时间
|
||||
'maxLifetime' => 7200,
|
||||
// 过期时间
|
||||
'cookieExpires' => 0,
|
||||
// 有效的服务器路径
|
||||
'cookiePath' => '/',
|
||||
// 有效域名/子域名
|
||||
'cookieDomain' => '',
|
||||
// 仅通过安全的 HTTPS 连接传给客户端
|
||||
'cookieSecure' => false,
|
||||
// 仅可通过 HTTP 协议访问
|
||||
'cookieHttpOnly' => false,
|
||||
],
|
||||
],
|
||||
|
||||
// Session Redis处理器
|
||||
[
|
||||
// 类路径
|
||||
'class' => \Mix\Session\RedisHandler::class,
|
||||
// 属性注入
|
||||
'properties' => [
|
||||
// 连接池
|
||||
'pool' => ['ref' => 'redisPool'],
|
||||
// Key前缀
|
||||
'keyPrefix' => 'SESSION:',
|
||||
],
|
||||
],
|
||||
|
||||
// Tcp服务器
|
||||
[
|
||||
// 名称
|
||||
'name' => 'tcpServer',
|
||||
// 类路径
|
||||
'class' => \Mix\Server\Server::class,
|
||||
// 构造函数注入
|
||||
'constructorArgs' => [
|
||||
// host
|
||||
'127.0.0.1',
|
||||
// port
|
||||
9502,
|
||||
// ssl
|
||||
false,
|
||||
],
|
||||
],
|
||||
|
||||
// Udp服务器
|
||||
[
|
||||
// 名称
|
||||
'name' => 'udpServer',
|
||||
// 类路径
|
||||
'class' => \Mix\Udp\Server\UdpServer::class,
|
||||
// 构造函数注入
|
||||
'constructorArgs' => [
|
||||
// host
|
||||
'127.0.0.1',
|
||||
// port
|
||||
9503,
|
||||
],
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
@ -1,46 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title><?= $message ?></title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
margin: 20px;
|
||||
}
|
||||
h1, p {
|
||||
font-family: Consolas, "Liberation Mono", Courier, Verdana, "微软雅黑";
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
padding: 5px;
|
||||
margin: 0px;
|
||||
}
|
||||
a {
|
||||
color: #4183c4;
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
span, a {
|
||||
background-color: red;
|
||||
color: white;
|
||||
padding: 3px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1><span><?= $message ?></span></h1>
|
||||
|
||||
<?php if (!empty($file)): ?>
|
||||
<p><?= $type ?> code <?= $code ?></p>
|
||||
<p><span><?= $file ?></span> line <span><?= $line ?></span></p>
|
||||
<p><?= str_replace("\n", '<br>', $trace); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<p style="margin-top: 20px;"><span><a href="http://mixphp.cn" target="_blank">MixPHP V2.1</a> —— Single-threaded coroutine PHP framework</span></p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,46 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title><?= $message ?></title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
margin: 20px;
|
||||
}
|
||||
h1, p {
|
||||
font-family: Consolas, "Liberation Mono", Courier, Verdana, "微软雅黑";
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
padding: 5px;
|
||||
margin: 0px;
|
||||
}
|
||||
a {
|
||||
color: #4183c4;
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
span, a {
|
||||
background-color: red;
|
||||
color: white;
|
||||
padding: 3px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1><span><?= $message ?></span></h1>
|
||||
|
||||
<?php if (!empty($file)): ?>
|
||||
<p><?= $type ?> code <?= $code ?></p>
|
||||
<p><span><?= $file ?></span> line <span><?= $line ?></span></p>
|
||||
<p><?= str_replace("\n", '<br>', $trace); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<p style="margin-top: 20px;"><span><a href="http://mixphp.cn" target="_blank">MixPHP V2.1</a> —— Single-threaded coroutine PHP framework</span></p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,10 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title><?= $this->title ?></title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?= $content ?>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,11 +0,0 @@
|
||||
<?php
|
||||
$this->title = 'Profile';
|
||||
?>
|
||||
|
||||
<p>id: <?= $id ?>, name: <?= $name ?>, age: <?= $age ?></p>
|
||||
<p>friends:</p>
|
||||
<ul>
|
||||
<?php foreach($friends as $name): ?>
|
||||
<li><?= $name ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Common\Dialers;
|
||||
|
||||
use Mix\Database\Coroutine\Connection;
|
||||
use Mix\Pool\DialerInterface;
|
||||
|
||||
/**
|
||||
* Class DatabaseDialer
|
||||
* @package App\Common\Dialers
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class DatabaseDialer implements DialerInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* 拨号
|
||||
* @return Connection
|
||||
*/
|
||||
public function dial()
|
||||
{
|
||||
// 创建一个连接并返回
|
||||
return context()->get(Connection::class);
|
||||
}
|
||||
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Common\Dialers;
|
||||
|
||||
use Mix\Pool\DialerInterface;
|
||||
use Mix\Redis\Coroutine\Connection;
|
||||
|
||||
/**
|
||||
* Class RedisDialer
|
||||
* @package App\Common\Dialers
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class RedisDialer implements DialerInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* 拨号
|
||||
* @return Connection
|
||||
*/
|
||||
public function dial()
|
||||
{
|
||||
// 创建一个连接并返回
|
||||
return context()->get(Connection::class);
|
||||
}
|
||||
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Common\Listeners;
|
||||
|
||||
use Mix\Database\Event\ExecuteEvent;
|
||||
use Mix\Event\ListenerInterface;
|
||||
|
||||
/**
|
||||
* Class DatabaseListener
|
||||
* @package App\Common\Listeners
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class DatabaseListener implements ListenerInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* 监听的事件
|
||||
* @return array
|
||||
*/
|
||||
public function events(): array
|
||||
{
|
||||
// TODO: Implement events() method.
|
||||
// 要监听的事件数组,可监听多个事件
|
||||
return [
|
||||
ExecuteEvent::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理事件
|
||||
* @param object $event
|
||||
* @return mixed|void
|
||||
*/
|
||||
public function process(object $event)
|
||||
{
|
||||
// TODO: Implement process() method.
|
||||
// 事件触发后,会执行该方法
|
||||
}
|
||||
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Common\Listeners;
|
||||
|
||||
use Mix\Event\ListenerInterface;
|
||||
use Mix\Redis\Event\ExecuteEvent;
|
||||
|
||||
/**
|
||||
* Class RedisListener
|
||||
* @package App\Common\Listeners
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class RedisListener implements ListenerInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* 监听的事件
|
||||
* @return array
|
||||
*/
|
||||
public function events(): array
|
||||
{
|
||||
// TODO: Implement events() method.
|
||||
// 要监听的事件数组,可监听多个事件
|
||||
return [
|
||||
ExecuteEvent::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理事件
|
||||
* @param object $event
|
||||
* @return mixed|void
|
||||
*/
|
||||
public function process(object $event)
|
||||
{
|
||||
// TODO: Implement process() method.
|
||||
// 事件触发后,会执行该方法
|
||||
}
|
||||
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Mix\Concurrent\Coroutine\Channel;
|
||||
use Mix\Database\Pool\ConnectionPool;
|
||||
|
||||
/**
|
||||
* Class CoroutineCommand
|
||||
* @package App\Console\Commands
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class CoroutineCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
xgo(function () {
|
||||
$time = time();
|
||||
$chan = new Channel();
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
xgo([$this, 'foo'], $chan);
|
||||
}
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$result = $chan->pop();
|
||||
}
|
||||
println(sprintf('Time: %ds', (time() - $time)));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数据
|
||||
* @param Channel $chan
|
||||
*/
|
||||
public function foo(Channel $chan)
|
||||
{
|
||||
/** @var ConnectionPool $dbPool */
|
||||
$dbPool = context()->get('dbPool');
|
||||
$db = $dbPool->getConnection();
|
||||
$result = $db->prepare('select sleep(5)')->queryAll();
|
||||
$db->release(); // 不手动释放的连接不会归还连接池,会在析构时丢弃
|
||||
$chan->push($result);
|
||||
}
|
||||
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Console\Workers\CoroutinePoolWorker;
|
||||
use Mix\Concurrent\CoroutinePool\Dispatcher;
|
||||
use Mix\Concurrent\Coroutine\Channel;
|
||||
|
||||
/**
|
||||
* Class CoroutinePoolCommand
|
||||
* @package App\Console\Commands
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class CoroutinePoolCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
xgo(function () {
|
||||
$maxWorkers = 20;
|
||||
$maxQueue = 10;
|
||||
$jobQueue = new Channel($maxQueue);
|
||||
$dispatch = new Dispatcher([
|
||||
'jobQueue' => $jobQueue,
|
||||
'maxWorkers' => $maxWorkers,
|
||||
]);
|
||||
$dispatch->start(CoroutinePoolWorker::class);
|
||||
// 投放任务
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
$jobQueue->push($i);
|
||||
}
|
||||
// 停止
|
||||
$dispatch->stop();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Console\Workers\CoroutinePoolDaemonWorker;
|
||||
use Mix\Concurrent\CoroutinePool\Dispatcher;
|
||||
use Mix\Console\CommandLine\Flag;
|
||||
use Mix\Concurrent\Coroutine\Channel;
|
||||
use Mix\Helper\ProcessHelper;
|
||||
use Mix\Redis\Pool\ConnectionPool;
|
||||
|
||||
/**
|
||||
* Class CoroutinePoolDaemonCommand
|
||||
* @package Daemon\Commands
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class CoroutinePoolDaemonCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* 退出
|
||||
* @var bool
|
||||
*/
|
||||
public $quit = false;
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
// 守护处理
|
||||
$daemon = Flag::bool(['d', 'daemon'], false);
|
||||
if ($daemon) {
|
||||
ProcessHelper::daemon();
|
||||
}
|
||||
// 捕获信号
|
||||
ProcessHelper::signal([SIGINT, SIGTERM, SIGQUIT], function ($signal) {
|
||||
$this->quit = true;
|
||||
ProcessHelper::signal([SIGINT, SIGTERM, SIGQUIT], null);
|
||||
});
|
||||
// 获取连接
|
||||
/** @var ConnectionPool $redisPool */
|
||||
$redisPool = context()->get('redisPool');
|
||||
$redis = $redisPool->getConnection();
|
||||
// 协程池执行任务
|
||||
xgo(function () use ($redis) {
|
||||
$maxWorkers = 20;
|
||||
$maxQueue = 20;
|
||||
$jobQueue = new Channel($maxQueue);
|
||||
$dispatch = new Dispatcher([
|
||||
'jobQueue' => $jobQueue,
|
||||
'maxWorkers' => $maxWorkers,
|
||||
]);
|
||||
$dispatch->start(CoroutinePoolDaemonWorker::class);
|
||||
// 投放任务
|
||||
while (true) {
|
||||
if ($this->quit) {
|
||||
$dispatch->stop();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$data = $redis->brPop(['test'], 3);
|
||||
} catch (\Throwable $e) {
|
||||
$dispatch->stop();
|
||||
return;
|
||||
}
|
||||
if (!$data) {
|
||||
continue;
|
||||
}
|
||||
$data = array_pop($data); // brPop命令最后一个键才是值
|
||||
$jobQueue->push($data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Mix\Console\CommandLine\Flag;
|
||||
|
||||
/**
|
||||
* Class HelloCommand
|
||||
* @package App\Console\Commands
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class HelloCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
$name = Flag::string(['n', 'name'], 'Xiao Ming');
|
||||
$say = Flag::string('say', 'Hello, World!');
|
||||
println("{$name}: {$say}");
|
||||
}
|
||||
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Mix\Concurrent\Timer;
|
||||
|
||||
/**
|
||||
* Class TimerCommand
|
||||
* @package App\Console\Commands
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class TimerCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
// 一次性定时
|
||||
Timer::new()->after(1000, function () {
|
||||
println(time());
|
||||
});
|
||||
|
||||
// 持续定时
|
||||
$timer = new Timer();
|
||||
$timer->tick(1000, function () {
|
||||
println(time());
|
||||
});
|
||||
|
||||
// 停止定时
|
||||
Timer::new()->after(10000, function () use ($timer) {
|
||||
$timer->clear();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Mix\Concurrent\Sync\WaitGroup;
|
||||
|
||||
/**
|
||||
* Class WaitGroupCommand
|
||||
* @package App\Console\Commands
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class WaitGroupCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
xgo(function () {
|
||||
$wg = WaitGroup::new();
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$wg->add(1);
|
||||
xgo([$this, 'foo'], $wg);
|
||||
}
|
||||
$wg->wait();
|
||||
println('all done!');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数据
|
||||
* @param WaitGroup $wg
|
||||
*/
|
||||
public function foo(WaitGroup $wg)
|
||||
{
|
||||
xdefer(function () use ($wg) {
|
||||
$wg->done();
|
||||
});
|
||||
println('work');
|
||||
//throw new \RuntimeException('ERROR');
|
||||
}
|
||||
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Workers;
|
||||
|
||||
use Mix\Concurrent\CoroutinePool\AbstractWorker;
|
||||
use Mix\Concurrent\CoroutinePool\WorkerInterface;
|
||||
|
||||
/**
|
||||
* Class CoroutinePoolDaemonWorker
|
||||
* @package Daemon\Libraries
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class CoroutinePoolDaemonWorker extends AbstractWorker implements WorkerInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* CoroutinePoolDaemonWorker constructor.
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct(array $config)
|
||||
{
|
||||
parent::__construct($config);
|
||||
// 实例化一些需重用的对象
|
||||
// ...
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理
|
||||
* @param $data
|
||||
*/
|
||||
public function handle($data)
|
||||
{
|
||||
// TODO: Implement handle() method.
|
||||
var_dump($data);
|
||||
}
|
||||
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Workers;
|
||||
|
||||
use Mix\Concurrent\CoroutinePool\AbstractWorker;
|
||||
use Mix\Concurrent\CoroutinePool\WorkerInterface;
|
||||
|
||||
/**
|
||||
* Class CoroutinePoolWorker
|
||||
* @package App\Console\Libraries
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class CoroutinePoolWorker extends AbstractWorker implements WorkerInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* CoroutinePoolWorker constructor.
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct(array $config)
|
||||
{
|
||||
parent::__construct($config);
|
||||
// 实例化一些需重用的对象
|
||||
// ...
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理
|
||||
* @param $data
|
||||
*/
|
||||
public function handle($data)
|
||||
{
|
||||
// TODO: Implement handle() method.
|
||||
var_dump($data);
|
||||
}
|
||||
|
||||
}
|
@ -1,217 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Commands;
|
||||
|
||||
use Mix\Console\CommandLine\Flag;
|
||||
use Mix\Helper\ProcessHelper;
|
||||
use Mix\Http\Message\Factory\StreamFactory;
|
||||
use Mix\Http\Message\ServerRequest;
|
||||
use Mix\Http\Message\Response;
|
||||
use Mix\Http\Server\HttpServer;
|
||||
use Mix\Http\Server\Middleware\MiddlewareDispatcher;
|
||||
use Mix\Log\Logger;
|
||||
use Mix\Route\Router;
|
||||
use Mix\View\View;
|
||||
|
||||
/**
|
||||
* Class StartCommand
|
||||
* @package App\Http\Commands
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class StartCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* @var HttpServer
|
||||
*/
|
||||
public $server;
|
||||
|
||||
/**
|
||||
* @var Logger
|
||||
*/
|
||||
public $log;
|
||||
|
||||
/**
|
||||
* @var Router
|
||||
*/
|
||||
public $route;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $localIp;
|
||||
|
||||
/**
|
||||
* StartCommand constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->log = context()->get('log');
|
||||
$this->route = context()->get('route');
|
||||
$this->server = context()->get('httpServer');
|
||||
}
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
// 守护处理
|
||||
$daemon = Flag::bool(['d', 'daemon'], false);
|
||||
if ($daemon) {
|
||||
ProcessHelper::daemon();
|
||||
}
|
||||
// 参数重写
|
||||
$host = Flag::string(['h', 'host'], '');
|
||||
if ($host) {
|
||||
$this->server->host = $host;
|
||||
}
|
||||
$port = Flag::string(['p', 'port'], '');
|
||||
if ($port) {
|
||||
$this->server->port = $port;
|
||||
}
|
||||
// 捕获信号
|
||||
ProcessHelper::signal([SIGINT, SIGTERM, SIGQUIT], function ($signal) {
|
||||
$this->log->info('received signal [{signal}]', ['signal' => $signal]);
|
||||
$this->log->info('server shutdown');
|
||||
$this->server->shutdown();
|
||||
ProcessHelper::signal([SIGINT, SIGTERM, SIGQUIT], null);
|
||||
});
|
||||
// 启动服务器
|
||||
$this->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动服务器
|
||||
*/
|
||||
public function start()
|
||||
{
|
||||
$server = $this->server;
|
||||
$server->handle('/', function (ServerRequest $request, Response $response) {
|
||||
$this->handle($request, $response);
|
||||
});
|
||||
$this->welcome();
|
||||
$this->log->info('server start');
|
||||
$server->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求处理
|
||||
* @param ServerRequest $request
|
||||
* @param Response $response
|
||||
* @throws \PhpDocReader\AnnotationException
|
||||
* @throws \ReflectionException
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function handle(ServerRequest $request, Response $response)
|
||||
{
|
||||
// 路由匹配
|
||||
try {
|
||||
$result = $this->route->match($request->getMethod(), $request->getServerParams()['path_info'] ?: '/');
|
||||
} catch (\Throwable $e) {
|
||||
// 404 处理
|
||||
static::show404($e, $response);
|
||||
return;
|
||||
}
|
||||
// 保存路由参数
|
||||
foreach ($result->getParams() as $key => $value) {
|
||||
$request->withAttribute($key, $value);
|
||||
}
|
||||
// 执行
|
||||
try {
|
||||
// 执行中间件
|
||||
$dispatcher = new MiddlewareDispatcher($result->getMiddleware(), $request, $response);
|
||||
$response = $dispatcher->dispatch();
|
||||
// 执行控制器
|
||||
if (!$response->getBody()) {
|
||||
$response = call_user_func($result->getCallback(), $request, $response);
|
||||
}
|
||||
/** @var Response $response */
|
||||
$response->end();
|
||||
} catch (\Throwable $e) {
|
||||
// 500 处理
|
||||
static::show500($e, $response);
|
||||
// 抛出错误,记录日志
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 404处理
|
||||
* @param \Throwable $e
|
||||
* @param Response $response
|
||||
* @throws \PhpDocReader\AnnotationException
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public static function show404(\Throwable $e, Response $response)
|
||||
{
|
||||
$dir = app()->basePath . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'res' . DIRECTORY_SEPARATOR . 'views';
|
||||
$view = new View($dir);
|
||||
$html = $view->render('errors.not_found', [
|
||||
'message' => $e->getMessage(),
|
||||
'type' => get_class($e),
|
||||
'code' => $e->getCode(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
$response->withBody((new StreamFactory())->createStream($html))
|
||||
->withStatus(404)
|
||||
->withContentType('text/html', 'utf-8')
|
||||
->end();
|
||||
}
|
||||
|
||||
/**
|
||||
* 500处理
|
||||
* @param \Throwable $e
|
||||
* @param Response $response
|
||||
* @throws \PhpDocReader\AnnotationException
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public static function show500(\Throwable $e, Response $response)
|
||||
{
|
||||
$dir = app()->basePath . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'res' . DIRECTORY_SEPARATOR . 'views';
|
||||
$view = new View($dir);
|
||||
$html = $view->render('errors.internal_server_error', [
|
||||
'message' => $e->getMessage(),
|
||||
'type' => get_class($e),
|
||||
'code' => $e->getCode(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
$response->withBody((new StreamFactory())->createStream($html))
|
||||
->withStatus(500)
|
||||
->withContentType('text/html', 'utf-8')
|
||||
->end();
|
||||
}
|
||||
|
||||
/**
|
||||
* 欢迎信息
|
||||
*/
|
||||
protected function welcome()
|
||||
{
|
||||
$phpVersion = PHP_VERSION;
|
||||
$swooleVersion = swoole_version();
|
||||
$host = $this->server->host;
|
||||
$port = $this->server->port;
|
||||
echo <<<EOL
|
||||
____
|
||||
______ ___ _____ ___ _____ / /_ _____
|
||||
/ __ `__ \/ /\ \/ /__ / __ \/ __ \/ __ \
|
||||
/ / / / / / / /\ \/ _ / /_/ / / / / /_/ /
|
||||
/_/ /_/ /_/_/ /_/\_\ / .___/_/ /_/ .___/
|
||||
/_/ /_/
|
||||
|
||||
|
||||
EOL;
|
||||
println('Server Name: mix-httpd');
|
||||
println('System Name: ' . strtolower(PHP_OS));
|
||||
println("PHP Version: {$phpVersion}");
|
||||
println("Swoole Version: {$swooleVersion}");
|
||||
println('Framework Version: ' . \Mix::$version);
|
||||
println("Listen Addr: {$host}");
|
||||
println("Listen Port: {$port}");
|
||||
}
|
||||
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Helpers\SendHelper;
|
||||
use App\Http\Forms\FileForm;
|
||||
use Mix\Http\Message\ServerRequest;
|
||||
use Mix\Http\Message\Response;
|
||||
|
||||
/**
|
||||
* Class FileController
|
||||
* @package App\Http\Controllers
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class FileController
|
||||
{
|
||||
|
||||
/**
|
||||
* Upload
|
||||
* @param ServerRequest $request
|
||||
* @param Response $response
|
||||
* @return Response
|
||||
*/
|
||||
public function upload(ServerRequest $request, Response $response)
|
||||
{
|
||||
// 使用表单验证器
|
||||
$form = new FileForm($request->getAttributes(), $request->getUploadedFiles());
|
||||
$form->setScenario('upload');
|
||||
if (!$form->validate()) {
|
||||
$content = ['code' => 1, 'message' => 'FAILED', 'data' => $form->getErrors()];
|
||||
return SendHelper::json($response, $content);
|
||||
}
|
||||
|
||||
// 保存文件
|
||||
if ($form->file) {
|
||||
$targetPath = app()->basePath . '/runtime/uploads/' . date('Ymd') . '/' . $form->file->getClientFilename();
|
||||
$form->file->moveTo($targetPath);
|
||||
}
|
||||
|
||||
// 响应
|
||||
$content = ['code' => 0, 'message' => 'OK'];
|
||||
return SendHelper::json($response, $content);
|
||||
}
|
||||
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Helpers\SendHelper;
|
||||
use Mix\Http\Message\ServerRequest;
|
||||
use Mix\Http\Message\Response;
|
||||
|
||||
/**
|
||||
* Class IndexController
|
||||
* @package App\Http\Controllers
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class IndexController
|
||||
{
|
||||
|
||||
/**
|
||||
* Index
|
||||
* @param ServerRequest $request
|
||||
* @param Response $response
|
||||
* @return Response
|
||||
*/
|
||||
public function index(ServerRequest $request, Response $response)
|
||||
{
|
||||
$content = 'Hello, World!';
|
||||
return SendHelper::html($response, $content);
|
||||
}
|
||||
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Helpers\SendHelper;
|
||||
use Mix\Http\Message\Response;
|
||||
use Mix\Http\Message\ServerRequest;
|
||||
|
||||
/**
|
||||
* Class ProfileController
|
||||
* @package App\Http\Controllers
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class ProfileController
|
||||
{
|
||||
|
||||
/**
|
||||
* Index
|
||||
* @param ServerRequest $request
|
||||
* @param Response $response
|
||||
* @return Response
|
||||
*/
|
||||
public function index(ServerRequest $request, Response $response)
|
||||
{
|
||||
$data = [
|
||||
'id' => $request->getAttribute('id'),
|
||||
'name' => '小明',
|
||||
'age' => 18,
|
||||
'friends' => ['小红', '小花', '小飞'],
|
||||
];
|
||||
return SendHelper::view($response, 'profile.index', $data);
|
||||
}
|
||||
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Forms\UserForm;
|
||||
use App\Http\Helpers\SendHelper;
|
||||
use App\Http\Models\UserModel;
|
||||
use Mix\Http\Message\Response;
|
||||
use Mix\Http\Message\ServerRequest;
|
||||
|
||||
/**
|
||||
* Class UserController
|
||||
* @package App\Http\Controllers
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class UserController
|
||||
{
|
||||
|
||||
/**
|
||||
* Create
|
||||
* @param ServerRequest $request
|
||||
* @param Response $response
|
||||
* @return Response
|
||||
*/
|
||||
public function create(ServerRequest $request, Response $response)
|
||||
{
|
||||
// 使用表单验证器
|
||||
$form = new UserForm($request->getAttributes());
|
||||
$form->setScenario('create');
|
||||
if (!$form->validate()) {
|
||||
$content = ['code' => 1, 'message' => 'FAILED', 'data' => $form->getErrors()];
|
||||
return SendHelper::json($response, $content);
|
||||
}
|
||||
|
||||
// 执行保存数据库
|
||||
(new UserModel())->add($form);
|
||||
|
||||
// 响应
|
||||
$content = ['code' => 0, 'message' => 'OK'];
|
||||
return SendHelper::json($response, $content);
|
||||
}
|
||||
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Forms;
|
||||
|
||||
use Mix\Validate\Validator;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
|
||||
/**
|
||||
* Class FileForm
|
||||
* @package App\Http\Forms
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class FileForm extends Validator
|
||||
{
|
||||
|
||||
/**
|
||||
* @var UploadedFileInterface
|
||||
*/
|
||||
public $file;
|
||||
|
||||
/**
|
||||
* 规则
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'file' => ['file', 'mimes' => ['image/gif', 'image/jpeg', 'image/png', 'audio/mp3', 'video/mp4'], 'maxSize' => 1024 * 1],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景
|
||||
* @return array
|
||||
*/
|
||||
public function scenarios()
|
||||
{
|
||||
return [
|
||||
'upload' => ['required' => [], 'optional' => ['file']],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息
|
||||
* @return array
|
||||
*/
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'file.mimes' => '文件类型不支持.',
|
||||
'file.maxSize' => '文件大小不能超过1MB.',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Forms;
|
||||
|
||||
use Mix\Validate\Validator;
|
||||
|
||||
/**
|
||||
* Class UserForm
|
||||
* @package App\Http\Forms
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class UserForm extends Validator
|
||||
{
|
||||
|
||||
/**
|
||||
* 名称
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* 年龄
|
||||
* @var int
|
||||
*/
|
||||
public $age;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
* @var string
|
||||
*/
|
||||
public $email;
|
||||
|
||||
/**
|
||||
* 规则
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => ['string', 'maxLength' => 25, 'filter' => ['trim']],
|
||||
'age' => ['integer', 'unsigned' => true, 'min' => 1, 'max' => 120],
|
||||
'email' => ['email'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景
|
||||
* @return array
|
||||
*/
|
||||
public function scenarios()
|
||||
{
|
||||
return [
|
||||
'create' => ['required' => ['name'], 'optional' => ['email', 'age']],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息
|
||||
* @return array
|
||||
*/
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'name.required' => '名称不能为空.',
|
||||
'name.maxLength' => '名称最多不能超过25个字符.',
|
||||
'age.integer' => '年龄必须是数字.',
|
||||
'age.unsigned' => '年龄不能为负数.',
|
||||
'age.min' => '年龄不能小于1.',
|
||||
'age.max' => '年龄不能大于120.',
|
||||
'email' => '邮箱格式错误.',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Helpers;
|
||||
|
||||
use Mix\Helper\JsonHelper;
|
||||
use Mix\Helper\XmlHelper;
|
||||
use Mix\Http\Message\Response;
|
||||
use Mix\Http\Message\Factory\StreamFactory;
|
||||
use Mix\View\View;
|
||||
|
||||
/**
|
||||
* Class SendHelper
|
||||
* @package App\Http\Helpers
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class SendHelper
|
||||
{
|
||||
|
||||
/**
|
||||
* view
|
||||
* @param Response $response
|
||||
* @param string $name
|
||||
* @param array $data
|
||||
* @param string $layout
|
||||
* @return Response
|
||||
*/
|
||||
public static function view(Response $response, string $name, array $data = [], string $layout = 'main')
|
||||
{
|
||||
$dir = app()->basePath . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'res' . DIRECTORY_SEPARATOR . 'views';
|
||||
$view = new View($dir, $layout);
|
||||
$content = $view->render($name, $data);
|
||||
return static::html($response, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* html
|
||||
* @param Response $response
|
||||
* @param string $content
|
||||
* @return Response
|
||||
*/
|
||||
public static function html(Response $response, string $content)
|
||||
{
|
||||
$body = (new StreamFactory())->createStream($content);
|
||||
return $response
|
||||
->withContentType('text/html', 'utf-8')
|
||||
->withBody($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* json
|
||||
* @param Response $response
|
||||
* @param array $content
|
||||
* @return Response
|
||||
*/
|
||||
public static function json(Response $response, array $content)
|
||||
{
|
||||
$body = (new StreamFactory())->createStream(JsonHelper::encode($content, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
return $response
|
||||
->withContentType('application/json', 'utf-8')
|
||||
->withBody($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* xml
|
||||
* @param Response $response
|
||||
* @param array $content
|
||||
* @return Response
|
||||
*/
|
||||
public static function xml(Response $response, array $content)
|
||||
{
|
||||
$body = (new StreamFactory())->createStream(XmlHelper::encode($content));
|
||||
return $response
|
||||
->withContentType('application/xml', 'utf-8')
|
||||
->withBody($body);
|
||||
}
|
||||
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Mix\Http\Message\Response;
|
||||
use Mix\Http\Message\ServerRequest;
|
||||
use Mix\Http\Server\Middleware\MiddlewareInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
/**
|
||||
* Class ActionMiddleware
|
||||
* @package App\Http\Middleware
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class ActionMiddleware implements MiddlewareInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var ServerRequest
|
||||
*/
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* @var Response
|
||||
*/
|
||||
public $response;
|
||||
|
||||
/**
|
||||
* ActionMiddleware constructor.
|
||||
* @param ServerRequest $request
|
||||
* @param Response $response
|
||||
*/
|
||||
public function __construct(ServerRequest $request, Response $response)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
// TODO: Implement process() method.
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Http\Helpers\SendHelper;
|
||||
use Mix\Auth\Authorization;
|
||||
use Mix\Auth\BearerTokenExtractor;
|
||||
use Mix\Http\Message\Response;
|
||||
use Mix\Http\Message\ServerRequest;
|
||||
use Mix\Http\Server\Middleware\MiddlewareInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
/**
|
||||
* Class AuthMiddleware
|
||||
* @package App\Http\Middleware
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class AuthMiddleware implements MiddlewareInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var ServerRequest
|
||||
*/
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* @var Response
|
||||
*/
|
||||
public $response;
|
||||
|
||||
/**
|
||||
* @var Authorization
|
||||
*/
|
||||
public $auth;
|
||||
|
||||
/**
|
||||
* SessionMiddleware constructor.
|
||||
* @param ServerRequest $request
|
||||
* @param Response $response
|
||||
*/
|
||||
public function __construct(ServerRequest $request, Response $response)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
$this->auth = context()->get('auth');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
// TODO: Implement process() method.
|
||||
// 权限验证
|
||||
$tokenExtractor = new BearerTokenExtractor($request);
|
||||
try {
|
||||
$payload = $this->auth->getPayload($tokenExtractor);
|
||||
} catch (\Throwable $e) {
|
||||
// 中断执行,返回错误信息
|
||||
$content = ['code' => 100001, 'message' => 'No access'];
|
||||
$response = SendHelper::json($this->response, $content);
|
||||
return $response;
|
||||
}
|
||||
|
||||
// 把JWT Payload放入Request,方便其他位置调用
|
||||
$this->request->withJWTPayload($payload);
|
||||
|
||||
// 继续往下执行
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Mix\Http\Message\Factory\StreamFactory;
|
||||
use Mix\Http\Message\Response;
|
||||
use Mix\Http\Message\ServerRequest;
|
||||
use Mix\Http\Server\Middleware\MiddlewareInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
/**
|
||||
* Class CorsMiddleware
|
||||
* @package App\Http\Middleware
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class CorsMiddleware implements MiddlewareInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var ServerRequest
|
||||
*/
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* @var Response
|
||||
*/
|
||||
public $response;
|
||||
|
||||
/**
|
||||
* ActionMiddleware constructor.
|
||||
* @param ServerRequest $request
|
||||
* @param Response $response
|
||||
*/
|
||||
public function __construct(ServerRequest $request, Response $response)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
// TODO: Implement process() method.
|
||||
// 跨域处理
|
||||
$response = $this->response;
|
||||
if ($request->getMethod() == 'OPTIONS') {
|
||||
$response->withHeader('Access-Control-Allow-Origin', '*')
|
||||
->withHeader('Access-Control-Allow-Credentials', 'true')
|
||||
->withHeader('Access-Control-Allow-Headers', 'DNT,Keep-Alive,User-Agent,Cache-Control,Content-Type,Authorization')
|
||||
->withBody((new StreamFactory())->createStream(''));
|
||||
return $response;
|
||||
}
|
||||
|
||||
// 继续往下执行
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Mix\Http\Message\Response;
|
||||
use Mix\Http\Message\ServerRequest;
|
||||
use Mix\Http\Server\Middleware\MiddlewareInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
/**
|
||||
* Class GlobalMiddleware
|
||||
* @package App\Http\Middleware
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class GlobalMiddleware implements MiddlewareInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var ServerRequest
|
||||
*/
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* @var Response
|
||||
*/
|
||||
public $response;
|
||||
|
||||
/**
|
||||
* GlobalMiddleware constructor.
|
||||
* @param ServerRequest $request
|
||||
* @param Response $response
|
||||
*/
|
||||
public function __construct(ServerRequest $request, Response $response)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
// TODO: Implement process() method.
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Mix\Http\Message\Response;
|
||||
use Mix\Http\Message\ServerRequest;
|
||||
use Mix\Http\Server\Middleware\MiddlewareInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
/**
|
||||
* Class GroupMiddleware
|
||||
* @package App\Http\Middleware
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class GroupMiddleware implements MiddlewareInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var ServerRequest
|
||||
*/
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* @var Response
|
||||
*/
|
||||
public $response;
|
||||
|
||||
/**
|
||||
* GroupMiddleware constructor.
|
||||
* @param ServerRequest $request
|
||||
* @param Response $response
|
||||
*/
|
||||
public function __construct(ServerRequest $request, Response $response)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
// TODO: Implement process() method.
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Http\Helpers\SendHelper;
|
||||
use Mix\Http\Message\Response;
|
||||
use Mix\Http\Message\ServerRequest;
|
||||
use Mix\Http\Server\Middleware\MiddlewareInterface;
|
||||
use Mix\Session\Session;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
/**
|
||||
* Class SessionMiddleware
|
||||
* @package App\Http\Middleware
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class SessionMiddleware implements MiddlewareInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var ServerRequest
|
||||
*/
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* @var Response
|
||||
*/
|
||||
public $response;
|
||||
|
||||
/**
|
||||
* @var Session
|
||||
*/
|
||||
public $session;
|
||||
|
||||
/**
|
||||
* SessionMiddleware constructor.
|
||||
* @param ServerRequest $request
|
||||
* @param Response $response
|
||||
*/
|
||||
public function __construct(ServerRequest $request, Response $response)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
$this->session = context()->getBean('session', [
|
||||
'request' => $request,
|
||||
'response' => $response,
|
||||
]);
|
||||
$this->request->withSession($this->session); // 把Session放入Request,方便其他位置调用
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
// TODO: Implement process() method.
|
||||
// 会话验证
|
||||
$payload = $this->session->get('payload');
|
||||
if (!$payload) {
|
||||
// 中断执行,返回错误信息
|
||||
$content = ['code' => 100001, 'message' => 'No access'];
|
||||
$response = SendHelper::json($this->response, $content);
|
||||
return $response;
|
||||
}
|
||||
|
||||
// 继续往下执行
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Models;
|
||||
|
||||
use App\Http\Forms\UserForm;
|
||||
use Mix\Database\Pool\ConnectionPool;
|
||||
|
||||
/**
|
||||
* Class UserModel
|
||||
* @package App\Http\Models
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class UserModel
|
||||
{
|
||||
|
||||
/**
|
||||
* @var ConnectionPool
|
||||
*/
|
||||
public $pool;
|
||||
|
||||
/**
|
||||
* UserModel constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->pool = context()->get('dbPool');
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增用户
|
||||
* @param UserForm $model
|
||||
* @return bool|string
|
||||
*/
|
||||
public function add(UserForm $form)
|
||||
{
|
||||
$db = $this->pool->getConnection();
|
||||
$status = $db->insert('user', [
|
||||
'name' => $form->name,
|
||||
'age' => $form->age,
|
||||
'email' => $form->email,
|
||||
])->execute();
|
||||
$insertId = $status ? $db->getLastInsertId() : false;
|
||||
$db->release();
|
||||
return $insertId;
|
||||
}
|
||||
|
||||
}
|
@ -1,216 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tcp\Commands;
|
||||
|
||||
use Mix\Concurrent\Coroutine\Channel;
|
||||
use Mix\Console\CommandLine\Flag;
|
||||
use Mix\Helper\ProcessHelper;
|
||||
use Mix\Log\Logger;
|
||||
use Mix\Server\Connection;
|
||||
use Mix\Server\Exception\ReceiveFailureException;
|
||||
use Mix\Server\Server;
|
||||
use App\Tcp\Exceptions\ExecutionException;
|
||||
use App\Tcp\Helpers\SendHelper;
|
||||
|
||||
/**
|
||||
* Class StartCommand
|
||||
* @package App\Tcp\Commands
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class StartCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Server
|
||||
*/
|
||||
public $server;
|
||||
|
||||
/**
|
||||
* @var Logger
|
||||
*/
|
||||
public $log;
|
||||
|
||||
/**
|
||||
* @var callable[]
|
||||
*/
|
||||
public $methods = [
|
||||
'hello.world' => [\App\Tcp\Controllers\HelloController::class, 'world'],
|
||||
];
|
||||
|
||||
/**
|
||||
* EOF
|
||||
*/
|
||||
const EOF = "\r\n";
|
||||
|
||||
/**
|
||||
* StartCommand constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->log = context()->get('log');
|
||||
$this->server = context()->get('tcpServer');
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
// 实例化控制器
|
||||
foreach ($this->methods as $method => $callback) {
|
||||
list($class, $action) = $callback;
|
||||
$this->methods[$method] = [new $class, $action];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
// 守护处理
|
||||
$daemon = Flag::bool(['d', 'daemon'], false);
|
||||
if ($daemon) {
|
||||
ProcessHelper::daemon();
|
||||
}
|
||||
// 参数重写
|
||||
$host = Flag::string(['h', 'host'], '');
|
||||
if ($host) {
|
||||
$this->server->host = $host;
|
||||
}
|
||||
$port = Flag::string(['p', 'port'], '');
|
||||
if ($port) {
|
||||
$this->server->port = $port;
|
||||
}
|
||||
// 捕获信号
|
||||
ProcessHelper::signal([SIGINT, SIGTERM, SIGQUIT], function ($signal) {
|
||||
$this->log->info('received signal [{signal}]', ['signal' => $signal]);
|
||||
$this->log->info('server shutdown');
|
||||
$this->server->shutdown();
|
||||
ProcessHelper::signal([SIGINT, SIGTERM, SIGQUIT], null);
|
||||
});
|
||||
// 启动服务器
|
||||
$this->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动服务器
|
||||
*/
|
||||
public function start()
|
||||
{
|
||||
$server = $this->server;
|
||||
$server->handle(function (Connection $conn) {
|
||||
$this->handle($conn);
|
||||
});
|
||||
$server->set([
|
||||
'open_eof_check' => true,
|
||||
'package_eof' => static::EOF,
|
||||
]);
|
||||
$this->welcome();
|
||||
$this->log->info('server start');
|
||||
$server->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接处理
|
||||
* @param Connection $conn
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function handle(Connection $conn)
|
||||
{
|
||||
// 消息发送
|
||||
$sendChan = new Channel(5);
|
||||
xdefer(function () use ($sendChan) {
|
||||
$sendChan->close();
|
||||
});
|
||||
xgo(function () use ($sendChan, $conn) {
|
||||
while (true) {
|
||||
$data = $sendChan->pop();
|
||||
if (!$data) {
|
||||
return;
|
||||
}
|
||||
$conn->send($data);
|
||||
}
|
||||
});
|
||||
// 消息读取
|
||||
while (true) {
|
||||
try {
|
||||
$data = $conn->recv();
|
||||
} catch (\Throwable $e) {
|
||||
// 忽略服务器主动断开连接异常
|
||||
if ($e instanceof ReceiveFailureException && $e->getCode() == 104) {
|
||||
return;
|
||||
}
|
||||
// 抛出异常
|
||||
throw $e;
|
||||
}
|
||||
$this->runAction($sendChan, $data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行功能
|
||||
* @param Channel $sendChan
|
||||
* @param $data
|
||||
*/
|
||||
public function runAction(Channel $sendChan, $data)
|
||||
{
|
||||
// 解析数据
|
||||
$data = json_decode($data, true);
|
||||
if (!$data) {
|
||||
SendHelper::error($sendChan, -32600, 'Invalid Request');
|
||||
return;
|
||||
}
|
||||
if (!isset($data['method']) || !isset($data['params']) || !isset($data['id'])) {
|
||||
SendHelper::error($sendChan, -32700, 'Parse error');
|
||||
return;
|
||||
}
|
||||
// 定义变量
|
||||
$method = $data['method'];
|
||||
$params = $data['params'];
|
||||
$id = $data['id'];
|
||||
// 路由到控制器
|
||||
if (!isset($this->methods[$method])) {
|
||||
SendHelper::error($sendChan, -32601, 'Method not found', $id);
|
||||
return;
|
||||
}
|
||||
// 执行
|
||||
try {
|
||||
$result = call_user_func($this->methods[$method], $params);
|
||||
} catch (ExecutionException $exception) {
|
||||
SendHelper::error($sendChan, $exception->getCode(), $exception->getMessage(), $id);
|
||||
return;
|
||||
}
|
||||
SendHelper::result($sendChan, $result, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 欢迎信息
|
||||
*/
|
||||
protected function welcome()
|
||||
{
|
||||
$phpVersion = PHP_VERSION;
|
||||
$swooleVersion = swoole_version();
|
||||
$host = $this->server->host;
|
||||
$port = $this->server->port;
|
||||
echo <<<EOL
|
||||
____
|
||||
______ ___ _____ ___ _____ / /_ _____
|
||||
/ __ `__ \/ /\ \/ /__ / __ \/ __ \/ __ \
|
||||
/ / / / / / / /\ \/ _ / /_/ / / / / /_/ /
|
||||
/_/ /_/ /_/_/ /_/\_\ / .___/_/ /_/ .___/
|
||||
/_/ /_/
|
||||
|
||||
|
||||
EOL;
|
||||
println('Server Name: mix-tcpd');
|
||||
println('System Name: ' . strtolower(PHP_OS));
|
||||
println("PHP Version: {$phpVersion}");
|
||||
println("Swoole Version: {$swooleVersion}");
|
||||
println('Framework Version: ' . \Mix::$version);
|
||||
println("Listen Addr: {$host}");
|
||||
println("Listen Port: {$port}");
|
||||
}
|
||||
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tcp\Controllers;
|
||||
|
||||
/**
|
||||
* Class HelloController
|
||||
* @package App\Tcp\Controllers
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class HelloController
|
||||
{
|
||||
|
||||
/**
|
||||
* Method demo
|
||||
* @param $params
|
||||
* @return array
|
||||
*/
|
||||
public function world($params)
|
||||
{
|
||||
return [
|
||||
'Hello, World!',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tcp\Exceptions;
|
||||
|
||||
/**
|
||||
* Class NotFoundException
|
||||
* @package App\Tcp\Exceptions
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class ExecutionException extends \RuntimeException
|
||||
{
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tcp\Helpers;
|
||||
|
||||
use App\Tcp\Commands\StartCommand;
|
||||
use Mix\Helper\JsonHelper;
|
||||
|
||||
/**
|
||||
* Class JsonRpcHelper
|
||||
* @package App\Tcp\Helpers
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class JsonRpcHelper
|
||||
{
|
||||
|
||||
/**
|
||||
* Error
|
||||
* @param $code
|
||||
* @param $message
|
||||
* @param null $id
|
||||
* @return string
|
||||
*/
|
||||
public static function error($code, $message, $id = null)
|
||||
{
|
||||
$data = [
|
||||
'jsonrpc' => '2.0',
|
||||
'error' => [
|
||||
'code' => $code,
|
||||
'message' => $message,
|
||||
],
|
||||
'id' => $id,
|
||||
];
|
||||
return JsonHelper::encode($data, JSON_UNESCAPED_UNICODE). StartCommand::EOF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result
|
||||
* @param $result
|
||||
* @param null $id
|
||||
* @return string
|
||||
*/
|
||||
public static function result($result, $id = null)
|
||||
{
|
||||
$data = [
|
||||
'jsonrpc' => '2.0',
|
||||
'error' => null,
|
||||
'result' => $result,
|
||||
'id' => $id,
|
||||
];
|
||||
return JsonHelper::encode($data, JSON_UNESCAPED_UNICODE). StartCommand::EOF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification
|
||||
* @param $result
|
||||
* @param null $id
|
||||
* @return string
|
||||
*/
|
||||
public static function notification($method, $result)
|
||||
{
|
||||
$data = [
|
||||
'jsonrpc' => '2.0',
|
||||
'method' => $method,
|
||||
'params' => $result,
|
||||
'id' => null,
|
||||
];
|
||||
return JsonHelper::encode($data, JSON_UNESCAPED_UNICODE). StartCommand::EOF;
|
||||
}
|
||||
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tcp\Helpers;
|
||||
|
||||
use Mix\Concurrent\Coroutine\Channel;
|
||||
|
||||
/**
|
||||
* Class SendHelper
|
||||
* @package App\Tcp\Helpers
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class SendHelper
|
||||
{
|
||||
|
||||
/**
|
||||
* Send error
|
||||
* @param Channel $sendChan
|
||||
* @param $code
|
||||
* @param $message
|
||||
* @param null $id
|
||||
*/
|
||||
public static function error(Channel $sendChan, $code, $message, $id = null)
|
||||
{
|
||||
$data = JsonRpcHelper::error($code, $message, $id);
|
||||
$sendChan->push($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send result
|
||||
* @param Channel $sendChan
|
||||
* @param $result
|
||||
* @param null $id
|
||||
*/
|
||||
public static function result(Channel $sendChan, $result, $id = null)
|
||||
{
|
||||
$data = JsonRpcHelper::result($result, $id);
|
||||
$sendChan->push($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification
|
||||
* @param Channel $sendChan
|
||||
* @param $result
|
||||
* @param null $id
|
||||
*/
|
||||
public static function notification(Channel $sendChan, $method, $result)
|
||||
{
|
||||
$data = JsonRpcHelper::notification($method, $result);
|
||||
$sendChan->push($data);
|
||||
}
|
||||
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Udp\Commands;
|
||||
|
||||
use Mix\Console\CommandLine\Flag;
|
||||
use Mix\Helper\ProcessHelper;
|
||||
use Mix\Log\Logger;
|
||||
use Mix\Udp\Server\UdpServer;
|
||||
|
||||
/**
|
||||
* Class StartCommand
|
||||
* @package App\Tcp\Commands
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class StartCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* @var UdpServer
|
||||
*/
|
||||
public $server;
|
||||
|
||||
/**
|
||||
* @var Logger
|
||||
*/
|
||||
public $log;
|
||||
|
||||
/**
|
||||
* StartCommand constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->log = context()->get('log');
|
||||
$this->server = context()->get('udpServer');
|
||||
}
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
// 守护处理
|
||||
$daemon = Flag::bool(['d', 'daemon'], false);
|
||||
if ($daemon) {
|
||||
ProcessHelper::daemon();
|
||||
}
|
||||
// 参数重写
|
||||
$host = Flag::string(['h', 'host'], '');
|
||||
if ($host) {
|
||||
$this->server->host = $host;
|
||||
}
|
||||
$port = Flag::string(['p', 'port'], '');
|
||||
if ($port) {
|
||||
$this->server->port = $port;
|
||||
}
|
||||
// 捕获信号
|
||||
ProcessHelper::signal([SIGINT, SIGTERM, SIGQUIT], function ($signal) {
|
||||
$this->log->info('received signal [{signal}]', ['signal' => $signal]);
|
||||
$this->log->info('server shutdown');
|
||||
$this->server->shutdown();
|
||||
ProcessHelper::signal([SIGINT, SIGTERM, SIGQUIT], null);
|
||||
});
|
||||
// 启动服务器
|
||||
$this->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动服务器
|
||||
*/
|
||||
public function start()
|
||||
{
|
||||
$server = $this->server;
|
||||
$server->handle(function (\Swoole\Coroutine\Socket $socket, string $data, array $peer) {
|
||||
$this->handle($socket, $data, $peer);
|
||||
});
|
||||
$this->welcome();
|
||||
$this->log->info('server start');
|
||||
$server->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息处理
|
||||
* @param \Swoole\Coroutine\Socket $socket
|
||||
* @param string $data
|
||||
* @param array $peer
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function handle(\Swoole\Coroutine\Socket $socket, string $data, array $peer)
|
||||
{
|
||||
// 回复消息
|
||||
$socket->sendTo($peer['address'], $peer['port'], "Receive successful!\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* 欢迎信息
|
||||
*/
|
||||
protected function welcome()
|
||||
{
|
||||
$phpVersion = PHP_VERSION;
|
||||
$swooleVersion = swoole_version();
|
||||
$host = $this->server->host;
|
||||
$port = $this->server->port;
|
||||
echo <<<EOL
|
||||
____
|
||||
______ ___ _____ ___ _____ / /_ _____
|
||||
/ __ `__ \/ /\ \/ /__ / __ \/ __ \/ __ \
|
||||
/ / / / / / / /\ \/ _ / /_/ / / / / /_/ /
|
||||
/_/ /_/ /_/_/ /_/\_\ / .___/_/ /_/ .___/
|
||||
/_/ /_/
|
||||
|
||||
|
||||
EOL;
|
||||
println('Server Name: mix-udpd');
|
||||
println('System Name: ' . strtolower(PHP_OS));
|
||||
println("PHP Version: {$phpVersion}");
|
||||
println("Swoole Version: {$swooleVersion}");
|
||||
println('Framework Version: ' . \Mix::$version);
|
||||
println("Listen Addr: {$host}");
|
||||
println("Listen Port: {$port}");
|
||||
}
|
||||
|
||||
}
|
@ -1,156 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\WebSocket\Commands;
|
||||
|
||||
use Mix\Console\CommandLine\Flag;
|
||||
use Mix\Helper\ProcessHelper;
|
||||
use Mix\Http\Message\Factory\StreamFactory;
|
||||
use Mix\Http\Message\Response;
|
||||
use Mix\Http\Message\ServerRequest;
|
||||
use Mix\Log\Logger;
|
||||
use Mix\Http\Server\HttpServer;
|
||||
use Mix\WebSocket\Upgrader;
|
||||
|
||||
/**
|
||||
* Class StartCommand
|
||||
* @package App\Tcp\Commands
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class StartCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* @var HttpServer
|
||||
*/
|
||||
public $server;
|
||||
|
||||
/**
|
||||
* @var Logger
|
||||
*/
|
||||
public $log;
|
||||
|
||||
/**
|
||||
* @var callable[]
|
||||
*/
|
||||
public $patterns = [
|
||||
'/websocket' => \App\WebSocket\Handlers\WebSocketHandler::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var Upgrader
|
||||
*/
|
||||
public $upgrader;
|
||||
|
||||
/**
|
||||
* StartCommand constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->log = context()->get('log');
|
||||
$this->server = context()->get('httpServer');
|
||||
$this->upgrader = new Upgrader();
|
||||
}
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
// 守护处理
|
||||
$daemon = Flag::bool(['d', 'daemon'], false);
|
||||
if ($daemon) {
|
||||
ProcessHelper::daemon();
|
||||
}
|
||||
// 参数重写
|
||||
$host = Flag::string(['h', 'host'], '');
|
||||
if ($host) {
|
||||
$this->server->host = $host;
|
||||
}
|
||||
$port = Flag::string(['p', 'port'], '');
|
||||
if ($port) {
|
||||
$this->server->port = $port;
|
||||
}
|
||||
// 捕获信号
|
||||
ProcessHelper::signal([SIGINT, SIGTERM, SIGQUIT], function ($signal) {
|
||||
$this->log->info('received signal [{signal}]', ['signal' => $signal]);
|
||||
$this->log->info('server shutdown');
|
||||
$this->server->shutdown();
|
||||
$this->upgrader->destroy();
|
||||
ProcessHelper::signal([SIGINT, SIGTERM, SIGQUIT], null);
|
||||
});
|
||||
// 启动服务器
|
||||
$this->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动服务器
|
||||
*/
|
||||
public function start()
|
||||
{
|
||||
$server = $this->server;
|
||||
$server->handle('/', function (ServerRequest $request, Response $response) {
|
||||
$this->handle($request, $response);
|
||||
});
|
||||
$this->welcome();
|
||||
$this->log->info('server start');
|
||||
$server->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求处理
|
||||
* @param ServerRequest $request
|
||||
* @param Response $response
|
||||
*/
|
||||
public function handle(ServerRequest $request, Response $response)
|
||||
{
|
||||
$pathinfo = $request->getServerParams()['path_info'] ?: '/';
|
||||
if (!isset($this->patterns[$pathinfo])) {
|
||||
$response
|
||||
->withBody((new StreamFactory())->createStream('404 Not Found'))
|
||||
->withStatus(404)
|
||||
->end();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$conn = $this->upgrader->Upgrade($request, $response);
|
||||
} catch (\Throwable $e) {
|
||||
$response
|
||||
->withBody((new StreamFactory())->createStream('401 Unauthorized'))
|
||||
->withStatus(401)
|
||||
->end();
|
||||
return;
|
||||
}
|
||||
$class = $this->patterns[$pathinfo];
|
||||
$callback = new $class($conn);
|
||||
call_user_func($callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 欢迎信息
|
||||
*/
|
||||
protected function welcome()
|
||||
{
|
||||
$phpVersion = PHP_VERSION;
|
||||
$swooleVersion = swoole_version();
|
||||
$host = $this->server->host;
|
||||
$port = $this->server->port;
|
||||
echo <<<EOL
|
||||
____
|
||||
______ ___ _____ ___ _____ / /_ _____
|
||||
/ __ `__ \/ /\ \/ /__ / __ \/ __ \/ __ \
|
||||
/ / / / / / / /\ \/ _ / /_/ / / / / /_/ /
|
||||
/_/ /_/ /_/_/ /_/\_\ / .___/_/ /_/ .___/
|
||||
/_/ /_/
|
||||
|
||||
|
||||
EOL;
|
||||
println('Server Name: mix-websocketd');
|
||||
println('System Name: ' . strtolower(PHP_OS));
|
||||
println("PHP Version: {$phpVersion}");
|
||||
println("Swoole Version: {$swooleVersion}");
|
||||
println('Framework Version: ' . \Mix::$version);
|
||||
println("Listen Addr: {$host}");
|
||||
println("Listen Port: {$port}");
|
||||
}
|
||||
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\WebSocket\Controllers;
|
||||
|
||||
use Mix\Concurrent\Coroutine\Channel;
|
||||
use Mix\Redis\Coroutine\Connection;
|
||||
use Mix\Redis\Pool\ConnectionPool;
|
||||
use Swoole\WebSocket\Frame;
|
||||
use App\WebSocket\Exceptions\ExecutionException;
|
||||
use App\WebSocket\Helpers\JsonRpcHelper;
|
||||
use App\WebSocket\Libraries\CloseConnection;
|
||||
use App\WebSocket\Forms\JoinForm;
|
||||
use App\WebSocket\Libraries\SessionStorage;
|
||||
|
||||
/**
|
||||
* Class JoinController
|
||||
* @package App\WebSocket\Controllers
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class JoinController
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $quitChannel;
|
||||
|
||||
/**
|
||||
* JoinController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->quitChannel = 'quit_' . md5(__FILE__);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入房间
|
||||
* @param Channel $sendChan
|
||||
* @param SessionStorage $sessionStorage
|
||||
* @param $params
|
||||
* @return array
|
||||
*/
|
||||
public function room(Channel $sendChan, SessionStorage $sessionStorage, $params)
|
||||
{
|
||||
// 验证数据
|
||||
$attributes = [
|
||||
'roomId' => (string)array_shift($params),
|
||||
'name' => (string)array_shift($params),
|
||||
];
|
||||
$model = new JoinForm($attributes);
|
||||
$model->setScenario('room');
|
||||
if (!$model->validate()) {
|
||||
throw new ExecutionException($model->getError(), 100001);
|
||||
}
|
||||
|
||||
// 订阅房间的频道
|
||||
if (!$sessionStorage->subChan || !$sessionStorage->subStopChan) {
|
||||
$subChan = new Channel();
|
||||
$subStopChan = new Channel();
|
||||
$sessionStorage->subChan = $subChan;
|
||||
$sessionStorage->subStopChan = $subStopChan;
|
||||
$subChan->push([$model->roomId, $model->name]); // 推送订阅信息
|
||||
xgo(function () use ($subStopChan) {
|
||||
while (true) {
|
||||
$stop = $subStopChan->pop();
|
||||
|
||||
// 由于phpredis无新增订阅功能,又无法在其他协程close实例,因此使用publish消息的方法平滑关闭redis
|
||||
// 为避免与其他程序干扰,退出通道与类文件关联唯一
|
||||
/** @var ConnectionPool $pool */
|
||||
$pool = context()->get('redisPool');
|
||||
$redis = $pool->getConnection();
|
||||
$redis->publish($this->quitChannel, true);
|
||||
$redis->release();
|
||||
|
||||
if (!$stop) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
xgo(function () use ($sendChan, $subChan, $sessionStorage) {
|
||||
while (true) {
|
||||
$data = $subChan->pop();
|
||||
if (!$data) {
|
||||
return;
|
||||
}
|
||||
list($roomId, $name) = $data;
|
||||
try {
|
||||
// 创建连接
|
||||
/** @var $redis Connection $pool */
|
||||
$redis = context()->get(Connection::class);
|
||||
// 给其他订阅当前房间的连接发送加入消息
|
||||
$message = JsonRpcHelper::notification('message.update', [
|
||||
"{$name} joined the room",
|
||||
$roomId,
|
||||
]);
|
||||
$redis->publish("room_{$roomId}", $message);
|
||||
// 保存当前房间
|
||||
$sessionStorage->joinRoomId = $roomId;
|
||||
// 订阅房间的频道
|
||||
$channel = "room_{$roomId}";
|
||||
$redis->subscribe([$this->quitChannel, $channel], function ($instance, $channel, $message) use ($sendChan) {
|
||||
// 退出订阅
|
||||
if ($channel == $this->quitChannel) {
|
||||
/** @var $instance Connection */
|
||||
$instance->close();
|
||||
return;
|
||||
}
|
||||
// 发消息
|
||||
$frame = new Frame();
|
||||
$frame->data = $message;
|
||||
$sendChan->push($frame);
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
// redis连接异常断开处理
|
||||
$sendChan->push(new CloseConnection());
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$sessionStorage->subChan->push([$model->roomId, $model->name]); // 推送订阅信息
|
||||
$sessionStorage->subStopChan->push(true);
|
||||
}
|
||||
|
||||
// 给当前连接发送加入消息
|
||||
return [
|
||||
'status' => 'success',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\WebSocket\Controllers;
|
||||
|
||||
use Mix\Concurrent\Coroutine\Channel;
|
||||
use Mix\Redis\Pool\ConnectionPool;
|
||||
use App\WebSocket\Exceptions\ExecutionException;
|
||||
use App\WebSocket\Forms\MessageForm;
|
||||
use App\WebSocket\Helpers\JsonRpcHelper;
|
||||
use App\WebSocket\Libraries\SessionStorage;
|
||||
|
||||
/**
|
||||
* Class MessageController
|
||||
* @package App\WebSocket\Controllers
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class MessageController
|
||||
{
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
* @param Channel $sendChan
|
||||
* @param SessionStorage $sessionStorage
|
||||
* @param $params
|
||||
* @return array
|
||||
*/
|
||||
public function emit(Channel $sendChan, SessionStorage $sessionStorage, $params)
|
||||
{
|
||||
// 使用模型
|
||||
$attributes = [
|
||||
'text' => array_shift($params),
|
||||
];
|
||||
$model = new MessageForm($attributes);
|
||||
$model->setScenario('emit');
|
||||
// 验证失败
|
||||
if (!$model->validate()) {
|
||||
throw new ExecutionException($model->getError(), 100001);
|
||||
}
|
||||
|
||||
// 获取加入的房间id
|
||||
if (empty($sessionStorage->joinRoomId)) {
|
||||
// 给当前连接发送消息
|
||||
throw new ExecutionException("You didn't join any room", 100002);
|
||||
}
|
||||
|
||||
// 给当前加入的房间发送消息
|
||||
xgo(function () use ($model, $sessionStorage) {
|
||||
$message = JsonRpcHelper::notification('message.update', [
|
||||
$model->text,
|
||||
$sessionStorage->joinRoomId,
|
||||
]);
|
||||
/** @var ConnectionPool $pool */
|
||||
$pool = context()->get('redisPool');
|
||||
$redis = $pool->getConnection();
|
||||
$redis->publish("room_{$sessionStorage->joinRoomId}", $message);
|
||||
$redis->release();
|
||||
});
|
||||
|
||||
// 给当前连接发送消息
|
||||
return [
|
||||
'status' => 'success',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\WebSocket\Exceptions;
|
||||
|
||||
/**
|
||||
* Class NotFoundException
|
||||
* @package App\WebSocket\Exceptions
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class ExecutionException extends \RuntimeException
|
||||
{
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\WebSocket\Forms;
|
||||
|
||||
use Mix\Validate\Validator;
|
||||
|
||||
/**
|
||||
* Class JoinForm
|
||||
* @package App\WebSocket\Forms
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class JoinForm extends Validator
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $roomId;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* 规则
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'roomId' => ['integer', 'unsigned' => true, 'minLength' => 1, 'maxLength' => 10],
|
||||
'name' => ['string', 'filter' => ['trim']],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景
|
||||
* @return array
|
||||
*/
|
||||
public function scenarios()
|
||||
{
|
||||
return [
|
||||
'room' => ['required' => ['roomId', 'name']],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\WebSocket\Forms;
|
||||
|
||||
use Mix\Validate\Validator;
|
||||
|
||||
/**
|
||||
* Class MessageForm
|
||||
* @package App\WebSocket\Forms
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class MessageForm extends Validator
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $text;
|
||||
|
||||
/**
|
||||
* 规则
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'text' => ['string', 'minLength' => 1, 'maxLength' => 300],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景
|
||||
* @return array
|
||||
*/
|
||||
public function scenarios()
|
||||
{
|
||||
return [
|
||||
'emit' => ['required' => ['text']],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\WebSocket\Handlers;
|
||||
|
||||
use Mix\Concurrent\Coroutine\Channel;
|
||||
use Mix\WebSocket\Connection;
|
||||
use Mix\WebSocket\Exception\CloseFrameException;
|
||||
use Mix\WebSocket\Exception\ReceiveFailureException;
|
||||
use App\WebSocket\Controllers\JoinController;
|
||||
use App\WebSocket\Controllers\MessageController;
|
||||
use App\WebSocket\Exceptions\ExecutionException;
|
||||
use App\WebSocket\Helpers\SendHelper;
|
||||
use App\WebSocket\Libraries\CloseConnection;
|
||||
use App\WebSocket\Libraries\SessionStorage;
|
||||
|
||||
/**
|
||||
* Class WebSocketHandler
|
||||
* @package App\WebSocket\Handlers
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class WebSocketHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
public $conn;
|
||||
|
||||
/**
|
||||
* @var Channel
|
||||
*/
|
||||
public $sendChan;
|
||||
|
||||
/**
|
||||
* @var SessionStorage
|
||||
*/
|
||||
public $sessionStorage;
|
||||
|
||||
/**
|
||||
* @var callable[]
|
||||
*/
|
||||
public $methods = [
|
||||
'join.room' => [JoinController::class, 'room'],
|
||||
'message.emit' => [MessageController::class, 'emit'],
|
||||
];
|
||||
|
||||
/**
|
||||
* WebSocketHandler constructor.
|
||||
* @param Connection $connection
|
||||
*/
|
||||
public function __construct(Connection $conn)
|
||||
{
|
||||
$this->conn = $conn;
|
||||
$this->sendChan = new Channel(5);
|
||||
$this->sessionStorage = new SessionStorage();
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
// 实例化控制器
|
||||
foreach ($this->methods as $method => $callback) {
|
||||
list($class, $action) = $callback;
|
||||
$this->methods[$method] = [new $class, $action];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke
|
||||
*/
|
||||
function __invoke()
|
||||
{
|
||||
// 消息发送
|
||||
xgo(function () {
|
||||
while (true) {
|
||||
$frame = $this->sendChan->pop();
|
||||
if (!$frame) {
|
||||
return;
|
||||
}
|
||||
if ($frame instanceof CloseConnection) {
|
||||
$this->conn->close();
|
||||
continue;
|
||||
}
|
||||
$this->conn->send($frame);
|
||||
}
|
||||
});
|
||||
// 消息读取
|
||||
while (true) {
|
||||
try {
|
||||
$frame = $this->conn->recv();
|
||||
} catch (\Throwable $e) {
|
||||
// 销毁
|
||||
$this->destroy();
|
||||
// 忽略服务器主动断开连接异常
|
||||
if ($e instanceof ReceiveFailureException && $e->getCode() == 104) {
|
||||
return;
|
||||
}
|
||||
// 忽略客户端主动断开连接异常
|
||||
if ($e instanceof CloseFrameException && in_array($e->getCode(), [1000, 1001])) {
|
||||
return;
|
||||
}
|
||||
// 抛出异常
|
||||
throw $e;
|
||||
}
|
||||
$this->runAction($frame->data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行功能
|
||||
* @param $data
|
||||
*/
|
||||
public function runAction($data)
|
||||
{
|
||||
// 解析数据
|
||||
$data = json_decode($data);
|
||||
if (!$data) {
|
||||
SendHelper::error($this->sendChan, -32600, 'Invalid Request');
|
||||
return;
|
||||
}
|
||||
if (!isset($data->method) || (!isset($data->params) or !is_array($data->params)) || !isset($data->id)) {
|
||||
SendHelper::error($this->sendChan, -32700, 'Parse error');
|
||||
return;
|
||||
}
|
||||
// 定义变量
|
||||
$method = $data->method;
|
||||
$params = $data->params;
|
||||
$id = $data->id;
|
||||
// 路由到控制器
|
||||
if (!isset($this->methods[$method])) {
|
||||
SendHelper::error($this->sendChan, -32601, 'Method not found', $id);
|
||||
return;
|
||||
}
|
||||
// 执行
|
||||
try {
|
||||
$result = call_user_func($this->methods[$method], $this->sendChan, $this->sessionStorage, $params);
|
||||
} catch (ExecutionException $exception) {
|
||||
SendHelper::error($this->sendChan, $exception->getCode(), $exception->getMessage(), $id);
|
||||
return;
|
||||
}
|
||||
SendHelper::result($this->sendChan, $result, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁
|
||||
*/
|
||||
public function destroy()
|
||||
{
|
||||
// TODO: Implement __destruct() method.
|
||||
$this->sendChan->close();
|
||||
$this->sessionStorage->clear();
|
||||
}
|
||||
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\WebSocket\Helpers;
|
||||
|
||||
use Mix\Helper\JsonHelper;
|
||||
|
||||
/**
|
||||
* Class JsonRpcHelper
|
||||
* @package App\WebSocket\Helpers
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class JsonRpcHelper
|
||||
{
|
||||
|
||||
/**
|
||||
* Error
|
||||
* @param $code
|
||||
* @param $message
|
||||
* @param null $id
|
||||
* @return string
|
||||
*/
|
||||
public static function error($code, $message, $id = null)
|
||||
{
|
||||
$data = [
|
||||
'jsonrpc' => '2.0',
|
||||
'error' => [
|
||||
'code' => $code,
|
||||
'message' => $message,
|
||||
],
|
||||
'id' => $id,
|
||||
];
|
||||
return JsonHelper::encode($data, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Result
|
||||
* @param $result
|
||||
* @param null $id
|
||||
* @return string
|
||||
*/
|
||||
public static function result($result, $id = null)
|
||||
{
|
||||
$data = [
|
||||
'jsonrpc' => '2.0',
|
||||
'error' => null,
|
||||
'result' => $result,
|
||||
'id' => $id,
|
||||
];
|
||||
return JsonHelper::encode($data, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification
|
||||
* @param $result
|
||||
* @param null $id
|
||||
* @return string
|
||||
*/
|
||||
public static function notification($method, $result)
|
||||
{
|
||||
$data = [
|
||||
'jsonrpc' => '2.0',
|
||||
'method' => $method,
|
||||
'params' => $result,
|
||||
'id' => null,
|
||||
];
|
||||
return JsonHelper::encode($data, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\WebSocket\Helpers;
|
||||
|
||||
use Mix\Concurrent\Coroutine\Channel;
|
||||
use Swoole\WebSocket\Frame;
|
||||
|
||||
/**
|
||||
* Class SendHelper
|
||||
* @package App\WebSocket\Helpers
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class SendHelper
|
||||
{
|
||||
|
||||
/**
|
||||
* Send error
|
||||
* @param Channel $sendChan
|
||||
* @param $code
|
||||
* @param $message
|
||||
* @param null $id
|
||||
*/
|
||||
public static function error(Channel $sendChan, $code, $message, $id = null)
|
||||
{
|
||||
$frame = new Frame();
|
||||
$frame->opcode = SWOOLE_WEBSOCKET_OPCODE_TEXT;
|
||||
$frame->data = JsonRpcHelper::error($code, $message, $id);
|
||||
$sendChan->push($frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send result
|
||||
* @param Channel $sendChan
|
||||
* @param $result
|
||||
* @param null $id
|
||||
*/
|
||||
public static function result(Channel $sendChan, $result, $id = null)
|
||||
{
|
||||
$frame = new Frame();
|
||||
$frame->opcode = SWOOLE_WEBSOCKET_OPCODE_TEXT;
|
||||
$frame->data = JsonRpcHelper::result($result, $id);
|
||||
$sendChan->push($frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification
|
||||
* @param Channel $sendChan
|
||||
* @param $result
|
||||
* @param null $id
|
||||
*/
|
||||
public static function notification(Channel $sendChan, $method, $result)
|
||||
{
|
||||
$frame = new Frame();
|
||||
$frame->opcode = SWOOLE_WEBSOCKET_OPCODE_TEXT;
|
||||
$frame->data = JsonRpcHelper::notification($method, $result);
|
||||
$sendChan->push($frame);
|
||||
}
|
||||
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\WebSocket\Libraries;
|
||||
|
||||
/**
|
||||
* Class CloseConnection
|
||||
* @package App\WebSocket\Libraries
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class CloseConnection
|
||||
{
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\WebSocket\Libraries;
|
||||
|
||||
use Mix\Concurrent\Coroutine\Channel;
|
||||
|
||||
/**
|
||||
* Class SessionStorage
|
||||
* @package App\WebSocket\Libraries
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
||||
class SessionStorage
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $joinRoomId;
|
||||
|
||||
/**
|
||||
* @var Channel
|
||||
*/
|
||||
public $subChan;
|
||||
|
||||
/**
|
||||
* @var Channel
|
||||
*/
|
||||
public $subStopChan;
|
||||
|
||||
/**
|
||||
* 清除
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->subChan and $this->subChan->close();
|
||||
$this->subStopChan and $this->subStopChan->close();
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 用户助手函数
|
||||
* @author liu,jian <coder.keda@gmail.com>
|
||||
*/
|
12
bin/mix.php
12
bin/mix.php
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
// Autoload
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
// Environment
|
||||
$dotenv = new Symfony\Component\Dotenv\Dotenv();
|
||||
$dotenv->load(__DIR__ . '/../.env');
|
||||
|
||||
// Run application
|
||||
$config = require __DIR__ . '/../app/manifest.php';
|
||||
(new Mix\Console\Application($config))->run();
|
@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "mix/mix",
|
||||
"description": "基于 Swoole 4.4+ 单线程协程 PHP 框架 / Single-threaded coroutine PHP framework / http://www.mixphp.cn",
|
||||
"type": "project",
|
||||
"description": "基于 Swoole 4.4+ 单线程协程 PHP 框架 / Single-threaded coroutine PHP framework (http://mixphp.cn)",
|
||||
"type": "framework",
|
||||
"keywords": [
|
||||
"framework",
|
||||
"mixphp",
|
||||
"mix",
|
||||
"swoole"
|
||||
],
|
||||
"homepage": "http://www.mixphp.cn/",
|
||||
@ -15,40 +15,31 @@
|
||||
"email": "coder.keda@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.2.0",
|
||||
"mix/bean": "v2.1.0-beta",
|
||||
"mix/concurrent": "v2.1.0-beta",
|
||||
"mix/helper": "v2.1.0-beta",
|
||||
"mix/log": "v2.1.0-beta",
|
||||
"mix/validate": "v2.1.0-beta",
|
||||
"mix/pool": "v2.1.0-beta",
|
||||
"mix/event": "v2.1.0-beta",
|
||||
"mix/database": "v2.1.0-beta",
|
||||
"mix/redis": "v2.1.0-beta",
|
||||
"mix/cache": "v2.1.0-beta",
|
||||
"mix/auth": "v2.1.0-beta",
|
||||
"mix/session": "v2.1.0-beta",
|
||||
"mix/console": "v2.1.0-beta",
|
||||
"mix/route": "v2.1.0-beta",
|
||||
"mix/view": "v2.1.0-beta",
|
||||
"mix/server": "v2.1.0-beta",
|
||||
"mix/udp-server": "v2.1.0-beta",
|
||||
"mix/http-message": "v2.1.0-beta",
|
||||
"mix/http-server": "v2.1.0-beta",
|
||||
"mix/websocket": "v2.1.0-beta",
|
||||
"symfony/dotenv": "~4.3"
|
||||
"replace": {
|
||||
"mix/bean": "self.version",
|
||||
"mix/concurrent": "self.version",
|
||||
"mix/helper": "self.version",
|
||||
"mix/log": "self.version",
|
||||
"mix/validate": "self.version",
|
||||
"mix/pool": "self.version",
|
||||
"mix/event": "self.version",
|
||||
"mix/database": "self.version",
|
||||
"mix/redis": "self.version",
|
||||
"mix/cache": "self.version",
|
||||
"mix/auth": "self.version",
|
||||
"mix/session": "self.version",
|
||||
"mix/console": "self.version",
|
||||
"mix/route": "self.version",
|
||||
"mix/view": "self.version",
|
||||
"mix/server": "self.version",
|
||||
"mix/udp-server": "self.version",
|
||||
"mix/http-message": "self.version",
|
||||
"mix/http-server": "self.version",
|
||||
"mix/websocket": "self.version"
|
||||
},
|
||||
"require-dev": {
|
||||
"swoole/ide-helper": "dev-master"
|
||||
},
|
||||
"minimum-stability": "beta",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "app/src/"
|
||||
},
|
||||
"files": [
|
||||
"app/src/functions.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB |
2
runtime/.gitignore
vendored
2
runtime/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
1
src/auth
Submodule
1
src/auth
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 5aac30e65c8d6e220cf45352067baaebb9d23098
|
1
src/bean
Submodule
1
src/bean
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit b901a6cd4b04d5de4bfeb5576f0aaaf463cb34c8
|
1
src/cache
Submodule
1
src/cache
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit ac36b127f9877c425facaf4316c5c306aef2624a
|
1
src/concurrent
Submodule
1
src/concurrent
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit d356c7628cac002cd9aec54d64be7bedbe13b2f4
|
1
src/console
Submodule
1
src/console
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 2840e5bd1a6ec09f7482650363da0d0ec4336d63
|
1
src/database
Submodule
1
src/database
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 5a0f2c5cbc46281ed40047dbed1600869f7dc8d8
|
1
src/event
Submodule
1
src/event
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 325967f9e7131d12d3d252a323ef7ace652cf9d0
|
1
src/helper
Submodule
1
src/helper
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 180c5c63cf7c0912ca92af2f3aa0cdc4cdaa0ffb
|
1
src/http-message
Submodule
1
src/http-message
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit e27890f51cda04d27e62726af7d208db45cccf10
|
1
src/http-server
Submodule
1
src/http-server
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 691e08cb2ddd9296d842e2eb1ee5f2625d43f7ce
|
1
src/log
Submodule
1
src/log
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 90f157174aaa4d7772a2e29350e717c50608b8a9
|
1
src/pool
Submodule
1
src/pool
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 1113b0d323e5525ee794311d1517eb2987423388
|
1
src/redis
Submodule
1
src/redis
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 1a07f180510085d7021da63ce942ddd82a96a958
|
1
src/route
Submodule
1
src/route
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit e54657024408566ea00a7288f77ecad9989bc29c
|
1
src/server
Submodule
1
src/server
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 2bca712cc10d7ddc998f34efac8345e105ea662c
|
1
src/session
Submodule
1
src/session
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 8bd3ead7169fbb30cf1b4d99dc9f6a984a89349c
|
1
src/udp-server
Submodule
1
src/udp-server
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit fb0cf7a58eee8d7b89bf5388e99f60b1b06248b1
|
1
src/validate
Submodule
1
src/validate
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 730a8bf20d1c0edee6ea0f7906d21e071da5b3a3
|
1
src/view
Submodule
1
src/view
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit ff798ccf251322e09ae95e2a3b1b08a68fd4c120
|
1
src/websocket
Submodule
1
src/websocket
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 674d0b20842063375125e97ce174cb46fcf68d35
|
2
vendor/.gitignore
vendored
2
vendor/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
Loading…
Reference in New Issue
Block a user