github swoole/swoole-src v6.1.0

2 days ago

Major Changes in Swoole 6.1.0

1. Introduction of "Standard Library Extensions" - Revolutionary Syntax Enhancements
This is the most eye-catching feature at the language level in this update, allowing object-oriented operations on basic types.

  • Object-Oriented Basic Types: Methods can be called directly on variables of basic types like string, array, and stream (e.g., $text->length()->lower()), greatly improving code readability and development experience.
  • Typed Arrays: Introduced the typed_array syntax, allowing constraints on key and value types (e.g., <int, string>), with type checks performed on write operations, enhancing code robustness.

2. A Major Leap in Underlying Architecture and Stability
This version invests heavily in code quality and stability, laying a solid foundation for future development.

  • Significant Increase in Test Coverage to 86%: By systematically adding test cases, potential errors are greatly reduced, improving the overall reliability and maintainability of the project.
  • Large-Scale Code Refactoring and Static Analysis: Refactored multiple core modules like Reactor, SSL, and Socket, and used clang-tidy for code standardization, significantly improving code quality and performance.

3. Major Simplification and Unification of the Lock API
A groundbreaking refactoring of the core component for concurrent programming – the lock.

  • API Simplification: Removed methods like lockwait and trylock, retaining only three core methods – __construct, lock, and unlock – for Swoole\Coroutine\Lock, Swoole\Lock, and Swoole\Thread\Lock.
  • Unified Behavior: The new API design is closer to PHP's native flock function, reducing the learning curve, and uses standard constants like LOCK_EX | LOCK_NB to implement advanced features such as non-blocking.

4. Enhanced Coroutine Capabilities and Fine-Grained Control
More flexible coroutine operation capabilities.

  • Coroutine Cancellation Mechanism: Swoole\Coroutine::cancel() now includes a $throw_exception parameter, allowing a catchable Swoole\Coroutine\CanceledException to be thrown into the target coroutine upon cancellation, enabling safer and more controllable coroutine lifecycle management.
  • Coroutine State Isolation for Serialization Operations: Fixed the issue of global state isolation for serialize/unserialize in coroutine environments. Global variables related to serialization are now automatically saved and restored during coroutine switches, ensuring isolated serialization contexts for each coroutine.

5. Default HTTP Parser Upgrade, Websocket Feature Enhancements, and Platform Compatibility Improvements
Important improvements in performance and compatibility.

  • llHTTP Enabled by Default: Replaced the old http_parser with the more performant and modern llhttp, improving the efficiency and reliability of HTTP processing.
  • WebSocket Feature Enhancements: Both client and server now support receiving fragmented messages, actively disconnecting (disconnect), and sending Ping frames (ping), making the WebSocket protocol implementation more complete.
  • Deprecating Select, Embracing Poll: Completely deprecated the flawed select event mechanism. On platforms that do not support epoll/kqueue (like Cygwin), poll is now used by default, enabling support for a large number of concurrent connections on these platforms as well.

🎉 Standard Library Extensions

Swoole-Cli version 6.1 will add PHP standard library extension syntax, providing some additional new syntax while maintaining full compatibility with the official PHP syntax. These new extension syntaxes are all optional and do not affect existing PHP code. The standard library extension syntax will be implemented in the stdext module of ext-swoole.

  • swoole-cli users can use it directly without any additional parameters.
  • ext-swoole users need to add --enable-swoole-stdext during compilation to enable it.
  • stdext can be used in php-cli, php-fpm, cli-http-server, and swoole modes.

1. Object-Oriented Basic Types

Version 6.1 implements object-oriented encapsulation of array, string, and stream basic types, supporting method calls in an object-oriented style. Methods can be called directly on variables of these types.

The built-in methods for basic types actually still call the PHP standard library implementations. There is a one-to-one correspondence between the built-in methods and PHP's str_ or array_ series of functions. For example, $text->replace() corresponds to the str_replace function. The underlying layer only adjusts the function names, the parameter order for a few methods, and the parameter and return value types for a couple of methods. Please refer to the Swoole Documentation for details.

