PHP代码的解析过程

又有一周没更新了,也没人催更,决定主动更新一篇。

这段时间国家开始推行『区块链』,央行也即将发行数字货币DCEP(Digital Currency Electronic Payment)。这个数字货币对支付宝和微信支付不会有太大的影响,他们两家加起来那点份额对央行来说就是毛毛雨,不过云闪付可能会退位让贤。

这不是重点,重点是国家已经规划在多个领域应用区块链技术。强调要探索“区块链+”在民生领域的应用,积极推动区块链技术在教育、就业、养老、精准脱贫、医疗健康、商品防伪、食品安全、公益、社会救助、政务等领域的应用。对于程序员来说这又是一个挣零花钱的好机会,这是一个最好的时代。

国家十三五规划(2016-2020)给腾讯和阿里都分了任务,腾讯主管智慧医疗、警务、政务。阿里主管云计算、大数据、工业。2020年马上要交卷了,两家企业做的都不错,特别是阿里。马上要十四五规划,看区块链技术花落谁家,我没猜错的话应该会是阿里牵头。所以小程序也是未来5年的一个爆发点。

正文

问:PHP代码更新的时候会不会中断用户正在进行的请求?

答:会,但有办法防止这种情况出现。

PHP属于热更新语言,在不开Opcache缓存的情况下修改代码能实时生效,因为这个灵活的特性也导致PHP在发布代码时容易遇到问题,这点和前端资源的发布很像。前端需要保证多个js和css文件更新后同时生效,但是文件更新肯定会有先后顺序,存在时间差,如何处理时间差带来的风险也是值得深究的话题。主要有如下问题:文件的更新顺序导致用户请求出错。

a.php -> b.php -> c.php

有如上调用流程,在更新b和c文件时,凑巧用户请求刚加载完更新后的b文件,此时如果c文件还在更新中,就会导致顺序出错,变成如下情况。

a.php -> 新 b.php -> c.php

对用户来说这次请求多半会报错。如果当前请求有I/O操作更会造成灾难性的后果。

这里说了”可能”、”如果”、”凑巧”三个概率性的词,在编程时千万不要相信概率,请迷信墨菲定律。

PHP是如何解析执行的?

  1. Scanning(Lexing) ,将PHP代码转换为语言片段(Tokens)

  2. Parsing, 将Tokens转换成简单而有意义的表达式,再通过Bison转换成ZendAPI

  3. Compilation, 将表达式编译成Opocdes

  4. Execution, 顺次执行Opcodes,每次一条,从而实现PHP脚本的功能。

文件加载到内存后,无论怎么修改文件都不会影响当次请求。所以只要知道include的PHP文件是什么时候被加载到内存的,问题也迎刃而解。

假设我们有两个php文件,内容如下:

1
2
3
4
5
6
7
8
9
//a.php
<?php
echo 123;
include "b.php";
?>
//b.php
<?php
echo 456;
?>
  1. Scanning(Lexing) 词法分析结果
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
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
Array
(
[0] => Array
(
[0] => 379
[1] => <?php
[2] => 1
)

[1] => Array
(
[0] => 328
[1] => echo
[2] => 1
)

[2] => Array
(
[0] => 382
[1] =>
[2] => 1
)

[3] => Array
(
[0] => 317
[1] => 123
[2] => 1
)

[4] => ;
[5] => Array
(
[0] => 258
[1] => include
[2] => 1
)

[6] => Array
(
[0] => 382
[1] =>
[2] => 1
)

[7] => Array
(
[0] => 323
[1] => "b.php"
[2] => 1
)

[8] => ;
)
  1. Compilation, 将表达式编译成Opocdes

    compilation-php

    从Opocdes可以看出此时已经将b.php文件加载到了内存,也就是说在PHP代码被转换成Opocdes前已经完成了所有代码的加载。

最后得出结论,如果用户发送的请求在Compilation之前,我们更新代码是会导致服务中断的。

如何解决更新过程中请求中断的问题?

通常会给PHP项目文件夹设置一个软链接。每次更新项目时创建一个新文件夹,然后将PHP项目的全量代码拷贝过去,最后修改软链接指向新文件夹。这样就能保证用户请求不受文件更新的影响。gitlab的CD流程中已经集成该方法。