├── .gitignore ├── LICENSE ├── README.md ├── Swoole.docset ├── ._icon.png ├── ._icon@2x.png ├── Contents │ ├── Info.plist │ └── Resources │ │ ├── Documents │ │ ├── ._css │ │ ├── ._index.html │ │ ├── CONTRIBUTING.html │ │ ├── blog_list.html │ │ ├── case.html │ │ ├── client.html │ │ ├── client_init.html │ │ ├── consts.html │ │ ├── coroutine.html │ │ ├── coroutine12barrier.html │ │ ├── coroutine12channel.html │ │ ├── coroutine12conn_pool.html │ │ ├── coroutine12coroutine.html │ │ ├── coroutine12gdb.html │ │ ├── coroutine12http_server.html │ │ ├── coroutine12multi_call.html │ │ ├── coroutine12notice.html │ │ ├── coroutine12proc_open.html │ │ ├── coroutine12scheduler.html │ │ ├── coroutine12server.html │ │ ├── coroutine12system.html │ │ ├── coroutine12wait_group.html │ │ ├── coroutine12ws_server.html │ │ ├── coroutine_client12client.html │ │ ├── coroutine_client12fastcgi.html │ │ ├── coroutine_client12http2_client.html │ │ ├── coroutine_client12http_client.html │ │ ├── coroutine_client12init.html │ │ ├── coroutine_client12mysql.html │ │ ├── coroutine_client12postgresql.html │ │ ├── coroutine_client12redis.html │ │ ├── coroutine_client12socket.html │ │ ├── css │ │ │ ├── ._Word2Chm.css │ │ │ ├── ._bootstrap.css │ │ │ ├── ._default.css │ │ │ ├── ._noframe.css │ │ │ ├── Word2Chm.css │ │ │ ├── bootstrap.css │ │ │ └── noframe.css │ │ ├── environment.html │ │ ├── event.html │ │ ├── functions.html │ │ ├── getting_started12extension.html │ │ ├── getting_started12notice.html │ │ ├── http_server.html │ │ ├── index.html │ │ ├── learn.html │ │ ├── learn_other.html │ │ ├── library.html │ │ ├── memory12atomic.html │ │ ├── memory12lock.html │ │ ├── memory12table.html │ │ ├── other12alias.html │ │ ├── other12config.html │ │ ├── other12discussion.html │ │ ├── other12donate.html │ │ ├── other12errno.html │ │ ├── other12issue.html │ │ ├── other12signal.html │ │ ├── other12sysctl.html │ │ ├── other12tools.html │ │ ├── process12process.html │ │ ├── process12process_manager.html │ │ ├── process12process_pool.html │ │ ├── question12install.html │ │ ├── question12swoole.html │ │ ├── question12use.html │ │ ├── redis_server.html │ │ ├── runtime.html │ │ ├── server12co_init.html │ │ ├── server12event_class.html │ │ ├── server12events.html │ │ ├── server12init.html │ │ ├── server12methods.html │ │ ├── server12packet_class.html │ │ ├── server12pipemessage_class.html │ │ ├── server12port.html │ │ ├── server12properties.html │ │ ├── server12server_port.html │ │ ├── server12setting.html │ │ ├── server12statusinfo_class.html │ │ ├── server12task_class.html │ │ ├── server12taskresult_class.html │ │ ├── server12tcp_init.html │ │ ├── start12coroutine.html │ │ ├── start12start_http_server.html │ │ ├── start12start_mqtt.html │ │ ├── start12start_server.html │ │ ├── start12start_task.html │ │ ├── start12start_tcp_server.html │ │ ├── start12start_udp_server.html │ │ ├── start12start_ws_server.html │ │ ├── timer.html │ │ ├── version12bc.html │ │ ├── version12log.html │ │ ├── version12supported.html │ │ └── websocket_server.html │ │ ├── docSet.dsidx │ │ └── docset.sql ├── icon.png └── icon@2x.png ├── Swoole4.tgz ├── dash-demo.png ├── demo.gif └── src ├── README.md ├── composer.json ├── composer.lock └── run.php /.gitignore: -------------------------------------------------------------------------------- 1 | src/vendor/ 2 | src/docset.sql 3 | src/html/ 4 | src/md/ 5 | src/result.log 6 | 7 | 8 | /vendor/ 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # swoole-chinese-docset 2 | 3 | Swoole Chinese Docset for Dash 4 | 5 | [generation method](./src/README.md) 6 | 7 | [Swooke Wiki](http://wiki.swoole.com/) 8 | 9 | ![demo](./demo.gif) 10 | 11 | ![dash-demo](./dash-demo.png) 12 | 13 | 14 | ## CHANGELOG 15 | - Added `swoole 4 is supported` [20240901] 16 | 17 | [Kapeli/Dash-User-Contributions](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/Swoole_Chinese) 18 | -------------------------------------------------------------------------------- /Swoole.docset/._icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfstring/swoole-chinese-docset/fcdf5205efdffc1bf46ba415993d980995efb0df/Swoole.docset/._icon.png -------------------------------------------------------------------------------- /Swoole.docset/._icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfstring/swoole-chinese-docset/fcdf5205efdffc1bf46ba415993d980995efb0df/Swoole.docset/._icon@2x.png -------------------------------------------------------------------------------- /Swoole.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dashIndexFilePath 6 | index.htm 7 | CFBundleIdentifier 8 | swoole 9 | CFBundleName 10 | swoole 11 | DocSetPlatformFamily 12 | swoole 13 | isDashDocset 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/._css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfstring/swoole-chinese-docset/fcdf5205efdffc1bf46ba415993d980995efb0df/Swoole.docset/Contents/Resources/Documents/._css -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/._index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfstring/swoole-chinese-docset/fcdf5205efdffc1bf46ba415993d980995efb0df/Swoole.docset/Contents/Resources/Documents/._index.html -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/CONTRIBUTING.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

文档贡献者

17 |

感谢以下为了Swoole文档更加优秀而提出贡献的同学。

18 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |

sdfjklmin

📖

baly2000

📖

zhmm

📖

吴亲库里

📖

✨小透明・宸✨

📖

Lingjie Lin

📖

Arun Fung

📖

jie295053415

📖

huanghui

📖

nhzex

📖

Success

📖

yuntian001

📖

SETSESSION

📖

baicai

📖
46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/blog_list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Swoole系列文章

17 |

方便学习Swoole,此章节整理并汇总Swoole系列文章。 18 |

19 |

PHP & Swoole

20 | 53 | 54 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/case.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

用户与案例

17 |

如果您在使用Swoole,欢迎您将信息提交至此列表,让更多的用户了解Swoole的实际使用场景,以建立更好的Swoole生态。 18 |

19 |

期待您的信息

20 |

swoole-inc/report提交一个issue,包括以下信息:

21 | 132 | 133 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/client_init.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Swoole客户端

17 |

此节包含所有Swoole提供的客户端,包括同步阻塞客户端和协程客户端,Swoole4不再支持异步客户端,相应的需求完全可以用协程客户端代替,参考

18 | 19 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/consts.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

常量

17 |

!> 此处不包含所有常量,如需查看所有常量请访问或安装:ide-helper 18 |

19 |

Swoole

20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
常量作用
SWOOLE_VERSION当前Swoole的版本号,字符串类型,如1.6.0
34 |

35 |

构造方法参数

36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 |
常量作用
SWOOLE_BASE使用Base模式,业务代码在Reactor进程中直接执行
SWOOLE_PROCESS使用进程模式,业务代码在Worker进程中执行
54 |

55 |

Socket 类型

56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 |
常量作用
SWOOLE_SOCK_TCP创建tcp socket
SWOOLE_SOCK_TCP6创建tcp ipv6 socket
SWOOLE_SOCK_UDP创建udp socket
SWOOLE_SOCK_UDP6创建udp ipv6 socket
SWOOLE_SOCK_UNIX_DGRAM创建unix dgram socket
SWOOLE_SOCK_UNIX_STREAM创建unix stream socket
SWOOLE_SOCK_SYNC同步客户端
94 |

95 |

SSL 加密方法

96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 |
常量作用
SWOOLE_SSLv3_METHOD-
SWOOLE_SSLv3_SERVER_METHOD-
SWOOLE_SSLv3_CLIENT_METHOD-
SWOOLE_SSLv23_METHOD(默认加密方法)-
SWOOLE_SSLv23_SERVER_METHOD-
SWOOLE_SSLv23_CLIENT_METHOD-
SWOOLE_TLSv1_METHOD-
SWOOLE_TLSv1_SERVER_METHOD-
SWOOLE_TLSv1_CLIENT_METHOD-
SWOOLE_TLSv1_1_METHOD-
SWOOLE_TLSv1_1_SERVER_METHOD-
SWOOLE_TLSv1_1_CLIENT_METHOD-
SWOOLE_TLSv1_2_METHOD-
SWOOLE_TLSv1_2_SERVER_METHOD-
SWOOLE_TLSv1_2_CLIENT_METHOD-
SWOOLE_DTLSv1_METHOD-
SWOOLE_DTLSv1_SERVER_METHOD-
SWOOLE_DTLSv1_CLIENT_METHOD-
SWOOLE_DTLS_SERVER_METHOD-
SWOOLE_DTLS_CLIENT_METHOD-
186 |

!> SWOOLE_DTLSv1_METHODSWOOLE_DTLSv1_SERVER_METHODSWOOLE_DTLSv1_CLIENT_METHOD已在 Swoole 版本 >= v4.5.0 中移除。 187 |

188 |

SSL 协议

189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 |
常量作用
SWOOLE_SSL_TLSv1-
SWOOLE_SSL_TLSv1_1-
SWOOLE_SSL_TLSv1_2-
SWOOLE_SSL_TLSv1_3-
SWOOLE_SSL_SSLv2-
SWOOLE_SSL_SSLv3-
223 |

!> Swoole版本 >= v4.5.4 可用 224 |

225 |

日志等级

226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 |
常量作用
SWOOLE_LOG_DEBUG调试日志,仅作为内核开发调试使用
SWOOLE_LOG_TRACE跟踪日志,可用于跟踪系统问题,调试日志是经过精心设置的,会携带关键性信息
SWOOLE_LOG_INFO普通信息,仅作为信息展示
SWOOLE_LOG_NOTICE提示信息,系统可能存在某些行为,如重启、关闭
SWOOLE_LOG_WARNING警告信息,系统可能存在某些问题
SWOOLE_LOG_ERROR错误信息,系统发生了某些关键性的错误,需要即时解决
SWOOLE_LOG_NONE相当于关闭日志信息,日志信息不会抛出
264 |

!> SWOOLE_LOG_DEBUGSWOOLE_LOG_TRACE两种日志,必须在编译Swoole扩展时使用--enable-debug-log--enable-trace-log后才可以使用。正常版本中即使设置了log_level = SWOOLE_LOG_TRACE也是无法打印此类日志的。 265 |