$text = 'Hello Swoole';
var_dump($text->length());
echo $text->lower()->replace("swoole", "php");
echo fopen("/tmp/test.log")->read(1024);

2. Typed-Array Strongly Typed Arrays

Allows restricting array types, such as Map or ArrayList, and restricting the types of key and value. Supports multi-level nested formats. A typed array is still essentially an array type; the underlying layer only performs parameter checks on write operations. A typed array can be passed as an array to other functions or class methods.

$array = typed_array('<int, string>');
$array[1000] = "hello"; // Correct
$array[2000] = 2025; // Error, throws an exception

New Features

  • llHTTP Default Parser: Starting from v6.1.0, Swoole uses the more performant llhttp instead of http_parser as the default HTTP message parser.
  • Coroutine-based File Download: Swoole\Coroutine\Http\Client now supports complete coroutine-based file download operations.
  • Asynchronous File Truncation: Added iouring ftruncate support, expanding asynchronous file operation capabilities.
  • Swoole\Coroutine::cancel() adds a $throw_exception parameter, supporting throwing an exception when canceling a coroutine.
  • Swoole\Coroutine\Http\Client and Swoole\Coroutine\Http\Server now support receiving WebSocket fragmented messages.
  • Added disconnect and ping methods for Swoole\Coroutine\Http\Client and Swoole\Coroutine\Http\Server.
  • Refactored Swoole\Coroutine\Lock, Swoole\Lock, and Swoole\Thread\Lock, retaining only the __construct, lock, and unlock methods, making usage closer to php flock.

🐛 Bug Fixes

  • Thread Mode Optimization:
    • Fixed the issue where hook_flags settings became ineffective in SWOOLE_THREAD mode.
    • Fixed the issue where the heartbeat thread held invalid pointers and accessed freed memory after worker threads restarted due to reaching the max_request limit.
    • Fixed the issue of not supporting restarting Task processes individually.
    • Fixed an issue where Swoole\Websocket\Server might still trigger the onMessage callback after a handshake failure or connection closure.
    • Fixed execution errors in Swoole\Coroutine\Http\Server caused by inconsistencies between function return value types and signatures.
    • Fixed the issue where Swoole\Coroutine\Http\Server did not support enabling HTTP/2.
    • Fixed the timeout precision conversion issue in Swoole\Lock::lockwait().
    • Fixed the issue causing ReactorEpoll::add(): failed to add event warnings when using the coroutine-based curl module, due to PHP 8 compatibility adjustments and incomplete cleanup of keepalive connections.
    • Fixed the issue where Swoole\Server::taskWaitMulti() could not be used in multi-threaded mode.
    • Fixed resource competition issues in Swoole\Server::taskCo() in multi-threaded mode.
  • Stability Improvements:
    • Fixed the issue where synchronous processes (including manager and task processes) could not normally use process::signal().
    • Corrected the resource cleanup mechanism when worker processes exit abnormally.
    • Resolved compilation failure issues on the Cygwin platform.
    • Fixed the issue where curl with keepalive enabled could not reuse sockets.
    • Improved the log component to resolve data consistency issues in multi-threaded mode.
    • Fixed the compilation failure of the io_uring futex feature on ubuntu24.04.
    • Fixed the compilation failure on older Linux systems due to struct order assignment.
    • Fixed the uninitialized id property issue for directly instantiated Swoole\Process objects.
    • Fixed the missing --enable-zstd option in composer.json when compiling swoole with PIE.
    • Fixed the issue where worker processes could not send signals to the manager process due to insufficient permissions after specifying a user group.
  • Compatibility Improvements:
    • Fixed compatibility issues with php_swoole_register_shutdown_function under PHP 8.5.
    • Corrected the handling of null parameters in Swoole\Table::get().
  • Protocol Handling:
    • Optimized the handling mechanism for duplicate fields in HTTP request and response headers to ensure correct merging.
    • Fixed the issue where processing a client Accept-Encoding: zlib request header would trigger an unknown compression method warning when the system lacked zlib.

