This commit is contained in:
CismonX 2017-08-18 23:44:26 +08:00
commit 3d3f6d6b8b
Signed by: cismonx
GPG Key ID: 3094873E29A482FB
5 changed files with 250 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
vendor/
composer.lock
.idea/

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 CismonX
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

35
README.md Normal file
View File

@ -0,0 +1,35 @@
## Workerman-Uv
### 简介
[libuv](http://libuv.org/) 是一个事件驱动的异步 I/O 库,最初是为 Node.js 开发的。随后也在 Python、Julia 等语言中被应用。
[php-uv](https://pecl.php.net/package/uv) 是对 libuv 的封装,使其可以在 PHP 中被应用。
本项目将 php-uv 的 event-loop 应用于 Workerman从而可以在基于 Workerman 的项目中利用 php-uv 提供的特性。
### 使用说明
1. 使用包管理器安装 libuv 和 libuv-devel可能需要手动添加源
2. 使用 pecl 安装 php-uv也可以从 pecl 官网或 GitHub 仓库下载源码后手动编译)。
3. 使用 composer 加载`Workerman\\Events\\Uv`。
```bash
composer require cismonx/workerman-uv
```
4. 在项目中使用 `Workerman\Events\Uv` 提供的 event-loop。如下
```php
Worker::$eventLoopClass = '\\Workerman\\Events\\Uv';
```
### 注意
1. 使用 libuv 的 event-loop 后Workerman 的子进程处理 SIGINT 事件时会 exit 2 (no such file or directory),这个问题待解决。
2. 如果需要使用 libuv 的多线程特性需要线程安全ZTS的 PHP。
3. php-uv 目前处于 Beta 阶段,其稳定性不能保证。请避免将其应用于生产环境。

15
composer.json Normal file
View File

@ -0,0 +1,15 @@
{
"name" : "cismonx/workerman-uv",
"description" : "Libuv event loop for Workerman.",
"license" : "MIT",
"type" : "library",
"require" : {
"workerman/workerman": "^3.4",
"ext-uv": "*"
},
"autoload" : {
"psr-4" : {
"Workerman\\Events\\" : "src/"
}
}
}

176
src/Uv.php Normal file
View File

@ -0,0 +1,176 @@
<?php
/**
* Workerman-Uv
* 2017 CismonX <admin@cismon.net>
*/
namespace Workerman\Events;
use Workerman\Worker;
class Uv implements EventInterface {
/**
* Libuv event loop.
* @var resource
*/
protected $_loop;
/**
* Read & write events.
* @var array
*/
protected $_allEvents = [];
/**
* Signals.
* @var array
*/
protected $_eventSignal = [];
/**
* Timers.
* @var array
*/
protected $_eventTimer = [];
/**
* Timer id counter.
* @var int
*/
protected static $_timerId = 1;
/**
* Identifies a socket with both read and write events registered.
*/
const EV_RW = 3;
/**
* Constructor.
*/
public function __construct() {
$this->_loop = uv_default_loop();
}
/**
* {@inheritdoc}
*/
public function add($fd, $flag, $func, $args = null) {
switch ($flag) {
case self::EV_READ:
case self::EV_WRITE:
$fd_key = intval($fd);
//uv_poll_init() can only be called once for a same file descriptor.
if (!isset($this->_allEvents[$fd_key]))
$this->_allEvents[$fd_key][0] = uv_poll_init($this->_loop, $fd);
$event = $this->_allEvents[$fd_key][0];
$this->_allEvents[$fd_key][$flag] = $func;
if (isset($this->_allEvents[$fd_key][self::EV_RW - $flag]))
$flag = self::EV_RW;
//Call uv_poll_start() with both existing flags.
uv_poll_start($event, $flag, function ($poll, $stat, $ev, $conn) use ($func) {
$func($conn);
});
break;
case self::EV_SIGNAL:
$fd_key = intval($fd);
$event = uv_signal_init();
uv_signal_start($event, function ($ev, $signal) use ($func) {
$func($signal);
}, $fd_key);
$this->_eventSignal[$fd_key] = $event;
break;
case self::EV_TIMER:
case self::EV_TIMER_ONCE:
$event = uv_timer_init();
$param = [$func, (array)$args, $flag, self::$_timerId];
$interval = $fd * 1000;
uv_timer_start($event, $interval, $interval, \Closure::bind(function () use ($param) {
$timer_id = $param[3];
if ($param[2] === self::EV_TIMER_ONCE) {
uv_timer_stop($this->_eventTimer[$timer_id]);
unset($this->_eventTimer[$timer_id]);
}
try {
call_user_func_array($param[0], $param[1]);
} catch (\Exception $e) {
Worker::log($e);
exit(250);
} catch (\Error $e) {
Worker::log($e);
exit(250);
}
}, $this, __CLASS__));
$this->_eventTimer[self::$_timerId] = $event;
return self::$_timerId++;
default:
break;
}
return false;
}
/**
* {@inheritdoc}
*/
public function del($fd, $flag) {
switch ($flag) {
case self::EV_READ:
case self::EV_WRITE:
$fd_key = intval($fd);
if (isset($this->_allEvents[$fd_key][$flag])) {
unset($this->_allEvents[$fd_key][$flag]);
if (isset($this->_allEvents[$fd_key][self::EV_RW - $flag])) {
$func = $this->_allEvents[$fd_key][self::EV_RW - $flag];
//Call uv_poll_start() with the remaining flag instead of call uv_poll_stop().
uv_poll_start($this->_allEvents[$fd_key][0], self::EV_RW - $flag,
function ($poll, $stat, $ev, $conn) use ($func) {
$func($conn);
}
);
} else
uv_poll_stop($this->_allEvents[$fd_key][0]);
}
break;
case self::EV_SIGNAL:
$fd_key = intval($fd);
if (isset($this->_eventSignal[$fd_key])) {
uv_signal_stop($this->_eventSignal[$fd_key]);
unset($this->_eventSignal[$fd_key]);
}
break;
case self::EV_TIMER:
case self::EV_TIMER_ONCE:
if (isset($this->_eventTimer[$fd])) {
uv_timer_stop($this->_eventTimer[$fd]);
unset($this->_eventTimer[$fd]);
}
break;
}
}
/**
* {@inheritdoc}
*/
public function loop() {
uv_run();
}
/**
* {@inheritdoc}
*/
public function clearAllTimer() {
foreach ($this->_eventTimer as $event)
uv_timer_stop($event);
$this->_eventTimer = [];
}
/**
* {@inheritdoc}
*/
public function destroy() {
foreach ($this->_eventSignal as $event)
uv_signal_stop($event);
}
public function getTimerCount() {
return count($this->_eventTimer);
}
}