PHP Generator相关的设计失误

PHP的Generator,也就是 yield/yield from 语法,使得函数调用可以“暂停”执行,并保留上下文,并在后续可以恢复执行。

但是,在PHP后续的设计中,很多地方都没有考虑到Generator:

Return Type Declarations(返回类型声明)

RFC见https://wiki.php.net/rfc/return_types。简而言之,可以给函数声明返回类型。先来看一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
declare(strict_types=1);

function inner(): Iterator
{
yield __FUNCTION__;
return 1;
}

function gen(): Generator
{
yield __FUNCTION__;
return yield from inner();
}

try {
$gen = gen();
foreach ($gen as $yielded) {
echo "yield: ".$yielded . PHP_EOL;
}
echo "return: " . $gen->getReturn() . PHP_EOL;
} catch (Exception $e) {
echo PHP_EOL;
echo $e->getTraceAsString();
echo PHP_EOL;
}

gen/inner函数是一个generator,yield的的类型是string,return的类型是int。但为了让PHP不报错,gen/inner函数的返回类型只能声明为Iterator或Generator——这完全破坏了返回类型声明的初衷:

对于普通函数,返回类型声明用来表示返回类型;对于generator,返回类型声明用来表示这是一个迭代器/Generator。(听着很不一致的样子)

好,没关系,无伤大雅。没法通过返回类型声明来推倒类型而已,我们通过PhpDoc来总可以了吧?

通过PhpDoc来标记返回类型

但是这个怎么写呢?对于刚刚的inner函数(yield的的类型是string,return的类型是int),最自然的写法是这样:

阅读更多

php-fpm的reload过程

背景

谈谈PHP的Reload操作中提到reload会让sleep提前结束,所以就探究了下fpm的reload操作如何实现。

本文在php7.0 fpm下分析,process_control_timeout设置不为0。


重启信号

首先,我们从PHP源码可以知道,fpm的reload操作实际上就是对fpm进程发送了USR2信号。

程序在处理信号的时候,主进程的逻辑相当于“暂停”了,如果在这儿执行一些操作的话,第一,有些局部变量拿不到;第二,可能会打断主进程的逻辑。所以,信号处理函数仅仅是通知主进程,用户发送了这个信号。

信号处理函数的注册

fpm的master进程中,fpm_signals_init_main函数通过sigaction注册了信号处理函数sig_handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int fpm_signals_init_main() /* {{{ */
{
struct sigaction act;

// 。。。。。。

memset(&act, 0, sizeof(act));
act.sa_handler = sig_handler;
sigfillset(&act.sa_mask);

if (0 > sigaction(SIGTERM, &act, 0) ||
0 > sigaction(SIGINT, &act, 0) ||
0 > sigaction(SIGUSR1, &act, 0) ||
0 > sigaction(SIGUSR2, &act, 0) ||
0 > sigaction(SIGCHLD, &act, 0) ||
0 > sigaction(SIGQUIT, &act, 0)) {

zlog(ZLOG_SYSERROR, "failed to init signals: sigaction()");
return -1;
}
return 0;
}
/* }}} */
阅读更多