❌️ Deprecations

  • No longer supports the select event mechanism. select only supports event monitoring for up to 1024 file descriptors and has serious flaws. On platforms that do not support epoll/kqueue, poll will be used as the event polling mechanism, allowing the Cygwin platform to also support a large number of concurrent connections.
  • In the Swoole\Server::stop() method, the second parameter $waitEvent has been deprecated; please use the reload_async parameter instead.

🛠️ Architectural Optimizations

Version 6.1 adds a large number of unit tests for core modules, increasing test coverage to 86%. By systematically supplementing test cases and refactoring underlying code logic, combined with full static code analysis using the clang-tidy toolchain, automated code formatting and redundancy cleanup have been achieved, significantly improving code readability and maintainability.

  • Optimized the underlying SSL module.
  • Optimized the underlying Socket module.
  • Refactored the underlying synchronous signal module.
  • Refactored the underlying reactor and string modules.
  • Refactored the underlying dynamic reloading of log files based on SIGRTMIN signals.
  • Optimized the underlying static file server, removing C-style code and unifying it into C++ style code.
  • Optimized thread initialization logic, improved multi-threading performance, independently managed structure memory, eliminating the need for locking during thread creation and exit.
  • Removed the socket_dontwait option in the underlying async module, as it was deprecated.
  • Improved test coverage for WebSocket mask handling.
  • Optimized the sendfile functionality; the tcp_cork option is not set when the file is smaller than 64KB.
  • Added unit tests for aarch64 and macOS environments.
  • Optimized underlying client network address resolution and settings.
  • Added ssl_cafile/ssl_capath configuration items for the Server module.
  • Added the print_backtrace_on_error configuration, which can print C function stack traces when unknown errors occur.
  • Added the Address Sanitizer debugging tool.
  • Added Cygwin testing, significantly improving compatibility and stability on the Cygwin platform.
  • In macOS systems, because the kqueue event mechanism does not support cross-process pipe event monitoring, it is impossible to use SWOOLE_PROCESS mode and Task process features. Therefore, the system will default to using poll as the underlying event mechanism. If you need to enable kqueue, you can manually enable it via the Swoole\Server::set(['enable_kqueue' => true]) and swoole_async_set(['enable_kqueue' => true]) methods.
  • Upgraded the Swoole library version.
  • Disabled the -Wdate-time warning to resolve errors when compiling with Zig/Clang.
  • Removed PHP 8.0 related compatibility code.
  • Added the SWOOLE_ODBC_LIBS variable in config.m4.
  • Ensured that timespec.tv_nsec values are less than 1,000,000,000 to comply with POSIX standard specifications.
  • Refactored the underlying code for swoole coroutine-based curl to improve compatibility with the synchronous blocking version.

⚠️ Notes

  • Starting from Swoole 6.1, Swoole\Coroutine\Http\Client and Swoole\Coroutine\Http\Server will automatically handle WebSocket control frames unless open_websocket_ping_frame, open_websocket_pong_frame, or open_websocket_close_frame are explicitly set.
  • Swoole\Coroutine::cancel() cannot cancel file coroutine operations; forcing cancellation may cause segmentation faults.
  • If --enable-iouring is enabled, a warning will be thrown if the kernel version or liburing version does not meet the requirements.
  • The Swoole\Websocket\Server::push method does not automatically close the connection after sending a close frame. To close the connection, please use the Swoole\Websocket\Server::disconnect() method.
  • When processing control frames, the Swoole protocol actively sets the FIN flag to 1 and ensures compression is not enabled (i.e., the RSV1 bit is 0). User settings for this will be ignored.
  • When handling compression and sending of consecutive frames, Swoole delegates the responsibility of compression and data chunking to the application layer. Users need to manually implement the relevant logic. See the example at the end.
  • The system has automatically enabled the HTTP 100 Continue response feature by default.
  • During coroutine switches, global variables related to serialize and unsealize operations will be saved and restored.

