增加子模块

This commit is contained in:
Jian Liu 2019-09-19 17:34:59 +08:00
parent 81302c7107
commit 567594d2e8
84 changed files with 105 additions and 3442 deletions

13
.env
View File

@ -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
View File

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

View File

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

View File

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

View File

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

View File

@ -1,10 +0,0 @@
<html>
<head>
<title><?= $this->title ?></title>
</head>
<body>
<?= $content ?>
</body>
</html>

View File

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

View File

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

View File

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

View File

@ -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.
// 事件触发后,会执行该方法
}
}

View File

@ -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.
// 事件触发后,会执行该方法
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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' => '邮箱格式错误.',
];
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,12 +0,0 @@
<?php
namespace App\WebSocket\Libraries;
/**
* Class CloseConnection
* @package App\WebSocket\Libraries
* @author liu,jian <coder.keda@gmail.com>
*/
class CloseConnection
{
}

View File

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

View File

@ -1,6 +0,0 @@
<?php
/**
* 用户助手函数
* @author liu,jian <coder.keda@gmail.com>
*/

View File

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

View File

@ -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
View File

@ -1,2 +0,0 @@
*
!.gitignore

1
src/auth Submodule

@ -0,0 +1 @@
Subproject commit 5aac30e65c8d6e220cf45352067baaebb9d23098

1
src/bean Submodule

@ -0,0 +1 @@
Subproject commit b901a6cd4b04d5de4bfeb5576f0aaaf463cb34c8

1
src/cache Submodule

@ -0,0 +1 @@
Subproject commit ac36b127f9877c425facaf4316c5c306aef2624a

1
src/concurrent Submodule

@ -0,0 +1 @@
Subproject commit d356c7628cac002cd9aec54d64be7bedbe13b2f4

1
src/console Submodule

@ -0,0 +1 @@
Subproject commit 2840e5bd1a6ec09f7482650363da0d0ec4336d63

1
src/database Submodule

@ -0,0 +1 @@
Subproject commit 5a0f2c5cbc46281ed40047dbed1600869f7dc8d8

1
src/event Submodule

@ -0,0 +1 @@
Subproject commit 325967f9e7131d12d3d252a323ef7ace652cf9d0

1
src/helper Submodule

@ -0,0 +1 @@
Subproject commit 180c5c63cf7c0912ca92af2f3aa0cdc4cdaa0ffb

1
src/http-message Submodule

@ -0,0 +1 @@
Subproject commit e27890f51cda04d27e62726af7d208db45cccf10

1
src/http-server Submodule

@ -0,0 +1 @@
Subproject commit 691e08cb2ddd9296d842e2eb1ee5f2625d43f7ce

1
src/log Submodule

@ -0,0 +1 @@
Subproject commit 90f157174aaa4d7772a2e29350e717c50608b8a9

1
src/pool Submodule

@ -0,0 +1 @@
Subproject commit 1113b0d323e5525ee794311d1517eb2987423388

1
src/redis Submodule

@ -0,0 +1 @@
Subproject commit 1a07f180510085d7021da63ce942ddd82a96a958

1
src/route Submodule

@ -0,0 +1 @@
Subproject commit e54657024408566ea00a7288f77ecad9989bc29c

1
src/server Submodule

@ -0,0 +1 @@
Subproject commit 2bca712cc10d7ddc998f34efac8345e105ea662c

1
src/session Submodule

@ -0,0 +1 @@
Subproject commit 8bd3ead7169fbb30cf1b4d99dc9f6a984a89349c

1
src/udp-server Submodule

@ -0,0 +1 @@
Subproject commit fb0cf7a58eee8d7b89bf5388e99f60b1b06248b1

1
src/validate Submodule

@ -0,0 +1 @@
Subproject commit 730a8bf20d1c0edee6ea0f7906d21e071da5b3a3

1
src/view Submodule

@ -0,0 +1 @@
Subproject commit ff798ccf251322e09ae95e2a3b1b08a68fd4c120

1
src/websocket Submodule

@ -0,0 +1 @@
Subproject commit 674d0b20842063375125e97ce174cb46fcf68d35

2
vendor/.gitignore vendored
View File

@ -1,2 +0,0 @@
*
!.gitignore