## File: README.md

<h2 align=center>
 
    Swoole is an event-driven, asynchronous, coroutine-based concurrency library with high performance for PHP.
</h2>

[](https://github.com/swoole/swoole-src/actions/workflows/ext.yml)
[](https://github.com/swoole/swoole-src/actions/workflows/core.yml)
[](https://github.com/swoole/swoole-src/actions/workflows/unit.yml)
[](https://github.com/swoole/swoole-src/actions/workflows/thread.yml)
[](https://github.com/swoole/swoole-src/actions/workflows/iouring.yml)
[](https://github.com/swoole/swoole-src/actions/workflows/framework.yml)

[](https://twitter.com/phpswoole)
[](https://discord.swoole.dev)
[](https://github.com/swoole/swoole-src/releases/)
[](https://github.com/swoole/swoole-src/blob/master/LICENSE)
[](https://scan.coverity.com/projects/swoole-swoole-src)
[](https://codecov.io/gh/swoole/swoole-src)

## ⚙️ Quick Start

Run Swoole program by [Docker](https://github.com/swoole/docker-swoole)

```bash
docker run --rm phpswoole/swoole "php --ri swoole"
```

> For details on how to use it, see: [How to Use This Image](https://github.com/swoole/docker-swoole#how-to-use-this-image).

## Documentation
<https://wiki.swoole.com/>

### HTTP Service
```php
$http = new Swoole\Http\Server('127.0.0.1', 9501);
$http->set(['hook_flags' => SWOOLE_HOOK_ALL]);

$http->on('request', function ($request, $response) {
    $result = [];
    Co::join([
        go(function () use (&$result) {
            $result['google'] = file_get_contents("https://www.google.com/");
        }),
        go(function () use (&$result) {
            $result['taobao'] = file_get_contents("https://www.taobao.com/");
        })
    ]);
    $response->end(json_encode($result));
});

$http->start();
```

### Concurrency
```php
Co\run(function() {
    Co\go(function() {
        while(1) {
            sleep(1);
            $fp = stream_socket_client("tcp://127.0.0.1:8000", $errno, $errstr, 30);
            echo fread($fp, 8192), PHP_EOL;
        }
    });

    Co\go(function() {
        $fp = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);
        while(1) {
            $conn = stream_socket_accept($fp);
            fwrite($conn, 'The local time is ' . date('n/j/Y g:i a'));
        }
    });

    Co\go(function() {
        $redis = new Redis();
        $redis->connect('127.0.0.1', 6379);
        while(true) {
            $redis->subscribe(['test'], function ($instance, $channelName, $message) {
                echo 'New redis message: '.$channelName, "==>", $message, PHP_EOL;
            });
        }
    });

    Co\go(function() {
        $redis = new Redis();
        $redis->connect('127.0.0.1', 6379);
        $count = 0;
        while(true) {
            sleep(2);
            $redis->publish('test','hello, world, count='.$count++);
        }
    });
});
```

## Runtime Hook

**Swoole hooks the blocking io function of PHP at the `bottom layer` and `automatically` converts it to a non-blocking function, so that these functions can be called concurrently in coroutines.**

### Supported extension/functions

* `ext-curl` (Support `symfony` and `guzzle`)
* `ext-redis`
* `ext-mysqli`
* `ext-pdo_mysql`
* `ext-pdo_pgsql`
* `ext-pdo_sqlite`
* `ext-pdo_oracle`
* `ext-pdo_odbc`
* `stream functions` (e.g. `stream_socket_client`/`stream_socket_server`), Supports `TCP`/`UDP`/`UDG`/`Unix`/`SSL/TLS`/`FileSystem API`/`Pipe`
* `ext-sockets`
* `ext-soap`
* `sleep`/`usleep`/`time_sleep_until`
* `proc_open`
* `gethostbyname`/`shell_exec`/`exec`
* `fread`/`fopen`/`fsockopen`/`fwrite`/`flock`


## 🛠 Develop & Discussion

+ __IDE Helper & API__: <https://github.com/swoole/ide-helper>
+ __Twitter__: <https://twitter.com/phpswoole>
+ __Discord__: <https://discord.swoole.dev>
+ __中文社区__: <https://wiki.swoole.com/#/other/discussion>

## 💎 Awesome Swoole
Project [Awesome Swoole](https://github.com/swoole/awesome-swoole) maintains a curated list of awesome things related to Swoole, including

* Swoole-based frameworks and libraries.
* Packages to integrate Swoole with popular PHP frameworks, including Laravel, Symfony, Slim, and Yii.
* Books, videos, and other learning materials about Swoole.
* Debugging, profiling, and testing tools for developing Swoole-based applications.
* Coroutine-friendly packages and libraries.
* Other Swoole related projects and resources.

## ✨ Event-based

The network layer in Swoole is event-based and takes full advantage of the underlying epoll/kqueue implementation, making it really easy to serve millions of requests.

Swoole 4.x uses a brand new engine kernel and now it has a full-time developer team, so we are entering an unprecedented period in PHP history which offers a unique possibility for rapid evolution in performance.

## ⚡ Coroutine

Swoole 4.x or later supports the built-in coroutine with high availability, and you can use fully synchronized code to implement asynchronous performance. PHP code without any additional keywords, the underlying automatic coroutine-scheduling.

Developers can understand coroutines as ultra-lightweight threads, and you can easily create thousands of coroutines in a single process.

### MySQL

Concurrency 10K requests to read data from MySQL takes only 0.2s!

```php
$s = microtime(true);
Co\run(function() {
    for ($c = 100; $c--;) {
        go(function () {
            $mysql = new Swoole\Coroutine\MySQL;
            $mysql->connect([
                'host' => '127.0.0.1',
                'user' => 'root',
                'password' => 'root',
                'database' => 'test'
            ]);
            $statement = $mysql->prepare('SELECT * FROM `user`');
            for ($n = 100; $n--;) {
                $result = $statement->execute();
                assert(count($result) > 0);
            }
        });
    }
});
echo 'use ' . (microtime(true) - $s) . ' s';
```

### Mixed server

You can create multiple services on the single event loop: TCP, HTTP, Websocket and HTTP2, and easily handle thousands of requests.

```php
function tcp_pack(string $data): string
{
    return pack('N', strlen($data)) . $data;
}
function tcp_unpack(string $data): string
{
    return substr($data, 4, unpack('N', substr($data, 0, 4))[1]);
}
$tcp_options = [
    'open_length_check' => true,
    'package_length_type' => 'N',
    'package_length_offset' => 0,
    'package_body_offset' => 4
];
```

```php
$server = new Swoole\WebSocket\Server('127.0.0.1', 9501, SWOOLE_BASE);
$server->set(['open_http2_protocol' => true]);
// http && http2
$server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) {
    $response->end('Hello ' . $request->rawcontent());
});
// websocket
$server->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) {
    $server->push($frame->fd, 'Hello ' . $frame->data);
});
// tcp
$tcp_server = $server->listen('127.0.0.1', 9502, SWOOLE_TCP);
$tcp_server->set($tcp_options);
$tcp_server->on('receive', function (Swoole\Server $server, int $fd, int $reactor_id, string $data) {
    $server->send($fd, tcp_pack('Hello ' . tcp_unpack($data)));
});
$server->start();
```

### Coroutine clients

Whether you DNS query or send requests or receive responses, all of these are scheduled by coroutine automatically.

```php
go(function () {
    // http
    $http_client = new Swoole\Coroutine\Http\Client('127.0.0.1', 9501);
    assert($http_client->post('/', 'Swoole Http'));
    var_dump($http_client->body);
    // websocket
    $http_client->upgrade('/');
    $http_client->push('Swoole Websocket');
    var_dump($http_client->recv()->data);
});
go(function () {
    // http2
    $http2_client = new Swoole\Coroutine\Http2\Client('localhost', 9501);
    $http2_client->connect();
    $http2_request = new Swoole\Http2\Request;
    $http2_request->method = 'POST';
    $http2_request->data = 'Swoole Http2';
    $http2_client->send($http2_request);
    $http2_response = $http2_client->recv();
    var_dump($http2_response->data);
});
go(function () use ($tcp_options) {
    // tcp
    $tcp_client = new Swoole\Coroutine\Client(SWOOLE_TCP);
    $tcp_client->set($tcp_options);
    $tcp_client->connect('127.0.0.1', 9502);
    $tcp_client->send(tcp_pack('Swoole Tcp'));
    var_dump(tcp_unpack($tcp_client->recv()));
});
```

### Channel

Channel is the only way for exchanging data between coroutines, the development combination of the `Coroutine + Channel` is the famous CSP programming model.

In Swoole development, Channel is usually used for implementing connection pool or scheduling coroutine concurrent.

#### The simplest example of a connection pool

In the following example, we have a thousand concurrently requests to redis. Normally, this has exceeded the maximum number of Redis connections setting and will throw a connection exception, but the connection pool based on Channel can perfectly schedule requests. We don't have to worry about connection overload.

```php
class RedisPool
{
    /**@var \Swoole\Coroutine\Channel */
    protected $pool;

    /**
     * RedisPool constructor.
     * @param int $size max connections
     */
    public function __construct(int $size = 100)
    {
        $this->pool = new \Swoole\Coroutine\Channel($size);
        for ($i = 0; $i < $size; $i++) {
            $redis = new \Swoole\Coroutine\Redis();
            $res = $redis->connect('127.0.0.1', 6379);
            if ($res == false) {
                throw new \RuntimeException("failed to connect redis server.");
            } else {
                $this->put($redis);
            }
        }
    }

    public function get(): \Swoole\Coroutine\Redis
    {
        return $this->pool->pop();
    }

    public function put(\Swoole\Coroutine\Redis $redis)
    {
        $this->pool->push($redis);
    }

    public function close(): void
    {
        $this->pool->close();
        $this->pool = null;
    }
}

go(function () {
    $pool = new RedisPool();
    // max concurrency num is more than max connections
    // but it's no problem, channel will help you with scheduling
    for ($c = 0; $c < 1000; $c++) {
        go(function () use ($pool, $c) {
            for ($n = 0; $n < 100; $n++) {
                $redis = $pool->get();
                assert($redis->set("awesome-{$c}-{$n}", 'swoole'));
                assert($redis->get("awesome-{$c}-{$n}") === 'swoole');
                assert($redis->delete("awesome-{$c}-{$n}"));
                $pool->put($redis);
            }
        });
    }
});
```

#### Producer and consumers

Some Swoole's clients implement the defer mode for concurrency, but you can still implement it flexible with a combination of coroutines and channels.

```php
go(function () {
    // User: I need you to bring me some information back.
    // Channel: OK! I will be responsible for scheduling.
    $channel = new Swoole\Coroutine\Channel;
    go(function () use ($channel) {
        // Coroutine A: Ok! I will show you the github addr info
        $addr_info = Co::getaddrinfo('github.com');
        $channel->push(['A', json_encode($addr_info, JSON_PRETTY_PRINT)]);
    });
    go(function () use ($channel) {
        // Coroutine B: Ok! I will show you what your code look like
        $mirror = Co::readFile(__FILE__);
        $channel->push(['B', $mirror]);
    });
    go(function () use ($channel) {
        // Coroutine C: Ok! I will show you the date
        $channel->push(['C', date(DATE_W3C)]);
    });
    for ($i = 3; $i--;) {
        list($id, $data) = $channel->pop();
        echo "From {$id}:\n {$data}\n";
    }
    // User: Amazing, I got every information at earliest time!
});
```

### Timer

```php
$id = Swoole\Timer::tick(100, function () {
    echo "⚙️ Do something...\n";
});
Swoole\Timer::after(500, function () use ($id) {
    Swoole\Timer::clear($id);
    echo "⏰ Done\n";
});
Swoole\Timer::after(1000, function () use ($id) {
    if (!Swoole\Timer::exists($id)) {
        echo "✅ All right!\n";
    }
});
```
#### The way of coroutine

```php
go(function () {
    $i = 0;
    while (true) {
        Co::sleep(0.1);
        echo "📝 Do something...\n";
        if (++$i === 5) {
            echo "🛎 Done\n";
            break;
        }
    }
    echo "🎉 All right!\n";
});
```

## 🔥 Amazing runtime hooks

**As of Swoole v4.1.0, we added the ability to transform synchronous PHP network libraries into co-routine libraries using a single line of code.**

Simply call the `Swoole\Runtime::enableCoroutine()` method at the top of your script. In the sample below we connect to php-redis and concurrently read 10k requests in 0.1s:

```php
Swoole\Runtime::enableCoroutine();
$s = microtime(true);
Co\run(function() {
    for ($c = 100; $c--;) {
        go(function () {
            ($redis = new Redis)->connect('127.0.0.1', 6379);
            for ($n = 100; $n--;) {
                assert($redis->get('awesome') === 'swoole');
            }
        });
    }
});
echo 'use ' . (microtime(true) - $s) . ' s';
```

By calling this method, the Swoole kernel replaces ZendVM stream function pointers. If you use `php_stream` based extensions, all socket operations can be dynamically converted to be asynchronous IO scheduled by coroutine at runtime!

### How many things you can do in 1s?

Sleep 10K times, read, write, check and delete files 10K times, use PDO and MySQLi to communicate with the database 10K times, create a TCP server and multiple clients to communicate with each other 10K times, create a UDP server and multiple clients to communicate with each other 10K times... Everything works well in one process!

Just see what the Swoole brings, just imagine...

```
/* Detailed source-code truncated for AI context efficiency. */
```

## ⌛️ Installation

> As with any open source project, Swoole always provides the most reliable stability and the most powerful features in **the latest released version**. Please ensure as much as possible that you are using the latest version.

### Compiling requirements

+ Linux, OS X or Cygwin, WSL
+ PHP 8.2.0 or later (The higher the version, the better the performance.)
+ GCC 4.8 or later

### 1. Install via PECL (beginners)

```shell
pecl install swoole
```

### 2. Install from source (recommended)

Please download the source packages from [Releases](https://github.com/swoole/swoole-src/releases) or clone a specific version. Don't use `master` branch as it may be in development.

To clone the source code from git specify a tag:
```shell
git clone --branch v6.0.0 --single-branch https://github.com/swoole/swoole-src.git && \
cd swoole-src
```

Compile and install at the source folder:

```shell
phpize && \
./configure && \
make && make install
```

#### Enable extension in PHP

After compiling and installing to the system successfully, you have to add a new line `extension=swoole.so` to `php.ini` to enable Swoole extension.

#### Extra compiler configurations

> for example: `./configure --enable-openssl --enable-sockets`

+ `--enable-openssl` or `--with-openssl-dir=DIR`
+ `--enable-sockets`
+ `--enable-mysqlnd` (need mysqlnd, it just for supporting `$mysql->escape` method)
+ `--enable-swoole-curl`

### Upgrade

>  ⚠️ If you upgrade from source, don't forget to `make clean` before you upgrade your swoole

1. `pecl upgrade swoole`
2. `cd swoole-src && git pull && make clean && make && sudo make install`
3. if you change your PHP version, please re-run `phpize clean && phpize` then try to compile

### Major change since version 4.3.0

Async clients and API are moved to a separate PHP extension `swoole_async` since version 4.3.0, install `swoole_async`:

```shell
git clone https://github.com/swoole/ext-async.git
cd ext-async
phpize
./configure
make -j 4
sudo make install
```

Enable it by adding a new line `extension=swoole_async.so` to `php.ini`.

## 🍭 Benchmark

+ On the open source [Techempower Web Framework benchmarks](https://www.techempower.com/benchmarks/#section=data-r17) Swoole used MySQL database benchmark to rank first, and all performance tests ranked in the first echelon.
+ You can just run [Benchmark Script](https://github.com/swoole/benchmark/blob/master/benchmark.php) to quickly test the maximum QPS of Swoole-HTTP-Server on your machine.

## 🔰️ Security issues

Security issues should be reported privately, via email, to the Swoole develop team [team@swoole.com](mailto:team@swoole.com). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message.

## 🖊️ Contribution

Your contribution to Swoole development is very welcome!

You may contribute in the following ways:

* [Report issues and feedback](https://github.com/swoole/swoole-src/issues)
* Submit fixes, features via Pull Request
* Write/polish documentation

## ❤️ Contributors

This project exists thanks to all the people who contribute. [[Contributors](https://github.com/swoole/swoole-src/graphs/contributors)].
[](https://github.com/swoole/swoole-src/graphs/contributors)

## 🎙️ Official Evangelist

[Demin](https://deminy.in) has been playing with PHP since 2000, focusing on building high-performance, secure web services. He is an occasional conference speaker on PHP and Swoole, and has been working for companies in the states like eBay, Visa and Glu Mobile for years. You may find Demin on [Twitter](https://twitter.com/deminy) or [GitHub](https://github.com/deminy).

## 📃 License

Apache License Version 2.0 see http://www.apache.org/licenses/LICENSE-2.0.html

---

## File: docs/CODE-STYLE.md

Code Style
=======
* Google C++ Style
* Indent adjusted to 4 characters
* Line width adjusted to 120 characters

---

## File: docs/ISSUE.md

# Bug reports

## Instruction

If you think you have found a bug in Swoole, please report it.
The Swoole developers probably don't know about it,
and unless you report it, chances are it won't be fixed.
You can report bugs at https://github.com/swoole/swoole-src/issues.
Please do not send bug reports in the mailing list or personal letters.
The issue page is also suitable to submit feature requests.

Please read the **How to report a bug document** before submitting any bug reports.

## New issue

First, while creating an issue, the system will give the following template:

```markdown
Please answer these questions before submitting your issue. Thanks!
1. What did you do? If possible, provide a simple script for reproducing the error.
2. What did you expect to see?
3. What did you see instead?
4. What version of Swoole are you using (`php --ri swoole`)?
5. What is your machine environment used (including the version of kernel & php & gcc)?
```
The most important thing is to provide a simple script for reproducing the error, otherwise, you must provide as much information as possible.

## Memory detection (recommended)

In addition to using `gdb` analysis, you can use the `valgrind` tool to check if the program is working properly.

```shell
USE_ZEND_ALLOC=0 valgrind --log-file=/tmp/valgrind.log php your_file.php
```

* After the program is executed to the wrong location, `ctrl+c` is interrupted, and upload the `/tmp/valgrind.log` file.

## Address Sanitizer
In certain cases, crash issues only occur in complex production environments, not consistently reproducible, and with a very low probability of occurrence. The use of `Valgrind` significantly impacts performance, whereas `Address Sanitizer` allows for memory detection without affecting performance.

We have created a `debug` version of `Swoole`, which can be compiled with the `--enable-debug` and `--enable-address-sanitizer` parameters to enable `Address Sanitizer`. The usage is as follows:

```shell
docker pull phpswoole/swoole:dev-debug
docker run -it --rm phpswoole/swoole:dev-debug /bin/bash
php your_file.php
```

> To use `ASAN`, be sure to disable Address Space Layout Randomization (`ASLR`):

```shell
echo 0 > /proc/sys/kernel/randomize_va_space
```

This docker image can be used directly in a production environment, and `Address Sanitizer` will print error messages and call stack information when memory errors occur. This information can assist developers in quickly pinpointing issues.

## CoreDump

Besides, In a special case, you can use debugging tools to help developers locate problems

```shell
WARNING	Worker::report_error(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=11
```

When a segmentation error occurs with Swoole, You can use the `gdb` tool and use `bt` command.
> Using `gdb` to track the core file need to add the `--enable-debug` parameter when compiling `swoole`.

Enable core dump
```shell
ulimit -c unlimited
```

Use `gdb` to view the `core dump` information. The `core` file is usually in the current directory. If the operating system does the processing, put the `core dump` file in another directory, please replace it with the corresponding path.
```
gdb php core
gdb php /tmp/core.4596
```

Enter bt under gdb to view the call stack information.
```
(gdb) bt
```
Use the f command in gdb to view the code segment corresponding to the ID.
```
(gdb)f 1
(gdb)f 0
```

If there is no function call stack information, it may be that the compiler has removed the debug information. Please manually modify the `Makefile` file in the swoole source directory and modify CFLAGS to

```shell
CFLAGS = -Wall -pthread -g -O0
```

---

## File: docs/sponsors.md

<hr id="title1" />

# [成为 Swoole 的赞助者](#title1) 
---

Swoole 是采用 Apache 许可的开源项目，是完全免费的。

维护这样一个庞大的生态系统和为项目开发新功能所需的巨大努力，只有在我们的赞助者慷慨的财务支持下才得以持续。
捐赠募集到的资金将全部用于开源项目的维护和发展。包括但不限于：
- 向核心贡献者支付报酬
- 服务器租赁费用
- 网站和文档的维护
- 举办线下活动
- 为社区活跃者提供奖励

> 您可以扫码添加微信客服或通过邮件 `service@swoole.com` 与我们联系，沟通赞助事宜

<hr id="title2" />

## [赞助渠道](#title2) 

---
来自世界各地的企业可以通过赞助来支持 `Swoole` 项目的开发。
根据赞助商赞助金额的不同分为顶级赞助商、金牌赞助商，在获得捐赠的第一年内我们将为赞助商做以下品牌展示：

> 顶级赞助商
- 官网、文档、问答社区首屏顶部显著位置展示
- GitHub 仓库首页顶部赞助商标识 
- 官方微信公众号、QQ 群、微博、知乎等官方社交平台赞助商宣传内容推送

> 金牌赞助商
- 官网、文档、问答社区首屏侧边位置展示

## 顶级赞助商


    [](https://crmeb.com/)



## 金牌赞助商

    [](https://www.apipost.cn/)


    [](https://cloud.tencent.com/)


    [](https://www.w7.cc/)


    [](https://www.zentao.net/)


    [](https://zh.51talk.com/)


    [](https://www.easycorp.cn/)


    [](https://www.liuxingedu.com/)


    [](https://guojiang.club/)


## 银牌赞助商

    [](https://shopxo.net/)


    [](https://segmentfault.com/)



    [](https://codecasts.com/)

---

## File: docs/SUPPORTED.md

## Supported Versions

| Branch                                                                  | PHP Version | Initialization | Active Support Until | Security Support Until |
|-------------------------------------------------------------------------|-------------|----------------|----------------------|------------------------|
| [v6.1.x](https://github.com/swoole/swoole-src/tree/6.1)                 | 8.1 - 8.4   | 2025-10-24     | 2026-10-24           | 2027-04-24             |
| [v6.2.x](https://github.com/swoole/swoole-src/tree/6.2)                 | 8.2 - 8.5   | 2026-04-07     | 2027-04-07           | 2027-10-07             |
| [v6.3.x (unreleased)](https://github.com/swoole/swoole-src/tree/master) | 8.2 - 8.5   | Unreleased     | TBD                  | TBD                    |

- **Active support**： A release that is being actively supported. Reported bugs and security issues are fixed and regular point releases are made.
- **Security fixes only**:  A release that is supported for critical security issues only. Releases are only made on an as-needed basis.
- **Standard support period**: A non-LTS branch receives one year of active support followed by six months of security-only support, for a total of 1.5 years.
- **LTS support period**: A branch explicitly designated as LTS receives a total of three years of support. The active and security-only dates are listed when the LTS release is announced.

## PHP Version Support

1. Each branch (`MINOR version`) supports a fixed range of PHP versions. The `RELEASE VERSIONS` for that branch will not increase support for higher PHP versions.
2. The upper limit is four PHP versions; any additional versions will not be supported. For example, version 6.0 only supports PHP 8.1 to 8.4.
3. No support for any DEV or RC stage of PHP

The pace of PHP version updates is rapid, with each version introducing numerous underlying changes. The developers of Swoole have had to invest significant time and effort to support new releases, and we lack sufficient resources to keep up with PHP updates. Therefore, there will be a delay of one MINOR version before supporting new PHP versions.

## Unsupported Branches

> These releases that are no longer supported. Users of this release should upgrade as soon as possible, as they may be exposed to unpatched security vulnerabilities.

 
| Branch                   | PHP Version | Duration                |
|--------------------------|-------------|-------------------------|
| `1.x.x`                  | 5.4 - 7.2   | 2012-07-01 ~ 2018-05-14 |
| `2.x.x`                  | 7.0 - 7.3   | 2016-12-30 ~ 2018-05-23 |
| `4.0.x` ~ `4.3.x`        | 7.0 - 7.4   | 2018-06-14 ~ 2019-12-31 |
| `4.4.x`                  | 7.1 - 7.4   | 2019-04-15 ~ 2022-07-31 |
| `4.5.x`,`4.6.x`, `4.7.x` | 7.1 - 7.4   | 2019-12-20 ~ 2021-12-31 |
| `4.8.x`                  | 7.3 - 8.2   | 2021-10-14 ~ 2024-06-30 |
| `5.0.x`                  | 7.4 - 8.3   | 2022-01-20 ~ 2023-07-20 |
| `5.1.x`                  | 8.0 - 8.3   | 2023-11-29 ~ 2025-04-29 |
| `6.0.x`                  | 8.1 - 8.4   | 2024-12-31 ~ 2026-06-30 |

---

## File: docs/TESTS.md

## Swoole Tests

## Core Tests
Used to test the core code in the `src/` directory, only as a C++ library, not related to php.
The core tests depends on the googletest framework, and googletest needs to be installed.
The core test cases must be written with `C++`.

`GCC/G++` version `8.0` or higher is required, with full support for `C++17`.

### **Build googletest**
Since swoole core unit testing rely on googletest, we need compile googletest at first.

```shell
wget https://github.com/google/googletest/releases/download/v1.15.2/googletest-1.15.2.tar.gz
tar zxf googletest-1.15.2.tar.gz
cd googletest-1.15.2
mkdir ./build && cd ./build
cmake ..
make -j
sudo make install
```

### Build libswoole.so

```shell
cd swoole-src
cmake .
make -j$(nproc) lib-swoole
```
### Build core-tests
```shell
make -j$(nproc) core-tests
```

### Run core-tests
```shell
# run all dependency services
cd core-tests
docker compose up -d
# run all tests
./bin/core-tests
# run some test cases
./bin/core-tests --gtest_filter=server.*
# list all tests
./bin/core_tests --gtest_list_tests
```

## PHP Tests
Used to test the php extension code in the `ext-src/` directory. The swoole php test depends on php environment.
The `php-dev` related components must be installed.

The php test cases must be written with `PHP`.

### Build ext-swoole
```shell
cd swoole-src
phpize
./configure ${options}
make -j$(nproc)
make install
```
Need to configure `php.ini`, add `extension=swoole.so` to enable `ext-swoole`.

### Run tests
```shell
./scripts/route.sh
```

The automated test scripts in this directory can not only run on Github Action CI. Powered by docker container technology, it can run on any systems. You only need to run the `route.sh` script to create containers of multiple PHP environments then it will run Swoole's build tests and unit tests on multiple systems automatically.

### With special branch

```shell
SWOOLE_BRANCH=alpine ./scripts/route.sh
```

### Enter the container

> You can cancel the unit test by `CTRL+C`

```shell
docker exec -it -e LINES=$(tput lines) -e COLUMNS=$(tput cols) swoole /bin/bash
```

---

## File: docs/WORKFLOW-PARAMETERS.md

# Workflow Parameters
Adding parameters in the Git commit log can control the workflow.

## --filter
This parameter specifies which workflows to run, instead of running all of them.
The command format is: `--filter=[flow1][flow2][flow...]` ,
and it supports setting multiple workflows, with names matching the filename of the yml file.

## --valgrind
Setting this parameter will cause the test program to be run with `valgrind`.

```shell
git commit -m "commit message --filter=[core][unit] --valgrind"
```

## --asan
Setting this parameter will cause the test program to be run with `ASAN`.

---

## File: thirdparty/hiredis/README.md

[](https://travis-ci.org/redis/hiredis)

**This Readme reflects the latest changed in the master branch. See [v0.13.3](https://github.com/redis/hiredis/tree/v0.13.3) for the Readme and documentation for the latest release.**

# HIREDIS

Hiredis is a minimalistic C client library for the [Redis](http://redis.io/) database.

It is minimalistic because it just adds minimal support for the protocol, but
at the same time it uses a high level printf-alike API in order to make it
much higher level than otherwise suggested by its minimal code base and the
lack of explicit bindings for every Redis command.

Apart from supporting sending commands and receiving replies, it comes with
a reply parser that is decoupled from the I/O layer. It
is a stream parser designed for easy reusability, which can for instance be used
in higher level language bindings for efficient reply parsing.

Hiredis only supports the binary-safe Redis protocol, so you can use it with any
Redis version >= 1.2.0.

The library comes with multiple APIs. There is the
*synchronous API*, the *asynchronous API* and the *reply parsing API*.

## Upgrading to `1.0.0`

Version 1.0.0 marks a stable release of hiredis.
It includes some minor breaking changes, mostly to make the exposed API more uniform and self-explanatory.
It also bundles the updated `sds` library, to sync up with upstream and Redis.
For most applications a recompile against the new hiredis should be enough.
For code changes see the [Changelog](CHANGELOG.md).

## Upgrading from `<0.9.0`

Version 0.9.0 is a major overhaul of hiredis in every aspect. However, upgrading existing
code using hiredis should not be a big pain. The key thing to keep in mind when
upgrading is that hiredis >= 0.9.0 uses a `redisContext*` to keep state, in contrast to
the stateless 0.0.1 that only has a file descriptor to work with.

## Synchronous API

To consume the synchronous API, there are only a few function calls that need to be introduced:

```c
redisContext *redisConnect(const char *ip, int port);
void *redisCommand(redisContext *c, const char *format, ...);
void freeReplyObject(void *reply);
```

### Connecting

The function `redisConnect` is used to create a so-called `redisContext`. The
context is where Hiredis holds state for a connection. The `redisContext`
struct has an integer `err` field that is non-zero when the connection is in
an error state. The field `errstr` will contain a string with a description of
the error. More information on errors can be found in the **Errors** section.
After trying to connect to Redis using `redisConnect` you should
check the `err` field to see if establishing the connection was successful:
```c
redisContext *c = redisConnect("127.0.0.1", 6379);
if (c == NULL || c->err) {
    if (c) {
        printf("Error: %s\n", c->errstr);
        // handle error
    } else {
        printf("Can't allocate redis context\n");
    }
}
```

*Note: A `redisContext` is not thread-safe.*

### Sending commands

There are several ways to issue commands to Redis. The first that will be introduced is
`redisCommand`. This function takes a format similar to printf. In the simplest form,
it is used like this:
```c
reply = redisCommand(context, "SET foo bar");
```

The specifier `%s` interpolates a string in the command, and uses `strlen` to
determine the length of the string:
```c
reply = redisCommand(context, "SET foo %s", value);
```
When you need to pass binary safe strings in a command, the `%b` specifier can be
used. Together with a pointer to the string, it requires a `size_t` length argument
of the string:
```c
reply = redisCommand(context, "SET foo %b", value, (size_t) valuelen);
```
Internally, Hiredis splits the command in different arguments and will
convert it to the protocol used to communicate with Redis.
One or more spaces separates arguments, so you can use the specifiers
anywhere in an argument:
```c
reply = redisCommand(context, "SET key:%s %s", myid, value);
```

### Using replies

The return value of `redisCommand` holds a reply when the command was
successfully executed. When an error occurs, the return value is `NULL` and
the `err` field in the context will be set (see section on **Errors**).
Once an error is returned the context cannot be reused and you should set up
a new connection.

The standard replies that `redisCommand` are of the type `redisReply`. The
`type` field in the `redisReply` should be used to test what kind of reply
was received:

* **`REDIS_REPLY_STATUS`**:
    * The command replied with a status reply. The status string can be accessed using `reply->str`.
      The length of this string can be accessed using `reply->len`.

* **`REDIS_REPLY_ERROR`**:
    *  The command replied with an error. The error string can be accessed identical to `REDIS_REPLY_STATUS`.

* **`REDIS_REPLY_INTEGER`**:
    * The command replied with an integer. The integer value can be accessed using the
      `reply->integer` field of type `long long`.

* **`REDIS_REPLY_NIL`**:
    * The command replied with a **nil** object. There is no data to access.

* **`REDIS_REPLY_STRING`**:
    * A bulk (string) reply. The value of the reply can be accessed using `reply->str`.
      The length of this string can be accessed using `reply->len`.

* **`REDIS_REPLY_ARRAY`**:
    * A multi bulk reply. The number of elements in the multi bulk reply is stored in
      `reply->elements`. Every element in the multi bulk reply is a `redisReply` object as well
      and can be accessed via `reply->element[..index..]`.
      Redis may reply with nested arrays but this is fully supported.

Replies should be freed using the `freeReplyObject()` function.
Note that this function will take care of freeing sub-reply objects
contained in arrays and nested arrays, so there is no need for the user to
free the sub replies (it is actually harmful and will corrupt the memory).

**Important:** the current version of hiredis (0.10.0) frees replies when the
asynchronous API is used. This means you should not call `freeReplyObject` when
you use this API. The reply is cleaned up by hiredis _after_ the callback
returns. This behavior will probably change in future releases, so make sure to
keep an eye on the changelog when upgrading (see issue #39).

### Cleaning up

To disconnect and free the context the following function can be used:
```c
void redisFree(redisContext *c);
```
This function immediately closes the socket and then frees the allocations done in
creating the context.

### Sending commands (cont'd)

Together with `redisCommand`, the function `redisCommandArgv` can be used to issue commands.
It has the following prototype:
```c
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
```
It takes the number of arguments `argc`, an array of strings `argv` and the lengths of the
arguments `argvlen`. For convenience, `argvlen` may be set to `NULL` and the function will
use `strlen(3)` on every argument to determine its length. Obviously, when any of the arguments
need to be binary safe, the entire array of lengths `argvlen` should be provided.

The return value has the same semantic as `redisCommand`.

### Pipelining

To explain how Hiredis supports pipelining in a blocking connection, there needs to be
understanding of the internal execution flow.

When any of the functions in the `redisCommand` family is called, Hiredis first formats the
command according to the Redis protocol. The formatted command is then put in the output buffer
of the context. This output buffer is dynamic, so it can hold any number of commands.
After the command is put in the output buffer, `redisGetReply` is called. This function has the
following two execution paths:

1. The input buffer is non-empty:
    * Try to parse a single reply from the input buffer and return it
    * If no reply could be parsed, continue at *2*
2. The input buffer is empty:
    * Write the **entire** output buffer to the socket
    * Read from the socket until a single reply could be parsed

The function `redisGetReply` is exported as part of the Hiredis API and can be used when a reply
is expected on the socket. To pipeline commands, the only things that needs to be done is
filling up the output buffer. For this cause, two commands can be used that are identical
to the `redisCommand` family, apart from not returning a reply:
```c
void redisAppendCommand(redisContext *c, const char *format, ...);
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
```
After calling either function one or more times, `redisGetReply` can be used to receive the
subsequent replies. The return value for this function is either `REDIS_OK` or `REDIS_ERR`, where
the latter means an error occurred while reading a reply. Just as with the other commands,
the `err` field in the context can be used to find out what the cause of this error is.

The following examples shows a simple pipeline (resulting in only a single call to `write(2)` and
a single call to `read(2)`):
```c
redisReply *reply;
redisAppendCommand(context,"SET foo bar");
redisAppendCommand(context,"GET foo");
redisGetReply(context,&reply); // reply for SET
freeReplyObject(reply);
redisGetReply(context,&reply); // reply for GET
freeReplyObject(reply);
```
This API can also be used to implement a blocking subscriber:
```c
reply = redisCommand(context,"SUBSCRIBE foo");
freeReplyObject(reply);
while(redisGetReply(context,&reply) == REDIS_OK) {
    // consume message
    freeReplyObject(reply);
}
```
### Errors

When a function call is not successful, depending on the function either `NULL` or `REDIS_ERR` is
returned. The `err` field inside the context will be non-zero and set to one of the
following constants:

* **`REDIS_ERR_IO`**:
    There was an I/O error while creating the connection, trying to write
    to the socket or read from the socket. If you included `errno.h` in your
    application, you can use the global `errno` variable to find out what is
    wrong.

* **`REDIS_ERR_EOF`**:
    The server closed the connection which resulted in an empty read.

* **`REDIS_ERR_PROTOCOL`**:
    There was an error while parsing the protocol.

* **`REDIS_ERR_OTHER`**:
    Any other error. Currently, it is only used when a specified hostname to connect
    to cannot be resolved.

In every case, the `errstr` field in the context will be set to hold a string representation
of the error.

## Asynchronous API

Hiredis comes with an asynchronous API that works easily with any event library.
Examples are bundled that show using Hiredis with [libev](http://software.schmorp.de/pkg/libev.html)
and [libevent](http://monkey.org/~provos/libevent/).

### Connecting

The function `redisAsyncConnect` can be used to establish a non-blocking connection to
Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The `err` field
should be checked after creation to see if there were errors creating the connection.
Because the connection that will be created is non-blocking, the kernel is not able to
instantly return if the specified host and port is able to accept a connection.

*Note: A `redisAsyncContext` is not thread-safe.*

```c
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
    printf("Error: %s\n", c->errstr);
    // handle error
}
```

The asynchronous context can hold a disconnect callback function that is called when the
connection is disconnected (either because of an error or per user request). This function should
have the following prototype:
```c
void(const redisAsyncContext *c, int status);
```
On a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the
user, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `err`
field in the context can be accessed to find out the cause of the error.

The context object is always freed after the disconnect callback fired. When a reconnect is needed,
the disconnect callback is a good point to do so.

Setting the disconnect callback can only be done once per context. For subsequent calls it will
return `REDIS_ERR`. The function to set the disconnect callback has the following prototype:
```c
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
```
### Sending commands and their callbacks

In an asynchronous context, commands are automatically pipelined due to the nature of an event loop.
Therefore, unlike the synchronous API, there is only a single way to send commands.
Because commands are sent to Redis asynchronously, issuing a command requires a callback function
that is called when the reply is received. Reply callbacks should have the following prototype:
```c
void(redisAsyncContext *c, void *reply, void *privdata);
```
The `privdata` argument can be used to curry arbitrary data to the callback from the point where
the command is initially queued for execution.

The functions that can be used to issue commands in an asynchronous context are:
```c
int redisAsyncCommand(
  redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,
  const char *format, ...);
int redisAsyncCommandArgv(
  redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,
  int argc, const char **argv, const size_t *argvlen);
```
Both functions work like their blocking counterparts. The return value is `REDIS_OK` when the command
was successfully added to the output buffer and `REDIS_ERR` otherwise. Example: when the connection
is being disconnected per user-request, no new commands may be added to the output buffer and `REDIS_ERR` is
returned on calls to the `redisAsyncCommand` family.

If the reply for a command with a `NULL` callback is read, it is immediately freed. When the callback
for a command is non-`NULL`, the memory is freed immediately following the callback: the reply is only
valid for the duration of the callback.

All pending callbacks are called with a `NULL` reply when the context encountered an error.

### Disconnecting

An asynchronous connection can be terminated using:
```c
void redisAsyncDisconnect(redisAsyncContext *ac);
```
When this function is called, the connection is **not** immediately terminated. Instead, new
commands are no longer accepted and the connection is only terminated when all pending commands
have been written to the socket, their respective replies have been read and their respective
callbacks have been executed. After this, the disconnection callback is executed with the
`REDIS_OK` status and the context object is freed.

### Hooking it up to event library *X*

There are a few hooks that need to be set on the context object after it is created.
See the `adapters/` directory for bindings to *libev* and *libevent*.

## Reply parsing API

Hiredis comes with a reply parsing API that makes it easy for writing higher
level language bindings.

The reply parsing API consists of the following functions:
```c
redisReader *redisReaderCreate(void);
void redisReaderFree(redisReader *reader);
int redisReaderFeed(redisReader *reader, const char *buf, size_t len);
int redisReaderGetReply(redisReader *reader, void **reply);
```
The same set of functions are used internally by hiredis when creating a
normal Redis context, the above API just exposes it to the user for a direct
usage.

### Usage

The function `redisReaderCreate` creates a `redisReader` structure that holds a
buffer with unparsed data and state for the protocol parser.

Incoming data -- most likely from a socket -- can be placed in the internal
buffer of the `redisReader` using `redisReaderFeed`. This function will make a
copy of the buffer pointed to by `buf` for `len` bytes. This data is parsed
when `redisReaderGetReply` is called. This function returns an integer status
and a reply object (as described above) via `void **reply`. The returned status
can be either `REDIS_OK` or `REDIS_ERR`, where the latter means something went
wrong (either a protocol error, or an out of memory error).

The parser limits the level of nesting for multi bulk payloads to 7. If the
multi bulk nesting level is higher than this, the parser returns an error.

### Customizing replies

The function `redisReaderGetReply` creates `redisReply` and makes the function
argument `reply` point to the created `redisReply` variable. For instance, if
the response of type `REDIS_REPLY_STATUS` then the `str` field of `redisReply`
will hold the status as a vanilla C string. However, the functions that are
responsible for creating instances of the `redisReply` can be customized by
setting the `fn` field on the `redisReader` struct. This should be done
immediately after creating the `redisReader`.

For example, [hiredis-rb](https://github.com/pietern/hiredis-rb/blob/master/ext/hiredis_ext/reader.c)
uses customized reply object functions to create Ruby objects.

### Reader max buffer

Both when using the Reader API directly or when using it indirectly via a
normal Redis context, the redisReader structure uses a buffer in order to
accumulate data from the server.
Usually this buffer is destroyed when it is empty and is larger than 16
KiB in order to avoid wasting memory in unused buffers

However when working with very big payloads destroying the buffer may slow
down performances considerably, so it is possible to modify the max size of
an idle buffer changing the value of the `maxbuf` field of the reader structure
to the desired value. The special value of 0 means that there is no maximum
value for an idle buffer, so the buffer will never get freed.

For instance if you have a normal Redis context you can set the maximum idle
buffer to zero (unlimited) just with:
```c
context->reader->maxbuf = 0;
```
This should be done only in order to maximize performances when working with
large payloads. The context should be set back to `REDIS_READER_MAX_BUF` again
as soon as possible in order to prevent allocation of useless memory.

## AUTHORS

Hiredis was written by Salvatore Sanfilippo (antirez at gmail) and
Pieter Noordhuis (pcnoordhuis at gmail) and is released under the BSD license.  
Hiredis is currently maintained by Matt Stancliff (matt at genges dot com) and
Jan-Erik Rediger (janerik at fnordig dot com)