💡 API Changes

New Lock API

Function Prototypes

namespace Swoole\Coroutine {
    class Lock {
        public function __construct(bool $shared = false) {}
        public function lock(int $operation = LOCK_EX): bool {}
        public function unlock(): bool {}
    }
}

namespace Swoole {
    class Lock {
        public function __construct(int $type = SWOOLE_MUTEX) {}
        public function lock(int $operation = LOCK_EX, float $timeout = -1): bool {}
        public function unlock(): bool {}
    }
}

namespace Swoole\Thread {
    class Lock {
        public function __construct(int $type = SWOOLE_MUTEX) {}
        public function lock(int $operation = LOCK_EX, float $timeout = -1): bool {}
        public function unlock(): bool {}
    }
}

Usage Example

$lock = new Swoole\Lock;
$lock->lock(LOCK_EX | LOCK_NB, 0.5);
$lock->unlock();

Canceling a Coroutine

Co\run(function () {
    $cid = Co\go(function () {
        try {
            while (true) {
                System::sleep(0.1);
                echo "co 2 running\n";
            }
            var_dump('end');
        } catch (Swoole\Coroutine\CanceledException $e) {
            var_dump('cancelled');
        }
    });

    System::sleep(0.3);
    Co::cancel($cid, true);

    System::sleep(0.2);
    echo "co 1 end\n";
});

WebSocket Compression and Sending Consecutive Frames

Asynchronous Server

use Swoole\WebSocket\Server;
use Swoole\WebSocket\Frame;

