初始化代码
This commit is contained in:
6
vendor/topthink/think-swoole/.gitignore
vendored
Normal file
6
vendor/topthink/think-swoole/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
.DS_Store
|
||||
*.xml
|
||||
.idea/think-swoole.iml
|
||||
composer.lock
|
||||
vendor
|
||||
201
vendor/topthink/think-swoole/LICENSE
vendored
Normal file
201
vendor/topthink/think-swoole/LICENSE
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
36
vendor/topthink/think-swoole/README.md
vendored
Normal file
36
vendor/topthink/think-swoole/README.md
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
ThinkPHP Swoole 扩展
|
||||
===============
|
||||
|
||||
## 安装
|
||||
|
||||
首先按照Swoole官网说明安装swoole扩展,然后使用
|
||||
~~~
|
||||
composer require topthink/think-swoole
|
||||
~~~
|
||||
安装swoole扩展。
|
||||
|
||||
## 使用方法
|
||||
|
||||
|
||||
直接在命令行下启动HTTP服务端。
|
||||
|
||||
~~~
|
||||
php think swoole
|
||||
~~~
|
||||
|
||||
启动完成后,默认会在0.0.0.0:80启动一个HTTP Server,可以直接访问当前的应用。
|
||||
|
||||
swoole的相关参数可以在`config/swoole.php`里面配置(具体参考配置文件内容)。
|
||||
|
||||
如果需要使用守护进程方式运行,可以配置
|
||||
|
||||
~~~
|
||||
'options' => [
|
||||
'daemonize' => true
|
||||
]
|
||||
~~~
|
||||
|
||||
支持的操作包括
|
||||
~~~
|
||||
php think swoole [start|stop|reload|restart]
|
||||
~~~
|
||||
42
vendor/topthink/think-swoole/composer.json
vendored
Normal file
42
vendor/topthink/think-swoole/composer.json
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "topthink/think-swoole",
|
||||
"description": "Swoole extend for thinkphp",
|
||||
"license": "Apache-2.0",
|
||||
"authors": [
|
||||
{
|
||||
"name": "liu21st",
|
||||
"email": "liu21st@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-swoole": ">=4.0.0",
|
||||
"topthink/framework": "^6.0",
|
||||
"topthink/think-helper": "^3.0.4",
|
||||
"symfony/finder": "^4.3.2",
|
||||
"swoole/ide-helper": "^4.3",
|
||||
"nette/php-generator": "^3.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"think\\swoole\\": "src"
|
||||
},
|
||||
"files": [
|
||||
"src/helpers.php"
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
"think": {
|
||||
"services": [
|
||||
"think\\swoole\\Service"
|
||||
],
|
||||
"config": {
|
||||
"swoole": "src/config/swoole.php"
|
||||
}
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"require-dev": {
|
||||
"symfony/var-dumper": "^4.3"
|
||||
}
|
||||
}
|
||||
32
vendor/topthink/think-swoole/src/App.php
vendored
Normal file
32
vendor/topthink/think-swoole/src/App.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole;
|
||||
|
||||
use think\swoole\coroutine\Context;
|
||||
use think\swoole\rpc\client\Proxy;
|
||||
|
||||
class App extends \think\App
|
||||
{
|
||||
public function runningInConsole()
|
||||
{
|
||||
return !!Context::getData('_fd');
|
||||
}
|
||||
|
||||
protected function isRpcInterface($abstract)
|
||||
{
|
||||
if (interface_exists($abstract) && defined($abstract . '::RPC')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function make(string $abstract, array $vars = [], bool $newInstance = false)
|
||||
{
|
||||
if ($this->isRpcInterface($abstract) && !$this->bound($abstract)) {
|
||||
//rpc接口
|
||||
$this->bind($abstract, Proxy::getClassName($abstract));
|
||||
}
|
||||
|
||||
return parent::make($abstract, $vars, $newInstance);
|
||||
}
|
||||
}
|
||||
51
vendor/topthink/think-swoole/src/FileWatcher.php
vendored
Normal file
51
vendor/topthink/think-swoole/src/FileWatcher.php
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole;
|
||||
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
class FileWatcher
|
||||
{
|
||||
protected $finder;
|
||||
|
||||
protected $files = [];
|
||||
|
||||
public function __construct($directory, $exclude, $name)
|
||||
{
|
||||
$this->finder = new Finder();
|
||||
$this->finder->files()
|
||||
->name($name)
|
||||
->in($directory)
|
||||
->exclude($exclude);
|
||||
}
|
||||
|
||||
protected function findFiles()
|
||||
{
|
||||
$files = [];
|
||||
/** @var SplFileInfo $f */
|
||||
foreach ($this->finder as $f) {
|
||||
$files[$f->getRealpath()] = $f->getMTime();
|
||||
}
|
||||
return $files;
|
||||
}
|
||||
|
||||
public function watch(callable $callback)
|
||||
{
|
||||
$this->files = $this->findFiles();
|
||||
|
||||
swoole_timer_tick(1000, function () use ($callback) {
|
||||
|
||||
$files = $this->findFiles();
|
||||
|
||||
foreach ($files as $path => $time) {
|
||||
if (empty($this->files[$path]) || $this->files[$path] != $time) {
|
||||
call_user_func($callback);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->files = $files;
|
||||
});
|
||||
}
|
||||
}
|
||||
66
vendor/topthink/think-swoole/src/Http.php
vendored
Normal file
66
vendor/topthink/think-swoole/src/Http.php
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole;
|
||||
|
||||
use think\Middleware;
|
||||
use think\Route;
|
||||
|
||||
/**
|
||||
* Class Http
|
||||
* @package think\swoole
|
||||
* @property $request
|
||||
*/
|
||||
class Http extends \think\Http
|
||||
{
|
||||
/** @var Middleware */
|
||||
protected static $middleware;
|
||||
|
||||
/** @var Route */
|
||||
protected static $route;
|
||||
|
||||
protected function loadMiddleware(): void
|
||||
{
|
||||
if (!isset(self::$middleware)) {
|
||||
parent::loadMiddleware();
|
||||
self::$middleware = clone $this->app->middleware;
|
||||
}
|
||||
|
||||
$middleware = clone self::$middleware;
|
||||
|
||||
$app = $this->app;
|
||||
$closure = function () use ($app) {
|
||||
$this->app = $app;
|
||||
};
|
||||
|
||||
$resetMiddleware = $closure->bindTo($middleware, $middleware);
|
||||
$resetMiddleware();
|
||||
|
||||
$this->app->instance("middleware", $middleware);
|
||||
}
|
||||
|
||||
protected function loadRoutes(): void
|
||||
{
|
||||
if (!isset(self::$route)) {
|
||||
parent::loadRoutes();
|
||||
self::$route = clone $this->app->route;
|
||||
}
|
||||
}
|
||||
|
||||
protected function dispatchToRoute($request)
|
||||
{
|
||||
if (isset(self::$route)) {
|
||||
$newRoute = clone self::$route;
|
||||
$app = $this->app;
|
||||
$closure = function () use ($app) {
|
||||
$this->app = $app;
|
||||
};
|
||||
|
||||
$resetRouter = $closure->bindTo($newRoute, $newRoute);
|
||||
$resetRouter();
|
||||
|
||||
$this->app->instance("route", $newRoute);
|
||||
}
|
||||
|
||||
return parent::dispatchToRoute($request);
|
||||
}
|
||||
}
|
||||
282
vendor/topthink/think-swoole/src/Manager.php
vendored
Normal file
282
vendor/topthink/think-swoole/src/Manager.php
vendored
Normal file
@@ -0,0 +1,282 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\swoole;
|
||||
|
||||
use Closure;
|
||||
use Exception;
|
||||
use Swoole\Process;
|
||||
use Swoole\Server;
|
||||
use think\App;
|
||||
use think\console\Output;
|
||||
use think\exception\Handle;
|
||||
use think\helper\Str;
|
||||
use think\swoole\App as SwooleApp;
|
||||
use think\swoole\concerns\InteractsWithHttp;
|
||||
use think\swoole\concerns\InteractsWithRpc;
|
||||
use think\swoole\concerns\InteractsWithServer;
|
||||
use think\swoole\concerns\InteractsWithSwooleTable;
|
||||
use think\swoole\concerns\InteractsWithWebsocket;
|
||||
use think\swoole\pool\Cache;
|
||||
use think\swoole\pool\Db;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Class Manager
|
||||
*/
|
||||
class Manager
|
||||
{
|
||||
use InteractsWithServer, InteractsWithSwooleTable, InteractsWithHttp, InteractsWithWebsocket, InteractsWithRpc;
|
||||
|
||||
/**
|
||||
* @var App
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var SwooleApp
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/** @var PidManager */
|
||||
protected $pidManager;
|
||||
|
||||
/**
|
||||
* Server events.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $events = [
|
||||
'start',
|
||||
'shutDown',
|
||||
'workerStart',
|
||||
'workerStop',
|
||||
'workerError',
|
||||
'packet',
|
||||
'task',
|
||||
'finish',
|
||||
'pipeMessage',
|
||||
'managerStart',
|
||||
'managerStop',
|
||||
'request',
|
||||
];
|
||||
|
||||
/**
|
||||
* Manager constructor.
|
||||
* @param App $container
|
||||
* @param PidManager $pidManager
|
||||
*/
|
||||
public function __construct(App $container, PidManager $pidManager)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->pidManager = $pidManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动服务
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$this->initialize();
|
||||
if ($this->getConfig('hot_update.enable', false)) {
|
||||
//热更新
|
||||
$this->addHotUpdateProcess();
|
||||
}
|
||||
|
||||
$this->getServer()->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止服务
|
||||
*/
|
||||
public function stop(): void
|
||||
{
|
||||
$this->getServer()->shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize.
|
||||
*/
|
||||
protected function initialize(): void
|
||||
{
|
||||
$this->createTables();
|
||||
$this->prepareWebsocket();
|
||||
$this->setSwooleServerListeners();
|
||||
$this->prepareRpc();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @param string $name
|
||||
* @param null $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConfig(string $name, $default = null)
|
||||
{
|
||||
return $this->container->config->get("swoole.{$name}", $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发事件
|
||||
* @param $event
|
||||
* @param $params
|
||||
*/
|
||||
protected function triggerEvent(string $event, $params = null): void
|
||||
{
|
||||
$this->container->event->trigger("swoole.{$event}", $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听事件
|
||||
* @param string $event
|
||||
* @param $listener
|
||||
* @param bool $first
|
||||
*/
|
||||
protected function onEvent(string $event, $listener, bool $first = false): void
|
||||
{
|
||||
$this->container->event->listen("swoole.{$event}", $listener, $first);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Server
|
||||
*/
|
||||
public function getServer()
|
||||
{
|
||||
return $this->container->make(Server::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set swoole server listeners.
|
||||
*/
|
||||
protected function setSwooleServerListeners()
|
||||
{
|
||||
foreach ($this->events as $event) {
|
||||
$listener = Str::camel("on_$event");
|
||||
$callback = method_exists($this, $listener) ? [$this, $listener] : function () use ($event) {
|
||||
$this->triggerEvent($event, func_get_args());
|
||||
};
|
||||
|
||||
$this->getServer()->on($event, $callback);
|
||||
}
|
||||
}
|
||||
|
||||
protected function prepareApplication()
|
||||
{
|
||||
if (!$this->app instanceof SwooleApp) {
|
||||
$this->app = new SwooleApp($this->container->getRootPath());
|
||||
$this->app->bind(SwooleApp::class, App::class);
|
||||
//绑定连接池
|
||||
if ($this->getConfig('pool.db.enable', true)) {
|
||||
$this->app->bind('db', Db::class);
|
||||
}
|
||||
if ($this->getConfig('pool.cache.enable', true)) {
|
||||
$this->app->bind('cache', Cache::class);
|
||||
}
|
||||
$this->app->initialize();
|
||||
$this->prepareConcretes();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预加载
|
||||
*/
|
||||
protected function prepareConcretes()
|
||||
{
|
||||
$defaultConcretes = ['db', 'cache', 'event'];
|
||||
|
||||
$concretes = array_merge($defaultConcretes, $this->getConfig('concretes', []));
|
||||
|
||||
foreach ($concretes as $concrete) {
|
||||
if ($this->app->has($concrete)) {
|
||||
$this->app->make($concrete);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除apc、op缓存
|
||||
*/
|
||||
protected function clearCache()
|
||||
{
|
||||
if (extension_loaded('apc')) {
|
||||
apc_clear_cache();
|
||||
}
|
||||
|
||||
if (extension_loaded('Zend OPcache')) {
|
||||
opcache_reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set process name.
|
||||
*
|
||||
* @param $process
|
||||
*/
|
||||
protected function setProcessName($process)
|
||||
{
|
||||
// Mac OSX不支持进程重命名
|
||||
if (stristr(PHP_OS, 'DAR')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$serverName = 'swoole_http_server';
|
||||
$appName = $this->container->config->get('app.name', 'ThinkPHP');
|
||||
|
||||
$name = sprintf('%s: %s for %s', $serverName, $process, $appName);
|
||||
|
||||
swoole_set_process_name($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 热更新
|
||||
*/
|
||||
protected function addHotUpdateProcess()
|
||||
{
|
||||
$process = new Process(function () {
|
||||
$watcher = new FileWatcher($this->getConfig('hot_update.include', []), $this->getConfig('hot_update.exclude', []), $this->getConfig('hot_update.name', []));
|
||||
|
||||
$watcher->watch(function () {
|
||||
$this->getServer()->reload();
|
||||
});
|
||||
}, false, 0);
|
||||
|
||||
$this->getServer()->addProcess($process);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在沙箱中执行
|
||||
* @param Closure $callable
|
||||
* @param null $fd
|
||||
* @param bool $persistent
|
||||
*/
|
||||
protected function runInSandbox(Closure $callable, $fd = null, $persistent = false)
|
||||
{
|
||||
/** @var Sandbox $sandbox */
|
||||
$sandbox = $this->app->make(Sandbox::class);
|
||||
|
||||
$sandbox->run($callable, $fd, $persistent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log server error.
|
||||
*
|
||||
* @param Throwable|Exception $e
|
||||
*/
|
||||
public function logServerError(Throwable $e)
|
||||
{
|
||||
/** @var Handle $handle */
|
||||
$handle = $this->container->make(Handle::class);
|
||||
|
||||
$handle->renderForConsole(new Output(), $e);
|
||||
|
||||
$handle->report($e);
|
||||
}
|
||||
}
|
||||
119
vendor/topthink/think-swoole/src/PidManager.php
vendored
Normal file
119
vendor/topthink/think-swoole/src/PidManager.php
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole;
|
||||
|
||||
use RuntimeException;
|
||||
use Swoole\Process;
|
||||
use think\helper\Arr;
|
||||
|
||||
class PidManager
|
||||
{
|
||||
/** @var string */
|
||||
protected $file;
|
||||
|
||||
public function __construct(string $file = null)
|
||||
{
|
||||
$this->file = $file ?? (sys_get_temp_dir() . '/swoole.pid');
|
||||
}
|
||||
|
||||
public function create(int $masterPid, int $managerPid)
|
||||
{
|
||||
if (!is_writable($this->file)
|
||||
&& !is_writable(dirname($this->file))
|
||||
) {
|
||||
throw new RuntimeException(
|
||||
sprintf('Pid file "%s" is not writable', $this->file)
|
||||
);
|
||||
}
|
||||
|
||||
file_put_contents($this->file, $masterPid . ',' . $managerPid);
|
||||
}
|
||||
|
||||
public function getMasterPid()
|
||||
{
|
||||
return $this->getPids()['masterPid'];
|
||||
}
|
||||
|
||||
public function getManagerPid()
|
||||
{
|
||||
return $this->getPids()['managerPid'];
|
||||
}
|
||||
|
||||
public function getPids(): array
|
||||
{
|
||||
$pids = [];
|
||||
|
||||
if (is_readable($this->file)) {
|
||||
$content = file_get_contents($this->file);
|
||||
$pids = explode(',', $content);
|
||||
}
|
||||
|
||||
return [
|
||||
'masterPid' => $pids[0] ?? null,
|
||||
'managerPid' => $pids[1] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否运行中
|
||||
* @return bool
|
||||
*/
|
||||
public function isRunning()
|
||||
{
|
||||
$pids = $this->getPids();
|
||||
|
||||
if (!count($pids)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$masterPid = $pids['masterPid'] ?? null;
|
||||
$managerPid = $pids['managerPid'] ?? null;
|
||||
|
||||
if ($managerPid) {
|
||||
// Swoole process mode
|
||||
return $masterPid && $managerPid && Process::kill((int) $managerPid, 0);
|
||||
}
|
||||
|
||||
// Swoole base mode, no manager process
|
||||
return $masterPid && Process::kill((int) $masterPid, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill process.
|
||||
*
|
||||
* @param int $sig
|
||||
* @param int $wait
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function killProcess($sig, $wait = 0)
|
||||
{
|
||||
Process::kill(
|
||||
Arr::first($this->getPids()),
|
||||
$sig
|
||||
);
|
||||
|
||||
if ($wait) {
|
||||
$start = time();
|
||||
|
||||
do {
|
||||
if (!$this->isRunning()) {
|
||||
break;
|
||||
}
|
||||
|
||||
usleep(100000);
|
||||
} while (time() < $start + $wait);
|
||||
}
|
||||
|
||||
return $this->isRunning();
|
||||
}
|
||||
|
||||
public function remove(): bool
|
||||
{
|
||||
if (is_writable($this->file)) {
|
||||
return unlink($this->file);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
255
vendor/topthink/think-swoole/src/Sandbox.php
vendored
Normal file
255
vendor/topthink/think-swoole/src/Sandbox.php
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole;
|
||||
|
||||
use Closure;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\VarDumper\VarDumper;
|
||||
use think\App;
|
||||
use think\Config;
|
||||
use think\Container;
|
||||
use think\Event;
|
||||
use think\Http;
|
||||
use think\service\PaginatorService;
|
||||
use think\swoole\contract\ResetterInterface;
|
||||
use think\swoole\coroutine\Context;
|
||||
use think\swoole\middleware\ResetVarDumper;
|
||||
use think\swoole\resetters\ClearInstances;
|
||||
use think\swoole\resetters\ResetConfig;
|
||||
use think\swoole\resetters\ResetEvent;
|
||||
use think\swoole\resetters\ResetService;
|
||||
use Throwable;
|
||||
|
||||
class Sandbox
|
||||
{
|
||||
/**
|
||||
* The app containers in different coroutine environment.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $snapshots = [];
|
||||
|
||||
/** @var Manager */
|
||||
protected $manager;
|
||||
|
||||
/** @var App */
|
||||
protected $app;
|
||||
|
||||
/** @var Config */
|
||||
protected $config;
|
||||
|
||||
/** @var Event */
|
||||
protected $event;
|
||||
|
||||
/** @var ResetterInterface[] */
|
||||
protected $resetters = [];
|
||||
protected $services = [];
|
||||
|
||||
public function __construct(Container $app, Manager $manager)
|
||||
{
|
||||
$this->manager = $manager;
|
||||
$this->setBaseApp($app);
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
public function setBaseApp(Container $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBaseApp()
|
||||
{
|
||||
return $this->app;
|
||||
}
|
||||
|
||||
protected function initialize()
|
||||
{
|
||||
Container::setInstance(function () {
|
||||
return $this->getApplication();
|
||||
});
|
||||
|
||||
$this->app->bind(Http::class, \think\swoole\Http::class);
|
||||
|
||||
$this->setInitialConfig();
|
||||
$this->setInitialServices();
|
||||
$this->setInitialEvent();
|
||||
$this->setInitialResetters();
|
||||
//兼容var-dumper
|
||||
$this->compatibleVarDumper();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function run(Closure $callable, $fd = null, $persistent = false)
|
||||
{
|
||||
$this->init($fd);
|
||||
|
||||
try {
|
||||
$this->getApplication()->invoke($callable, [$this]);
|
||||
} catch (Throwable $e) {
|
||||
$this->manager->logServerError($e);
|
||||
} finally {
|
||||
$this->clear(!$persistent);
|
||||
}
|
||||
}
|
||||
|
||||
public function init($fd = null)
|
||||
{
|
||||
if (!is_null($fd)) {
|
||||
Context::setData('_fd', $fd);
|
||||
}
|
||||
$this->setInstance($app = $this->getApplication());
|
||||
$this->resetApp($app);
|
||||
}
|
||||
|
||||
public function clear($snapshot = true)
|
||||
{
|
||||
if ($snapshot) {
|
||||
unset($this->snapshots[$this->getSnapshotId()]);
|
||||
}
|
||||
|
||||
Context::clear();
|
||||
$this->setInstance($this->getBaseApp());
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
public function getApplication()
|
||||
{
|
||||
$snapshot = $this->getSnapshot();
|
||||
if ($snapshot instanceof Container) {
|
||||
return $snapshot;
|
||||
}
|
||||
|
||||
$snapshot = clone $this->getBaseApp();
|
||||
$this->setSnapshot($snapshot);
|
||||
|
||||
return $snapshot;
|
||||
}
|
||||
|
||||
protected function getSnapshotId()
|
||||
{
|
||||
if ($fd = Context::getData('_fd')) {
|
||||
return "fd_" . $fd;
|
||||
} else {
|
||||
return Context::getCoroutineId();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current snapshot.
|
||||
* @return App|null
|
||||
*/
|
||||
public function getSnapshot()
|
||||
{
|
||||
return $this->snapshots[$this->getSnapshotId()] ?? null;
|
||||
}
|
||||
|
||||
public function setSnapshot(Container $snapshot)
|
||||
{
|
||||
$this->snapshots[$this->getSnapshotId()] = $snapshot;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setInstance(Container $app)
|
||||
{
|
||||
$app->instance('app', $app);
|
||||
$app->instance(Container::class, $app);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set initial config.
|
||||
*/
|
||||
protected function setInitialConfig()
|
||||
{
|
||||
$this->config = clone $this->getBaseApp()->config;
|
||||
}
|
||||
|
||||
protected function setInitialEvent()
|
||||
{
|
||||
$this->event = clone $this->getBaseApp()->event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get config snapshot.
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
public function getEvent()
|
||||
{
|
||||
return $this->event;
|
||||
}
|
||||
|
||||
public function getServices()
|
||||
{
|
||||
return $this->services;
|
||||
}
|
||||
|
||||
protected function setInitialServices()
|
||||
{
|
||||
$app = $this->getBaseApp();
|
||||
|
||||
$services = [
|
||||
PaginatorService::class,
|
||||
];
|
||||
|
||||
$services = array_merge($services, $this->config->get('swoole.services', []));
|
||||
|
||||
foreach ($services as $service) {
|
||||
if (class_exists($service) && !in_array($service, $this->services)) {
|
||||
$serviceObj = new $service($app);
|
||||
$this->services[$service] = $serviceObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize resetters.
|
||||
*/
|
||||
protected function setInitialResetters()
|
||||
{
|
||||
$app = $this->getBaseApp();
|
||||
|
||||
$resetters = [
|
||||
ClearInstances::class,
|
||||
ResetConfig::class,
|
||||
ResetEvent::class,
|
||||
ResetService::class,
|
||||
];
|
||||
|
||||
$resetters = array_merge($resetters, $this->config->get('swoole.resetters', []));
|
||||
|
||||
foreach ($resetters as $resetter) {
|
||||
$resetterClass = $app->make($resetter);
|
||||
if (!$resetterClass instanceof ResetterInterface) {
|
||||
throw new RuntimeException("{$resetter} must implement " . ResetterInterface::class);
|
||||
}
|
||||
$this->resetters[$resetter] = $resetterClass;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset Application.
|
||||
*
|
||||
* @param Container $app
|
||||
*/
|
||||
protected function resetApp(Container $app)
|
||||
{
|
||||
foreach ($this->resetters as $resetter) {
|
||||
$resetter->handle($app, $this);
|
||||
}
|
||||
}
|
||||
|
||||
protected function compatibleVarDumper()
|
||||
{
|
||||
if (class_exists(VarDumper::class)) {
|
||||
$this->app->middleware->add(ResetVarDumper::class);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
87
vendor/topthink/think-swoole/src/Service.php
vendored
Normal file
87
vendor/topthink/think-swoole/src/Service.php
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\swoole;
|
||||
|
||||
use Swoole\Http\Server as HttpServer;
|
||||
use Swoole\Server;
|
||||
use Swoole\Websocket\Server as WebsocketServer;
|
||||
use think\Route;
|
||||
use think\swoole\command\RpcInterface;
|
||||
use think\swoole\command\Server as ServerCommand;
|
||||
use think\swoole\websocket\socketio\Controller;
|
||||
|
||||
class Service extends \think\Service
|
||||
{
|
||||
protected $isWebsocket = false;
|
||||
|
||||
/**
|
||||
* @var HttpServer | WebsocketServer
|
||||
*/
|
||||
protected static $server;
|
||||
|
||||
public function register()
|
||||
{
|
||||
$this->isWebsocket = $this->app->config->get('swoole.websocket.enable', false);
|
||||
|
||||
$this->app->bind(Server::class, function () {
|
||||
if (is_null(static::$server)) {
|
||||
$this->createSwooleServer();
|
||||
}
|
||||
|
||||
return static::$server;
|
||||
});
|
||||
|
||||
$this->app->bind("swoole.server", Server::class);
|
||||
|
||||
$this->app->bind(PidManager::class, function () {
|
||||
return new PidManager($this->app->config->get("swoole.server.options.pid_file"));
|
||||
});
|
||||
}
|
||||
|
||||
public function boot()
|
||||
{
|
||||
$this->commands(ServerCommand::class, RpcInterface::class);
|
||||
|
||||
if ($this->isWebsocket) {
|
||||
$this->registerRoutes(function (Route $route) {
|
||||
$route->group(function () use ($route) {
|
||||
$route->get('socket.io/', '@upgrade');
|
||||
$route->post('socket.io/', '@reject');
|
||||
})
|
||||
->prefix(Controller::class)
|
||||
->allowCrossDomain([
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
'X-XSS-Protection' => 0,
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create swoole server.
|
||||
*/
|
||||
protected function createSwooleServer()
|
||||
{
|
||||
$server = $this->isWebsocket ? WebsocketServer::class : HttpServer::class;
|
||||
$config = $this->app->config;
|
||||
$host = $config->get('swoole.server.host');
|
||||
$port = $config->get('swoole.server.port');
|
||||
$socketType = $config->get('swoole.server.socket_type', SWOOLE_SOCK_TCP);
|
||||
$mode = $config->get('swoole.server.mode', SWOOLE_PROCESS);
|
||||
|
||||
static::$server = new $server($host, $port, $mode, $socketType);
|
||||
|
||||
$options = $config->get('swoole.server.options');
|
||||
|
||||
static::$server->set($options);
|
||||
}
|
||||
}
|
||||
73
vendor/topthink/think-swoole/src/Table.php
vendored
Normal file
73
vendor/topthink/think-swoole/src/Table.php
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\swoole;
|
||||
|
||||
use Swoole\Table as SwooleTable;
|
||||
|
||||
class Table
|
||||
{
|
||||
/**
|
||||
* Registered swoole tables.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $tables = [];
|
||||
|
||||
/**
|
||||
* Add a swoole table to existing tables.
|
||||
*
|
||||
* @param string $name
|
||||
* @param SwooleTable $table
|
||||
*
|
||||
* @return Table
|
||||
*/
|
||||
public function add(string $name, SwooleTable $table)
|
||||
{
|
||||
$this->tables[$name] = $table;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a swoole table by its name from existing tables.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return SwooleTable $table
|
||||
*/
|
||||
public function get(string $name)
|
||||
{
|
||||
return $this->tables[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all existing swoole tables.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAll()
|
||||
{
|
||||
return $this->tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically access table.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return SwooleTable
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
return $this->get($key);
|
||||
}
|
||||
}
|
||||
228
vendor/topthink/think-swoole/src/Websocket.php
vendored
Normal file
228
vendor/topthink/think-swoole/src/Websocket.php
vendored
Normal file
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole;
|
||||
|
||||
use Swoole\Server;
|
||||
use think\swoole\contract\websocket\ParserInterface;
|
||||
use think\swoole\coroutine\Context;
|
||||
use think\swoole\websocket\Room;
|
||||
|
||||
/**
|
||||
* Class Websocket
|
||||
*/
|
||||
class Websocket
|
||||
{
|
||||
|
||||
const PUSH_ACTION = 'push';
|
||||
const EVENT_CONNECT = 'connect';
|
||||
|
||||
/**
|
||||
* @var Server
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* @var Room
|
||||
*/
|
||||
protected $room;
|
||||
|
||||
/**
|
||||
* @var ParserInterface
|
||||
*/
|
||||
protected $parser;
|
||||
|
||||
/**
|
||||
* Websocket constructor.
|
||||
*
|
||||
* @param Server $server
|
||||
* @param Room $room
|
||||
* @param ParserInterface $parser
|
||||
*/
|
||||
public function __construct(Server $server, Room $room, ParserInterface $parser)
|
||||
{
|
||||
$this->server = $server;
|
||||
$this->room = $room;
|
||||
$this->parser = $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set broadcast to true.
|
||||
*/
|
||||
public function broadcast(): self
|
||||
{
|
||||
Context::setData('websocket._broadcast', true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get broadcast status value.
|
||||
*/
|
||||
public function isBroadcast()
|
||||
{
|
||||
return Context::getData('websocket._broadcast', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set multiple recipients fd or room names.
|
||||
*
|
||||
* @param integer, string, array
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function to($values): self
|
||||
{
|
||||
$values = is_string($values) || is_integer($values) ? func_get_args() : $values;
|
||||
|
||||
$to = Context::getData("websocket._to", []);
|
||||
|
||||
foreach ($values as $value) {
|
||||
if (!in_array($value, $to)) {
|
||||
$to[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
Context::setData("websocket._to", $to);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get push destinations (fd or room name).
|
||||
*/
|
||||
public function getTo()
|
||||
{
|
||||
return Context::getData("websocket._to", []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join sender to multiple rooms.
|
||||
*
|
||||
* @param string, array $rooms
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function join($rooms): self
|
||||
{
|
||||
$rooms = is_string($rooms) || is_integer($rooms) ? func_get_args() : $rooms;
|
||||
|
||||
$this->room->add($this->getSender(), $rooms);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sender leave multiple rooms.
|
||||
*
|
||||
* @param array $rooms
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function leave($rooms = []): self
|
||||
{
|
||||
$rooms = is_string($rooms) || is_integer($rooms) ? func_get_args() : $rooms;
|
||||
|
||||
$this->room->delete($this->getSender(), $rooms);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit data and reset some status.
|
||||
*
|
||||
* @param string
|
||||
* @param mixed
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function emit(string $event, $data = null): bool
|
||||
{
|
||||
$fds = $this->getFds();
|
||||
$assigned = !empty($this->getTo());
|
||||
|
||||
try {
|
||||
if (empty($fds) && $assigned) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $this->server->task([
|
||||
'action' => static::PUSH_ACTION,
|
||||
'data' => [
|
||||
'sender' => $this->getSender(),
|
||||
'descriptors' => $fds,
|
||||
'broadcast' => $this->isBroadcast(),
|
||||
'assigned' => $assigned,
|
||||
'payload' => $this->parser->encode($event, $data),
|
||||
],
|
||||
]);
|
||||
|
||||
return $result !== false;
|
||||
} finally {
|
||||
$this->reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close current connection.
|
||||
*
|
||||
* @param integer
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function close(int $fd = null)
|
||||
{
|
||||
return $this->server->close($fd ?: $this->getSender());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sender fd.
|
||||
*
|
||||
* @param integer
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSender(int $fd)
|
||||
{
|
||||
Context::setData('websocket._sender', $fd);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current sender fd.
|
||||
*/
|
||||
public function getSender()
|
||||
{
|
||||
return Context::getData('websocket._sender');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all fds we're going to push data to.
|
||||
*/
|
||||
protected function getFds()
|
||||
{
|
||||
$to = $this->getTo();
|
||||
$fds = array_filter($to, function ($value) {
|
||||
return is_integer($value);
|
||||
});
|
||||
$rooms = array_diff($to, $fds);
|
||||
|
||||
foreach ($rooms as $room) {
|
||||
$clients = $this->room->getClients($room);
|
||||
// fallback fd with wrong type back to fds array
|
||||
if (empty($clients) && is_numeric($room)) {
|
||||
$fds[] = $room;
|
||||
} else {
|
||||
$fds = array_merge($fds, $clients);
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_unique($fds));
|
||||
}
|
||||
|
||||
protected function reset()
|
||||
{
|
||||
Context::removeData("websocket._to");
|
||||
Context::removeData('websocket._broadcast');
|
||||
}
|
||||
}
|
||||
79
vendor/topthink/think-swoole/src/command/RpcInterface.php
vendored
Normal file
79
vendor/topthink/think-swoole/src/command/RpcInterface.php
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\command;
|
||||
|
||||
use Nette\PhpGenerator\ClassType;
|
||||
use Nette\PhpGenerator\Helpers;
|
||||
use Nette\PhpGenerator\PhpFile;
|
||||
use think\console\Command;
|
||||
use think\helper\Arr;
|
||||
use think\swoole\contract\rpc\ParserInterface;
|
||||
use think\swoole\exception\RpcResponseException;
|
||||
use think\swoole\rpc\client\Client;
|
||||
use think\swoole\rpc\Error;
|
||||
use think\swoole\rpc\JsonParser;
|
||||
use think\swoole\rpc\server\Dispatcher;
|
||||
|
||||
class RpcInterface extends Command
|
||||
{
|
||||
public function configure()
|
||||
{
|
||||
$this->setName('rpc:interface')
|
||||
->setDescription('Generate Rpc Service Interfaces');
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
go(function () {
|
||||
$clients = $this->app->config->get('swoole.rpc.client', []);
|
||||
|
||||
$file = new PhpFile;
|
||||
$file->addComment('This file is auto-generated.');
|
||||
$file->setStrictTypes();
|
||||
|
||||
foreach ($clients as $name => $config) {
|
||||
|
||||
$client = new Client($config['host'], $config['port']);
|
||||
|
||||
$response = $client->sendAndRecv(Dispatcher::ACTION_INTERFACE);
|
||||
|
||||
$parserClass = Arr::get($config, 'parser', JsonParser::class);
|
||||
/** @var ParserInterface $parser */
|
||||
$parser = new $parserClass;
|
||||
|
||||
$result = $parser->decodeResponse($response);
|
||||
|
||||
if ($result instanceof Error) {
|
||||
throw new RpcResponseException($result);
|
||||
}
|
||||
|
||||
$namespace = $file->addNamespace("rpc\\contract\\${name}");
|
||||
foreach ($result as $interface => $methods) {
|
||||
$class = $namespace->addInterface($interface);
|
||||
$class->addConstant("RPC", $name);
|
||||
|
||||
foreach ($methods as $methodName => ['parameters' => $parameters, 'returnType' => $returnType, 'comment' => $comment]) {
|
||||
$method = $class->addMethod($methodName)
|
||||
->setVisibility(ClassType::VISIBILITY_PUBLIC)
|
||||
->setComment(Helpers::unformatDocComment($comment))
|
||||
->setReturnType($returnType);
|
||||
|
||||
foreach ($parameters as $parameter) {
|
||||
|
||||
$param = $method->addParameter($parameter['name'])
|
||||
->setTypeHint($parameter['type']);
|
||||
|
||||
if (array_key_exists("default", $parameter)) {
|
||||
$param->setDefaultValue($parameter['default']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents($this->app->getBasePath() . 'rpc.php', $file);
|
||||
|
||||
$this->output->writeln('<info>Succeed!</info>');
|
||||
});
|
||||
}
|
||||
}
|
||||
154
vendor/topthink/think-swoole/src/command/Server.php
vendored
Normal file
154
vendor/topthink/think-swoole/src/command/Server.php
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\swoole\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\input\Argument;
|
||||
use think\swoole\Manager;
|
||||
use think\swoole\PidManager;
|
||||
|
||||
/**
|
||||
* Swoole HTTP 命令行,支持操作:start|stop|restart|reload
|
||||
* 支持应用配置目录下的swoole.php文件进行参数配置
|
||||
*/
|
||||
class Server extends Command
|
||||
{
|
||||
|
||||
public function configure()
|
||||
{
|
||||
$this->setName('swoole')
|
||||
->addArgument('action', Argument::OPTIONAL, "start|stop|restart|reload", 'start')
|
||||
->setDescription('Swoole HTTP Server for ThinkPHP');
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->checkEnvironment();
|
||||
|
||||
$action = $this->input->getArgument('action');
|
||||
|
||||
if (in_array($action, ['start', 'stop', 'reload', 'restart'])) {
|
||||
$this->app->invokeMethod([$this, $action], [], true);
|
||||
} else {
|
||||
$this->output->writeln("<error>Invalid argument action:{$action}, Expected start|stop|restart|reload .</error>");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查环境
|
||||
*/
|
||||
protected function checkEnvironment()
|
||||
{
|
||||
if (!extension_loaded('swoole')) {
|
||||
$this->output->error('Can\'t detect Swoole extension installed.');
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!version_compare(swoole_version(), '4.3.1', 'ge')) {
|
||||
$this->output->error('Your Swoole version must be higher than `4.3.1`.');
|
||||
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动server
|
||||
* @access protected
|
||||
* @param Manager $manager
|
||||
* @param PidManager $pidManager
|
||||
* @return void
|
||||
*/
|
||||
protected function start(Manager $manager, PidManager $pidManager)
|
||||
{
|
||||
if ($pidManager->isRunning()) {
|
||||
$this->output->writeln('<error>swoole http server process is already running.</error>');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->output->writeln('Starting swoole http server...');
|
||||
|
||||
$host = $manager->getConfig('server.host');
|
||||
$port = $manager->getConfig('server.port');
|
||||
|
||||
$this->output->writeln("Swoole http server started: <http://{$host}:{$port}>");
|
||||
$this->output->writeln('You can exit with <info>`CTRL-C`</info>');
|
||||
|
||||
$manager->run();
|
||||
}
|
||||
|
||||
/**
|
||||
* 柔性重启server
|
||||
* @access protected
|
||||
* @param PidManager $manager
|
||||
* @return void
|
||||
*/
|
||||
protected function reload(PidManager $manager)
|
||||
{
|
||||
if (!$manager->isRunning()) {
|
||||
$this->output->writeln('<error>no swoole http server process running.</error>');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->output->writeln('Reloading swoole http server...');
|
||||
|
||||
if (!$manager->killProcess(SIGUSR1)) {
|
||||
$this->output->error('> failure');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->output->writeln('> success');
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止server
|
||||
* @access protected
|
||||
* @param PidManager $manager
|
||||
* @return void
|
||||
*/
|
||||
protected function stop(PidManager $manager)
|
||||
{
|
||||
if (!$manager->isRunning()) {
|
||||
$this->output->writeln('<error>no swoole http server process running.</error>');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->output->writeln('Stopping swoole http server...');
|
||||
|
||||
$isRunning = $manager->killProcess(SIGTERM, 15);
|
||||
|
||||
if ($isRunning) {
|
||||
$this->output->error('Unable to stop the swoole_http_server process.');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->output->writeln('> success');
|
||||
}
|
||||
|
||||
/**
|
||||
* 重启server
|
||||
* @access protected
|
||||
* @param Manager $manager
|
||||
* @param PidManager $pidManager
|
||||
* @return void
|
||||
*/
|
||||
protected function restart(Manager $manager, PidManager $pidManager)
|
||||
{
|
||||
if ($pidManager->isRunning()) {
|
||||
$this->stop($pidManager);
|
||||
}
|
||||
|
||||
$this->start($manager, $pidManager);
|
||||
}
|
||||
|
||||
}
|
||||
122
vendor/topthink/think-swoole/src/concerns/InteractsWithHttp.php
vendored
Normal file
122
vendor/topthink/think-swoole/src/concerns/InteractsWithHttp.php
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\concerns;
|
||||
|
||||
use Swoole\Http\Request;
|
||||
use Swoole\Http\Response;
|
||||
use Swoole\Server;
|
||||
use think\App;
|
||||
use think\Container;
|
||||
use think\Cookie;
|
||||
use think\Event;
|
||||
use think\exception\Handle;
|
||||
use think\Http;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Trait InteractsWithHttp
|
||||
* @package think\swoole\concerns
|
||||
* @property App $app
|
||||
* @property Container $container
|
||||
* @method Server getServer()
|
||||
*/
|
||||
trait InteractsWithHttp
|
||||
{
|
||||
|
||||
/**
|
||||
* "onRequest" listener.
|
||||
*
|
||||
* @param Request $req
|
||||
* @param Response $res
|
||||
*/
|
||||
public function onRequest($req, $res)
|
||||
{
|
||||
$args = func_get_args();
|
||||
$this->runInSandbox(function (Http $http, Event $event, App $app) use ($args, $req, $res) {
|
||||
$event->trigger('swoole.request', $args);
|
||||
|
||||
$request = $this->prepareRequest($req);
|
||||
try {
|
||||
$response = $this->handleRequest($http, $request);
|
||||
} catch (Throwable $e) {
|
||||
$response = $this->app
|
||||
->make(Handle::class)
|
||||
->render($request, $e);
|
||||
}
|
||||
|
||||
$this->sendResponse($res, $response, $app->cookie);
|
||||
});
|
||||
}
|
||||
|
||||
protected function handleRequest(Http $http, $request)
|
||||
{
|
||||
$level = ob_get_level();
|
||||
ob_start();
|
||||
|
||||
$response = $http->run($request);
|
||||
|
||||
$content = $response->getContent();
|
||||
|
||||
if (ob_get_level() == 0) {
|
||||
ob_start();
|
||||
}
|
||||
|
||||
$http->end($response);
|
||||
|
||||
if (ob_get_length() > 0) {
|
||||
$response->content(ob_get_contents() . $content);
|
||||
}
|
||||
|
||||
while (ob_get_level() > $level) {
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
protected function prepareRequest(Request $req)
|
||||
{
|
||||
$header = $req->header ?: [];
|
||||
$server = $req->server ?: [];
|
||||
|
||||
foreach ($header as $key => $value) {
|
||||
$server["http_" . str_replace('-', '_', $key)] = $value;
|
||||
}
|
||||
|
||||
// 重新实例化请求对象 处理swoole请求数据
|
||||
/** @var \think\Request $request */
|
||||
$request = $this->app->make('request', [], true);
|
||||
|
||||
return $request->withHeader($header)
|
||||
->withServer($server)
|
||||
->withGet($req->get ?: [])
|
||||
->withPost($req->post ?: [])
|
||||
->withCookie($req->cookie ?: [])
|
||||
->withInput($req->rawContent())
|
||||
->withFiles($req->files ?: [])
|
||||
->setBaseUrl($req->server['request_uri'])
|
||||
->setUrl($req->server['request_uri'] . (!empty($req->server['query_string']) ? '?' . $req->server['query_string'] : ''))
|
||||
->setPathinfo(ltrim($req->server['path_info'], '/'));
|
||||
}
|
||||
|
||||
protected function sendResponse(Response $res, \think\Response $response, Cookie $cookie)
|
||||
{
|
||||
// 发送Header
|
||||
foreach ($response->getHeader() as $key => $val) {
|
||||
$res->header($key, $val);
|
||||
}
|
||||
|
||||
// 发送状态码
|
||||
$res->status($response->getCode());
|
||||
|
||||
foreach ($cookie->getCookie() as $name => $val) {
|
||||
list($value, $expire, $option) = $val;
|
||||
|
||||
$res->cookie($name, $value, $expire, $option['path'], $option['domain'], $option['secure'] ? true : false, $option['httponly'] ? true : false);
|
||||
}
|
||||
|
||||
$content = $response->getContent();
|
||||
|
||||
$res->end($content);
|
||||
}
|
||||
}
|
||||
78
vendor/topthink/think-swoole/src/concerns/InteractsWithPool.php
vendored
Normal file
78
vendor/topthink/think-swoole/src/concerns/InteractsWithPool.php
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\concerns;
|
||||
|
||||
use RuntimeException;
|
||||
use Swoole\Coroutine\Channel;
|
||||
|
||||
trait InteractsWithPool
|
||||
{
|
||||
|
||||
/** @var Channel[] */
|
||||
protected $pools = [];
|
||||
|
||||
protected $connectionCount = [];
|
||||
|
||||
/**
|
||||
* 获取连接池
|
||||
* @param $name
|
||||
* @return Channel
|
||||
*/
|
||||
protected function getPool($name)
|
||||
{
|
||||
if (empty($this->pools[$name])) {
|
||||
$this->pools[$name] = new Channel($this->getPoolMaxActive($name));
|
||||
}
|
||||
return $this->pools[$name];
|
||||
}
|
||||
|
||||
protected function getPoolConnection($name)
|
||||
{
|
||||
$pool = $this->getPool($name);
|
||||
|
||||
if (!isset($this->connectionCount[$name])) {
|
||||
$this->connectionCount[$name] = 0;
|
||||
}
|
||||
|
||||
if ($this->connectionCount[$name] < $this->getPoolMaxActive($name)) {
|
||||
//新建
|
||||
$connection = $this->createPoolConnection($name);
|
||||
$this->connectionCount[$name]++;
|
||||
} else {
|
||||
$connection = $pool->pop($this->getPoolMaxWaitTime($name));
|
||||
|
||||
if ($connection === false) {
|
||||
throw new RuntimeException(sprintf(
|
||||
'Borrow the connection timeout in %.2f(s), connections in pool: %d, all connections: %d',
|
||||
$this->getPoolMaxWaitTime($name),
|
||||
$pool->length(),
|
||||
$this->connectionCount[$name] ?? 0
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->buildPoolConnection($connection, $pool);
|
||||
}
|
||||
|
||||
abstract protected function buildPoolConnection($connection, Channel $pool);
|
||||
|
||||
abstract protected function createPoolConnection(string $name);
|
||||
|
||||
abstract protected function getPoolMaxActive($name): int;
|
||||
|
||||
abstract protected function getPoolMaxWaitTime($name): int;
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
foreach ($this->pools as $pool) {
|
||||
while (true) {
|
||||
if ($pool->isEmpty()) {
|
||||
break;
|
||||
}
|
||||
$handler = $pool->pop(0.001);
|
||||
unset($handler);
|
||||
}
|
||||
$pool->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
42
vendor/topthink/think-swoole/src/concerns/InteractsWithPoolConnector.php
vendored
Normal file
42
vendor/topthink/think-swoole/src/concerns/InteractsWithPoolConnector.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\concerns;
|
||||
|
||||
use Swoole\Coroutine\Channel;
|
||||
|
||||
trait InteractsWithPoolConnector
|
||||
{
|
||||
protected $handler;
|
||||
|
||||
protected $pool;
|
||||
|
||||
protected $release = true;
|
||||
|
||||
public function __construct($handler, Channel $pool)
|
||||
{
|
||||
$this->handler = $handler;
|
||||
$this->pool = $pool;
|
||||
}
|
||||
|
||||
public function __call($method, $arguments)
|
||||
{
|
||||
return $this->handler->{$method}(...$arguments);
|
||||
}
|
||||
|
||||
public function release()
|
||||
{
|
||||
if (!$this->release) {
|
||||
return;
|
||||
}
|
||||
$this->release = false;
|
||||
|
||||
if (!$this->pool->isFull()) {
|
||||
$this->pool->push($this->handler, 0.001);
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->release();
|
||||
}
|
||||
}
|
||||
122
vendor/topthink/think-swoole/src/concerns/InteractsWithRpc.php
vendored
Normal file
122
vendor/topthink/think-swoole/src/concerns/InteractsWithRpc.php
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\concerns;
|
||||
|
||||
use Swoole\Server;
|
||||
use think\App;
|
||||
use think\Event;
|
||||
use think\helper\Str;
|
||||
use think\swoole\contract\rpc\ParserInterface;
|
||||
use think\swoole\rpc\client\Pool;
|
||||
use think\swoole\rpc\JsonParser;
|
||||
use think\swoole\rpc\server\Dispatcher;
|
||||
|
||||
/**
|
||||
* Trait InteractsWithRpc
|
||||
* @package think\swoole\concerns
|
||||
* @property App $app
|
||||
* @method Server getServer()
|
||||
*/
|
||||
trait InteractsWithRpc
|
||||
{
|
||||
|
||||
protected $rpcEvents = [
|
||||
'connect',
|
||||
'receive',
|
||||
'close',
|
||||
];
|
||||
|
||||
protected $isRpcServer = false;
|
||||
|
||||
protected function prepareRpc()
|
||||
{
|
||||
if ($this->isRpcServer = $this->getConfig('rpc.server.enable', false)) {
|
||||
$host = $this->getConfig('server.host');
|
||||
$port = $this->getConfig('rpc.server.port', 9000);
|
||||
|
||||
$rpcServer = $this->getServer()->addlistener($host, $port, SWOOLE_SOCK_TCP);
|
||||
|
||||
$rpcServer->set([
|
||||
'open_eof_check' => true,
|
||||
'open_eof_split' => true,
|
||||
'package_eof' => ParserInterface::EOF,
|
||||
]);
|
||||
|
||||
$this->setRpcServerListeners($rpcServer);
|
||||
}
|
||||
|
||||
$this->onEvent('workerStart', function () {
|
||||
if ($this->isRpcServer) {
|
||||
$this->bindRpcParser();
|
||||
$this->bindRpcDispatcher();
|
||||
}
|
||||
$this->bindRpcClientPool();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Server\Port $server
|
||||
*/
|
||||
protected function setRpcServerListeners($server)
|
||||
{
|
||||
foreach ($this->rpcEvents as $event) {
|
||||
$listener = Str::camel("on_rpc_$event");
|
||||
$callback = method_exists($this, $listener) ? [$this, $listener] : function () use ($event) {
|
||||
$this->triggerEvent("rpc." . $event, func_get_args());
|
||||
};
|
||||
|
||||
$server->on($event, $callback);
|
||||
}
|
||||
}
|
||||
|
||||
protected function bindRpcClientPool()
|
||||
{
|
||||
if (!empty($clients = $this->getConfig('rpc.client'))) {
|
||||
$this->app->instance(Pool::class, new Pool($clients));
|
||||
//引入rpc接口文件
|
||||
if (file_exists($rpc = $this->app->getBasePath() . 'rpc.php')) {
|
||||
include_once $rpc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function bindRpcDispatcher()
|
||||
{
|
||||
$services = $this->getConfig('rpc.server.services', []);
|
||||
|
||||
$this->app->make(Dispatcher::class, [$services]);
|
||||
}
|
||||
|
||||
protected function bindRpcParser()
|
||||
{
|
||||
$parserClass = $this->getConfig('rpc.server.parser', JsonParser::class);
|
||||
|
||||
$this->app->bind(ParserInterface::class, $parserClass);
|
||||
$this->app->make(ParserInterface::class);
|
||||
}
|
||||
|
||||
public function onRpcConnect(Server $server, int $fd, int $reactorId)
|
||||
{
|
||||
$args = func_get_args();
|
||||
$this->runInSandbox(function (Event $event, Dispatcher $dispatcher) use ($fd, $server, $args) {
|
||||
$event->trigger("swoole.rpc.Connect", $args);
|
||||
}, $fd, true);
|
||||
}
|
||||
|
||||
public function onRpcReceive(Server $server, $fd, $reactorId, $data)
|
||||
{
|
||||
$data = rtrim($data, ParserInterface::EOF);
|
||||
$this->runInSandbox(function (Dispatcher $dispatcher) use ($fd, $data, $server) {
|
||||
$dispatcher->dispatch($fd, $data);
|
||||
}, $fd, true);
|
||||
}
|
||||
|
||||
public function onRpcClose(Server $server, int $fd, int $reactorId)
|
||||
{
|
||||
$args = func_get_args();
|
||||
$this->runInSandbox(function (Event $event) use ($args) {
|
||||
$event->trigger("swoole.rpc.Close", $args);
|
||||
}, $fd);
|
||||
}
|
||||
|
||||
}
|
||||
97
vendor/topthink/think-swoole/src/concerns/InteractsWithServer.php
vendored
Normal file
97
vendor/topthink/think-swoole/src/concerns/InteractsWithServer.php
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\concerns;
|
||||
|
||||
use Exception;
|
||||
use Swoole\Runtime;
|
||||
use Swoole\Server\Task;
|
||||
use think\Event;
|
||||
use think\swoole\PidManager;
|
||||
|
||||
/**
|
||||
* Trait InteractsWithServer
|
||||
* @package think\swoole\concerns
|
||||
* @property PidManager $pidManager
|
||||
*/
|
||||
trait InteractsWithServer
|
||||
{
|
||||
|
||||
/**
|
||||
* "onStart" listener.
|
||||
*/
|
||||
public function onStart()
|
||||
{
|
||||
$this->setProcessName('master process');
|
||||
$this->pidManager->create($this->getServer()->master_pid, $this->getServer()->manager_pid ?? 0);
|
||||
|
||||
$this->triggerEvent("start", func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* The listener of "managerStart" event.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function onManagerStart()
|
||||
{
|
||||
$this->setProcessName('manager process');
|
||||
$this->triggerEvent("managerStart", func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* "onWorkerStart" listener.
|
||||
*
|
||||
* @param \Swoole\Http\Server|mixed $server
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function onWorkerStart($server)
|
||||
{
|
||||
Runtime::enableCoroutine($this->getConfig('coroutine.enable', true), $this->getConfig('coroutine.flags', SWOOLE_HOOK_ALL));
|
||||
|
||||
$this->clearCache();
|
||||
|
||||
$this->setProcessName($server->taskworker ? 'task process' : 'worker process');
|
||||
|
||||
$this->prepareApplication();
|
||||
|
||||
$this->bindSwooleTable();
|
||||
|
||||
$this->triggerEvent("workerStart");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set onTask listener.
|
||||
*
|
||||
* @param mixed $server
|
||||
* @param Task $task
|
||||
*/
|
||||
public function onTask($server, Task $task)
|
||||
{
|
||||
$this->runInSandbox(function (Event $event) use ($task) {
|
||||
$event->trigger('swoole.task', $task);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set onFinish listener.
|
||||
*
|
||||
* @param mixed $server
|
||||
* @param string $taskId
|
||||
* @param mixed $data
|
||||
*/
|
||||
public function onFinish($server, $taskId, $data)
|
||||
{
|
||||
$this->triggerEvent('finish', func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set onShutdown listener.
|
||||
*/
|
||||
public function onShutdown()
|
||||
{
|
||||
$this->triggerEvent('shutdown');
|
||||
$this->pidManager->remove();
|
||||
}
|
||||
|
||||
}
|
||||
72
vendor/topthink/think-swoole/src/concerns/InteractsWithSwooleTable.php
vendored
Normal file
72
vendor/topthink/think-swoole/src/concerns/InteractsWithSwooleTable.php
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\swoole\concerns;
|
||||
|
||||
use Swoole\Table as SwooleTable;
|
||||
use think\App;
|
||||
use think\Container;
|
||||
use think\swoole\Table;
|
||||
|
||||
/**
|
||||
* Trait InteractsWithSwooleTable
|
||||
*
|
||||
* @property Container $container
|
||||
* @property App $app
|
||||
*/
|
||||
trait InteractsWithSwooleTable
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Table
|
||||
*/
|
||||
protected $currentTable;
|
||||
|
||||
/**
|
||||
* Register customized swoole talbes.
|
||||
*/
|
||||
protected function createTables()
|
||||
{
|
||||
$this->currentTable = new Table();
|
||||
$this->registerTables();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register user-defined swoole tables.
|
||||
*/
|
||||
protected function registerTables()
|
||||
{
|
||||
$tables = $this->container->make('config')->get('swoole.tables', []);
|
||||
|
||||
foreach ($tables as $key => $value) {
|
||||
$table = new SwooleTable($value['size']);
|
||||
$columns = $value['columns'] ?? [];
|
||||
foreach ($columns as $column) {
|
||||
if (isset($column['size'])) {
|
||||
$table->column($column['name'], $column['type'], $column['size']);
|
||||
} else {
|
||||
$table->column($column['name'], $column['type']);
|
||||
}
|
||||
}
|
||||
$table->create();
|
||||
|
||||
$this->currentTable->add($key, $table);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind swoole table to app container.
|
||||
*/
|
||||
protected function bindSwooleTable()
|
||||
{
|
||||
$this->app->instance(Table::class, $this->currentTable);
|
||||
}
|
||||
}
|
||||
238
vendor/topthink/think-swoole/src/concerns/InteractsWithWebsocket.php
vendored
Normal file
238
vendor/topthink/think-swoole/src/concerns/InteractsWithWebsocket.php
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\concerns;
|
||||
|
||||
use Swoole\Http\Request;
|
||||
use Swoole\Server\Task;
|
||||
use Swoole\Websocket\Frame;
|
||||
use Swoole\Websocket\Server;
|
||||
use think\App;
|
||||
use think\Container;
|
||||
use think\Event;
|
||||
use think\helper\Str;
|
||||
use think\swoole\contract\websocket\HandlerInterface;
|
||||
use think\swoole\contract\websocket\ParserInterface;
|
||||
use think\swoole\contract\websocket\RoomInterface;
|
||||
use think\swoole\Websocket;
|
||||
use think\swoole\websocket\Pusher;
|
||||
use think\swoole\websocket\Room;
|
||||
use think\swoole\websocket\socketio\Handler;
|
||||
use think\swoole\websocket\socketio\Parser as SocketioParser;
|
||||
|
||||
/**
|
||||
* Trait InteractsWithWebsocket
|
||||
* @package think\swoole\concerns
|
||||
*
|
||||
* @property App $app
|
||||
* @property Container $container
|
||||
* @method \Swoole\Server getServer()
|
||||
*/
|
||||
trait InteractsWithWebsocket
|
||||
{
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected $isWebsocketServer = false;
|
||||
|
||||
/**
|
||||
* @var RoomInterface
|
||||
*/
|
||||
protected $websocketRoom;
|
||||
|
||||
/**
|
||||
* Websocket server events.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $wsEvents = ['open', 'message', 'close'];
|
||||
|
||||
/**
|
||||
* "onOpen" listener.
|
||||
*
|
||||
* @param Server $server
|
||||
* @param Request $req
|
||||
*/
|
||||
public function onOpen($server, $req)
|
||||
{
|
||||
/** @var Websocket $websocket */
|
||||
$websocket = $this->app->make(Websocket::class);
|
||||
$websocket->setSender($req->fd);
|
||||
|
||||
$this->runInSandbox(function (Event $event, HandlerInterface $handler) use ($req) {
|
||||
$request = $this->prepareRequest($req);
|
||||
|
||||
if (!$handler->onOpen($req->fd, $request)) {
|
||||
$event->trigger("swoole.websocket.Connect", $request);
|
||||
}
|
||||
}, $req->fd, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* "onMessage" listener.
|
||||
*
|
||||
* @param Server $server
|
||||
* @param Frame $frame
|
||||
*/
|
||||
public function onMessage($server, $frame)
|
||||
{
|
||||
/** @var Websocket $websocket */
|
||||
$websocket = $this->app->make(Websocket::class);
|
||||
$websocket->setSender($frame->fd);
|
||||
|
||||
$this->runInSandbox(function (Event $event, ParserInterface $parser, HandlerInterface $handler) use ($frame) {
|
||||
if (!$handler->onMessage($frame)) {
|
||||
$payload = $parser->decode($frame);
|
||||
|
||||
['event' => $name, 'data' => $data] = $payload;
|
||||
$name = Str::studly($name);
|
||||
if (!in_array($name, ['Close', 'Connect'])) {
|
||||
$event->trigger("swoole.websocket." . $name, $data);
|
||||
}
|
||||
}
|
||||
}, $frame->fd, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* "onClose" listener.
|
||||
*
|
||||
* @param Server $server
|
||||
* @param int $fd
|
||||
* @param int $reactorId
|
||||
*/
|
||||
public function onClose($server, $fd, $reactorId)
|
||||
{
|
||||
if (!$this->isWebsocketServer($fd) || !$server instanceof Server) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var Websocket $websocket */
|
||||
$websocket = $this->app->make(Websocket::class);
|
||||
$websocket->setSender($fd);
|
||||
|
||||
$this->runInSandbox(function (Event $event, HandlerInterface $handler) use ($websocket, $fd, $reactorId) {
|
||||
try {
|
||||
if (!$handler->onClose($fd, $reactorId)) {
|
||||
$event->trigger("swoole.websocket.Close");
|
||||
}
|
||||
} finally {
|
||||
// leave all rooms
|
||||
$websocket->leave();
|
||||
}
|
||||
}, $fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare settings if websocket is enabled.
|
||||
*/
|
||||
protected function prepareWebsocket()
|
||||
{
|
||||
if (!$this->isWebsocketServer = $this->getConfig('websocket.enable', false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->events = array_merge($this->events ?? [], $this->wsEvents);
|
||||
|
||||
$this->prepareWebsocketRoom();
|
||||
|
||||
$this->onEvent('workerStart', function () {
|
||||
$this->bindWebsocketRoom();
|
||||
$this->bindWebsocketParser();
|
||||
$this->bindWebsocketHandler();
|
||||
$this->prepareWebsocketListener();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if it's a websocket fd.
|
||||
*
|
||||
* @param int $fd
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isWebsocketServer(int $fd): bool
|
||||
{
|
||||
return $this->getServer()->getClientInfo($fd)['websocket_status'] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare websocket room.
|
||||
*/
|
||||
protected function prepareWebsocketRoom()
|
||||
{
|
||||
// create room instance and initialize
|
||||
$this->websocketRoom = $this->container->make(Room::class);
|
||||
$this->websocketRoom->prepare();
|
||||
}
|
||||
|
||||
protected function prepareWebsocketListener()
|
||||
{
|
||||
$listeners = $this->getConfig('websocket.listen', []);
|
||||
|
||||
foreach ($listeners as $event => $listener) {
|
||||
$this->app->event->listen('swoole.websocket.' . Str::studly($event), $listener);
|
||||
}
|
||||
|
||||
$subscribers = $this->getConfig('websocket.subscribe', []);
|
||||
|
||||
foreach ($subscribers as $subscriber) {
|
||||
$this->app->event->observe($subscriber, 'swoole.websocket.');
|
||||
}
|
||||
|
||||
//消息推送任务
|
||||
$this->app->event->listen('swoole.task', function (Task $task, App $app) {
|
||||
if ($this->isWebsocketPushPayload($task->data)) {
|
||||
$pusher = $app->make(Pusher::class, $task->data['data']);
|
||||
$pusher->push();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare websocket handler for onOpen and onClose callback.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function bindWebsocketHandler()
|
||||
{
|
||||
$handlerClass = $this->getConfig('websocket.handler', Handler::class);
|
||||
|
||||
$this->app->bind(HandlerInterface::class, $handlerClass);
|
||||
|
||||
$this->app->make(HandlerInterface::class);
|
||||
}
|
||||
|
||||
protected function bindWebsocketParser()
|
||||
{
|
||||
$parserClass = $this->getConfig('websocket.parser', SocketioParser::class);
|
||||
|
||||
$this->app->bind(ParserInterface::class, $parserClass);
|
||||
|
||||
$this->app->make(ParserInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind room instance to app container.
|
||||
*/
|
||||
protected function bindWebsocketRoom(): void
|
||||
{
|
||||
$this->app->instance(Room::class, $this->websocketRoom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the payload is websocket push.
|
||||
*
|
||||
* @param mixed $payload
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isWebsocketPushPayload($payload): bool
|
||||
{
|
||||
if (!is_array($payload)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->isWebsocketServer
|
||||
&& ($payload['action'] ?? null) === Websocket::PUSH_ACTION
|
||||
&& array_key_exists('data', $payload);
|
||||
}
|
||||
}
|
||||
94
vendor/topthink/think-swoole/src/config/swoole.php
vendored
Normal file
94
vendor/topthink/think-swoole/src/config/swoole.php
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
use think\swoole\websocket\socketio\Handler;
|
||||
use think\swoole\websocket\socketio\Parser;
|
||||
|
||||
return [
|
||||
'server' => [
|
||||
'host' => env('SWOOLE_HOST', '127.0.0.1'), // 监听地址
|
||||
'port' => env('SWOOLE_PORT', 80), // 监听端口
|
||||
'mode' => SWOOLE_PROCESS, // 运行模式 默认为SWOOLE_PROCESS
|
||||
'sock_type' => SWOOLE_SOCK_TCP, // sock type 默认为SWOOLE_SOCK_TCP
|
||||
'options' => [
|
||||
'pid_file' => runtime_path() . 'swoole.pid',
|
||||
'log_file' => runtime_path() . 'swoole.log',
|
||||
'daemonize' => false,
|
||||
// Normally this value should be 1~4 times larger according to your cpu cores.
|
||||
'reactor_num' => swoole_cpu_num(),
|
||||
'worker_num' => swoole_cpu_num(),
|
||||
'task_worker_num' => swoole_cpu_num(),
|
||||
'task_enable_coroutine' => true,
|
||||
'task_max_request' => 3000,
|
||||
'enable_static_handler' => true,
|
||||
'document_root' => root_path('public'),
|
||||
'package_max_length' => 20 * 1024 * 1024,
|
||||
'buffer_output_size' => 10 * 1024 * 1024,
|
||||
'socket_buffer_size' => 128 * 1024 * 1024,
|
||||
'max_request' => 3000,
|
||||
'send_yield' => true,
|
||||
],
|
||||
],
|
||||
'websocket' => [
|
||||
'enable' => false,
|
||||
'handler' => Handler::class,
|
||||
'parser' => Parser::class,
|
||||
'ping_interval' => 25000,
|
||||
'ping_timeout' => 60000,
|
||||
'room' => [
|
||||
'type' => 'table',
|
||||
'table' => [
|
||||
'room_rows' => 4096,
|
||||
'room_size' => 2048,
|
||||
'client_rows' => 8192,
|
||||
'client_size' => 2048,
|
||||
],
|
||||
'redis' => [
|
||||
|
||||
],
|
||||
],
|
||||
'listen' => [],
|
||||
'subscribe' => [],
|
||||
],
|
||||
'rpc' => [
|
||||
'server' => [
|
||||
'enable' => false,
|
||||
'port' => 9000,
|
||||
'services' => [
|
||||
],
|
||||
],
|
||||
'client' => [
|
||||
],
|
||||
],
|
||||
'hot_update' => [
|
||||
'enable' => env('APP_DEBUG', false),
|
||||
'name' => ['*.php'],
|
||||
'include' => [app_path()],
|
||||
'exclude' => [],
|
||||
],
|
||||
//连接池
|
||||
'pool' => [
|
||||
'db' => [
|
||||
'enable' => true,
|
||||
'max_active' => 3,
|
||||
'max_wait_time' => 5,
|
||||
],
|
||||
'cache' => [
|
||||
'enable' => true,
|
||||
'max_active' => 3,
|
||||
'max_wait_time' => 5,
|
||||
],
|
||||
],
|
||||
'coroutine' => [
|
||||
'enable' => true,
|
||||
'flags' => SWOOLE_HOOK_ALL,
|
||||
],
|
||||
'tables' => [],
|
||||
//每个worker里需要预加载以共用的实例
|
||||
'concretes' => [],
|
||||
//重置器
|
||||
'resetters' => [],
|
||||
//每次请求前需要清空的实例
|
||||
'instances' => [],
|
||||
//每次请求前需要重新执行的服务
|
||||
'services' => [],
|
||||
];
|
||||
17
vendor/topthink/think-swoole/src/contract/ResetterInterface.php
vendored
Normal file
17
vendor/topthink/think-swoole/src/contract/ResetterInterface.php
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\contract;
|
||||
|
||||
use think\Container;
|
||||
use think\swoole\Sandbox;
|
||||
|
||||
interface ResetterInterface
|
||||
{
|
||||
/**
|
||||
* "handle" function for resetting app.
|
||||
*
|
||||
* @param Container $app
|
||||
* @param Sandbox $sandbox
|
||||
*/
|
||||
public function handle(Container $app, Sandbox $sandbox);
|
||||
}
|
||||
39
vendor/topthink/think-swoole/src/contract/rpc/ParserInterface.php
vendored
Normal file
39
vendor/topthink/think-swoole/src/contract/rpc/ParserInterface.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\contract\rpc;
|
||||
|
||||
use think\swoole\rpc\Protocol;
|
||||
|
||||
interface ParserInterface
|
||||
{
|
||||
|
||||
const EOF = "\r\n\r\n";
|
||||
|
||||
/**
|
||||
* @param Protocol $protocol
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function encode(Protocol $protocol): string;
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
*
|
||||
* @return Protocol
|
||||
*/
|
||||
public function decode(string $string): Protocol;
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function decodeResponse(string $string);
|
||||
|
||||
/**
|
||||
* @param mixed $result
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function encodeResponse($result): string;
|
||||
}
|
||||
42
vendor/topthink/think-swoole/src/contract/websocket/HandlerInterface.php
vendored
Normal file
42
vendor/topthink/think-swoole/src/contract/websocket/HandlerInterface.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\swoole\contract\websocket;
|
||||
|
||||
use Swoole\Websocket\Frame;
|
||||
use think\Request;
|
||||
|
||||
interface HandlerInterface
|
||||
{
|
||||
/**
|
||||
* "onOpen" listener.
|
||||
*
|
||||
* @param int $fd
|
||||
* @param Request $request
|
||||
*/
|
||||
public function onOpen($fd, Request $request);
|
||||
|
||||
/**
|
||||
* "onMessage" listener.
|
||||
* only triggered when event handler not found
|
||||
*
|
||||
* @param Frame $frame
|
||||
*/
|
||||
public function onMessage(Frame $frame);
|
||||
|
||||
/**
|
||||
* "onClose" listener.
|
||||
*
|
||||
* @param int $fd
|
||||
* @param int $reactorId
|
||||
*/
|
||||
public function onClose($fd, $reactorId);
|
||||
}
|
||||
29
vendor/topthink/think-swoole/src/contract/websocket/ParserInterface.php
vendored
Normal file
29
vendor/topthink/think-swoole/src/contract/websocket/ParserInterface.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\contract\websocket;
|
||||
|
||||
use Swoole\WebSocket\Frame;
|
||||
|
||||
interface ParserInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Encode output payload for websocket push.
|
||||
*
|
||||
* @param string $event
|
||||
* @param mixed $data
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function encode(string $event, $data);
|
||||
|
||||
/**
|
||||
* Input message on websocket connected.
|
||||
* Define and return event name and payload data here.
|
||||
*
|
||||
* @param Frame $frame
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function decode($frame);
|
||||
}
|
||||
61
vendor/topthink/think-swoole/src/contract/websocket/RoomInterface.php
vendored
Normal file
61
vendor/topthink/think-swoole/src/contract/websocket/RoomInterface.php
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\contract\websocket;
|
||||
|
||||
interface RoomInterface
|
||||
{
|
||||
/**
|
||||
* Rooms key
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
public const ROOMS_KEY = 'rooms';
|
||||
|
||||
/**
|
||||
* Descriptors key
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
public const DESCRIPTORS_KEY = 'fds';
|
||||
|
||||
/**
|
||||
* Do some init stuffs before workers started.
|
||||
*
|
||||
* @return RoomInterface
|
||||
*/
|
||||
public function prepare(): RoomInterface;
|
||||
|
||||
/**
|
||||
* Add multiple socket fds to a room.
|
||||
*
|
||||
* @param int fd
|
||||
* @param array|string rooms
|
||||
*/
|
||||
public function add(int $fd, $rooms);
|
||||
|
||||
/**
|
||||
* Delete multiple socket fds from a room.
|
||||
*
|
||||
* @param int fd
|
||||
* @param array|string rooms
|
||||
*/
|
||||
public function delete(int $fd, $rooms);
|
||||
|
||||
/**
|
||||
* Get all sockets by a room key.
|
||||
*
|
||||
* @param string room
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getClients(string $room);
|
||||
|
||||
/**
|
||||
* Get all rooms by a fd.
|
||||
*
|
||||
* @param int fd
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRooms(int $fd);
|
||||
}
|
||||
96
vendor/topthink/think-swoole/src/coroutine/Context.php
vendored
Normal file
96
vendor/topthink/think-swoole/src/coroutine/Context.php
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\coroutine;
|
||||
|
||||
use Closure;
|
||||
use Swoole\Coroutine;
|
||||
|
||||
class Context
|
||||
{
|
||||
|
||||
/**
|
||||
* The data in different coroutine environment.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $data = [];
|
||||
|
||||
/**
|
||||
* Get data by current coroutine id.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @param null $default
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function getData(string $key, $default = null)
|
||||
{
|
||||
return static::$data[static::getCoroutineId()][$key] ?? $default;
|
||||
}
|
||||
|
||||
public static function hasData(string $key)
|
||||
{
|
||||
return isset(static::$data[static::getCoroutineId()]) && array_key_exists($key, static::$data[static::getCoroutineId()]);
|
||||
}
|
||||
|
||||
public static function rememberData(string $key, $value)
|
||||
{
|
||||
if (self::hasData($key)) {
|
||||
return self::getData($key);
|
||||
}
|
||||
|
||||
if ($value instanceof Closure) {
|
||||
// 获取缓存数据
|
||||
$value = $value();
|
||||
}
|
||||
|
||||
self::setData($key, $value);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data by current coroutine id.
|
||||
*
|
||||
* @param string $key
|
||||
* @param $value
|
||||
*/
|
||||
public static function setData(string $key, $value)
|
||||
{
|
||||
static::$data[static::getCoroutineId()][$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove data by current coroutine id.
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
public static function removeData(string $key)
|
||||
{
|
||||
unset(static::$data[static::getCoroutineId()][$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data keys by current coroutine id.
|
||||
*/
|
||||
public static function getDataKeys()
|
||||
{
|
||||
return array_keys(static::$data[static::getCoroutineId()] ?? []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear data by current coroutine id.
|
||||
*/
|
||||
public static function clear()
|
||||
{
|
||||
unset(static::$data[static::getCoroutineId()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current coroutine id.
|
||||
*/
|
||||
public static function getCoroutineId()
|
||||
{
|
||||
return Coroutine::getuid();
|
||||
}
|
||||
}
|
||||
10
vendor/topthink/think-swoole/src/exception/RpcClientException.php
vendored
Normal file
10
vendor/topthink/think-swoole/src/exception/RpcClientException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
class RpcClientException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
22
vendor/topthink/think-swoole/src/exception/RpcResponseException.php
vendored
Normal file
22
vendor/topthink/think-swoole/src/exception/RpcResponseException.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\exception;
|
||||
|
||||
use Exception;
|
||||
use think\swoole\rpc\Error;
|
||||
|
||||
class RpcResponseException extends Exception
|
||||
{
|
||||
protected $error;
|
||||
|
||||
public function __construct(Error $error)
|
||||
{
|
||||
parent::__construct($error->getMessage(), $error->getCode());
|
||||
$this->error = $error;
|
||||
}
|
||||
|
||||
public function getError()
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
}
|
||||
22
vendor/topthink/think-swoole/src/facade/Server.php
vendored
Normal file
22
vendor/topthink/think-swoole/src/facade/Server.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\swoole\facade;
|
||||
|
||||
use think\Facade;
|
||||
|
||||
class Server extends Facade
|
||||
{
|
||||
protected static function getFacadeClass()
|
||||
{
|
||||
return 'swoole.server';
|
||||
}
|
||||
}
|
||||
20
vendor/topthink/think-swoole/src/helpers.php
vendored
Normal file
20
vendor/topthink/think-swoole/src/helpers.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
if (!function_exists('swoole_cpu_num')) {
|
||||
function swoole_cpu_num(): int
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!defined('SWOOLE_SOCK_TCP')) {
|
||||
define('SWOOLE_SOCK_TCP', 1);
|
||||
}
|
||||
|
||||
if (!defined('SWOOLE_PROCESS')) {
|
||||
define('SWOOLE_PROCESS', 3);
|
||||
}
|
||||
|
||||
if (!defined('SWOOLE_HOOK_ALL')) {
|
||||
define('SWOOLE_HOOK_ALL', 1879048191);
|
||||
}
|
||||
32
vendor/topthink/think-swoole/src/middleware/ResetVarDumper.php
vendored
Normal file
32
vendor/topthink/think-swoole/src/middleware/ResetVarDumper.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\middleware;
|
||||
|
||||
use Closure;
|
||||
use Symfony\Component\VarDumper\Caster\ReflectionCaster;
|
||||
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
|
||||
use Symfony\Component\VarDumper\VarDumper;
|
||||
use think\Request;
|
||||
|
||||
class ResetVarDumper
|
||||
{
|
||||
protected $cloner;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->cloner = new VarCloner();
|
||||
$this->cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO);
|
||||
}
|
||||
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
$prevHandler = VarDumper::setHandler(function ($var) {
|
||||
$dumper = new HtmlDumper();
|
||||
$dumper->dump($this->cloner->cloneVar($var));
|
||||
});
|
||||
$response = $next($request);
|
||||
VarDumper::setHandler($prevHandler);
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
47
vendor/topthink/think-swoole/src/pool/Cache.php
vendored
Normal file
47
vendor/topthink/think-swoole/src/pool/Cache.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\pool;
|
||||
|
||||
use Swoole\Coroutine\Channel;
|
||||
use think\swoole\concerns\InteractsWithPool;
|
||||
use think\swoole\coroutine\Context;
|
||||
use think\swoole\pool\cache\Store;
|
||||
|
||||
class Cache extends \think\Cache
|
||||
{
|
||||
use InteractsWithPool;
|
||||
|
||||
protected function getPoolMaxActive($name): int
|
||||
{
|
||||
return $this->app->config->get('swoole.pool.cache.max_active', 3);
|
||||
}
|
||||
|
||||
protected function getPoolMaxWaitTime($name): int
|
||||
{
|
||||
return $this->app->config->get('swoole.pool.cache.max_wait_time', 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取驱动实例
|
||||
* @param null|string $name
|
||||
* @return mixed
|
||||
*/
|
||||
protected function driver(string $name = null)
|
||||
{
|
||||
$name = $name ?: $this->getDefaultDriver();
|
||||
|
||||
return Context::rememberData("cache.store.{$name}", function () use ($name) {
|
||||
return $this->getPoolConnection($name);
|
||||
});
|
||||
}
|
||||
|
||||
protected function buildPoolConnection($connection, Channel $pool)
|
||||
{
|
||||
return new Store($connection, $pool);
|
||||
}
|
||||
|
||||
protected function createPoolConnection(string $name)
|
||||
{
|
||||
return $this->createDriver($name);
|
||||
}
|
||||
}
|
||||
72
vendor/topthink/think-swoole/src/pool/Db.php
vendored
Normal file
72
vendor/topthink/think-swoole/src/pool/Db.php
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\pool;
|
||||
|
||||
use Swoole\Coroutine\Channel;
|
||||
use think\Config;
|
||||
use think\db\ConnectionInterface;
|
||||
use think\swoole\concerns\InteractsWithPool;
|
||||
use think\swoole\coroutine\Context;
|
||||
use think\swoole\pool\db\Connection;
|
||||
|
||||
/**
|
||||
* Class Db
|
||||
* @package think\swoole\pool
|
||||
* @property Config $config
|
||||
*/
|
||||
class Db extends \think\Db
|
||||
{
|
||||
use InteractsWithPool;
|
||||
|
||||
protected function getPoolMaxActive($name): int
|
||||
{
|
||||
return $this->config->get('swoole.pool.db.max_active', 3);
|
||||
}
|
||||
|
||||
protected function getPoolMaxWaitTime($name): int
|
||||
{
|
||||
return $this->config->get('swoole.pool.db.max_wait_time', 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数据库连接实例
|
||||
* @access protected
|
||||
* @param string|null $name 连接标识
|
||||
* @param bool $force 强制重新连接
|
||||
* @return ConnectionInterface
|
||||
*/
|
||||
protected function instance(string $name = null, bool $force = false): ConnectionInterface
|
||||
{
|
||||
if (empty($name)) {
|
||||
$name = $this->getConfig('default', 'mysql');
|
||||
}
|
||||
|
||||
if ($force) {
|
||||
return $this->createConnection($name);
|
||||
}
|
||||
|
||||
return Context::rememberData("db.connection.{$name}", function () use ($name) {
|
||||
return $this->getPoolConnection($name);
|
||||
});
|
||||
}
|
||||
|
||||
protected function buildPoolConnection($connection, Channel $pool)
|
||||
{
|
||||
return new Connection($connection, $pool);
|
||||
}
|
||||
|
||||
protected function createPoolConnection(string $name)
|
||||
{
|
||||
return $this->createConnection($name);
|
||||
}
|
||||
|
||||
protected function getConnectionConfig(string $name): array
|
||||
{
|
||||
$config = parent::getConnectionConfig($name);
|
||||
|
||||
//打开断线重连
|
||||
$config['break_reconnect'] = true;
|
||||
return $config;
|
||||
}
|
||||
|
||||
}
|
||||
10
vendor/topthink/think-swoole/src/pool/cache/Store.php
vendored
Normal file
10
vendor/topthink/think-swoole/src/pool/cache/Store.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\pool\cache;
|
||||
|
||||
use think\swoole\concerns\InteractsWithPoolConnector;
|
||||
|
||||
class Store
|
||||
{
|
||||
use InteractsWithPoolConnector;
|
||||
}
|
||||
233
vendor/topthink/think-swoole/src/pool/db/Connection.php
vendored
Normal file
233
vendor/topthink/think-swoole/src/pool/db/Connection.php
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\pool\db;
|
||||
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use think\db\BaseQuery;
|
||||
use think\db\ConnectionInterface;
|
||||
use think\DbManager;
|
||||
use think\swoole\concerns\InteractsWithPoolConnector;
|
||||
|
||||
/**
|
||||
* Class Connection
|
||||
* @package think\swoole\pool\db
|
||||
* @property ConnectionInterface $handler
|
||||
*/
|
||||
class Connection implements ConnectionInterface
|
||||
{
|
||||
use InteractsWithPoolConnector;
|
||||
|
||||
/**
|
||||
* 获取当前连接器类对应的Query类
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getQueryClass(): string
|
||||
{
|
||||
return $this->handler->getQueryClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接数据库方法
|
||||
* @access public
|
||||
* @param array $config 接参数
|
||||
* @param integer $linkNum 连接序号
|
||||
* @return mixed
|
||||
*/
|
||||
public function connect(array $config = [], $linkNum = 0)
|
||||
{
|
||||
return $this->handler->connect($config, $linkNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前的数据库Db对象
|
||||
* @access public
|
||||
* @param DbManager $db
|
||||
* @return void
|
||||
*/
|
||||
public function setDb(DbManager $db)
|
||||
{
|
||||
$this->handler->setDb($db);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前的缓存对象
|
||||
* @access public
|
||||
* @param CacheInterface $cache
|
||||
* @return void
|
||||
*/
|
||||
public function setCache(CacheInterface $cache)
|
||||
{
|
||||
$this->handler->setCache($cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库的配置参数
|
||||
* @access public
|
||||
* @param string $config 配置名称
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConfig(string $config = '')
|
||||
{
|
||||
return $this->handler->getConfig($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭数据库(或者重新连接)
|
||||
* @access public
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return $this->handler->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找单条记录
|
||||
* @access public
|
||||
* @param BaseQuery $query 查询对象
|
||||
* @return array
|
||||
*/
|
||||
public function find(BaseQuery $query): array
|
||||
{
|
||||
return $this->handler->find($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找记录
|
||||
* @access public
|
||||
* @param BaseQuery $query 查询对象
|
||||
* @return array
|
||||
*/
|
||||
public function select(BaseQuery $query): array
|
||||
{
|
||||
return $this->handler->select($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入记录
|
||||
* @access public
|
||||
* @param BaseQuery $query 查询对象
|
||||
* @param boolean $getLastInsID 返回自增主键
|
||||
* @return mixed
|
||||
*/
|
||||
public function insert(BaseQuery $query, bool $getLastInsID = false)
|
||||
{
|
||||
return $this->handler->insert($query, $getLastInsID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量插入记录
|
||||
* @access public
|
||||
* @param BaseQuery $query 查询对象
|
||||
* @param mixed $dataSet 数据集
|
||||
* @return integer
|
||||
* @throws \Exception
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function insertAll(BaseQuery $query, array $dataSet = []): int
|
||||
{
|
||||
return $this->handler->insertAll($query, $dataSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新记录
|
||||
* @access public
|
||||
* @param BaseQuery $query 查询对象
|
||||
* @return integer
|
||||
*/
|
||||
public function update(BaseQuery $query): int
|
||||
{
|
||||
return $this->handler->update($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除记录
|
||||
* @access public
|
||||
* @param BaseQuery $query 查询对象
|
||||
* @return int
|
||||
*/
|
||||
public function delete(BaseQuery $query): int
|
||||
{
|
||||
return $this->handler->delete($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到某个字段的值
|
||||
* @access public
|
||||
* @param BaseQuery $query 查询对象
|
||||
* @param string $field 字段名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function value(BaseQuery $query, string $field, $default = null)
|
||||
{
|
||||
return $this->handler->value($query, $field, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到某个列的数组
|
||||
* @access public
|
||||
* @param BaseQuery $query 查询对象
|
||||
* @param string $column 字段名 多个字段用逗号分隔
|
||||
* @param string $key 索引
|
||||
* @return array
|
||||
*/
|
||||
public function column(BaseQuery $query, string $column, string $key = ''): array
|
||||
{
|
||||
return $this->handler->column($query, $column, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行数据库事务
|
||||
* @access public
|
||||
* @param callable $callback 数据操作方法回调
|
||||
* @return mixed
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function transaction(callable $callback)
|
||||
{
|
||||
return $this->handler->transaction($callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动事务
|
||||
* @access public
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function startTrans()
|
||||
{
|
||||
$this->handler->startTrans();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于非自动提交状态下面的查询提交
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
$this->handler->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* 事务回滚
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function rollback()
|
||||
{
|
||||
$this->handler->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近一次查询的sql语句
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getLastSql(): string
|
||||
{
|
||||
return $this->handler->getLastSql();
|
||||
}
|
||||
|
||||
}
|
||||
23
vendor/topthink/think-swoole/src/resetters/ClearInstances.php
vendored
Normal file
23
vendor/topthink/think-swoole/src/resetters/ClearInstances.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\resetters;
|
||||
|
||||
use think\Container;
|
||||
use think\swoole\Sandbox;
|
||||
use think\swoole\contract\ResetterInterface;
|
||||
|
||||
class ClearInstances implements ResetterInterface
|
||||
{
|
||||
public function handle(Container $app, Sandbox $sandbox)
|
||||
{
|
||||
$instances = ['log'];
|
||||
|
||||
$instances = array_merge($instances, $sandbox->getConfig()->get('swoole.instances', []));
|
||||
|
||||
foreach ($instances as $instance) {
|
||||
$app->delete($instance);
|
||||
}
|
||||
|
||||
return $app;
|
||||
}
|
||||
}
|
||||
18
vendor/topthink/think-swoole/src/resetters/ResetConfig.php
vendored
Normal file
18
vendor/topthink/think-swoole/src/resetters/ResetConfig.php
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\resetters;
|
||||
|
||||
use think\Container;
|
||||
use think\swoole\Sandbox;
|
||||
use think\swoole\contract\ResetterInterface;
|
||||
|
||||
class ResetConfig implements ResetterInterface
|
||||
{
|
||||
|
||||
public function handle(Container $app, Sandbox $sandbox)
|
||||
{
|
||||
$app->instance('config', clone $sandbox->getConfig());
|
||||
|
||||
return $app;
|
||||
}
|
||||
}
|
||||
32
vendor/topthink/think-swoole/src/resetters/ResetEvent.php
vendored
Normal file
32
vendor/topthink/think-swoole/src/resetters/ResetEvent.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\resetters;
|
||||
|
||||
use think\Container;
|
||||
use think\swoole\Sandbox;
|
||||
use think\swoole\contract\ResetterInterface;
|
||||
|
||||
/**
|
||||
* Class ResetEvent
|
||||
* @package think\swoole\resetters
|
||||
* @property Container $app;
|
||||
*/
|
||||
class ResetEvent implements ResetterInterface
|
||||
{
|
||||
|
||||
public function handle(Container $app, Sandbox $sandbox)
|
||||
{
|
||||
$event = clone $sandbox->getEvent();
|
||||
|
||||
$closure = function () use ($app) {
|
||||
$this->app = $app;
|
||||
};
|
||||
|
||||
$resetEvent = $closure->bindTo($event, $event);
|
||||
$resetEvent();
|
||||
|
||||
$app->instance('event', $event);
|
||||
|
||||
return $app;
|
||||
}
|
||||
}
|
||||
45
vendor/topthink/think-swoole/src/resetters/ResetService.php
vendored
Normal file
45
vendor/topthink/think-swoole/src/resetters/ResetService.php
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\resetters;
|
||||
|
||||
use think\Container;
|
||||
use think\swoole\contract\ResetterInterface;
|
||||
use think\swoole\Sandbox;
|
||||
|
||||
/**
|
||||
* Class ResetService
|
||||
* @package think\swoole\resetters
|
||||
* @property Container $app;
|
||||
*/
|
||||
class ResetService implements ResetterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* "handle" function for resetting app.
|
||||
*
|
||||
* @param Container $app
|
||||
* @param Sandbox $sandbox
|
||||
*/
|
||||
public function handle(Container $app, Sandbox $sandbox)
|
||||
{
|
||||
foreach ($sandbox->getServices() as $service) {
|
||||
$this->rebindServiceContainer($app, $service);
|
||||
if (method_exists($service, 'register')) {
|
||||
$service->register();
|
||||
}
|
||||
if (method_exists($service, 'boot')) {
|
||||
$app->invoke([$service, 'boot']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function rebindServiceContainer($app, $service)
|
||||
{
|
||||
$closure = function () use ($app) {
|
||||
$this->app = $app;
|
||||
};
|
||||
|
||||
$resetService = $closure->bindTo($service, $service);
|
||||
$resetService();
|
||||
}
|
||||
}
|
||||
72
vendor/topthink/think-swoole/src/rpc/Error.php
vendored
Normal file
72
vendor/topthink/think-swoole/src/rpc/Error.php
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc;
|
||||
|
||||
class Error implements \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $code = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $message = '';
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @param int $code
|
||||
* @param string $message
|
||||
* @param mixed $data
|
||||
*
|
||||
* @return Error
|
||||
*/
|
||||
public static function make(int $code, string $message, $data = null): self
|
||||
{
|
||||
$instance = new static();
|
||||
|
||||
$instance->code = $code;
|
||||
$instance->message = $message;
|
||||
$instance->data = $data;
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getCode(): int
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMessage(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return [
|
||||
'code' => $this->code,
|
||||
'message' => $this->message,
|
||||
'data' => $this->data,
|
||||
];
|
||||
}
|
||||
}
|
||||
124
vendor/topthink/think-swoole/src/rpc/JsonParser.php
vendored
Normal file
124
vendor/topthink/think-swoole/src/rpc/JsonParser.php
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc;
|
||||
|
||||
use Exception;
|
||||
use think\swoole\contract\rpc\ParserInterface;
|
||||
|
||||
class JsonParser implements ParserInterface
|
||||
{
|
||||
/**
|
||||
* Json-rpc version
|
||||
*/
|
||||
const VERSION = '2.0';
|
||||
|
||||
const DELIMITER = "@";
|
||||
|
||||
/**
|
||||
* @param Protocol $protocol
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function encode(Protocol $protocol): string
|
||||
{
|
||||
$interface = $protocol->getInterface();
|
||||
$methodName = $protocol->getMethod();
|
||||
|
||||
$method = $interface . self::DELIMITER . $methodName;
|
||||
$data = [
|
||||
'jsonrpc' => self::VERSION,
|
||||
'method' => $method,
|
||||
'params' => $protocol->getParams(),
|
||||
'id' => '',
|
||||
];
|
||||
|
||||
$string = json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
*
|
||||
* @return Protocol
|
||||
*/
|
||||
public function decode(string $string): Protocol
|
||||
{
|
||||
$data = json_decode($string, true);
|
||||
|
||||
$error = json_last_error();
|
||||
if ($error != JSON_ERROR_NONE) {
|
||||
throw new Exception(
|
||||
sprintf('Data(%s) is not json format!', $string)
|
||||
);
|
||||
}
|
||||
|
||||
$method = $data['method'] ?? '';
|
||||
$params = $data['params'] ?? [];
|
||||
|
||||
if (empty($method)) {
|
||||
throw new Exception(
|
||||
sprintf('Method(%s) cant not be empty!', $string)
|
||||
);
|
||||
}
|
||||
|
||||
$methodAry = explode(self::DELIMITER, $method);
|
||||
if (count($methodAry) < 2) {
|
||||
throw new Exception(
|
||||
sprintf('Method(%s) is bad format!', $method)
|
||||
);
|
||||
}
|
||||
|
||||
[$interfaceClass, $methodName] = $methodAry;
|
||||
|
||||
if (empty($interfaceClass) || empty($methodName)) {
|
||||
throw new Exception(
|
||||
sprintf('Interface(%s) or Method(%s) can not be empty!', $interfaceClass, $method)
|
||||
);
|
||||
}
|
||||
|
||||
return Protocol::make($interfaceClass, $methodName, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function decodeResponse(string $string)
|
||||
{
|
||||
$data = json_decode($string, true);
|
||||
|
||||
if (array_key_exists('result', $data)) {
|
||||
return $data['result'];
|
||||
}
|
||||
|
||||
$code = $data['error']['code'] ?? 0;
|
||||
$message = $data['error']['message'] ?? '';
|
||||
$data = $data['error']['data'] ?? null;
|
||||
|
||||
return Error::make($code, $message, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $result
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function encodeResponse($result): string
|
||||
{
|
||||
$data = [
|
||||
'jsonrpc' => self::VERSION,
|
||||
];
|
||||
|
||||
if ($result instanceof Error) {
|
||||
$data['error'] = $result;
|
||||
} else {
|
||||
$data['result'] = $result;
|
||||
}
|
||||
|
||||
$string = json_encode($data);
|
||||
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
67
vendor/topthink/think-swoole/src/rpc/Protocol.php
vendored
Normal file
67
vendor/topthink/think-swoole/src/rpc/Protocol.php
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc;
|
||||
|
||||
class Protocol
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $interface = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $method = '';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $params = [];
|
||||
|
||||
/**
|
||||
* Replace constructor
|
||||
*
|
||||
* @param string $interface
|
||||
* @param string $method
|
||||
* @param array $params
|
||||
*
|
||||
* @return Protocol
|
||||
*/
|
||||
public static function make(string $interface, string $method, array $params)
|
||||
{
|
||||
$instance = new static();
|
||||
|
||||
$instance->interface = $interface;
|
||||
$instance->method = $method;
|
||||
$instance->params = $params;
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getInterface(): string
|
||||
{
|
||||
return $this->interface;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMethod(): string
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getParams(): array
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
}
|
||||
87
vendor/topthink/think-swoole/src/rpc/client/Client.php
vendored
Normal file
87
vendor/topthink/think-swoole/src/rpc/client/Client.php
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc\client;
|
||||
|
||||
use think\swoole\contract\rpc\ParserInterface;
|
||||
use think\swoole\exception\RpcClientException;
|
||||
|
||||
/**
|
||||
* Class Client
|
||||
* @package think\swoole\rpc\client
|
||||
*/
|
||||
class Client
|
||||
{
|
||||
protected $host;
|
||||
protected $port;
|
||||
protected $timeout;
|
||||
protected $options;
|
||||
|
||||
/** @var \Swoole\Coroutine\Client */
|
||||
protected $handler;
|
||||
|
||||
public function __construct($host, $port, $timeout = 0.5, $options = [])
|
||||
{
|
||||
$this->host = $host;
|
||||
$this->port = $port;
|
||||
$this->timeout = $timeout;
|
||||
$this->options = $options;
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
public function sendAndRecv(string $data, bool $reconnect = false)
|
||||
{
|
||||
if ($reconnect) {
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
try {
|
||||
if (!$this->send($data)) {
|
||||
throw new RpcClientException(swoole_strerror($this->handler->errCode), $this->handler->errCode);
|
||||
}
|
||||
|
||||
$result = $this->handler->recv();
|
||||
|
||||
if ($result === false || empty($result)) {
|
||||
throw new RpcClientException(swoole_strerror($this->handler->errCode), $this->handler->errCode);
|
||||
}
|
||||
|
||||
return $result;
|
||||
} catch (RpcClientException $e) {
|
||||
if ($reconnect) {
|
||||
throw $e;
|
||||
}
|
||||
return $this->sendAndRecv($data, true);
|
||||
}
|
||||
}
|
||||
|
||||
public function send($data)
|
||||
{
|
||||
return $this->handler->send($data . ParserInterface::EOF);
|
||||
}
|
||||
|
||||
protected function connect()
|
||||
{
|
||||
$client = new \Swoole\Coroutine\Client(SWOOLE_SOCK_TCP);
|
||||
|
||||
$client->set([
|
||||
'open_eof_check' => true,
|
||||
'open_eof_split' => true,
|
||||
'package_eof' => ParserInterface::EOF,
|
||||
]);
|
||||
|
||||
if (!$client->connect($this->host, $this->port, $this->timeout)) {
|
||||
throw new RpcClientException(
|
||||
sprintf('Connect failed host=%s port=%d', $this->host, $this->port)
|
||||
);
|
||||
}
|
||||
|
||||
$this->handler = $client;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->handler) {
|
||||
$this->handler->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
15
vendor/topthink/think-swoole/src/rpc/client/Connection.php
vendored
Normal file
15
vendor/topthink/think-swoole/src/rpc/client/Connection.php
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc\client;
|
||||
|
||||
use think\swoole\concerns\InteractsWithPoolConnector;
|
||||
|
||||
/**
|
||||
* Class Connection
|
||||
* @package think\swoole\rpc\client
|
||||
* @mixin Client
|
||||
*/
|
||||
class Connection
|
||||
{
|
||||
use InteractsWithPoolConnector;
|
||||
}
|
||||
57
vendor/topthink/think-swoole/src/rpc/client/Pool.php
vendored
Normal file
57
vendor/topthink/think-swoole/src/rpc/client/Pool.php
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc\client;
|
||||
|
||||
use Swoole\Coroutine\Channel;
|
||||
use think\helper\Arr;
|
||||
use think\swoole\concerns\InteractsWithPool;
|
||||
|
||||
class Pool
|
||||
{
|
||||
use InteractsWithPool;
|
||||
|
||||
protected $clients;
|
||||
|
||||
public function __construct($clients)
|
||||
{
|
||||
$this->clients = $clients;
|
||||
}
|
||||
|
||||
protected function getPoolMaxActive($name)
|
||||
{
|
||||
return $this->getClientConfig($name, 'max_active', 3);
|
||||
}
|
||||
|
||||
protected function getPoolMaxWaitTime($name)
|
||||
{
|
||||
return $this->getClientConfig($name, 'max_wait_time', 3);
|
||||
}
|
||||
|
||||
public function getClientConfig($client, $name, $default = null)
|
||||
{
|
||||
return Arr::get($this->clients, $client . "." . $name, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return Connection
|
||||
*/
|
||||
public function connect($name)
|
||||
{
|
||||
return $this->getPoolConnection($name);
|
||||
}
|
||||
|
||||
protected function buildPoolConnection($client, Channel $pool)
|
||||
{
|
||||
return new Connection($client, $pool);
|
||||
}
|
||||
|
||||
protected function createPoolConnection(string $name)
|
||||
{
|
||||
$host = $this->getClientConfig($name, 'host', '127.0.0.1');
|
||||
$port = $this->getClientConfig($name, 'port', 9000);
|
||||
$timeout = $this->getClientConfig($name, 'timeout', 0.5);
|
||||
|
||||
return new Client($host, $port, $timeout);
|
||||
}
|
||||
}
|
||||
102
vendor/topthink/think-swoole/src/rpc/client/Proxy.php
vendored
Normal file
102
vendor/topthink/think-swoole/src/rpc/client/Proxy.php
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc\client;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Nette\PhpGenerator\Factory;
|
||||
use Nette\PhpGenerator\PhpFile;
|
||||
use ReflectionClass;
|
||||
use RuntimeException;
|
||||
use think\swoole\contract\rpc\ParserInterface;
|
||||
use think\swoole\exception\RpcResponseException;
|
||||
use think\swoole\rpc\Error;
|
||||
use think\swoole\rpc\JsonParser;
|
||||
use think\swoole\rpc\Protocol;
|
||||
|
||||
class Proxy
|
||||
{
|
||||
protected $client;
|
||||
protected $interface;
|
||||
|
||||
/** @var Pool */
|
||||
protected $pool;
|
||||
|
||||
/** @var ParserInterface */
|
||||
protected $parser;
|
||||
|
||||
public function __construct(Pool $pool)
|
||||
{
|
||||
$this->pool = $pool;
|
||||
|
||||
$parserClass = $this->pool->getClientConfig($this->client, 'parser', JsonParser::class);
|
||||
$this->parser = new $parserClass;
|
||||
}
|
||||
|
||||
protected function proxyCall($method, $params)
|
||||
{
|
||||
$protocol = Protocol::make($this->interface, $method, $params);
|
||||
$data = $this->parser->encode($protocol);
|
||||
|
||||
$client = $this->pool->connect($this->client);
|
||||
|
||||
$response = $client->sendAndRecv($data);
|
||||
|
||||
$client->release();
|
||||
|
||||
$result = $this->parser->decodeResponse($response);
|
||||
|
||||
if ($result instanceof Error) {
|
||||
throw new RpcResponseException($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function getClassName($interface)
|
||||
{
|
||||
if (!interface_exists($interface)) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf('%s must be exist interface!', $interface)
|
||||
);
|
||||
}
|
||||
|
||||
$name = constant($interface . "::RPC");
|
||||
$proxyName = class_basename($interface) . "Service";
|
||||
$className = "rpc\\service\\${name}\\{$proxyName}";
|
||||
|
||||
if (!class_exists($className, false)) {
|
||||
$file = new PhpFile;
|
||||
$namespace = $file->addNamespace("rpc\\service\\${name}");
|
||||
$namespace->addUse(Proxy::class);
|
||||
$namespace->addUse($interface);
|
||||
|
||||
$class = $namespace->addClass($proxyName);
|
||||
|
||||
$class->setExtends(Proxy::class);
|
||||
$class->addImplement($interface);
|
||||
$class->addProperty('client', $name);
|
||||
$class->addProperty('interface', class_basename($interface));
|
||||
|
||||
$reflection = new ReflectionClass($interface);
|
||||
|
||||
foreach ($reflection->getMethods() as $methodRef) {
|
||||
$method = (new Factory)->fromMethodReflection($methodRef);
|
||||
$method->setBody("return \$this->proxyCall('{$methodRef->getName()}', func_get_args());");
|
||||
$class->addMember($method);
|
||||
}
|
||||
|
||||
if (function_exists('eval')) {
|
||||
eval($file);
|
||||
} else {
|
||||
$proxyFile = sprintf('%s/%s.php', sys_get_temp_dir(), $proxyName);
|
||||
$result = file_put_contents($proxyFile, $file);
|
||||
if ($result === false) {
|
||||
throw new RuntimeException(sprintf('Proxy file(%s) generate fail', $proxyFile));
|
||||
}
|
||||
require $proxyFile;
|
||||
unlink($proxyFile);
|
||||
}
|
||||
}
|
||||
return $className;
|
||||
}
|
||||
}
|
||||
169
vendor/topthink/think-swoole/src/rpc/server/Dispatcher.php
vendored
Normal file
169
vendor/topthink/think-swoole/src/rpc/server/Dispatcher.php
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc\server;
|
||||
|
||||
use Exception;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionNamedType;
|
||||
use Swoole\Server;
|
||||
use think\App;
|
||||
use think\swoole\contract\rpc\ParserInterface;
|
||||
use think\swoole\rpc\Error;
|
||||
use Throwable;
|
||||
|
||||
class Dispatcher
|
||||
{
|
||||
const ACTION_INTERFACE = '@action_interface';
|
||||
|
||||
/**
|
||||
* Parser error
|
||||
*/
|
||||
const PARSER_ERROR = -32700;
|
||||
|
||||
/**
|
||||
* Invalid Request
|
||||
*/
|
||||
const INVALID_REQUEST = -32600;
|
||||
|
||||
/**
|
||||
* Method not found
|
||||
*/
|
||||
const METHOD_NOT_FOUND = -32601;
|
||||
|
||||
/**
|
||||
* Invalid params
|
||||
*/
|
||||
const INVALID_PARAMS = -32602;
|
||||
|
||||
/**
|
||||
* Internal error
|
||||
*/
|
||||
const INTERNAL_ERROR = -32603;
|
||||
|
||||
protected $app;
|
||||
|
||||
protected $parser;
|
||||
|
||||
protected $services;
|
||||
|
||||
protected $server;
|
||||
|
||||
public function __construct(App $app, ParserInterface $parser, Server $server, $services)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->parser = $parser;
|
||||
$this->server = $server;
|
||||
$this->prepareServices($services);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务接口
|
||||
* @param $services
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected function prepareServices($services)
|
||||
{
|
||||
foreach ($services as $className) {
|
||||
$reflectionClass = new ReflectionClass($className);
|
||||
$interfaces = $reflectionClass->getInterfaceNames();
|
||||
|
||||
foreach ($interfaces as $interface) {
|
||||
$this->services[class_basename($interface)] = [
|
||||
'interface' => $interface,
|
||||
'class' => $className,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取接口信息
|
||||
* @return array
|
||||
*/
|
||||
protected function getInterfaces()
|
||||
{
|
||||
$interfaces = [];
|
||||
foreach ($this->services as $key => ['interface' => $interface]) {
|
||||
$interfaces[$key] = $this->getMethods($interface);
|
||||
}
|
||||
return $interfaces;
|
||||
}
|
||||
|
||||
protected function getMethods($interface)
|
||||
{
|
||||
$methods = [];
|
||||
|
||||
$reflection = new ReflectionClass($interface);
|
||||
foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
$returnType = $method->getReturnType();
|
||||
if ($returnType instanceof ReflectionNamedType) {
|
||||
$returnType = $returnType->getName();
|
||||
}
|
||||
$methods[$method->getName()] = [
|
||||
'parameters' => $this->getParameters($method),
|
||||
'returnType' => $returnType,
|
||||
'comment' => $method->getDocComment(),
|
||||
];
|
||||
}
|
||||
return $methods;
|
||||
}
|
||||
|
||||
protected function getParameters(ReflectionMethod $method)
|
||||
{
|
||||
$parameters = [];
|
||||
foreach ($method->getParameters() as $parameter) {
|
||||
$type = $parameter->getType();
|
||||
if ($type instanceof ReflectionNamedType) {
|
||||
$type = $type->getName();
|
||||
}
|
||||
$param = [
|
||||
'name' => $parameter->getName(),
|
||||
'type' => $type,
|
||||
];
|
||||
|
||||
if ($parameter->isOptional()) {
|
||||
$param['default'] = $parameter->getDefaultValue();
|
||||
}
|
||||
|
||||
$parameters[] = $param;
|
||||
}
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调度
|
||||
* @param int $fd
|
||||
* @param string $data
|
||||
*/
|
||||
public function dispatch(int $fd, string $data)
|
||||
{
|
||||
try {
|
||||
if ($data === Dispatcher::ACTION_INTERFACE) {
|
||||
$result = $this->getInterfaces();
|
||||
} else {
|
||||
$protocol = $this->parser->decode($data);
|
||||
|
||||
$interface = $protocol->getInterface();
|
||||
$method = $protocol->getMethod();
|
||||
$params = $protocol->getParams();
|
||||
$service = $this->services[$interface] ?? null;
|
||||
if (empty($service)) {
|
||||
throw new Exception(
|
||||
sprintf('Service %s is not founded!', $interface),
|
||||
self::INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
$result = $this->app->invoke([$this->app->make($service['class']), $method], $params);
|
||||
}
|
||||
} catch (Throwable | Exception $e) {
|
||||
$result = Error::make($e->getCode(), $e->getMessage());
|
||||
}
|
||||
|
||||
$data = $this->parser->encodeResponse($result);
|
||||
|
||||
$this->server->send($fd, $data . ParserInterface::EOF);
|
||||
}
|
||||
|
||||
}
|
||||
202
vendor/topthink/think-swoole/src/websocket/Pusher.php
vendored
Normal file
202
vendor/topthink/think-swoole/src/websocket/Pusher.php
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\websocket;
|
||||
|
||||
use Swoole\Server;
|
||||
|
||||
/**
|
||||
* Class Pusher
|
||||
*/
|
||||
class Pusher
|
||||
{
|
||||
/**
|
||||
* @var Server
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $sender;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $descriptors;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $broadcast;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $assigned;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $payload;
|
||||
|
||||
/**
|
||||
* Push constructor.
|
||||
*
|
||||
* @param Server $server
|
||||
* @param int $sender
|
||||
* @param array $descriptors
|
||||
* @param bool $broadcast
|
||||
* @param bool $assigned
|
||||
* @param string $payload
|
||||
*/
|
||||
public function __construct(
|
||||
Server $server,
|
||||
string $payload,
|
||||
int $sender = 0,
|
||||
array $descriptors = [],
|
||||
bool $broadcast = false,
|
||||
bool $assigned = false
|
||||
)
|
||||
{
|
||||
$this->sender = $sender;
|
||||
$this->descriptors = $descriptors;
|
||||
$this->broadcast = $broadcast;
|
||||
$this->assigned = $assigned;
|
||||
$this->payload = $payload;
|
||||
$this->server = $server;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSender(): int
|
||||
{
|
||||
return $this->sender;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getDescriptors(): array
|
||||
{
|
||||
return $this->descriptors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $descriptor
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addDescriptor($descriptor): self
|
||||
{
|
||||
return $this->addDescriptors([$descriptor]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $descriptors
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addDescriptors(array $descriptors): self
|
||||
{
|
||||
$this->descriptors = array_values(
|
||||
array_unique(
|
||||
array_merge($this->descriptors, $descriptors)
|
||||
)
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $descriptor
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasDescriptor(int $descriptor): bool
|
||||
{
|
||||
return in_array($descriptor, $this->descriptors);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isBroadcast(): bool
|
||||
{
|
||||
return $this->broadcast;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isAssigned(): bool
|
||||
{
|
||||
return $this->assigned;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPayload(): string
|
||||
{
|
||||
return $this->payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldBroadcast(): bool
|
||||
{
|
||||
return $this->broadcast && empty($this->descriptors) && !$this->assigned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all descriptors that are websocket
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getWebsocketConnections(): array
|
||||
{
|
||||
return array_filter(iterator_to_array($this->server->connections), function ($fd) {
|
||||
return (bool) $this->server->getClientInfo($fd)['websocket_status'] ?? false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $fd
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function shouldPushToDescriptor(int $fd): bool
|
||||
{
|
||||
if (!$this->server->exist($fd)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->broadcast ? $this->sender !== (int) $fd : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push message to related descriptors
|
||||
* @return void
|
||||
*/
|
||||
public function push(): void
|
||||
{
|
||||
// attach sender if not broadcast
|
||||
if (!$this->broadcast && $this->sender && !$this->hasDescriptor($this->sender)) {
|
||||
$this->addDescriptor($this->sender);
|
||||
}
|
||||
|
||||
// check if to broadcast to other clients
|
||||
if ($this->shouldBroadcast()) {
|
||||
$this->addDescriptors($this->getWebsocketConnections());
|
||||
}
|
||||
|
||||
// push message to designated fds
|
||||
foreach ($this->descriptors as $descriptor) {
|
||||
if ($this->shouldPushToDescriptor($descriptor)) {
|
||||
$this->server->push($descriptor, $this->payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
vendor/topthink/think-swoole/src/websocket/Room.php
vendored
Normal file
30
vendor/topthink/think-swoole/src/websocket/Room.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\websocket;
|
||||
|
||||
use think\Manager;
|
||||
use think\swoole\websocket\room\Table;
|
||||
|
||||
/**
|
||||
* Class Room
|
||||
* @package think\swoole\websocket
|
||||
* @mixin Table
|
||||
*/
|
||||
class Room extends Manager
|
||||
{
|
||||
protected $namespace = "\\think\\swoole\\websocket\\room\\";
|
||||
|
||||
protected function resolveConfig(string $name)
|
||||
{
|
||||
return $this->app->config->get("swoole.websocket.room.{$name}", []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认驱动
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDefaultDriver()
|
||||
{
|
||||
return $this->app->config->get('swoole.websocket.room.type', 'table');
|
||||
}
|
||||
}
|
||||
46
vendor/topthink/think-swoole/src/websocket/SimpleParser.php
vendored
Normal file
46
vendor/topthink/think-swoole/src/websocket/SimpleParser.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\websocket;
|
||||
|
||||
use Swoole\Websocket\Frame;
|
||||
use think\swoole\contract\websocket\ParserInterface;
|
||||
|
||||
class SimpleParser implements ParserInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Encode output payload for websocket push.
|
||||
*
|
||||
* @param string $event
|
||||
* @param mixed $data
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function encode(string $event, $data)
|
||||
{
|
||||
return json_encode(
|
||||
[
|
||||
'event' => $event,
|
||||
'data' => $data,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Input message on websocket connected.
|
||||
* Define and return event name and payload data here.
|
||||
*
|
||||
* @param Frame $frame
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function decode($frame)
|
||||
{
|
||||
$data = json_decode($frame->data, true);
|
||||
|
||||
return [
|
||||
'event' => $data['event'] ?? null,
|
||||
'data' => $data['data'] ?? null,
|
||||
];
|
||||
}
|
||||
}
|
||||
231
vendor/topthink/think-swoole/src/websocket/room/Redis.php
vendored
Normal file
231
vendor/topthink/think-swoole/src/websocket/room/Redis.php
vendored
Normal file
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\websocket\room;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Redis as PHPRedis;
|
||||
use think\helper\Arr;
|
||||
use think\swoole\contract\websocket\RoomInterface;
|
||||
|
||||
/**
|
||||
* Class RedisRoom
|
||||
*/
|
||||
class Redis implements RoomInterface
|
||||
{
|
||||
/**
|
||||
* @var PHPRedis
|
||||
*/
|
||||
protected $redis;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix = 'swoole:';
|
||||
|
||||
/**
|
||||
* RedisRoom constructor.
|
||||
*
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct(array $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
|
||||
if ($prefix = Arr::get($this->config, 'prefix')) {
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RoomInterface
|
||||
*/
|
||||
public function prepare(): RoomInterface
|
||||
{
|
||||
$this->cleanRooms();
|
||||
|
||||
//关闭redis
|
||||
$this->redis->close();
|
||||
$this->redis = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set redis client.
|
||||
*
|
||||
*/
|
||||
protected function getRedis()
|
||||
{
|
||||
if (!$this->redis) {
|
||||
$host = Arr::get($this->config, 'host', '127.0.0.1');
|
||||
$port = Arr::get($this->config, 'port', 6379);
|
||||
|
||||
$this->redis = new PHPRedis();
|
||||
$this->redis->pconnect($host, $port);
|
||||
}
|
||||
return $this->redis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add multiple socket fds to a room.
|
||||
*
|
||||
* @param int fd
|
||||
* @param array|string rooms
|
||||
*/
|
||||
public function add(int $fd, $rooms)
|
||||
{
|
||||
$rooms = is_array($rooms) ? $rooms : [$rooms];
|
||||
|
||||
$this->addValue($fd, $rooms, RoomInterface::DESCRIPTORS_KEY);
|
||||
|
||||
foreach ($rooms as $room) {
|
||||
$this->addValue($room, [$fd], RoomInterface::ROOMS_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete multiple socket fds from a room.
|
||||
*
|
||||
* @param int fd
|
||||
* @param array|string rooms
|
||||
*/
|
||||
public function delete(int $fd, $rooms)
|
||||
{
|
||||
$rooms = is_array($rooms) ? $rooms : [$rooms];
|
||||
$rooms = count($rooms) ? $rooms : $this->getRooms($fd);
|
||||
|
||||
$this->removeValue($fd, $rooms, RoomInterface::DESCRIPTORS_KEY);
|
||||
|
||||
foreach ($rooms as $room) {
|
||||
$this->removeValue($room, [$fd], RoomInterface::ROOMS_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add value to redis.
|
||||
*
|
||||
* @param $key
|
||||
* @param array $values
|
||||
* @param string $table
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function addValue($key, array $values, string $table)
|
||||
{
|
||||
$this->checkTable($table);
|
||||
$redisKey = $this->getKey($key, $table);
|
||||
|
||||
$pipe = $this->getRedis()->multi(PHPRedis::PIPELINE);
|
||||
|
||||
foreach ($values as $value) {
|
||||
$pipe->sadd($redisKey, $value);
|
||||
}
|
||||
|
||||
$pipe->exec();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove value from reddis.
|
||||
*
|
||||
* @param $key
|
||||
* @param array $values
|
||||
* @param string $table
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function removeValue($key, array $values, string $table)
|
||||
{
|
||||
$this->checkTable($table);
|
||||
$redisKey = $this->getKey($key, $table);
|
||||
|
||||
$pipe = $this->getRedis()->multi(PHPRedis::PIPELINE);
|
||||
|
||||
foreach ($values as $value) {
|
||||
$pipe->srem($redisKey, $value);
|
||||
}
|
||||
|
||||
$pipe->exec();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all sockets by a room key.
|
||||
*
|
||||
* @param string room
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getClients(string $room)
|
||||
{
|
||||
return $this->getValue($room, RoomInterface::ROOMS_KEY) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all rooms by a fd.
|
||||
*
|
||||
* @param int fd
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRooms(int $fd)
|
||||
{
|
||||
return $this->getValue($fd, RoomInterface::DESCRIPTORS_KEY) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check table for rooms and descriptors.
|
||||
*
|
||||
* @param string $table
|
||||
*/
|
||||
protected function checkTable(string $table)
|
||||
{
|
||||
if (!in_array($table, [RoomInterface::ROOMS_KEY, RoomInterface::DESCRIPTORS_KEY])) {
|
||||
throw new InvalidArgumentException("Invalid table name: `{$table}`.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $table
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getValue(string $key, string $table)
|
||||
{
|
||||
$this->checkTable($table);
|
||||
|
||||
return $this->getRedis()->smembers($this->getKey($key, $table));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get key.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $table
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getKey(string $key, string $table)
|
||||
{
|
||||
return "{$this->prefix}{$table}:{$key}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean all rooms.
|
||||
*/
|
||||
protected function cleanRooms(): void
|
||||
{
|
||||
if (count($keys = $this->getRedis()->keys("{$this->prefix}*"))) {
|
||||
$this->getRedis()->del($keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
220
vendor/topthink/think-swoole/src/websocket/room/Table.php
vendored
Normal file
220
vendor/topthink/think-swoole/src/websocket/room/Table.php
vendored
Normal file
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\websocket\room;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Swoole\Table as SwooleTable;
|
||||
use think\swoole\contract\websocket\RoomInterface;
|
||||
|
||||
class Table implements RoomInterface
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [
|
||||
'room_rows' => 4096,
|
||||
'room_size' => 2048,
|
||||
'client_rows' => 8192,
|
||||
'client_size' => 2048,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var SwooleTable
|
||||
*/
|
||||
protected $rooms;
|
||||
|
||||
/**
|
||||
* @var SwooleTable
|
||||
*/
|
||||
protected $fds;
|
||||
|
||||
/**
|
||||
* TableRoom constructor.
|
||||
*
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct(array $config)
|
||||
{
|
||||
$this->config = array_merge($this->config, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do some init stuffs before workers started.
|
||||
*
|
||||
* @return RoomInterface
|
||||
*/
|
||||
public function prepare(): RoomInterface
|
||||
{
|
||||
$this->initRoomsTable();
|
||||
$this->initFdsTable();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add multiple socket fds to a room.
|
||||
*
|
||||
* @param int fd
|
||||
* @param array|string rooms
|
||||
*/
|
||||
public function add(int $fd, $roomNames)
|
||||
{
|
||||
$rooms = $this->getRooms($fd);
|
||||
$roomNames = is_array($roomNames) ? $roomNames : [$roomNames];
|
||||
|
||||
foreach ($roomNames as $room) {
|
||||
$fds = $this->getClients($room);
|
||||
|
||||
if (in_array($fd, $fds)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fds[] = $fd;
|
||||
$rooms[] = $room;
|
||||
|
||||
$this->setClients($room, $fds);
|
||||
}
|
||||
|
||||
$this->setRooms($fd, $rooms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete multiple socket fds from a room.
|
||||
*
|
||||
* @param int fd
|
||||
* @param array|string rooms
|
||||
*/
|
||||
public function delete(int $fd, $roomNames = [])
|
||||
{
|
||||
$allRooms = $this->getRooms($fd);
|
||||
$roomNames = is_array($roomNames) ? $roomNames : [$roomNames];
|
||||
$rooms = count($roomNames) ? $roomNames : $allRooms;
|
||||
|
||||
$removeRooms = [];
|
||||
foreach ($rooms as $room) {
|
||||
$fds = $this->getClients($room);
|
||||
|
||||
if (!in_array($fd, $fds)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->setClients($room, array_values(array_diff($fds, [$fd])));
|
||||
$removeRooms[] = $room;
|
||||
}
|
||||
|
||||
$this->setRooms($fd, collect($allRooms)->diff($removeRooms)->values()->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all sockets by a room key.
|
||||
*
|
||||
* @param string room
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getClients(string $room)
|
||||
{
|
||||
return $this->getValue($room, RoomInterface::ROOMS_KEY) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all rooms by a fd.
|
||||
*
|
||||
* @param int fd
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRooms(int $fd)
|
||||
{
|
||||
return $this->getValue($fd, RoomInterface::DESCRIPTORS_KEY) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $room
|
||||
* @param array $fds
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function setClients(string $room, array $fds)
|
||||
{
|
||||
return $this->setValue($room, $fds, RoomInterface::ROOMS_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $fd
|
||||
* @param array $rooms
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function setRooms(int $fd, array $rooms)
|
||||
{
|
||||
return $this->setValue($fd, $rooms, RoomInterface::DESCRIPTORS_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init rooms table
|
||||
*/
|
||||
protected function initRoomsTable(): void
|
||||
{
|
||||
$this->rooms = new SwooleTable($this->config['room_rows']);
|
||||
$this->rooms->column('value', SwooleTable::TYPE_STRING, $this->config['room_size']);
|
||||
$this->rooms->create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Init descriptors table
|
||||
*/
|
||||
protected function initFdsTable()
|
||||
{
|
||||
$this->fds = new SwooleTable($this->config['client_rows']);
|
||||
$this->fds->column('value', SwooleTable::TYPE_STRING, $this->config['client_size']);
|
||||
$this->fds->create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value to table
|
||||
*
|
||||
* @param $key
|
||||
* @param array $value
|
||||
* @param string $table
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setValue($key, array $value, string $table)
|
||||
{
|
||||
$this->checkTable($table);
|
||||
|
||||
$this->$table->set($key, ['value' => json_encode($value)]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value from table
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $table
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
public function getValue(string $key, string $table)
|
||||
{
|
||||
$this->checkTable($table);
|
||||
|
||||
$value = $this->$table->get($key);
|
||||
|
||||
return $value ? json_decode($value['value'], true) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check table for exists
|
||||
*
|
||||
* @param string $table
|
||||
*/
|
||||
protected function checkTable(string $table)
|
||||
{
|
||||
if (!property_exists($this, $table) || !$this->$table instanceof SwooleTable) {
|
||||
throw new InvalidArgumentException("Invalid table name: `{$table}`.");
|
||||
}
|
||||
}
|
||||
}
|
||||
54
vendor/topthink/think-swoole/src/websocket/socketio/Controller.php
vendored
Normal file
54
vendor/topthink/think-swoole/src/websocket/socketio/Controller.php
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\websocket\socketio;
|
||||
|
||||
use think\Config;
|
||||
use think\Cookie;
|
||||
use think\Request;
|
||||
|
||||
class Controller
|
||||
{
|
||||
protected $transports = ['polling', 'websocket'];
|
||||
|
||||
public function upgrade(Request $request, Config $config, Cookie $cookie)
|
||||
{
|
||||
if (!in_array($request->param('transport'), $this->transports)) {
|
||||
return json(
|
||||
[
|
||||
'code' => 0,
|
||||
'message' => 'Transport unknown',
|
||||
],
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
if ($request->has('sid')) {
|
||||
$response = response('1:6');
|
||||
} else {
|
||||
$sid = base64_encode(uniqid());
|
||||
$payload = json_encode(
|
||||
[
|
||||
'sid' => $sid,
|
||||
'upgrades' => ['websocket'],
|
||||
'pingInterval' => $config->get('swoole.websocket.ping_interval'),
|
||||
'pingTimeout' => $config->get('swoole.websocket.ping_timeout'),
|
||||
]
|
||||
);
|
||||
$cookie->set('io', $sid);
|
||||
$response = response('97:0' . $payload . '2:40');
|
||||
}
|
||||
|
||||
return $response->contentType('text/plain');
|
||||
}
|
||||
|
||||
public function reject(Request $request)
|
||||
{
|
||||
return json(
|
||||
[
|
||||
'code' => 3,
|
||||
'message' => 'Bad request',
|
||||
],
|
||||
400
|
||||
);
|
||||
}
|
||||
}
|
||||
98
vendor/topthink/think-swoole/src/websocket/socketio/Handler.php
vendored
Normal file
98
vendor/topthink/think-swoole/src/websocket/socketio/Handler.php
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\websocket\socketio;
|
||||
|
||||
use Swoole\Server;
|
||||
use Swoole\Websocket\Frame;
|
||||
use Swoole\WebSocket\Server as WebsocketServer;
|
||||
use think\Config;
|
||||
use think\Request;
|
||||
use think\swoole\contract\websocket\HandlerInterface;
|
||||
|
||||
class Handler implements HandlerInterface
|
||||
{
|
||||
/** @var WebsocketServer */
|
||||
protected $server;
|
||||
|
||||
/** @var Config */
|
||||
protected $config;
|
||||
|
||||
public function __construct(Server $server, Config $config)
|
||||
{
|
||||
$this->server = $server;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* "onOpen" listener.
|
||||
*
|
||||
* @param int $fd
|
||||
* @param Request $request
|
||||
*/
|
||||
public function onOpen($fd, Request $request)
|
||||
{
|
||||
if (!$request->param('sid')) {
|
||||
$payload = json_encode(
|
||||
[
|
||||
'sid' => base64_encode(uniqid()),
|
||||
'upgrades' => [],
|
||||
'pingInterval' => $this->config->get('swoole.websocket.ping_interval'),
|
||||
'pingTimeout' => $this->config->get('swoole.websocket.ping_timeout'),
|
||||
]
|
||||
);
|
||||
$initPayload = Packet::OPEN . $payload;
|
||||
$connectPayload = Packet::MESSAGE . Packet::CONNECT;
|
||||
|
||||
$this->server->push($fd, $initPayload);
|
||||
$this->server->push($fd, $connectPayload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* "onMessage" listener.
|
||||
* only triggered when event handler not found
|
||||
*
|
||||
* @param Frame $frame
|
||||
* @return bool
|
||||
*/
|
||||
public function onMessage(Frame $frame)
|
||||
{
|
||||
$packet = $frame->data;
|
||||
if (Packet::getPayload($packet)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->checkHeartbeat($frame->fd, $packet);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* "onClose" listener.
|
||||
*
|
||||
* @param int $fd
|
||||
* @param int $reactorId
|
||||
*/
|
||||
public function onClose($fd, $reactorId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
protected function checkHeartbeat($fd, $packet)
|
||||
{
|
||||
$packetLength = strlen($packet);
|
||||
$payload = '';
|
||||
|
||||
if ($isPing = Packet::isSocketType($packet, 'ping')) {
|
||||
$payload .= Packet::PONG;
|
||||
}
|
||||
|
||||
if ($isPing && $packetLength > 1) {
|
||||
$payload .= substr($packet, 1, $packetLength - 1);
|
||||
}
|
||||
|
||||
if ($isPing) {
|
||||
$this->server->push($fd, $payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
171
vendor/topthink/think-swoole/src/websocket/socketio/Packet.php
vendored
Normal file
171
vendor/topthink/think-swoole/src/websocket/socketio/Packet.php
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\websocket\socketio;
|
||||
|
||||
/**
|
||||
* Class Packet
|
||||
*/
|
||||
class Packet
|
||||
{
|
||||
/**
|
||||
* Socket.io packet type `open`.
|
||||
*/
|
||||
const OPEN = 0;
|
||||
|
||||
/**
|
||||
* Socket.io packet type `close`.
|
||||
*/
|
||||
const CLOSE = 1;
|
||||
|
||||
/**
|
||||
* Socket.io packet type `ping`.
|
||||
*/
|
||||
const PING = 2;
|
||||
|
||||
/**
|
||||
* Socket.io packet type `pong`.
|
||||
*/
|
||||
const PONG = 3;
|
||||
|
||||
/**
|
||||
* Socket.io packet type `message`.
|
||||
*/
|
||||
const MESSAGE = 4;
|
||||
|
||||
/**
|
||||
* Socket.io packet type 'upgrade'
|
||||
*/
|
||||
const UPGRADE = 5;
|
||||
|
||||
/**
|
||||
* Socket.io packet type `noop`.
|
||||
*/
|
||||
const NOOP = 6;
|
||||
|
||||
/**
|
||||
* Engine.io packet type `connect`.
|
||||
*/
|
||||
const CONNECT = 0;
|
||||
|
||||
/**
|
||||
* Engine.io packet type `disconnect`.
|
||||
*/
|
||||
const DISCONNECT = 1;
|
||||
|
||||
/**
|
||||
* Engine.io packet type `event`.
|
||||
*/
|
||||
const EVENT = 2;
|
||||
|
||||
/**
|
||||
* Engine.io packet type `ack`.
|
||||
*/
|
||||
const ACK = 3;
|
||||
|
||||
/**
|
||||
* Engine.io packet type `error`.
|
||||
*/
|
||||
const ERROR = 4;
|
||||
|
||||
/**
|
||||
* Engine.io packet type 'binary event'
|
||||
*/
|
||||
const BINARY_EVENT = 5;
|
||||
|
||||
/**
|
||||
* Engine.io packet type `binary ack`. For acks with binary arguments.
|
||||
*/
|
||||
const BINARY_ACK = 6;
|
||||
|
||||
/**
|
||||
* Socket.io packet types.
|
||||
*/
|
||||
public static $socketTypes = [
|
||||
0 => 'OPEN',
|
||||
1 => 'CLOSE',
|
||||
2 => 'PING',
|
||||
3 => 'PONG',
|
||||
4 => 'MESSAGE',
|
||||
5 => 'UPGRADE',
|
||||
6 => 'NOOP',
|
||||
];
|
||||
|
||||
/**
|
||||
* Engine.io packet types.
|
||||
*/
|
||||
public static $engineTypes = [
|
||||
0 => 'CONNECT',
|
||||
1 => 'DISCONNECT',
|
||||
2 => 'EVENT',
|
||||
3 => 'ACK',
|
||||
4 => 'ERROR',
|
||||
5 => 'BINARY_EVENT',
|
||||
6 => 'BINARY_ACK',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get socket packet type of a raw payload.
|
||||
*
|
||||
* @param string $packet
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public static function getSocketType(string $packet)
|
||||
{
|
||||
$type = $packet[0] ?? null;
|
||||
|
||||
if (!array_key_exists($type, static::$socketTypes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (int) $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data packet from a raw payload.
|
||||
*
|
||||
* @param string $packet
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public static function getPayload(string $packet)
|
||||
{
|
||||
$packet = trim($packet);
|
||||
$start = strpos($packet, '[');
|
||||
|
||||
if ($start === false || substr($packet, -1) !== ']') {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = substr($packet, $start, strlen($packet) - $start);
|
||||
$data = json_decode($data, true);
|
||||
|
||||
if (is_null($data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return [
|
||||
'event' => $data[0],
|
||||
'data' => $data[1] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if a socket packet belongs to specific type.
|
||||
*
|
||||
* @param $packet
|
||||
* @param string $typeName
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isSocketType($packet, string $typeName)
|
||||
{
|
||||
$type = array_search(strtoupper($typeName), static::$socketTypes);
|
||||
|
||||
if ($type === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return static::getSocketType($packet) === $type;
|
||||
}
|
||||
}
|
||||
45
vendor/topthink/think-swoole/src/websocket/socketio/Parser.php
vendored
Normal file
45
vendor/topthink/think-swoole/src/websocket/socketio/Parser.php
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\websocket\socketio;
|
||||
|
||||
use think\swoole\contract\websocket\ParserInterface;
|
||||
|
||||
class Parser implements ParserInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Encode output payload for websocket push.
|
||||
*
|
||||
* @param string $event
|
||||
* @param mixed $data
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function encode(string $event, $data)
|
||||
{
|
||||
$packet = Packet::MESSAGE . Packet::EVENT;
|
||||
$shouldEncode = is_array($data) || is_object($data);
|
||||
$data = $shouldEncode ? json_encode($data) : $data;
|
||||
$format = $shouldEncode ? '["%s",%s]' : '["%s","%s"]';
|
||||
|
||||
return $packet . sprintf($format, $event, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode message from websocket client.
|
||||
* Define and return payload here.
|
||||
*
|
||||
* @param \Swoole\Websocket\Frame $frame
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function decode($frame)
|
||||
{
|
||||
$payload = Packet::getPayload($frame->data);
|
||||
|
||||
return [
|
||||
'event' => $payload['event'] ?? null,
|
||||
'data' => $payload['data'] ?? null,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user