dvaknheo / swoolehttpd
2019年的 Swoole Http Server
Requires
- php: >=7.0.0
This package is auto-updated.
Last update: 2024-09-09 19:31:35 UTC
README
SwooleHttpd 是什么
SwooleHttpd 致力于 Swoole 代码和 fpm 平台代码几乎不用修改就可以在双平台上运行。它是 swoole_http_server 类的一个封装。
SwooleHttpd 最初来自 PHP 框架 DuckPhp 的前身 DNMVCS。不引用其他 PHP 代码,简单可靠。但 SwooleHttpd 是设计成几乎与 DuckPhp 无关的 Swoole 框架,所以我将其剥离。
理论上应该是高性能的
特色
直接使用超全局变量,直接使用 echo 输出。
最方便旧代码迁移。
当然,fpm 方式的代码还不能那么简单替代,我们使用 SwooleHttpd::GLOBALS() 替代全局变量,SwooleHttpd::STATICS() 替代静态变量,SwooleHttpd::CLASS_STATICS() 替代类内静态变量。
还有对系统函数的封装,如 SwooleHttpd::header(),SwooleHttpd::setcookie() 等。
尤其是 session 方面的 SwooleHttpd::session_start(),swoole_http_server 最常见的基本问题。
最后一个是无法处理的:require,include,以及重复包含文件导致函数的重复。
要处理这些问题,需要使用 php-parser,编写 SwooleHttpd::PHPFile() 或 SwooleHttpd::require(),SwooleHttpd::include() 来解决。由于不想进行太多改动,所以没有进行尝试。
基本应用
使用方法:
composer require dvaknheo/swoolehttpd
<?php use SwooleHttpd\SwooleHttpd; require(__DIR__.'/../autoload.php'); function hello() { echo "<h1> hello ,have a good start.</h1><pre>\n"; var_export($_SERVER,$_GET,$POST,$_REQUEST,$_COOKIE, $_SESSION); echo "</pre>"; return true; } $options=[ 'port'=>9528, 'http_handler'=>'hello', ]; SwooleHttpd::RunQuickly($options);
通过浏览器打开 http://127.0.0.1:9528/ 这个例子展现了 $_SERVER 中有的内容
选项
RunQuickly 的默认选项 $$options SwooleHttpd::DEFAULT_OPTIONS 有
const DEFAULT_OPTIONS=[ 'swoole_server'=>null, // swoole_http_server 对象,留空,则用 host,port 创建 'swoole_options'=>[], // swoole_http_server 的配置,合并入 swoole_server 'host'=>'0.0.0.0', // IP 'port'=>0, // 端口 'http_handler'=>null, // 启动方法,返回 false 表示 404 'http_handler_basepath'=>'', // 基础目录目录 ,搭配用于配置 http_handler_root ,http_handler_file 'http_handler_root'=>'', // PHP 目录模式。 'http_handler_file'=>'', // 映射所有 URI 到单一文件模式 'http_exception_handler'=>null, // 异常处理回调, set_exception_handler 会覆盖这个配置 'http_404_handler'=>null, // 404 的处理回调 'with_http_handler_root'=>false,// 复用 http_handler_root 404 后会从目录里载入 'with_http_handler_file'=>false,// 复用 http_handler_root 404 后会从文件里载入 'enable_fix_index'=>true, // http_handler 模式下,修正 index.php 为空 'enable_path_info'=>true, // http_handler_root 允许 path_info 'enable_not_php_file'=>true, // http_handler_root 允许包含资源文件 'base_class'=>null, // 替换 SwooleHttpd 类初始化 'silent_mode'=>false, // 安静模式,不在命令行中提示服务启动信息。 'enable_coroutine'=>true, // 启用 \Swoole\Runtime::enableCoroutine(); ];
难度级别
从难度低到高,大概是这样的级别以实现目的
- 使用默认选项实现目的
- 只改选项实现目的
- 调用 SwooleHttpd 类的静态方法实现目的
- 调用 SwooleHttpd 类的动态方法实现目的
- ---- 初级程序员和高级程序员分界线 ----
- 使用入口类扩展
- 调用扩展类,组件类的动态方法实现目的
- 继承接管特定类实现目的
- 魔改,硬改 SwooleHttpd 的代码实现目的
三种模式
SwooleHttpd 有三种模式
-
http_handler主要模式,所有 URL 请求都由这个回调处理。这个模式和后面两种模式的区别在于,当 with_http_handler_root 打开时,http_handler 返回 false 后继续进入 http_handler 搜索文件运行。
-
http_handler_root这和 document_root 一样。读取 php 文件然后运行的模式。注意重复包含类会导致异常。当 with_http_handler_file 打开时,找不到文件会进入 http_handler_file 处理。enable_not_php_file 允许读取资源文件,如图片,将会在浏览器显示图片。
-
http_handler_file这种模式是将所有 URL 转向文件如 index.php 来处理。
常用静态方法
常用静态方法,基本上都要用到的静态方法
RunQuickly(array $options=[],callable $after_init=null)
入口,等价于 SwooleHttpd::G()->init($options)->run();
如果 after_init不为 null 将会在 init 后执行
ThrowOn($flag,$message,$code=0)
如果 flag 成立抛出异常
和 DuckPhp 不同的是,这里抛出 SwooleException。
Server()
获得当前 swoole_server 对象
Request()
获得当前 swoole_request 对象
返回 SwooleContext::G()->request
Response()
获得当前 swoole_response 对象
返回 SwooleContext::G()->response
OnShow404()
处理404的通用方法,选项 http_404_handler 优先使用
OnException($ex)
异常的处理方法,选项 http_exception_handler 优先使用
超全局变量静态方法
代替超全局变量,基本由 SwooleSuperGlobal 的动态方法实现。高级程序员可以通过接管 SwooleSuperGlobal 来实现自己的解决方案。
&GLOBALS($k,$v=null)
全局变量 global 语法的替代方法
返回 SwooleSuperGlobal::G()->STATICS($k,$v)
&STATICS($k,$v=null)
静态变量 static 语法的替代方法
返回 SwooleSuperGlobal::G()->_STATICS($k,$v)
&CLASS_STATICS($class_name,$var_name)
类内静态变量 static 语法的替代方法
$class_name 传入类名,以确定是 self::class 还是 static::class
返回 SwooleSuperGlobal::G()->_CLASS_STATICS($class_name,$var_name)
系统封装静态方法
对应 PHP 手册中函数的全局函数的替代,因为相应的同名函数在 Swoole 环境中不可用。特殊函数 system_wrapper_get_providers 介绍了有多少系统替换函数。所有这些静态方法都是调用动态方法实现,以方便修改。
system_wrapper_get_providers()
特殊方法,对外提供本类有的系统封装函数
exit($code=0)
特殊方法,对应 exit() 语法。退出系统,swoole 里,直接 exit 也是可以的。
header(string $string, bool $replace = true , int $http_status_code=0)
header 函数
setcookie(string $key, string $value = '', int $expire = 0 , string $path = '/', string $domain = '', bool $secure = false , bool $httponly = false)
设置 cookie
set_exception_handler(callable $exception_handler)
设置异常函数
register_shutdown_function(callable $callback,...$args)
退出关闭函数
session_start(array $options=[])
开始 session
session_destroy()
结束 session
session_set_save_handler(\SessionHandlerInterface $handler)
设置 session_handler
WebSocket 服务器方法
Frame Fd IsClosing
高级静态方法
这些静态方法,初学者可以忽略
static G($object=null)
G 函数,可替换单例。
ReplaceDefaultSingletonHandler()
替换单例实现
实质返回 SwooleCoroutineSingleton::ReplaceDefaultSingletonHandler();
EnableCurrentCoSingleton()
开启协程内单例,比如 \go 函数里你需要用到自己的协程单例。
实质返回 SwooleCoroutineSingleton::EnableCurrentCoSingleton();
单例模式
SwooleHttpd 使用 trait SingletonEx。SingletonEx 定义了静态函数 G($object=null),如果默认参数的话得到当前单例。如果传入 $object 则替换单例,实现调用方式不变,实现方式改变的效果。
SwooleHttpd:: 通过使用 SwooleCoroutineSingleton 进一步扩展了 SingletonExTrait(通过 __SINGLETONEX_REPALACER 宏)实现了协程内单例。
如果协程内没有单例,会查找全局的单例($cid=0)的。
在协程结束时,会自动清理所有协程单例。
超全局变量和语法代替静态方法
由于 swoole 的协程使得跨领域的 global,static,类内 static 变量不可用,我们使用替代方法
<?php use DuckPhp\SwooleHttpd as DN; require (__DIR__.'/../autoload.php'); global $n; // => $n=&DN::GLOBALS('n'); static $n; // => $n=&DN::STATICS('n'); //别漏掉了 & $n++; var_dump($n); class B { protected static $var=10; public static function foo() { //static::$var++; //var_dump(static::$var); $_=&DN::CLASS_STATICS(static::class,'var');$_ ++; // 把 static::$var 替换成 $_=&DN::CLASS_STATICS(static::class,'var');$_ //别漏掉了 & var_dump(DN::CLASS_STATICS(static::class,'var')); // 没等号或 ++ -- 之类非左值不用 & } } class C extends B { protected static $var=100; } C::foo();C::foo();C::foo();
输出
int(1)
int(101)
int(102)
int(103)
高级内容
前面是使用者知道就够的内容,后面是高级内容了
SwooleHttpd 的其他对外动态方法
init($options=[])
初始化,这是最经常子类化完成自己功能的方法。
你可以扩展这个类,添加工程里的其他初始化。
run()
运行,运行后进入 swoole_http_server
set_http_exception_handler($ex)
设置异常
exit_request($code=0)
退出当前请求,等同于 exit
getDynamicClasses()
获取动态类 http_handler 模式
forkMasterInstances($classes,$exclude_classes=[])
把master 实例clone 到当前协程。exclude_classes 表示,如果是 克隆的实例有当前的类,则跳过不克隆
resetInstances()
重置 协程为0 的实例覆盖到当前协程,用空的实例,而不是原有实例。
简单 HTTP 服务器
SwooleHttpd 使用 trait SwooleHttpd_SimpleHttpd。单独使用这个 trait 你可以实现一个 http 服务器
protected function onHttpRun($request,$response){}
protected function onHttpException($ex){}
protected function onHttpClean(){}
public function onRequest($request,$response)
初始化 SwooleContext 和一些处理。
协程单例方法
不常用方法,主要提供给 init 前调用
getDymicClasses()
createCoInstance($class,$object)
forkMasterInstances($classes,$exclude_classes=[])
resetInstances()
SwooleException 扩展 \Exception
空类,异常类
Swoole404Exception 扩展 \Exception
空类,404 异常
SwooleSingleton
共享 trait
SwooleHttpd 重写了 可变单例 G 函数的实现,使得做到协程单例。
class SwooleCoroutineSingleton
用于协程单例,把主进程单例复制到协程单例
public static function ReplaceDefaultSingletonHandler() //替换默认Handler
public static function SingletonInstance($class,$object) // handelr 实现
public static function GetInstance($cid,$class)
public static function SetInstance($cid,$class,$object)
public static function DumpString() // 打印
public function cleanUp()
public function forkMasterInstances($classes,$exclude_classes=[]) //把主协程的实例扩充到协程
public function forkAllMasterClasses() // 主协程的 G 都扩充到 协程, 和上面例子不同的是...
public function _DumpString()
public static function Dump()
class SwooleContext
协程单例。Swoole 的运行信息
public function initHttp($request,$response)
public function initWebSocket($frame)
public function cleanUp()
public function onShutdown()
public function regShutDown($call_data)
public function isWebSocketClosing()
class SwooleSuperGlobal
SwooleSuperGlobal 是 Swoole 下 超全局变量 的实现。
同时处理 session
调用 SwooleSessionHandler ,
public $is_inited=false;
public function init()
public function &_GLOBALS($k, $v=null)
public function &_STATICS($name, $value=null, $parent=0)
public function &_CLASS_STATICS($class_name, $var_name)
public function session_set_save_handler($handler)
public function session_start(array $options=[])
public function session_id($session_id=null)
public function session_destroy()
public function writeClose()
public function create_sid()
SwooleSessionHandler 实现 \SessionHandlerInterface
因为默认的 SessionHandler 不能直接使用,这里做文件实现版本的 SessionHandler 。
如果你有自己的 SessionHandler ,用 SwooleHttpd::session_set_save_handler() 安装;
代码解读
基本流程 init()
开始检测是否有 base_class ,如果有,则替换当前单例为 base_class 的实现,
返回 base_class 的 G 实例的 init
载入选项 如果没有 server 对象则根据配置创建一个。
SwooleCoroutineSingleton::ReplaceDefaultSingletonHandler(); 替换单例
宏 DuckPhp_SUPER_GLOBAL_REPALACER 定为 SwooleSuperGlobal::G
宏 DuckPhp_SYSTEM_WRAPPER_INSTALLER 定为 static::system_wrapper_get_providers;
基本流程 run()
如果不是安静模式,则打印相关信息
$this->server->start();
基本流程 onRequest()
onRequest 实现于 trait SwooleHttpd_SimpleHttpd
trait SwooleHttpd_SimpleHttpd (估计现实也没人会用到 SwooleHttpd_SimpleHttpd 而不用 SwooleHttpd )
一开始就 defer 手动 gc
SwooleCoroutineSingleton::EnableCurrentCoSingleton 开启 onRequest 协程的协程单例
defer 配合 ob_start 处理直接 echo 输出
SwooleContext 初始化
SwooleSuperGlobal::G 初始化
注意代码 SwooleSuperGlobal::G(new SwooleSuperGlobal());
为什么不是 SwooleSuperGlobal::G();
因为要确保 SwooleSuperGlobal::G() 得到的单例是 协程内的单例。
接下来 正常流程 onHttpRun 处理 http 业务
出异常则 onHttpException 处理异常。
流程结束后,进入前面 defer 流程里处理善后
包括 伪 regist_shutdown_function 处理
其他信息则 onHttpClean 处理
SwooleContext 善后处理
关闭 response;
(这个 defer 折腾了一段时间处理顺序,没 bug 就暂时不要动了。)
基本流程 onHttpClean()
SwooleHttpd onHttpClean
处理 autoload ,防止 http_handler_root/http_handler_file 模式多次载入 spl_autoload
基本流程 onHttpException($ex)
这个很简单
如果是 \Swoole\ExitException 异常, 不用处理
如果是 Swoole404Exception 则 static::OnShow404();
否则 static::OnException($ex);
基本流程 onHttpRun
主要流程。
保存 spl_autoload_functions
如果 http_handler 模式
关闭自动清理 autoload
处理选项 enable_fix_index
运行 http_handler
如果得到的是 false 而且非 with_http_handler_root,非 http_handler_file 则404
否则打开自动清理 autoload,继续
如果 http_handler_root 模式
如果 http_handler_file 模式
DuckPhp handler 相关。
WebSocket(测试中)
配置
//* websocket 在测试中。未稳定 'websocket_open_handler'=>null, // websocket 打开 'websocket_handler'=>null, // websocket 处理 'websocket_exception_handler'=>null,// websocket 异常处理 'websocket_close_handler'=>null, // websocket 关闭
静态方法
Frame()
获得当前 frame (websocket 生效 )
FD()
获得当前 fd (websocket 生效)
IsClosing()
判断是否是关闭的包 (websocket 生效)
简单 websocket 服务器
SwooleHttpd 使用的 trait SwooleHttpd_WebSocket . 单独使用这个 trait 你可以实现一个 websocket 服务器
onRequest($request,$response)
//
onOpen(swoole_websocket_server $server, swoole_http_request $request)
//
onMessage($server,$frame)
//
没有 OnClose 。