$server = Server('127.0.0.1', 9502);
$server->set(['websocket_compression' => true]);
$server->on('message', function (Server $server, Frame $frame) {
    $data1 = bin2hex(random_bytes(10 * 1024));
    $data2 = bin2hex(random_bytes(20 * 2048));
    $data3 = bin2hex(random_bytes(40 * 4096));
    $context = deflate_init(ZLIB_ENCODING_RAW); // Stream compression
    $server->push($frame->fd, deflate_add($context, $data1, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_COMPRESS | SWOOLE_WEBSOCKET_FLAG_RSV1);
    $server->push($frame->fd, deflate_add($context, $data2, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);
    $server->push($frame->fd, deflate_add($context, $data3, ZLIB_FINISH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);
});
$server->start();

Coroutine Server

use function Swoole\Coroutine\run;
use Swoole\Coroutine\Http\Server;

run(function() {
    $server = new Server("127.0.0.1", 9502);
    $server->set(['websocket_compression' => true]);
    $server->handle('/', function ($request, $response) use ($data1, $data2, $data3) {
        $response->upgrade();
        $data1 = bin2hex(random_bytes(10 * 1024));
        $data2 = bin2hex(random_bytes(20 * 2048));
        $data3 = bin2hex(random_bytes(40 * 4096));
        $context = deflate_init(ZLIB_ENCODING_RAW);
        $response->push(deflate_add($context, $data1, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_COMPRESS | SWOOLE_WEBSOCKET_FLAG_RSV1);
        $response->push(deflate_add($context, $data2, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);
        $response->push(deflate_add($context, $data3, ZLIB_FINISH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);
    });
});

Coroutine Client

use function Swoole\Coroutine\run;
use Swoole\Coroutine\Http\Client;

run(function() {
    $client = new Client('127.0.0.1', 9502);
    $client->set(['websocket_compression' => true]);
    $ret = $client->upgrade('/');
    $context = deflate_init(ZLIB_ENCODING_RAW);
    $client->push(deflate_add($context, $data1, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_COMPRESS | SWOOLE_WEBSOCKET_FLAG_RSV1);
    $client->push(deflate_add($context, $data2, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);
    $client->push(deflate_add($context, $data3, ZLIB_FINISH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);
});

🙏 Thank You

Sincere thanks to @matyhtf @jingjingxyk @sy-records @KIMDONGYEON00 @ServBayDev @Appla @henderkes @NathanFreeman and all contributors for your professional dedication. Swoole is more powerful because of you! Wishing all friends in the open-source community good health, success in all your endeavors, and smooth work.

Swoole 6.1.0 最大的几个变更

1. 引入“标准库扩展” - 革命性的语法增强
这是本次更新在语言层面最引人注目的特性,它允许以面向对象的方式操作基础类型。

  • 基础类型对象化:可以直接对 stringarraystream 等基础类型的变量调用方法(如 $text->length()->lower()),这极大地提升了代码的可读性和编写体验。
  • 强类型数组:引入了 typed_array 语法,可以定义键值类型的约束(如 <int, string>),在写入时进行类型检查,增强了代码的健壮性。

2. 底层架构与稳定性的巨大飞跃
本次版本在代码质量和稳定性上投入巨大,为未来的发展奠定了坚实基础。

  • 测试覆盖率大幅提升至86%:通过系统性地补充测试用例,极大地减少了潜在的错误,提高了整个项目的可靠性和可维护性。
  • 大规模代码重构与静态分析:重构了 Reactor、SSL、Socket 等多个核心模块,并使用 clang-tidy 进行代码规整,显著提升了代码质量和性能。

3. 锁 API 的重大简化与统一
对并发编程的核心组件——锁,进行了颠覆性的重构。

  • API 精简:移除了 lockwaittrylock 等方法,只为 Swoole\Coroutine\LockSwoole\LockSwoole\Thread\Lock 保留 __constructlockunlock 三个核心方法。
  • 行为统一:新的 API 设计更贴近 PHP 原生的 flock 函数,降低了学习成本,并使用 LOCK_EX | LOCK_NB 等标准常量来实现非阻塞等高级功能。

4. 协程能力的强化与精细化控制
更加灵活的协程操作能力。

  • 协程取消机制Swoole\Coroutine::cancel() 新增 $throw_exception 参数,允许在取消协程时向目标协程内抛出一个可捕获的 Swoole\Coroutine\CanceledException,实现了更安全、更可控的协程生命周期管理。
  • 实现序列化操作的协程状态隔离:修复协程环境下 serialize/unserialize 的全局状态隔离问题。现已在协程切换时自动保存和恢复相关全局变量,确保各协程序列化上下文互不干扰。

5. 默认 HTTP 解析器升级,Websocket功能增强和平台兼容性改进
在性能和兼容性方面的重要提升。

  • 默认启用 llHTTP:用性能更优、更现代的 llhttp 替代了旧的 http_parser,提升了 HTTP 处理的效率和可靠性。
  • WebSocket 功能增强:客户端和服务端均支持了接收分块消息、主动断开连接(disconnect)和发送 Ping 帧(ping)等能力,使 WebSocket 协议的实现更加完整。
  • 废弃 Select,拥抱 Poll:彻底废弃了有严重缺陷的 select 事件机制,在不支持 epoll/kqueue 的平台上(如 Cygwin)默认使用 poll,从而在这些平台上也能支持大量并发连接。

🎉 标准库扩展

6.1 版本 Swoole-Cli 将增加 PHP 标准库扩展语法,在保持对 PHP 官方语法完整兼容的同时,提供一些额外的新语法。这些新的扩展语法均是可选的,不影响现有的PHP代码。标准库扩展语法将在ext-swoolestdext模块中实现。

  • swoole-cli 用户,可直接使用,无需设置任何额外的参数
  • ext-swoole 的用户,需在编译时添加--enable-swoole-stdext来启用
  • stdextphp-cliphp-fpmcli-http-serverswoole 模式均可使用

1. 基础类型对象化

6.1 版本实现了array/string/stream基础类型的对象化封装,支持面向对象风格的方法调用,可直接对这些类型的变量调用内置方法。

基础类型的内置方法实际上仍然是调用PHP标准库实现的,内置方法与PHPstr_array_系列函数是一一对应关系,例如$text->replace()对应的函数是str_replace。底层仅调整了函数名称,少量方法调整了参数顺序,还有几个方法调整了参数和返回值类型,详情请查看 Swoole 文档

$text = 'Hello Swoole';
var_dump($text->length());
echo $text->lower()->replace("swoole", "php");
echo fopen("/tmp/test.log")->read(1024);

2. Typed-Array 强类型数组

可限制数组类型,如:MapArrayList,以及限制keyvalue的类型。支持多层嵌套格式。typed array实际上依然是array类型,底层仅在写入时进行参数检查。typed array可作为array传递给其他函数或类方法。

$array = typed_array('<int, string>');
$array[1000] = "hello"// 正确
$array[2000] = 2025// 错误,抛出异常

新特性

  • llHTTP 默认解析器:自 v6.1.0 起,Swoole 采用性能更优的 llhttp 替代 http_parser 作为默认 HTTP 报文解析器。
  • 协程化文件下载Swoole\Coroutine\Http\Client 现支持完整的协程化文件下载操作。
  • 异步文件截断:新增 iouring ftruncate 支持,扩展异步文件操作能力。
  • Swoole\Coroutine::cancel() 新增 $throw_exception 参数,支持在取消协程时抛出异常。
  • Swoole\Coroutine\Http\ClientSwoole\Coroutine\Http\Server 现已支持接收 WebSocket 分块消息。
  • Swoole\Coroutine\Http\ClientSwoole\Coroutine\Http\Server 新增 disconnectping 方法。
  • 重构Swoole\Coroutine\LockSwoole\Lock和,Swoole\Thread\Lock,只保留__constructlockunlock方法,使用上更加贴近php flock

🐛 Bug 修复

  • 线程模式优化
    • 修复 SWOOLE_THREAD 模式下 hook_flags 设置失效问题。
    • 修复因工作线程达到max_request限制重启后,导致心跳线程持有失效指针并访问已释放内存的问题。
    • 修复不支持单独重启Task进程的问题。
    • 修复 Swoole\Websocket\Server 在握手失败或连接关闭后,仍可能触发 onMessage 回调的问题。
    • 修复 Swoole\Coroutine\Http\Server 因函数返回值类型与签名不一致导致的执行错误。
    • 修复 Swoole\Coroutine\Http\Server 不支持启用 HTTP/2 的问题。
    • 修复 Swoole\Lock::lockwait() 超时时间精度转换问题。
    • 修复因PHP 8兼容性调整及keepalive连接清理不彻底,导致使用协程化 curl 模块时出现 ReactorEpoll::add(): failed to add event 警告的问题。
    • 修复Swoole\Server::taskWaitMulti()在多线程模式下无法使用的问题。
    • 修复Swoole\Server::taskCo()在多线程模式下资源竞争问题。
  • 稳定性提升
    • 修复同步进程(含 manager 进程和 task 进程)无法正常使用process::signal()的问题。
    • 修正工作进程异常退出时的资源清理机制。
    • 解决 Cygwin 平台编译失败问题。
    • 修复curl开启keepalive无法复用socket的问题。
    • 改进日志组件以解决多线程模式下的数据一致性问题。
    • 修复io_uringfutex特性在ubuntu24.04编译失败的问题。
    • 修复结构体顺序赋值在低版本的linux系统会编译失败的问题。
    • 修复直接实例化的Swoole\Process对象其id属性未初始化的问题。
    • 修复使用PIE编译swoole时,composer.json文件缺少--enable-zstd的选项的问题。
    • 修复了指定用户组后,worker进程因权限不足而无法向manager进程发送信号的问题。
  • 兼容性改进
    • 修复 PHP 8.5 下 php_swoole_register_shutdown_function 兼容性问题。
    • 修正 Swoole\Table::get() 对 null 参数的处理。
  • 协议处理
    • 优化HTTP请求头与响应头中重复字段的处理机制,确保其正确合并。
    • 修复当系统没有zlib时,处理客户端Accept-Encoding: zlib请求头会触发未知压缩方法警告的问题。

❌️ 废弃

  • 不再支持select事件机制,select仅支持1024个句柄的事件监听,存在严重缺陷,在不支持epoll/kqueue的平台将使用poll作为事件轮训机制,这使得Cygwin平台下也可以支持大量并发。
  • Swoole\Server::stop()方法中,第二个参数$waitEvent已被废弃,请使用reload_async参数。

🛠️ 架构优化

6.1版本增加了大量核心模块的单元测试,测试覆盖率提升至86%。通过系统性地补充测试用例并重构底层代码逻辑,同时结合clang-tidy工具链开展全量静态代码分析,实现了代码的自动化格式规整与冗余清理,使得代码可读性与可维护性得到显著提升。

  • 优化底层SSL模块。
  • 优化底层Socket模块。
  • 重构底层同步信号模块。
  • 重构底层reactorstring模块。
  • 重构底层基于SIGRTMIN信号实现日志文件动态重载。
  • 优化底层静态文件服务器,移除C风格代码,统一为C++风格的代码。
  • 优化线程初始化逻辑,提高多线程性能,独立管理结构内存,线程创建和退出不需要再加锁。
  • 移除底层async模块中的socket_dontwait选项,该选项已废弃。
  • 完善WebSocket掩码处理的测试覆盖。
  • 优化sendfile功能,当文件小于64KB时不设置tcp_cork选项。
  • 增加aarch64macOS环境下的单元测试。
  • 优化底层客户端网络地址解析和设置。
  • Server模块增加ssl_cafile/ssl_capath配置项。
  • 增加print_backtrace_on_error配置,此配置可以在发生位置错误时候,打印C函数的堆栈。
  • 增加Address Sanitizer调试工具。
  • 增加Cygwin测试,显著提升Cygwin平台下的兼容性和稳定性
  • macOS 系统中,由于 kqueue 事件机制不支持跨进程管道事件监听,导致无法使用 SWOOLE_PROCESS 模式和 Task 进程功能。因此,系统将默认采用 poll 作为底层事件机制。如需启用 kqueue,可以通过Swoole\Server::set(['enable_kqueue' => true])swoole_async_set(['enable_kqueue' => true])方法手动开启。
  • 升级 Swoole library 版本。
  • 禁用 -Wdate-time 警告,解决使用 Zig/Clang 编译时的错误。
  • 移除 PHP 8.0 相关兼容代码。
  • config.m4 中新增 SWOOLE_ODBC_LIBS 变量。
  • 确保 timespec.tv_nsec 取值小于 1,000,000,000,以符合 POSIX 标准规范。
  • 重构swoole协程化curl底层代码,提高与同步阻塞版本的兼容性。

⚠️ ** 注意事项**

  • 自 Swoole 6.1 起,Swoole\Coroutine\Http\ClientSwoole\Coroutine\Http\Server 将自动处理 WebSocket 控制帧,除非显式设置 open_websocket_ping_frameopen_websocket_pong_frameopen_websocket_close_frame
  • Swoole\Coroutine::cancel() 无法取消文件协程化操作,强行取消可能导致段错误。
  • 若启用 --enable-iouring,在检测到内核版本或 liburing 版本不满足要求时,将抛出警告信息。
  • Swoole\Websocket\Server::push 方法在发送关闭帧后不会自动关闭连接,如需关闭连接,请使用 Swoole\Websocket\Server::disconnect() 方法。
  • Swoole协议在处理控制帧时,会主动设置 FIN 标志位为 1,并确保不启用压缩(即 RSV1 位为 0),用户对此的设置将被忽略。
  • 当处理连续帧的压缩和发送时,Swoole将压缩与数据分块的职责移交至应用层,用户需手动实现相关逻辑。具体请看最下面的例子。
  • 系统默认启用了自动HTTP 100 Continue响应功能。
  • 在协程切换期间,将会保存并恢复serializeunsealize操作相关的全局变量。

💡 API 变更

新的锁 API

函数原型

namespace Swoole\Coroutine {
    class Lock {
        public function __construct(bool $shared = false) {}
        public function lock(int $operation = LOCK_EX): bool {}
        public function unlock(): bool {}
    }
}

namespace Swoole {
    class Lock {
        public function __construct(int $type = SWOOLE_MUTEX) {}
        public function lock(int $operation = LOCK_EX, float $timeout = -1): bool {}
        public function unlock(): bool {}
    }
}

namespace Swoole\Thread {
    class Lock {
        public function __construct(int $type = SWOOLE_MUTEX) {}
        public function lock(int $operation = LOCK_EX, float $timeout = -1): bool {}
        public function unlock(): bool {}
    }
}

使用示例

$lock = new Swoole\Lock;
$lock->lock(LOCK_EX | LOCK_NB, 0.5);
$lock->unlock();

取消协程

Co\run(function () {
    $cid = Co\go(function () {
        try {
            while (true) {
                System::sleep(0.1);
                echo "co 2 running\n";
            }
            var_dump('end');
        } catch (Swoole\Coroutine\CanceledException $e) {
            var_dump('cancelled');
        }
    });

    System::sleep(0.3);
    Co::cancel($cid, true);

    System::sleep(0.2);
    echo "co 1 end\n";
});

WebSocket 压缩和发送连续帧

异步服务端

use Swoole\WebSocket\Server;
use Swoole\WebSocket\Frame;

$server = Server('127.0.0.1', 9502);
$server->set(['websocket_compression' => true]);
$server->on('message', function (Server $server, Frame $frame) {
    $data1 = bin2hex(random_bytes(10 * 1024));
    $data2 = bin2hex(random_bytes(20 * 2048));
    $data3 = bin2hex(random_bytes(40 * 4096));
    $context = deflate_init(ZLIB_ENCODING_RAW); // 流式压缩
    $server->push($frame->fd, deflate_add($context, $data1, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_COMPRESS | SWOOLE_WEBSOCKET_FLAG_RSV1);
    $server->push($frame->fd, deflate_add($context, $data2, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);
    $server->push($frame->fd, deflate_add($context, $data3, ZLIB_FINISH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);
});
$server->start();

协程服务端

use function Swoole\Coroutine\run;
use Swoole\Coroutine\Http\Server;

run(function() {
    $server = new Server("127.0.0.1", 9502);
    $server->set(['websocket_compression' => true]);
    $server->handle('/', function ($request, $response) use ($data1, $data2, $data3) {
        $response->upgrade();
        $data1 = bin2hex(random_bytes(10 * 1024));
        $data2 = bin2hex(random_bytes(20 * 2048));
        $data3 = bin2hex(random_bytes(40 * 4096));
        $context = deflate_init(ZLIB_ENCODING_RAW);
        $response->push(deflate_add($context, $data1, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_COMPRESS | SWOOLE_WEBSOCKET_FLAG_RSV1);
        $response->push(deflate_add($context, $data2, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);
        $response->push(deflate_add($context, $data3, ZLIB_FINISH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);
    });
});

协程客户端

use function Swoole\Coroutine\run;
use Swoole\Coroutine\Http\Client;

run(function() {
    $client = new Client('127.0.0.1', 9502);
    $client->set(['websocket_compression' => true]);
    $ret = $client->upgrade('/');
    $context = deflate_init(ZLIB_ENCODING_RAW);
    $client->push(deflate_add($context, $data1, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_COMPRESS | SWOOLE_WEBSOCKET_FLAG_RSV1);
    $client->push(deflate_add($context, $data2, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);
    $client->push(deflate_add($context, $data3, ZLIB_FINISH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);
});

🙏 致谢

诚挚感谢 @matyhtf @jingjingxyk @sy-records @KIMDONGYEON00 @ServBayDev @Appla @henderkes @NathanFreeman 所有贡献者的专业付出,Swoole 因你们而更强大!愿开源社区伙伴身体健康,万事如意,工作顺利。

Don't miss a new swoole-src release

NewReleases is sending notifications on new releases.