266 |

跟踪标签

267 |

线上运行的服务,随时都有大量请求在处理,底层抛出的日志数量非常巨大。可使用trace_flags设置跟踪日志的标签,仅打印部分跟踪日志。trace_flags支持使用|或操作符设置多个跟踪项。

268 |
$serv->set([
269 |     'log_level' => SWOOLE_LOG_TRACE,
270 |     'trace_flags' => SWOOLE_TRACE_SERVER | SWOOLE_TRACE_HTTP2,
271 | ]);
272 |

底层支持以下跟踪项,可使用SWOOLE_TRACE_ALL表示跟踪所有项目:

273 | 293 | 294 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/coroutine.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Coroutine

17 |

本节介绍一些协程基本概念和常见问题,也可以通过 Swoole视频教程 观看。 18 | 从4.0版本开始Swoole提供了完整的协程(Coroutine)+ 通道(Channel)特性,带来全新的CSP编程模型。

19 |
    20 |
  1. 开发者可以无感知的用同步的代码编写方式达到异步IO的效果和性能,避免了传统异步回调所带来的离散的代码逻辑和陷入多层回调中导致代码无法维护
  2. 21 |
  3. 同时由于底层封装了协程,所以对比传统的PHP层协程框架,开发者不需要使用yield关键词来标识一个协程IO操作,所以不再需要对yield的语义进行深入理解以及对每一级的调用都修改为yield,这极大的提高了开发效率
  4. 22 |
  5. 23 |

    提供了各种类型完善的协程客户端,可以满足大部分开发者的需求。 24 |

    25 |

    什么是协程

    26 |

    协程可以简单理解为线程,只不过这个线程是用户态的,不需要操作系统参与,创建销毁和切换的成本非常低,和线程不同的是协程没法利用多核CPU的,想利用多核CPU需要依赖Swoole的多进程模型。 27 |

    28 |

    什么是Channel

    29 |

    Channel可以理解为消息队列,只不过是协程间的消息队列,多个协程通过pushpop操作队列中的生产消息和消费消息,用来发送或者接收数据进行协程之间的通讯。需要注意的是Channel是没法跨进程的,只能一个Swoole进程里的协程间通讯,最典型的应用是连接池并发调用。 30 |

    31 |

    什么是协程容器

    32 |

    使用Coroutine::creatego()方法创建协程(参考别名小节),在创建的协程中才能使用协程API,而协程必须创建在协程容器里面,参考协程容器。 33 |

    34 |

    协程调度

    35 |

    这里将尽量通俗的讲述什么是协程调度,首先每个协程可以简单的理解为一个线程,大家知道多线程是为了提高程序的并发,同样的多协程也是为了提高并发。 36 | 用户的每个请求都会创建一个协程,请求结束后协程结束,如果同时有成千上万的并发请求,某一时刻某个进程内部会存在成千上万的协程,那么CPU资源是有限的,到底执行哪个协程的代码? 37 | 决定到底让CPU执行哪个协程的代码的决断过程就是协程调度Swoole的调度策略又是怎么样的呢?

    38 | 79 |
  6. 80 |
81 |

82 |

注意事项

83 |

在使用 Swoole 编程前应该注意的地方: 84 |

85 |

全局变量

86 |

协程使得原有的异步逻辑同步化,但是在协程间的切换是隐式发生的,所以在协程切换的前后不能保证全局变量以及static变量的一致性。 87 | 在 PHP-FPM 下可以通过全局变量获取到的请求参数,服务器的参数等,在 Swoole 内,无法 通过 $_GET/$_POST/$_REQUEST/$_SESSION/$_COOKIE/$_SERVER等以$_开头的变量获取到任何属性参数。 88 | 可以使用context用协程id做隔离,实现全局变量的隔离。 89 |

90 |

多协程共享TCP连接

91 |

参考

92 | 93 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/coroutine12barrier.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Coroutine\Barrier

17 |

Swoole Library 中底层提供了一个更便捷的协程并发管理工具:Coroutine\Barrier 协程屏障,或者叫协程栅栏。基于 PHP 引用计数和 Coroutine API 实现。 18 | 相比于Coroutine\WaitGroupCoroutine\Barrier使用更简单一些,只需通过参数传递或者闭包的use语法,引入子协程函数上即可。 19 | !> Swoole 版本 >= v4.5.5 时可用。 20 |

21 |

使用示例

22 |
use Swoole\Coroutine\Barrier;
23 | use Swoole\Coroutine\System;
24 | use function Swoole\Coroutine\run;
25 | use Swoole\Coroutine;
26 | run(function () {
27 |     $barrier = Barrier::make();
28 |     $count = 0;
29 |     $N = 4;
30 |     foreach (range(1, $N) as $i) {
31 |         Coroutine::create(function () use ($barrier, &$count) {
32 |             System::sleep(0.5);
33 |             $count++;
34 |         });
35 |     }
36 |     Barrier::wait($barrier);
37 | 
38 |     assert($count == $N);
39 | });
40 |

41 |

执行流程

42 | 50 | 51 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/coroutine12channel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Coroutine\Channel

17 |
18 |

建议先查看概览,了解一些协程基本概念后再看此节。 19 | 通道,用于协程间通讯,支持多生产者协程和多消费者协程。底层自动实现了协程的切换和调度。 20 |

21 |

实现原理

22 | 121 |
122 |
- `consumer_num` 消费者数量,表示当前通道为空,有`N`个协程正在等待其他协程调用`push`方法生产数据
123 | - `producer_num` 生产者数量,表示当前通道已满,有`N`个协程正在等待其他协程调用`pop`方法消费数据
124 | - `queue_num` 通道中的元素数量
125 |
array(
126 |   "consumer_num" => 0,
127 |   "producer_num" => 1,
128 |   "queue_num" => 10
129 | );
130 |

131 |

close()

132 |

关闭通道。并唤醒所有等待读写的协程。

133 |
Swoole\Coroutine\Channel->close(): bool
134 |

!> 唤醒所有生产者协程,push方法返回false;唤醒所有消费者协程,pop方法返回false 135 |

136 |

length()

137 |

获取通道中的元素数量。

138 |
Swoole\Coroutine\Channel->length(): int
139 |

140 |

isEmpty()

141 |

判断当前通道是否为空。

142 |
Swoole\Coroutine\Channel->isEmpty(): bool
143 |

144 |

isFull()

145 |

判断当前通道是否已满。

146 |
Swoole\Coroutine\Channel->isFull(): bool
147 |

148 |

属性

149 |

150 |

capacity

151 |

通道缓冲区容量。 152 | 构造函数中设定的容量会保存在此,不过如果设定的容量小于1则此变量会等于1

153 |
Swoole\Coroutine\Channel->capacity: int
154 |

155 |

errCode

156 |

获取错误码。

157 |
Swoole\Coroutine\Channel->errCode: int
158 | 189 | 190 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/coroutine12conn_pool.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

连接池

17 |

Swoole 从v4.4.13版本开始提供了内置协程连接池,本章节会说明如何使用对应的连接池。 18 |

19 |

ConnectionPool

20 |

ConnectionPool,原始连接池,基于Channel自动调度,支持传入任意构造器(callable),构造器需返回一个连接对象

21 | 198 | 199 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/coroutine12gdb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

调试协程

17 |

使用Swoole协程时,可以使用下面的方法进行调试 18 |

19 |

GDB调试

20 |

21 |

进入 gdb

22 |
gdb php test.php
23 |

24 |

gdbinit

25 |
(gdb) source /path/to/swoole-src/gdbinit
26 |

27 |

设置断点

28 |

例如 co::sleep 函数

29 |
(gdb) b zim_swoole_coroutine_util_sleep
30 |

31 |

打印当前进程的所有协程和状态

32 |
(gdb) co_list 
33 | coroutine 1 SW_CORO_YIELD
34 | coroutine 2 SW_CORO_RUNNING
35 |

36 |

打印当前运行时协程的调用栈

37 |
(gdb) co_bt 
38 | coroutine cid:[2]
39 | [0x7ffff148a100] Swoole\Coroutine->sleep(0.500000) [internal function]
40 | [0x7ffff148a0a0] {closure}() /home/shiguangqi/php/swoole-src/examples/coroutine/exception/test.php:7 
41 | [0x7ffff141e0c0] go(object[0x7ffff141e110]) [internal function]
42 | [0x7ffff141e030] (main) /home/shiguangqi/php/swoole-src/examples/coroutine/exception/test.php:10
43 |

44 |

打印指定协程id的调用栈

45 |
(gdb) co_bt 1
46 | [0x7ffff1487100] Swoole\Coroutine->sleep(0.500000) [internal function]
47 | [0x7ffff14870a0] {closure}() /home/shiguangqi/php/swoole-src/examples/coroutine/exception/test.php:3 
48 | [0x7ffff141e0c0] go(object[0x7ffff141e110]) [internal function]
49 | [0x7ffff141e030] (main) /home/shiguangqi/php/swoole-src/examples/coroutine/exception/test.php:10 
50 |

51 |

打印全局协程的状态

52 |
(gdb) co_status 
53 |      stack_size: 2097152
54 |      call_stack_size: 1
55 |      active: 1
56 |      coro_num: 2
57 |      max_coro_num: 3000
58 |      peak_coro_num: 2
59 |

60 |

PHP代码调试

61 |

遍历当前进程内的所有协程,并打印调用栈。

62 |
Swoole\Coroutine::listCoroutines(): Swoole\Coroitine\Iterator
63 |

!> 需要4.1.0或更高版本

64 | 74 | 75 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/coroutine12http_server.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

HTTP服务器

17 |

?> 完全协程化的HTTP服务器实现,Co\Http\Server由于HTTP解析性能原因使用C++编写,因此并非由PHP编写的Co\Server的子类。 18 | 与 Http\Server 的不同之处:

19 | 148 | 149 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/coroutine12multi_call.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

并发调用

17 |

[//]: # ( 18 | 此处删除了setDefer特性,因为支持setDefer的客户端都推荐用一键协程化了。 19 | ) 20 | 使用子协程(go)+通道(channel)实现并发请求。 21 | !>建议先看概览,了解协程基本概念再看此节。 22 |

23 |

实现原理

24 | 72 | 73 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/coroutine12proc_open.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

协程进程管理

17 |

由于在协程空间内fork进程会带着其他协程上下文,因此底层禁止了在Coroutine中使用Process模块。可以使用

18 | 54 | 55 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/coroutine12scheduler.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Coroutine\Scheduler

17 |

?> 所有的协程必须在协程容器里面创建Swoole程序启动的时候大部分情况会自动创建协程容器,用Swoole启动程序的方式一共有三种:

18 | 176 | 177 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/coroutine12wait_group.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Coroutine\WaitGroup

17 |

Swoole4中可以使用Channel实现协程间的通信、依赖管理、协程同步。基于Channel可以很容易地实现Golangsync.WaitGroup功能。 18 |

19 |

实现代码

20 |
21 |

此功能是使用PHP编写的功能,并不是C/C++代码,实现源代码在 Library 当中

22 | 77 |
78 | 79 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/coroutine12ws_server.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

WebSocket服务器

17 |

?> 完全协程化的WebSocket服务器实现,继承自Coroutine\Http\Server,底层提供了对WebSocket协议的支持,在此不再赘述,只说差异。 18 | !> 此章节在v4.4.13后可用。 19 |

20 |

完整示例

21 |
use Swoole\Http\Request;
 22 | use Swoole\Http\Response;
 23 | use Swoole\WebSocket\CloseFrame;
 24 | use Swoole\Coroutine\Http\Server;
 25 | use function Swoole\Coroutine\run;
 26 | run(function () {
 27 |     $server = new Server('127.0.0.1', 9502, false);
 28 |     $server->handle('/websocket', function (Request $request, Response $ws) {
 29 |         $ws->upgrade();
 30 |         while (true) {
 31 |             $frame = $ws->recv();
 32 |             if ($frame === '') {
 33 |                 $ws->close();
 34 |                 break;
 35 |             } else if ($frame === false) {
 36 |                 echo 'errorCode: ' . swoole_last_error() . "\n";
 37 |                 $ws->close();
 38 |                 break;
 39 |             } else {
 40 |                 if ($frame->data == 'close' || get_class($frame) === CloseFrame::class) {
 41 |                     $ws->close();
 42 |                     break;
 43 |                 }
 44 |                 $ws->push("Hello {$frame->data}!");
 45 |                 $ws->push("How are you, {$frame->data}?");
 46 |             }
 47 |         }
 48 |     });
 49 |     $server->handle('/', function (Request $request, Response $response) {
 50 |         $response->end(<<<HTML
 51 |     <h1>Swoole WebSocket Server</h1>
 52 |     <script>
 53 | var wsServer = 'ws://127.0.0.1:9502/websocket';
 54 | var websocket = new WebSocket(wsServer);
 55 | websocket.onopen = function (evt) {
 56 |     console.log("Connected to WebSocket server.");
 57 |     websocket.send('hello');
 58 | };
 59 | websocket.onclose = function (evt) {
 60 |     console.log("Disconnected");
 61 | };
 62 | websocket.onmessage = function (evt) {
 63 |     console.log('Retrieved data from server: ' + evt.data);
 64 | };
 65 | websocket.onerror = function (evt, e) {
 66 |     console.log('Error occured: ' + evt.data);
 67 | };
 68 | </script>
 69 | HTML
 70 |         );
 71 |     });
 72 |     $server->start();
 73 | });
74 |

75 |

群发示例

76 |
use Swoole\Http\Request;
 77 | use Swoole\Http\Response;
 78 | use Swoole\WebSocket\CloseFrame;
 79 | use Swoole\Coroutine\Http\Server;
 80 | use function Swoole\Coroutine\run;
 81 | run(function () {
 82 |     $server = new Server('127.0.0.1', 9502, false);
 83 |     $server->handle('/websocket', function (Request $request, Response $ws) {
 84 |         $ws->upgrade();
 85 |         global $wsObjects;
 86 |         $objectId = spl_object_id($ws);
 87 |         $wsObjects[$objectId] = $ws;
 88 |         while (true) {
 89 |             $frame = $ws->recv();
 90 |             if ($frame === '') {
 91 |                 unset($wsObjects[$objectId]);
 92 |                 $ws->close();
 93 |                 break;
 94 |             } else if ($frame === false) {
 95 |                 echo 'errorCode: ' . swoole_last_error() . "\n";
 96 |                 $ws->close();
 97 |                 break;
 98 |             } else {
 99 |                 if ($frame->data == 'close' || get_class($frame) === CloseFrame::class) {
100 |                     unset($wsObjects[$objectId]);
101 |                     $ws->close();
102 |                     break;
103 |                 }
104 |                 foreach ($wsObjects as $obj) {
105 |                     $obj->push("Server:{$frame->data}");
106 |                 }
107 |             }
108 |         }
109 |     });
110 |     $server->start();
111 | });
112 |

113 |

处理流程

114 | 173 | 174 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/coroutine_client12init.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

协程客户端

17 |

下列协程客户端是Swoole内置的类,其中标有 ⚠️ 标志的不推荐再继续使用,可以使用PHP原生的函数+一键协程化

18 | 82 | 83 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/css/._Word2Chm.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfstring/swoole-chinese-docset/fcdf5205efdffc1bf46ba415993d980995efb0df/Swoole.docset/Contents/Resources/Documents/css/._Word2Chm.css -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/css/._bootstrap.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfstring/swoole-chinese-docset/fcdf5205efdffc1bf46ba415993d980995efb0df/Swoole.docset/Contents/Resources/Documents/css/._bootstrap.css -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/css/._default.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfstring/swoole-chinese-docset/fcdf5205efdffc1bf46ba415993d980995efb0df/Swoole.docset/Contents/Resources/Documents/css/._default.css -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/css/._noframe.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfstring/swoole-chinese-docset/fcdf5205efdffc1bf46ba415993d980995efb0df/Swoole.docset/Contents/Resources/Documents/css/._noframe.css -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/css/Word2Chm.css: -------------------------------------------------------------------------------- 1 | body, td, div, li, ul { 2 | line-height: 15pt; 3 | font-size: 9pt; 4 | } 5 | 6 | 7 | td.moHeader { 8 | border-style: groove; 9 | border-width: 2px; 10 | border-color: #0066FF; 11 | font-size: 10.5pt; 12 | font-weight: bold; 13 | height: 32px 14 | } 15 | 16 | .moList { 17 | line-height: 18px; 18 | display: block; 19 | margin-top: 8px; 20 | margin-bottom: 8px; 21 | margin-left: 24px; 22 | } 23 | 24 | .moCopyright { 25 | font-family: Verdana, Arial, Helvetica, sans-serif; 26 | color: #6F6F6F 27 | } 28 | 29 | a.moLink, a:link.moLink, a:visited.moLink { 30 | font-size: 9pt; 31 | line-height: 18px; 32 | text-decoration: none; 33 | color: #0066FF; 34 | border-bottom-style: dashed; 35 | border-bottom-width: 1px; 36 | border-bottom-color: #BDBDBD; 37 | padding-bottom: 1px; 38 | } 39 | 40 | a:hover.moLink { 41 | font-size: 9pt; 42 | line-height: 18px; 43 | border-bottom-style: solid; 44 | border-bottom-width: 1px; 45 | border-bottom-color: #0066FF; 46 | } 47 | 48 | hr.moHR { 49 | border-top: 1px solid #D0D0D0; 50 | border-left: 1px solid #FFFFFF; 51 | border-right: 1px solid #FFFFFF; 52 | border-bottom: 1px solid #FFFFFF; 53 | } 54 | 55 | .moTitle { 56 | color: #0066FF; 57 | font-size: 10.5pt; 58 | font-weight: bold; 59 | line-height: 18px; 60 | } 61 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/css/noframe.css: -------------------------------------------------------------------------------- 1 | .wiki_main { 2 | margin: 0 auto; 3 | margin-top: 50px; 4 | width: 1200px; 5 | } 6 | .wiki_tree { 7 | width: 320px; 8 | margin: 12px; 9 | float: left; 10 | border: 1px solid #eee; 11 | background-color: #eee; 12 | padding: 12px; 13 | border-radius: 4px; 14 | box-shadow: rgba(100,100,100,1) 0px 0px 6px; 15 | } 16 | .wiki_tree li a{ 17 | padding: 5px; 18 | color: #333; 19 | } 20 | .wiki_tree li a:hover, .wiki_tree li a:focus { 21 | color: #693; 22 | border-bottom-color:#693; 23 | } 24 | 25 | #wiki_node_active a { 26 | color: #693; 27 | border-bottom-color:#693; 28 | } 29 | 30 | .wiki_content { 31 | width: 855px; 32 | margin-top: 13px; 33 | padding: 25px; 34 | float: left; 35 | border: 1px solid #fff; 36 | border-radius: 4px; 37 | box-shadow: rgba(100,100,100,1) 0px 0px 6px; 38 | } 39 | .li3 { 40 | margin-left: 16px; 41 | list-style-type: disc; 42 | } 43 | .li_active { 44 | background-color: #ddd; 45 | } 46 | .li2 { 47 | margin-left: 16px; 48 | list-style: inherit; 49 | } -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/getting_started12extension.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

扩展冲突

17 |

由于某些跟踪调试的PHP扩展大量使用了全局变量,可能会导致Swoole协程发生崩溃。请关闭以下相关扩展:

18 | 31 | 32 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/index.html: -------------------------------------------------------------------------------- 1 |

Swoole

Swoole 是一个使用 C++ 语言编写的基于异步事件驱动和协程的并行网络通信引擎,为 PHP 提供协程高性能网络编程支持。提供了多种通信协议的网络服务器和客户端模块,可以方便快速的实现 TCP/UDP服务高性能WebWebSocket服务物联网实时通讯游戏微服务等,使 PHP 不再局限于传统的 Web 领域。

Swoole 类图

可以直接点击链接到对应的文档页

2 | 3 |

官方网站

项目地址

开发工具

版权信息

本文档原始内容摘自之前的 旧版 Swoole 文档,旨在解决大家一直吐槽的文档问题,采用现代化的文档组织形式,只包含 Swoole4 的内容,修改了大量老文档中错误的内容,优化了文档细节,增加了示例代码和一些教学内容,对 Swoole 新手更友好。

本文档所有内容,包括所有文字、图片和音视频资料,版权均属 识沃网络科技有限公司 所有,任何媒体、网站或个人可以以外链的形式引用,但未经协议授权不得以任何形式复制发布 / 发表。

文档发起者

问题反馈

关于本文档中的内容问题(如错别字、示例错误、内容缺失等)以及需求建议,请统一至 swoole-inc/report 项目中提交 issue

一经采纳,将会添加提交者信息至 文档贡献者 列表当中以示感谢。

文档原则

使用直白的语言,尽量少介绍 Swoole 底层技术细节和一些底层的概念,底层的后续可以维护一个专门的 hack 章节;

有些概念绕不过去的时候,必须有一个集中的地方介绍此概念,其他地方内链过去。例如:事件循环

写文档时要转变思维,以小白的角度去审视别人能不能看得懂;

后续出现功能改动的时候一定要把所有涉及的地方都修改一遍,不能只修改一个地方;

每个功能模块必须要有一个完整示例;


4 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/learn_other.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

其他知识

17 |

18 |

设置DNS解析超时和重试

19 |

网络编程中经常使用gethostbynamegetaddrinfo来实现域名解析,这两个C函数并未提供超时参数。实际上可以修改/etc/resolv.conf来设置超时和重试逻辑。 20 | !> 可参考man resolv.conf文档 21 |

22 |

多个 NameServer

23 |
nameserver 192.168.1.3
24 | nameserver 192.168.1.5
25 | option rotate
26 |

可配置多个nameserver,底层会自动轮询,在第一个nameserver查询失败时会自动切换为第二个nameserver进行重试。 27 | option rotate配置的作用是,进行nameserver负载均衡,使用轮询模式。 28 |

29 |

超时控制

30 |
option timeout:1 attempts:2
31 | 63 | 64 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/library.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Library

17 |

Swoole 在 v4 版本后内置了 Library 模块,使用 PHP 代码编写内核功能,使得底层设施更加稳定可靠 18 | !> 该模块也可通过 composer 单独安装,单独安装使用时需要通过php.ini配置swoole.enable_library=Off关闭扩展内置的 library 19 | 目前提供了以下工具组件:

20 | 37 | 38 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/memory12lock.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

进程/线程间锁 Lock

17 | 161 | 162 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/other12alias.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

函数别名汇总

17 |

18 |

协程短名称

19 |

简化协程相关API的名称书写。可修改php.ini设置swoole.use_shortname=On/Off来开启/关闭短名,默认为开启。 20 | 所有的 Swoole\Coroutine 前缀的类名映射为Co。此外还有下面的一些映射: 21 |

22 |

创建协程

23 |
//Swoole\Coroutine::create等价于go函数
 24 | go(function () {
 25 |     Co::sleep(0.5);
 26 |     echo 'hello';
 27 | });
 28 | go('test');
 29 | go([$object, 'method']);
30 |

31 |

通道操作

32 |
//Coroutine\Channel可以简写为chan
 33 | $c = new chan(1);
 34 | $c->push($data);
 35 | $c->pop();
36 |

37 |

延迟执行

38 |
//Swoole\Coroutine::defer可以直接用defer
 39 | defer(function () use ($db) {
 40 |     $db->close();
 41 | });
42 |

43 |

短名称方法

44 |

!> 以下这种方式中godefer,Swoole 版本 >= v4.6.3 可用

45 |
use function Swoole\Coroutine\go;
 46 | use function Swoole\Coroutine\run;
 47 | use function Swoole\Coroutine\defer;
 48 | run(function () {
 49 |     defer(function () {
 50 |         echo "co1 end\n";
 51 |     });
 52 |     sleep(1);
 53 |     go(function () {
 54 |         usleep(100000);
 55 |         defer(function () {
 56 |             echo "co2 end\n";
 57 |         });
 58 |         echo "co2\n";
 59 |     });
 60 |     echo "co1\n";
 61 | });
62 |

63 |

协程System API

64 |

4.4.4版本中系统操作相关的协程APISwoole\Coroutine类中,迁移到了Swoole\Coroutine\System类中。独立为一个新模块。为了向下兼容,底层依然保留了在Coroutine类之上的别名方法。

65 | 202 | 203 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/other12config.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

ini配置

17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
配置默认值作用
swoole.enable_coroutineOnOn, Off 开关内置协程,详见
swoole.display_errorsOn开启/关闭Swoole错误信息。
swoole.unixsock_buffer_size8M设置进程间通信的Socket缓存区尺寸,等价于socket_buffer_size
swoole.use_shortnameOn是否启用短别名,详见
swoole.enable_preemptive_schedulerOff可防止某些协程死循环占用CPU时间过长(10ms的CPU时间)导致其它协程得不到调度示例
swoole.enable_libraryOn开启/关闭扩展内置的library
58 | 59 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/other12discussion.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

线上交流

17 |

18 |

问题反馈

19 |

最快响应:GitHub Issue区 (在此提问交流请尊重问题模板和GitHub社区规则) 20 | 查看如何提交错误报告 21 |

22 |

问答

23 |

有问题可以先去问答系统:wenda.swoole.com 中搜索相似问题,如得不到解决请统一在问答系统当中发帖询问。 24 | 发帖后会经过人工审核,有新的回复时,右上角我的问答中会有所提示。 25 | 为了确保问有所答,由Swoole开发组进行轮值策略,每人负责一天,专职来解决问答中的问题。 26 |

27 |

QQ群

28 | 46 | 47 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/other12issue.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

提交错误报告

17 |

18 |

需知

19 |

当你觉得发现了一个Swoole内核的BUG时,请提出报告。Swoole的内核开发者们或许还不知道问题的存在,除非你主动提出报告,否则BUG也许将很难被发现并修复,你可以在 GitHub的issue区 提出错误报告(即点击右上角绿色的New issue按钮),这里的错误报告将会被最优先解决。 20 | 请不要在邮件列表或私人信件中发送错误报告,GitHub的issue区同样可以提出对于Swoole的任何要求与建议。 21 | 在你提交错误报告之前,请先阅读以下的如何提交错误报告。 22 |

23 |

新建问题

24 |

首先在创建issue的同时,系统将会给出如下模板,请你认真填写它,否则issue由于缺乏信息可能会被忽略:

25 |
Please answer these questions before submitting your issue. Thanks!
26 | > 在提交Issue前请回答以下问题:
27 | 
28 | 1. What did you do? If possible,provide a simple script for reproducing the error.
29 | > 请详细描述问题的产生过程,贴出相关的代码,最好能提供一份可稳定重现的简单脚本代码。
30 | 2. What did you expect to see?
31 | > 期望的结果是什么?
32 | 3. What did you see instead?
33 | > 实际运行的结果是什么?
34 | 4. What version of Swoole are you using (`php --ri swoole`)?
35 | > 你的版本? 贴出 `php --ri swoole` 所打印的内容 
36 | 5. What is your machine environment used (including the version of kernel & php & gcc)?
37 | > 你使用的机器系统环境是什么(包括内核、PHP、gcc编译器版本信息)?   
38 | > 可以使用`uname -a`,`php -v`,`gcc -v` 命令打印
39 |

其中,最为关键的是提供可稳定重现的简单脚本代码,否则你必须提供尽可能多的其它信息来帮助开发者判断错误原因 40 |

41 |

内存分析 (强烈推荐)

42 |

更多时候,Valgrind比gdb更能发现内存问题,通过以下指令运行你的程序,直到触发BUG

43 |
USE_ZEND_ALLOC=0 valgrind --log-file=/tmp/valgrind.log php your_file.php
44 | 67 | 68 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/other12sysctl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

内核参数调整

17 |

18 |

ulimit 设置

19 |

ulimit -n 要调整为 100000 甚至更大。 命令行下执行 ulimit -n 100000 即可修改。如果不能修改,需要设置 /etc/security/limits.conf,加入

20 |
* soft nofile 262140
21 | * hard nofile 262140
22 | root soft nofile 262140
23 | root hard nofile 262140
24 | * soft core unlimited
25 | * hard core unlimited
26 | root soft core unlimited
27 | root hard core unlimited
28 |

注意,修改limits.conf文件后,需要重启系统生效 29 |

30 |

内核设置

31 |

Linux操作系统修改内核参数有 3 种方式:

32 | 98 | 99 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/process12process_manager.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Process\Manager

17 |

进程管理器,基于Process\Pool实现。可以管理多个进程。相比与Process\Pool,可以非常方便的创建多个执行不同任务的进程,并且可以控制每一个进程是否要处于协程环境。 18 |

19 |

版本支持情况

20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
版本号类名更新说明
v4.5.3Swoole\Process\ProcessManager-
v4.5.5Swoole\Process\Manager重命名,ProcessManager 为 Manager 的别名
41 |

!> 在v4.5.3以上的版本可用。 42 |

43 |

使用示例

44 |
use Swoole\Process\Manager;
 45 | use Swoole\Process\Pool;
 46 | $pm = new Manager();
 47 | for ($i = 0; $i < 2; $i++) {
 48 |     $pm->add(function (Pool $pool, int $workerId) {
 49 |     });
 50 | }
 51 | $pm->start();
52 |

53 |

方法

54 |

55 |

__construct()

56 |

构造方法。

57 |
Swoole\Process\Manager::__construct(int $ipcType = SWOOLE_IPC_NONE, int $msgQueueKey = 0);
58 | 139 | 140 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/question12swoole.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Swoole 项目起源和名字由来

17 |

!> 本页面由 Swoole 开源项目创始人 Rango 编写,仅代表其个人观点。 18 |

19 |

项目起源

20 |

Swoole 项目最初的想法是来自于之前所做的一个企业软件项目。当时大概是2010年底,公司产品有一个需求是用户可以任意生成一个 email 地址,然后其他用户可以向这个email发邮件,后台能实时将邮件内容解析成数据,并主动通知用户。当时项目使用PHP开发的,在实现这个需求时遇到了难题,PHP只能依赖其他的SMTP服务器,通过pop3协议定时查收新邮件来完成,这样就不是实时的。如果要实现的实时系统必须自己写一个TCP Socket Server实现SMTP协议接收数据。当时PHP在这个领域几乎是空白,没有一套成熟的网络通信框架。为了实现需求,我从socket学起到TCP/IP、IO复用、libevent、多进程,最后终于实现了这套程序。做完这个项目后我就想把这套程序开源出来,希望能帮助其他PHPer解决在这个领域的难题。如果能有这样一个框架,那么PHP就能从单纯地做一个Web网站延伸到更大的空间。 21 |

22 |

性能问题

23 |

还有一个重要的原因是PHP程序的性能问题,我最早是学Java出身的,工作后才转行成为一名PHP程序员。在使用PHP开发程序的过程中,我一直在思考的问题 PHP 和 Java 比最大的优势是什么?简单高效, PHP 在请求完成之后会释放所有资源和内存,无须担心内存泄漏。代码的质量无论高低一样运行的很流畅。但同时这也是 PHP 致命的缺点。一旦请求数量上升,并发很高的时候,快速创建资源,又马上释放,使得 PHP 程序运行效率急剧下降。另外一旦项目的功能的越来越复杂,代码增多后,对于 PHP 也会是灾难。这也是 PHP 的框架为什么没有被 PHP 程序员广泛接受,而 Java 不存在这个问题。再好的框架也会被这种低效的方式拖累,导致系统变慢。所以想到了使用 PHP 来开发 PHP 的应用服务器,让 PHP 的代码加载到内存后,拥有更长的生命周期,这样建立的数据库连接和其他大的对象,不被释放。每次请求只需要处理很少的代码,而这些代码只在第一次运行时,被 PHP 解析器编译,驻留内存。另外,之前 PHP 不能实现的,对象持久化、数据库连接池,缓存连接池都可以实现。系统的运行效率会大大提高。 24 | 经过一段时间研究,目前已经初步得到实现。使用 PHP 本身编写出 HTTP 服务器,以独立服务器方式运行,单个程序页面 ( 有对象生成,数据库连接、 smarty 模板操作 ) 的执行时间由原来的 0.0x 秒,下降到 0.00x 秒。使用 Apache AB 并发 100 测试。比传统 LAMP 方式, Request per Second 高出至少 10 倍。在我的测试机上 (Ubuntu10.04 Inter Core E5300 + 2G 内存 ) , Apache 只跑到 83RPS 。 Swoole Server 可以跑到 1150 多 RPS。 25 | 这个项目就是Swoole的雏形。这个版本一直持续维护了2年多,在这个过程中逐步有了一些经验积累,对这套技术方案的存在问题有了更深入的理解,比如性能差、限制较多无法直接调用操作系统接口、内存管理效率低下。 26 |

27 |

入职腾讯

28 |

2011年底我入职腾讯,负责朋友网的PHP平台开发工作。惊奇地发现朋友网的同事不光这样想了,他们直接做到了。朋友网团队已经在生产环境中使用了这套方案。朋友网有三架马车,第一个是PWS,这是一个纯PHP编写的WebServer,朋友网线上有600多台服务器运行在PWS上,完全没有使用Apache、PHP-FPM之类的程序。第二个是SAPS,这是使用纯PHP开发的一个分布式队列,当时大概由150台服务器的集群在跑,很多图片裁剪、头像处理、消息同时、数据同步等逻辑全部使用了SAPS做逻辑异步化。第三个是PSF,这是一个PHP实现的Server框架,朋友网很多逻辑层的服务器都是基于PSF实现的。大概有300台左右的集群在运行PSF服务器程序。在朋友网的这段时间,我学到了很多Linux底层、网络通信的知识,积累了很多大型集群高并发环境的网络通信跟踪、调试经验,为开发Swoole打下了一个很好的基础。 29 |

30 |

开发Swoole

31 |

在这期间也学习了解到了Node.js、Golang这些优秀的技术方案,得到了更多灵感。在2012年的时候就有了新的想法,决定使用C语言重新实现一个性能更强、功能更强大的版本。这就是现在的Swoole扩展。 32 | 现在Swoole已经被很多PHP技术团队用于实际项目的开发工作,国内国外都有。国内知名的有百度订单中心、百度地图、腾讯QQ公众号和企业QQ、战旗直播、360、当当网、穷游等。另外还有很多物联网、硬件、游戏项目也在使用Swoole 。另外基于Swoole的开源框架也越来越多,比如TSF、Blink、swPromise 等等,在GitHub上也能找到很多Swoole相关的项目和代码。 33 |

34 |

名字由来

35 |

Swoole这个名字不是一个英文单词,是由我创造的一个音近字。我最早想到的名字是叫做sword-server,寓意是为广大PHPer创造一把锋利的剑,后来联想到google也是凭空创造出来的,所以我就给它命名为swoole

36 | 37 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/server12co_init.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

服务端(协程风格)

17 |

Swoole\Coroutine\Server异步风格 的服务端不同之处在于,Swoole\Coroutine\Server 是完全协程化实现的服务器,参考 完整例子

18 |

19 |

优点:

20 | 50 | 51 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/server12event_class.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Swoole\Server\Event

17 |

这里是对Swoole\Server\Event的详细介绍。 18 |

19 |

属性

20 |

21 |

$reactor_id

22 |

返回所在的Reactor线程id,该属性是一个int类型的整数。

23 |
Swoole\Server\Event->reactor_id
24 |

25 |

$fd

26 |

返回该连接的文件描述符fd,该属性是一个int类型的整数。

27 |
Swoole\Server\Event->fd
28 |

29 |

$dispatch_time

30 |

返回该请求数据到达时间dispatch_time,该属性是一个double类型。只有在onReceive事件里该属性才不为0

31 |
Swoole\Server\Event->dispatch_time
32 |

33 |

$data

34 |

返回该客户端发送的数据data,该属性是一个string类型的字符串。只有在onReceive事件里该属性不为null

35 |
Swoole\Server\Event->data
36 | 37 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/server12init.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

服务端(异步风格)

17 |

方便的创建一个异步服务器程序,支持TCPUDPunixSocket 3 种socket类型,支持IPv4IPv6,支持SSL/TLS单向双向证书的隧道加密。使用者无需关注底层实现细节,仅需要设置网络事件的回调函数即可,示例参考快速启动。 18 | !> 只是Server端的风格是异步的(即所有事件都需要设置回调函数),但同时也是支持协程的,开启了enable_coroutine之后就支持协程了(默认开启),协程下所有的业务代码都是同步写法。 19 | 前往了解: 20 | Server 的三种运行模式介绍
21 | Process、ProcessPool、UserProcess的区别是什么
22 | Master进程、Reactor线程、Worker进程、Task进程、Manager进程的区别与联系
23 |

24 |

运行流程图

25 |

running_process 26 |

27 |

进程/线程结构图

28 |

process_structure 29 | process_structure_2

30 | 31 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/server12packet_class.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Swoole\Server\Packet

17 |

这里是对Swoole\Server\Packet的详细介绍。 18 |

19 |

属性

20 |

21 |

$server_socket

22 |

返回服务端文件描述符fd,该属性是一个int类型的整数。

23 |
Swoole\Server\Packet->server_socket
24 |

25 |

$server_port

26 |

返回服务端监听端口server_port,该属性是一个int类型的整数。

27 |
Swoole\Server\Packet->server_port
28 |

29 |

$dispatch_time

30 |

返回该请求数据到达时间dispatch_time,该属性是一个double类型。

31 |
Swoole\Server\Packet->dispatch_time
32 |

33 |

$address

34 |

返回客户端地址address,该属性是一个string类型的字符串。

35 |
Swoole\Server\Packet->address
36 |

37 |

$port

38 |

返回客户端监听端口port,该属性是一个int类型的整数。

39 |
Swoole\Server\Packet->port
40 |

41 |

$data

42 |

返回客户端的传递的数据data,该属性是一个string类型的字符串。

43 |
Swoole\Server\Packet->data
44 | 45 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/server12pipemessage_class.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Swoole\Server\PipeMessage

17 |

这里是对Swoole\Server\PipeMessage的详细介绍。 18 |

19 |

属性

20 |

21 |

$source_worker_id

22 |

返回数据来源方的worker进程id,该属性是一个int类型的整数。

23 |
Swoole\Server\PipeMessage->source_worker_id
24 |

25 |

$dispatch_time

26 |

返回该请求数据到达时间dispatch_time,该属性是一个double类型。

27 |
Swoole\Server\PipeMessage->dispatch_time
28 |

29 |

$data

30 |

返回该连接携带的数据data,该属性是一个string类型的字符串。

31 |
Swoole\Server\PipeMessage->data
32 | 33 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/server12port.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

多端口监听

17 |

Swoole\Server可以监听多个端口,每个端口都可以设置不同的协议处理方式,例如80端口处理HTTP协议,9507端口处理TCP协议。SSL/TLS传输加密也可以只对特定的端口启用。 18 | !> 例如主服务器是WebSocket或HTTP协议,新监听的TCP端口(listen的返回值,即Swoole\Server\Port对象,以下简称port)默认会继承主Server的协议设置,必须单独调用port对象的set方法和on方法设置新的协议才会启用新协议。 19 |

20 |

监听新端口

21 |
//返回port对象
 22 | $port1 = $server->listen("127.0.0.1", 9501, SWOOLE_SOCK_TCP);
 23 | $port2 = $server->listen("127.0.0.1", 9502, SWOOLE_SOCK_UDP);
 24 | $port3 = $server->listen("127.0.0.1", 9503, SWOOLE_SOCK_TCP | SWOOLE_SSL);
25 |

26 |

设置网络协议

27 |
//port对象的调用set方法
 28 | $port1->set([
 29 |     'open_length_check' => true,
 30 |     'package_length_type' => 'N',
 31 |     'package_length_offset' => 0,
 32 |     'package_max_length' => 800000,
 33 | ]);
 34 | $port3->set([
 35 |     'open_eof_split' => true,
 36 |     'package_eof' => "\r\n",
 37 |     'ssl_cert_file' => 'ssl.cert',
 38 |     'ssl_key_file' => 'ssl.key',
 39 | ]);
40 |

41 |

设置回调函数

42 |
//设置每个port的回调函数
 43 | $port1->on('connect', function ($serv, $fd){
 44 |     echo "Client:Connect.\n";
 45 | });
 46 | $port1->on('receive', function ($serv, $fd, $reactor_id, $data) {
 47 |     $serv->send($fd, 'Swoole: '.$data);
 48 |     $serv->close($fd);
 49 | });
 50 | $port1->on('close', function ($serv, $fd) {
 51 |     echo "Client: Close.\n";
 52 | });
 53 | $port2->on('packet', function ($serv, $data, $addr) {
 54 |     var_dump($data, $addr);
 55 | });
56 |

57 |

Http/WebSocket

58 |

Swoole\Http\ServerSwoole\WebSocket\Server因为是使用继承子类实现的,无法通过调用Swoole\Server实例的listen来方法创建HTTP或者WebSocket服务器。 59 | 如服务器的主要功能为RPC,但希望提供一个简单的Web管理界面。在这样的场景中,可以先创建HTTP/WebSocket服务器,然后再进行listen监听原生TCP的端口。 60 |

61 |

示例

62 |
$http_server = new Swoole\Http\Server('0.0.0.0',9998);
 63 | $http_server->set(['daemonize'=> false]);
 64 | $http_server->on('request', function ($request, $response) {
 65 |     $response->header("Content-Type", "text/html; charset=utf-8");
 66 |     $response->end("<h1>Hello Swoole. #".rand(1000, 9999)."</h1>");
 67 | });
 68 | //多监听一个TCP端口,对外开启TCP服务,并设置TCP服务器的回调
 69 | $tcp_server = $http_server->listen('0.0.0.0', 9999, SWOOLE_SOCK_TCP);
 70 | //默认新监听的端口 9999 会继承主服务器的设置,也是 HTTP 协议
 71 | //需要调用 set 方法覆盖主服务器的设置
 72 | $tcp_server->set([]);
 73 | $tcp_server->on('receive', function ($server, $fd, $threadId, $data) {
 74 |     echo $data;
 75 | });
 76 | $http_server->start();
77 |

通过这样的代码,就可以建立一个对外提供HTTP服务,又同时对外提供TCP服务的Server,更加具体的优雅代码组合则由你自己来实现。 78 |

79 |

TCP、HTTP、WebSocket多协议端口复合设置

80 |
$port1 = $server->listen("127.0.0.1", 9501, SWOOLE_SOCK_TCP);
 81 | $port1->set([
 82 |     'open_websocket_protocol' => true, // 设置使得这个端口支持WebSocket协议
 83 | ]);
84 |
$port1 = $server->listen("127.0.0.1", 9501, SWOOLE_SOCK_TCP);
 85 | $port1->set([
 86 |     'open_http_protocol' => false, // 设置这个端口关闭HTTP协议功能
 87 | ]);
88 |

同理还有:open_http_protocolopen_http2_protocolopen_mqtt_protocol 等参数 89 |

90 |

可选参数

91 | 104 |

105 |

TCP服务器

106 | 115 |

116 |

HTTP服务器

117 | 120 |

121 |

WebSocket服务器

122 | 152 | 153 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/server12properties.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

属性

17 |

18 |

$setting

19 |

Server->set()函数所设置的参数会保存到Server->$setting属性上。在回调函数中可以访问运行参数的值。该属性是一个array类型的数组。

20 |
Swoole\Server->setting
21 | 114 |

115 |

$worker_id

116 |

得到当前Worker进程的编号,包括 Task进程,该属性是一个int类型的整数。

117 |
Swoole\Server->worker_id
118 | 160 | 161 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/server12server_port.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Swoole\Server\Port

17 |

这里是对Swoole\Server\Port的详细介绍。 18 |

19 |

属性

20 |

21 |

$host

22 |

返回监听的主机地址,该属性是一个string类型的字符串。

23 |
Swoole\Server\Port->host
24 |

25 |

$port

26 |

返回监听的主机端口,该属性是一个int类型的整数。

27 |
Swoole\Server\Port->port
28 |

29 |

$type

30 |

返回这组server类型。该属性是一个枚举,返回SWOOLE_TCPSWOOLE_TCP6SWOOLE_UDPSWOOLE_UDP6SWOOLE_UNIX_DGRAMSWOOLE_UNIX_STREAM其中一个。

31 |
Swoole\Server\Port->type
32 |

33 |

$sock

34 |

返回监听的套接字,该属性是一个int类型的整数。

35 |
Swoole\Server\Port->sock
36 |

37 |

$ssl

38 |

返回是否开启ssl加密,该属性是一个bool类型。

39 |
Swoole\Server\Port->ssl
40 |

41 |

$setting

42 |

返回对该端口的设置,该属性是一个array的数组。

43 |
Swoole\Server\Port->setting
44 |

45 |

$connections

46 |

返回连接该端口的全部连接,该属性是一个迭代器。

47 |
Swoole\Server\Port->connections
48 |

49 |

方法

50 |

51 |

set()

52 |

用于设置Swoole\Server\Port运行时的各项参数,使用方式与Swoole\Server->set()一样。

53 |
Swoole\Server\Port->set(array $setting): void
54 |

55 |

on()

56 |

用于设置Swoole\Server\Port回调函数,使用方式与Swoole\Server->on()一样。

57 |
Swoole\Server\Port->on(string $event, callable $callback): bool
58 |

59 |

getCallback()

60 |

返回设置的回调函数。

61 |
Swoole\Server\Port->getCallback(string $name): ?callback
62 | 86 | 87 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/server12statusinfo_class.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Swoole\Server\StatusInfo

17 |

这里是对Swoole\Server\StatusInfo的详细介绍。 18 |

19 |

属性

20 |

21 |

$worker_id

22 |

返回当前worker进程id,该属性是一个int类型的整数。

23 |
Swoole\Server\StatusInfo->worker_id
24 |

25 |

$worker_pid

26 |

返回当前worker进程父进程id,该属性是一个int类型的整数。

27 |
Swoole\Server\StatusInfo->worker_pid
28 |

29 |

$status

30 |

返回进程状态status,该属性是一个int类型的整数。

31 |
Swoole\Server\StatusInfo->status
32 |

33 |

$exit_code

34 |

返回进程退出状态码exit_code,该属性是一个int类型的整数,范围是0-255

35 |
Swoole\Server\StatusInfo->exit_code
36 |

37 |

$signal

38 |

进程退出的信号signal,该属性是一个int类型的整数。

39 |
Swoole\Server\StatusInfo->signal
40 | 41 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/server12task_class.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Swoole\Server\Task

17 |

这里是对Swoole\Server\Task的详细介绍。这个类很简单,但是你也不能够通过new Swoole\Server\Task()来获得一个Task对象,这种对象完全不包含任何服务端的信息,并且你执行Swoole\Server\Task任意的方法都会有一个致命错误。

18 |
Invalid instance of Swoole\Server\Task in /home/task.php on line 3
19 |

20 |

属性

21 |

22 |

$data

23 |

worker进程传递给task进程的数据data,该属性是一个string类型的字符串。

24 |
Swoole\Server\Task->data
25 |

26 |

$dispatch_time

27 |

返回该数据到达task进程的时间dispatch_time,该属性是一个double类型。

28 |
Swoole\Server\Task->dispatch_time
29 |

30 |

$id

31 |

返回该数据到达task进程的时间dispatch_time,该属性是一个int类型的整数。

32 |
Swoole\Server\Task->id
33 |

34 |

$worker_id

35 |

返回该数据来自哪一个worker进程,该属性是一个int类型的整数。

36 |
Swoole\Server\Task->worker_id
37 |

38 |

$flags

39 |

该异步任务的一些标志位信息flags,该属性是一个int类型的整数。

40 |
Swoole\Server\Task->flags
41 |

?> flags返回的结果是以下几种类型:

42 | 112 | 113 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/server12taskresult_class.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Swoole\Server\TaskResult

17 |

这里是对Swoole\Server\TaskResult的详细介绍。 18 |

19 |

属性

20 |

21 |

$task_id

22 |

返回所在的Reactor线程id,该属性是一个int类型的整数。

23 |
Swoole\Server\TaskResult->task_id
24 |

25 |

$task_worker_id

26 |

返回该执行结果来自哪个task进程,该属性是一个int类型的整数。

27 |
Swoole\Server\TaskResult->task_worker_id
28 |

29 |

$dispatch_time

30 |

返回该连接携带的数据data,该属性是一个?string类型的字符串。

31 |
Swoole\Server\TaskResult->dispatch_time
32 |

33 |

$data

34 |

返回该连接携带的数据data,该属性是一个string类型的字符串。

35 |
Swoole\Server\StatusInfo->data
36 | 37 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/server12tcp_init.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Swoole\Server

17 |

此节包含Swoole\Server类的全部方法、属性、配置项以及所有的事件。Swoole\Server类是所有异步风格服务器的基类,后面章节的Swoole\Http\ServerSwoole\WebSocket\ServerSwoole\Redis\Server都是它的子类。

18 | 19 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/start12coroutine.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

协程初探

17 |

!> 建议先查看Coroutine,了解协程基本概念之后再看本文。 18 | Swoole4 使用全新的协程内核引擎,现在 Swoole 拥有一个全职的开发团队,因此正在进入PHP历史上前所未有的时期,为性能的高速提升提供了独一无二的可能性。 19 | Swoole4 或更高版本拥有高可用性的内置协程,可以使用完全同步的代码来实现异步IO,PHP代码没有任何额外的关键字,底层会自动进行协程调度。 20 |

21 |

使用协程你可以在一秒钟里做多少事?

22 |

睡眠1万次,读取,写入,检查和删除文件1万次,使用PDO和MySQLi与数据库通信1万次,创建TCP服务器和多个客户端相互通信1万次,创建UDP服务器和多个客户端相互通信1万次......一切都在一个进程中完美完成!

23 |
use Swoole\Runtime;
 24 | use Swoole\Coroutine;
 25 | use function Swoole\Coroutine\run;
 26 | // 此行代码后,文件操作,sleep,Mysqli,PDO,streams等都变成异步IO,见'一键协程化'章节。
 27 | Runtime::enableCoroutine();
 28 | $s = microtime(true);
 29 | // Swoole\Coroutine\run()见'协程容器'章节。
 30 | run(function() {
 31 |     // i just want to sleep...
 32 |     for ($c = 100; $c--;) {
 33 |         Coroutine::create(function () {
 34 |             for ($n = 100; $n--;) {
 35 |                 usleep(1000);
 36 |             }
 37 |         });
 38 |     }
 39 |     // 10k file read and write
 40 |     for ($c = 100; $c--;) {
 41 |         Coroutine::create(function () use ($c) {
 42 |             $tmp_filename = "/tmp/test-{$c}.php";
 43 |             for ($n = 100; $n--;) {
 44 |                 $self = file_get_contents(__FILE__);
 45 |                 file_put_contents($tmp_filename, $self);
 46 |                 assert(file_get_contents($tmp_filename) === $self);
 47 |             }
 48 |             unlink($tmp_filename);
 49 |         });
 50 |     }
 51 |     // 10k pdo and mysqli read
 52 |     for ($c = 50; $c--;) {
 53 |         Coroutine::create(function () {
 54 |             $pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8', 'root', 'root');
 55 |             $statement = $pdo->prepare('SELECT * FROM `user`');
 56 |             for ($n = 100; $n--;) {
 57 |                 $statement->execute();
 58 |                 assert(count($statement->fetchAll()) > 0);
 59 |             }
 60 |         });
 61 |     }
 62 |     for ($c = 50; $c--;) {
 63 |         Coroutine::create(function () {
 64 |             $mysqli = new Mysqli('127.0.0.1', 'root', 'root', 'test');
 65 |             $statement = $mysqli->prepare('SELECT `id` FROM `user`');
 66 |             for ($n = 100; $n--;) {
 67 |                 $statement->bind_result($id);
 68 |                 $statement->execute();
 69 |                 $statement->fetch();
 70 |                 assert($id > 0);
 71 |             }
 72 |         });
 73 |     }
 74 |     // php_stream tcp server & client with 12.8k requests in single process
 75 |     function tcp_pack(string $data): string
 76 |     {
 77 |         return pack('n', strlen($data)) . $data;
 78 |     }
 79 |     function tcp_length(string $head): int
 80 |     {
 81 |         return unpack('n', $head)[1];
 82 |     }
 83 |     Coroutine::create(function () {
 84 |         $ctx = stream_context_create(['socket' => ['so_reuseaddr' => true, 'backlog' => 128]]);
 85 |         $socket = stream_socket_server(
 86 |             'tcp://0.0.0.0:9502',
 87 |             $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctx
 88 |         );
 89 |         if (!$socket) {
 90 |             echo "{$errstr} ({$errno})\n";
 91 |         } else {
 92 |             $i = 0;
 93 |             while ($conn = stream_socket_accept($socket, 1)) {
 94 |                 stream_set_timeout($conn, 5);
 95 |                 for ($n = 100; $n--;) {
 96 |                     $data = fread($conn, tcp_length(fread($conn, 2)));
 97 |                     assert($data === "Hello Swoole Server #{$n}!");
 98 |                     fwrite($conn, tcp_pack("Hello Swoole Client #{$n}!"));
 99 |                 }
100 |                 if (++$i === 128) {
101 |                     fclose($socket);
102 |                     break;
103 |                 }
104 |             }
105 |         }
106 |     });
107 |     for ($c = 128; $c--;) {
108 |         Coroutine::create(function () {
109 |             $fp = stream_socket_client('tcp://127.0.0.1:9502', $errno, $errstr, 1);
110 |             if (!$fp) {
111 |                 echo "{$errstr} ({$errno})\n";
112 |             } else {
113 |                 stream_set_timeout($fp, 5);
114 |                 for ($n = 100; $n--;) {
115 |                     fwrite($fp, tcp_pack("Hello Swoole Server #{$n}!"));
116 |                     $data = fread($fp, tcp_length(fread($fp, 2)));
117 |                     assert($data === "Hello Swoole Client #{$n}!");
118 |                 }
119 |                 fclose($fp);
120 |             }
121 |         });
122 |     }
123 |     // udp server & client with 12.8k requests in single process
124 |     Coroutine::create(function () {
125 |         $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0);
126 |         $socket->bind('127.0.0.1', 9503);
127 |         $client_map = [];
128 |         for ($c = 128; $c--;) {
129 |             for ($n = 0; $n < 100; $n++) {
130 |                 $recv = $socket->recvfrom($peer);
131 |                 $client_uid = "{$peer['address']}:{$peer['port']}";
132 |                 $id = $client_map[$client_uid] = ($client_map[$client_uid] ?? -1) + 1;
133 |                 assert($recv === "Client: Hello #{$id}!");
134 |                 $socket->sendto($peer['address'], $peer['port'], "Server: Hello #{$id}!");
135 |             }
136 |         }
137 |         $socket->close();
138 |     });
139 |     for ($c = 128; $c--;) {
140 |         Coroutine::create(function () {
141 |             $fp = stream_socket_client('udp://127.0.0.1:9503', $errno, $errstr, 1);
142 |             if (!$fp) {
143 |                 echo "$errstr ($errno)\n";
144 |             } else {
145 |                 for ($n = 0; $n < 100; $n++) {
146 |                     fwrite($fp, "Client: Hello #{$n}!");
147 |                     $recv = fread($fp, 1024);
148 |                     list($address, $port) = explode(':', (stream_socket_get_name($fp, true)));
149 |                     assert($address === '127.0.0.1' && (int)$port === 9503);
150 |                     assert($recv === "Server: Hello #{$n}!");
151 |                 }
152 |                 fclose($fp);
153 |             }
154 |         });
155 |     }
156 | });
157 | echo 'use ' . (microtime(true) - $s) . ' s';
158 | 159 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/start12start_http_server.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

HTTP 服务器

17 |

18 |

程序代码

19 |

请将以下代码写入httpServer.php。

20 |
$http = new Swoole\Http\Server('0.0.0.0', 9501);
21 | $http->on('Request', function ($request, $response) {
22 |     $response->header('Content-Type', 'text/html; charset=utf-8');
23 |     $response->end('<h1>Hello Swoole. #' . rand(1000, 9999) . '</h1>');
24 | });
25 | $http->start();
26 |

HTTP服务器只需要关注请求响应即可,所以只需要监听一个onRequest事件。当有新的HTTP请求进入就会触发此事件。事件回调函数有2个参数,一个是$request对象,包含了请求的相关信息,如GET/POST请求的数据。 27 | 另外一个是response对象,对request的响应可以通过操作response对象来完成。$response->end()方法表示输出一段HTML内容,并结束此请求。

28 | 57 | 58 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/start12start_mqtt.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

MQTT(物联网)服务器

17 |

通过设置open_mqtt_protocol选项,启用后会解析MQTT包头,Worker 进程的onReceive事件每次会返回一个完整的MQTT数据包。 18 | 可以使用 Swoole 作为 MQTT 服务端或客户端,实现一套完整物联网(IOT)解决方案。

19 |
20 |

完整的 MQTT 协议解析和协程客户端可以使用 simps/mqtt 21 |

22 |

程序代码

23 |

请将以下代码写入mqttServer.php

24 |
function decodeValue($data)
25 | {
26 | return 256 * ord($data[0]) + ord($data[1]);
27 | }
28 | function decodeString($data)
29 | {
30 | $length = decodeValue($data);
31 | return substr($data, 2, $length);
32 | }
33 | function mqttGetHeader($data)
34 | {
35 | $byte = ord($data[0]);
36 | $header['type'] = ($byte & 0xF0) >> 4;
37 | $header['dup'] = ($byte & 0x08) >> 3;
38 | $header['qos'] = ($byte & 0x06) >> 1;
39 | $header['retain'] = $byte & 0x01;
40 | return $header;
41 | }
42 | function eventConnect($header, $data)
43 | {
44 | $connect_info['protocol_name'] = decodeString($data);
45 | $offset = strlen($connect_info['protocol_name']) + 2;
46 | $connect_info['version'] = ord(substr($data, $offset, 1));
47 | $offset += 1;
48 | $byte = ord($data[$offset]);
49 | $connect_info['willRetain'] = ($byte & 0x20 == 0x20);
50 | $connect_info['willQos'] = ($byte & 0x18 >> 3);
51 | $connect_info['willFlag'] = ($byte & 0x04 == 0x04);
52 | $connect_info['cleanStart'] = ($byte & 0x02 == 0x02);
53 | $offset += 1;
54 | $connect_info['keepalive'] = decodeValue(substr($data, $offset, 2));
55 | $offset += 2;
56 | $connect_info['clientId'] = decodeString(substr($data, $offset));
57 | return $connect_info;
58 | }
59 | $server = new Swoole\Server('127.0.0.1', 9501, SWOOLE_BASE);
60 | $server->set([
61 | 'open_mqtt_protocol' => true, // 启用 MQTT 协议
62 | 'worker_num' => 1,
63 | ]);
64 | $server->on('Connect', function ($server, $fd) {
65 | echo "Client:Connect.\n";
66 | });
67 | $server->on('Receive', function ($server, $fd, $reactor_id, $data) {
68 | $header = mqttGetHeader($data);
69 | var_dump($header);
70 | if ($header['type'] == 1) {
71 | $resp = chr(32) . chr(2) . chr(0) . chr(0);
72 | eventConnect($header, substr($data, 2));
73 | $server->send($fd, $resp);
74 | } elseif ($header['type'] == 3) {
75 | $offset = 2;
76 | $topic = decodeString(substr($data, $offset));
77 | $offset += strlen($topic) + 2;
78 | $msg = substr($data, $offset);
79 | echo "client msg: {$topic}\n----------\n{$msg}\n";
80 | //file_put_contents(__DIR__.'/data.log', $data);
81 | }
82 | echo "received length=" . strlen($data) . "\n";
83 | });
84 | $server->on('Close', function ($server, $fd) {
85 | echo "Client: Close.\n";
86 | });
87 | $server->start();
88 |
89 | 90 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/start12start_server.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

简单示例

17 |

Swoole的绝大部分功能只能用于cli命令行环境,请首先准备好Linux Shell环境。可使用VimEmacsPhpStorm或其他编辑器编写代码,并在命令行中通过如下指令执行程序。

18 |
php /path/to/your_file.php
19 |

成功执行Swoole服务器程序后,如果你的代码中没有任何echo语句,屏幕不会有任何输出,但实际上底层已经在监听网络端口,等待客户端发起连接。可使用相应的客户端工具和程序连接到服务器,进行测试。 20 |

21 |

进程管理

22 |

默认情况下,启动Swoole的服务后,通过启动的窗口CTRL+C就可以结束服务,但此时如果窗口退出会有问题,需要后台启动,详情参考守护进程化。 23 | !> 简单示例中的示例大部分都是异步风格的编程模式,用协程风格同样可以做到示例中的功能,参见服务端 (协程风格)。 24 | !> Swoole提供的绝大的部分模块只能用于cli命令行终端。目前只有同步阻塞客户端可以用于PHP-FPM环境下。

25 | 26 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/start12start_task.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

执行异步任务(Task)

17 |

在Server程序中如果需要执行很耗时的操作,比如一个聊天服务器发送广播,Web服务器中发送邮件。如果直接去执行这些函数就会阻塞当前进程,导致服务器响应变慢。 18 | Swoole提供了异步任务处理的功能,可以投递一个异步任务到TaskWorker进程池中执行,不影响当前请求的处理速度。 19 |

20 |

程序代码

21 |

基于第一个TCP服务器,只需要增加onTaskonFinish 2个事件回调函数即可。另外需要设置task进程数量,可以根据任务的耗时和任务量配置适量的task进程。 22 | 请将以下代码写入task.php。

23 |
$serv = new Swoole\Server('127.0.0.1', 9501);
24 | //设置异步任务的工作进程数量。
25 | $serv->set([
26 |     'task_worker_num' => 4
27 | ]);
28 | //此回调函数在worker进程中执行。
29 | $serv->on('Receive', function($serv, $fd, $reactor_id, $data) {
30 |     //投递异步任务
31 |     $task_id = $serv->task($data);
32 |     echo "Dispatch AsyncTask: id={$task_id}\n";
33 | });
34 | //处理异步任务(此回调函数在task进程中执行)。
35 | $serv->on('Task', function ($serv, $task_id, $reactor_id, $data) {
36 |     echo "New AsyncTask[id={$task_id}]".PHP_EOL;
37 |     //返回任务执行的结果
38 |     $serv->finish("{$data} -> OK");
39 | });
40 | //处理异步任务的结果(此回调函数在worker进程中执行)。
41 | $serv->on('Finish', function ($serv, $task_id, $data) {
42 |     echo "AsyncTask[{$task_id}] Finish: {$data}".PHP_EOL;
43 | });
44 | $serv->start();
45 |

调用$serv->task()后,程序立即返回,继续向下执行代码。onTask回调函数Task进程池内被异步执行。执行完成后调用$serv->finish()返回结果。 46 | !> finish操作是可选的,也可以不返回任何结果,如果在onTask事件中通过return返回结果时,相当于调用Swoole\Server::finish()操作。

47 | 48 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/start12start_tcp_server.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

TCP 服务器

17 |

18 |

程序代码

19 |

请将以下代码写入tcpServer.php。

20 |
//创建Server对象,监听 127.0.0.1:9501 端口。
21 | $server = new Swoole\Server('127.0.0.1', 9501);
22 | //监听连接进入事件。
23 | $server->on('Connect', function ($server, $fd) {
24 |     echo "Client: Connect.\n";
25 | });
26 | //监听数据接收事件。
27 | $server->on('Receive', function ($server, $fd, $reactor_id, $data) {
28 |     $server->send($fd, "Server: {$data}");
29 | });
30 | //监听连接关闭事件。
31 | $server->on('Close', function ($server, $fd) {
32 |     echo "Client: Close.\n";
33 | });
34 | //启动服务器
35 | $server->start(); 
36 |

这样就创建了一个TCP服务器,监听本机9501端口。它的逻辑很简单,当客户端Socket通过网络发送一个 hello 字符串时,服务器会回复一个 Server: hello 字符串。 37 | Server是异步服务器,所以是通过监听事件的方式来编写程序的。当对应的事件发生时底层会主动回调指定的函数。如当有新的TCP连接进入时会执行onConnect事件回调,当某个连接向服务器发送数据时会回调onReceive函数。

38 | 61 | 62 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/start12start_udp_server.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

UDP 服务器

17 |

18 |

程序代码

19 |

请将以下代码写入udpServer.php。

20 |
$server = new Swoole\Server('127.0.0.1', 9502, SWOOLE_PROCESS, SWOOLE_SOCK_UDP);
21 | //监听数据接收事件。
22 | $server->on('Packet', function ($server, $data, $clientInfo) {
23 |     var_dump($clientInfo);
24 |     $server->sendto($clientInfo['address'], $clientInfo['port'], "Server:{$data}");
25 | });
26 | //启动服务器
27 | $server->start();
28 |

UDP服务器与TCP服务器不同,UDP没有连接的概念。启动Server后,客户端无需Connect,直接可以向Server监听的9502端口发送数据包。对应的事件为onPacket。

29 | 42 | 43 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/start12start_ws_server.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

WebSocket服务器

17 |

18 |

程序代码

19 |

请将以下代码写入websocketServer.php。

20 |
//创建WebSocket Server对象,监听0.0.0.0:9502端口。
21 | $ws = new Swoole\WebSocket\Server('0.0.0.0', 9502);
22 | //监听WebSocket连接打开事件。
23 | $ws->on('Open', function ($ws, $request) {
24 |     $ws->push($request->fd, "hello, welcome\n");
25 | });
26 | //监听WebSocket消息事件。
27 | $ws->on('Message', function ($ws, $frame) {
28 |     echo "Message: {$frame->data}\n";
29 |     $ws->push($frame->fd, "server: {$frame->data}");
30 | });
31 | //监听WebSocket连接关闭事件。
32 | $ws->on('Close', function ($ws, $fd) {
33 |     echo "client-{$fd} is closed\n";
34 | });
35 | $ws->start();
36 | 62 | 63 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/version12bc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

向下不兼容改动

17 |

18 |

v5.0.0

19 | 102 | 103 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/Documents/version12supported.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swoole4 文档 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

支持计划

17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
分支PHP 版本开始时间积极支持截止时间安全维护截止时间
v4.8.x7.2 - 8.22021-10-142023-10-142024-06-30
v5.0.x8.0 - 8.22022-01-202023-01-202023-07-20
v5.1.x8.0 - 8.22023-09-302025-09-302026-09-30
v6.0.x8.1 - 8.32024-06-232026-06-232027-06-23
积极支持受到官方开发组的积极支持,已报告的错误和安全问题将会立即被修复,并按照常规流程发布正式的版本。
------------------------------------------------------------------------------------------------------
安全维护仅支持关键安全问题的修复,且仅在必要时发布正式的版本
70 |

71 |

不再支持的分支

72 |

!> 这些版本不再为官方所支持,仍在使用以下版本的用户应尽快升级,因为他们可能会遇到未修补的安全漏洞。

73 | 91 | 92 | -------------------------------------------------------------------------------- /Swoole.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfstring/swoole-chinese-docset/fcdf5205efdffc1bf46ba415993d980995efb0df/Swoole.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /Swoole.docset/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfstring/swoole-chinese-docset/fcdf5205efdffc1bf46ba415993d980995efb0df/Swoole.docset/icon.png -------------------------------------------------------------------------------- /Swoole.docset/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfstring/swoole-chinese-docset/fcdf5205efdffc1bf46ba415993d980995efb0df/Swoole.docset/icon@2x.png -------------------------------------------------------------------------------- /Swoole4.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfstring/swoole-chinese-docset/fcdf5205efdffc1bf46ba415993d980995efb0df/Swoole4.tgz -------------------------------------------------------------------------------- /dash-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfstring/swoole-chinese-docset/fcdf5205efdffc1bf46ba415993d980995efb0df/dash-demo.png -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/halfstring/swoole-chinese-docset/fcdf5205efdffc1bf46ba415993d980995efb0df/demo.gif -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Generate Swoole.docset 3 | 4 | 5 | ### fetch markdown && generate html 6 | ``` 7 | > php ./run.php 8 | ``` 9 | 10 | ### import database 11 | 12 | ``` 13 | > tar xzvf Swoole.tgz 14 | > cd /path/to/project/Swoole.docset/Contents/Resources 15 | > sqlite3 docSet.dsidx 16 | > .read docset.sql 17 | 18 | ``` 19 | 20 | ### copy html to document 21 | ``` 22 | > cp -R /path/to/project/src/html/* Swoole.docset/Contents/Resources/Documents 23 | ``` 24 | -------------------------------------------------------------------------------- /src/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "erusev/parsedown": "^1.7" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "f493960dbf3229f8998effb35c5e910d", 8 | "packages": [ 9 | { 10 | "name": "erusev/parsedown", 11 | "version": "1.7.4", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/erusev/parsedown.git", 15 | "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3", 20 | "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "ext-mbstring": "*", 25 | "php": ">=5.3.0" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "^4.8.35" 29 | }, 30 | "type": "library", 31 | "autoload": { 32 | "psr-0": { 33 | "Parsedown": "" 34 | } 35 | }, 36 | "notification-url": "https://packagist.org/downloads/", 37 | "license": [ 38 | "MIT" 39 | ], 40 | "authors": [ 41 | { 42 | "name": "Emanuil Rusev", 43 | "email": "hello@erusev.com", 44 | "homepage": "http://erusev.com" 45 | } 46 | ], 47 | "description": "Parser for Markdown.", 48 | "homepage": "http://parsedown.org", 49 | "keywords": [ 50 | "markdown", 51 | "parser" 52 | ], 53 | "support": { 54 | "issues": "https://github.com/erusev/parsedown/issues", 55 | "source": "https://github.com/erusev/parsedown/tree/1.7.x" 56 | }, 57 | "time": "2019-12-30T22:54:17+00:00" 58 | } 59 | ], 60 | "packages-dev": [], 61 | "aliases": [], 62 | "minimum-stability": "stable", 63 | "stability-flags": [], 64 | "prefer-stable": false, 65 | "prefer-lowest": false, 66 | "platform": [], 67 | "platform-dev": [], 68 | "plugin-api-version": "2.1.0" 69 | } 70 | -------------------------------------------------------------------------------- /src/run.php: -------------------------------------------------------------------------------- 1 | md = $md; 105 | 106 | if (!is_dir("./md")) { 107 | $this->mkdir("md"); 108 | } 109 | 110 | if (!is_dir("./html")) { 111 | $this->mkdir("html"); 112 | } 113 | } 114 | 115 | public function run() 116 | { 117 | $result = []; 118 | 119 | foreach ($this->md as $_md) { 120 | list($mdFile, $content) = $this->getMarkdown($_md); 121 | $htmlFile = $this->genHtml($_md, $content); 122 | 123 | $result[$_md] = [ 124 | 'html' => $htmlFile, 125 | 'md' => $mdFile, 126 | 'tag' => $this->tags[$_md] ?? [], 127 | "prefix" => trim($_md, ".md") 128 | ]; 129 | } 130 | 131 | return $result; 132 | } 133 | 134 | private function mkdir($folder) 135 | { 136 | $folder = trim($folder); 137 | $old = umask(0); 138 | mkdir($folder); 139 | umask($old); 140 | 141 | return true; 142 | } 143 | 144 | public function genSql($result) 145 | { 146 | $sqlTpl = "INSERT INTO `searchIndex`(name, type, path) VALUES ('{KEYWORD}','Keyword','{HTML-TPL}')"; 147 | 148 | $sqls = []; 149 | foreach ($result as $node) { 150 | $mdName = strtr($node['md'], [ 151 | "./md/" => '', 152 | '.md' => '', 153 | "12" => ':' 154 | ]); 155 | $htmlPath = str_replace("./html/", "", $node['html']); 156 | $sqls[] = strtr($sqlTpl, [ 157 | "{KEYWORD}" => $mdName, 158 | "{HTML-TPL}" => $htmlPath 159 | ]); 160 | 161 | if (!isset($node['tag']) || empty($node['tag'])) { 162 | continue; 163 | } 164 | 165 | foreach ($node['tag'] as $tag) { 166 | $sqls[] = strtr($sqlTpl, [ 167 | "{KEYWORD}" => $mdName . "\\" . $tag, 168 | "{HTML-TPL}" => $htmlPath . "#" . $tag 169 | ]); 170 | } 171 | 172 | } 173 | 174 | file_put_contents("docset.sql", implode(";\n", $sqls)); 175 | 176 | return true; 177 | } 178 | 179 | private function getMarkdown($fileName) 180 | { 181 | $file = './md/' . str_replace("/", "12", $fileName); 182 | 183 | $fileUrl = $this->swooleWiki . 'zh-cn/' . $fileName; 184 | echo $fileUrl , PHP_EOL; 185 | $content = file_get_contents($fileUrl); 186 | 187 | $lines = explode("\n", $content); 188 | $lines = array_filter($lines); 189 | 190 | $newContent = []; 191 | foreach ($lines as $line) { 192 | $tag = $this->hasTag($line); 193 | if ($tag !== false) { 194 | $newContent[] = ''; 195 | $this->tags[$fileName][] = $tag; 196 | } 197 | $newContent[] = $line; 198 | } 199 | 200 | file_put_contents($file, implode("\n", $newContent)); 201 | 202 | return [$file, implode("\n", $newContent)]; 203 | } 204 | 205 | private function hasTag($line) 206 | { 207 | if (stripos($line, "##") === 0) { 208 | $line = trim($line, "# "); 209 | $line = str_replace("()", "", $line); 210 | //if (preg_match("/^[a-zA-Z].*/", $line)) { 211 | return $line; 212 | //} 213 | } 214 | return false; 215 | } 216 | 217 | private function genHtml($fileName, $content) 218 | { 219 | $fileName = str_replace(".md", ".html", $fileName); 220 | $htmlFile = './html/' . str_replace("/", "12", $fileName); 221 | $logic = new Parsedown(); 222 | $logic->setMarkupEscaped(false); 223 | $logic->setSafeMode(false); 224 | $htmlBody = $logic->text($content); 225 | $htmlContent = << 227 | 228 | 229 | 230 | Swoole4 文档 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | {$htmlBody} 242 | 243 | 244 | HTMLCONTENT; 245 | 246 | file_put_contents($htmlFile, $htmlContent); 247 | 248 | return $htmlFile; 249 | } 250 | 251 | } 252 | 253 | 254 | $logic = new GenSwooleDocset($allMarkdown); 255 | $result = $logic->run(); 256 | 257 | file_put_contents("./result.log", var_export($result, true)); 258 | 259 | $logic->genSql($result); 260 | 261 | echo "over", PHP_EOL; 262 | 263 | --------------------------------------------------------------------------------