1. PHP7的新特性

1.太空船操作符 <=>

太空船操作符用于比较两个表达式

例如,当$a 小于、等于或大于$b 时它分别返回-1、0 或 1

2. 类型声明

img

3. NULL 合并操作符

img

4. 常量数组

img

5. NameSpace 批量导入

img

6. Throwable 接口

img

7. Closure::call()

8. intdiv 函数

img

9. list 的方括号写法

10.抽象语法树(AST)

2. 基本变量和内存管理

1.小而巧的 zval

定义:

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
typedef union _zend_value {
zend_long lval; /* long value */
double dval; /* double value */
zend_refcounted *counted;
zend_string *str;
zend_array *arr;
zend_object *obj;
zend_resource *res;
zend_reference *ref;
zend_ast_ref *ast;
zval *zv;
void *ptr;
zend_class_entry *ce;
zend_function *func;
struct {
uint32_t w1;
uint32_t w2;
} ww;
} zend_value;

struct _zval_struct {
zend_value value; /* value */
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar type, /* active type */
zend_uchar type_flags,
zend_uchar const_flags,
zend_uchar reserved) /* call info for EX(This) */
} v;
uint32_t type_info;
} u1;
union {
uint32_t next; /* hash collision chain */
uint32_t cache_slot; /* literal cache slot */
uint32_t lineno; /* line number (for ast nodes) */
uint32_t num_args; /* arguments number for EX(This) */
uint32_t fe_pos; /* foreach position */
uint32_t fe_iter_idx; /* foreach iterator index */
uint32_t access_flags; /* class constant access flags */
uint32_t property_guard; /* single property guard */
} u2;
};

大小:

类型:

1
2
3
4
5
6
7
8
9
10
11
12
/* regular data types */
#define IS_UNDEF 0
#define IS_NULL 1
#define IS_FALSE 2
#define IS_TRUE 3
#define IS_LONG 4
#define IS_DOUBLE 5
#define IS_STRING 6
#define IS_ARRAY 7
#define IS_OBJECT 8
#define IS_RESOURCE 9
#define IS_REFERENCE 10

2.不同变量对应的 zval

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
/**
* @date: 2022/3/9
* @createTime: 10:53 PM
*/

$a = 2;
echo $a;
$b = 1.1;
echo $b;
$c = null;
echo $c;
$d = true;
echo $d;
$e = false;
echo $e;
$f = "string";
echo $f;
$g = [1, 2, 3];
echo $g;
$h = new stdClass();
echo $h; // FATAL ERROR

3.zend_string 与写时复制(copy on write)

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
/**
* @date: 2022/3/10
* @createTime: 11:19 AM
*/

// 常量字符串
$c = 'hello world';
echo $c;
// 变量字符串
$a = time() . 'string';
echo $a;

// COW: copy on write
$b = $a;
echo $a;
echo $b;
$b = 'hello';
echo $a;
echo $b;

4.zend_reference 的使用

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
/**
* @date: 2022/3/10
* @createTime: 11:47 AM
*/

$a = 'string';
$b = &$a;
echo $a . "\n";
echo $b . "\n";

$b = 'hello';
echo $a . "\n";
echo $b . "\n";

unset($b);
echo $b . "\n";
echo $a . "\n";

5.PHP7 源码中数组的增删改查

例子:

6.内存管理基础知识

申请和释放内存

malloc的实现

img

6.1 PHP 内存接口

6.2 基本概念

img

  • chunk:2MB 大小的内存

  • page:4KB 大小的内存

  • slot:一个page可以划分为多个slot

img

1chunk = 512page

6.3 内存规格

  • 内存预分配:使用 mmap 分配 chunk

  • 内存分类

a. small内存(size<=3KB),30 种规格

b. large内存(3KB<size<=2MB-4KB),是 4KB 的整数倍

c. huge内存(size>2MB-4KB),是 2MB 的整数倍

6.4 内存分配流程

img

内存管理emalloc内存分配过程

6.5 small 内存

img

img

6.6 small 内存实战

例子:

注意:

  • 传过来的size大小,返回的不一定是size大小的内存。返回的是能够coversize的最小规格的内存。如申请7B的大小,返回的是8。

  • 申请小块内存时,先申请一个page,把它切割成相同大小的内存,然后把这些相同大小的内存的第一个返回,剩下的通过单向链表连接起来,挂在free_slot上,以备后需。

  • 根据内存的地址,可以快速的知道内存的大小。

6.7 内存对齐

img

img

6.8 全局变量

img

img

img

6.9 内存类型标记

img

img

img

6.10 内存标记和内存释放时大小的判断

例子:

img

3. PHP运行的生命周期

3.1 CLI 模式的生命周期

3.1.1 模块初始化部分函数调用图

img

img

img

img

3.1.2 请求初始化阶段

img

img

3.1.3 详细执行和管理阶段

img

3.1.4 请求关闭阶段

img

3.1.5 模块关闭阶段

img

img

3.2 FPM 模式的生命周期

img

3.2.1 何为 FPM 的三种模式?

  • pm=static

  • pm=dynamic

  • pm=ondemand

注意:Master 进程不提供服务,只是管理 worker 进程,当 worker 进程挂掉之后,会重新拉一个新的 worker 进程提供服务!

3.3 详解 FastCGI 协议

img

img

Nginx收到HTTP请求,然后Nginx直接把HTTP协议的包进行解包然后做一次封装,然后转发给PHP-FPM,一般PHP-FPM监听9000端口,NginxPHP-FPM发出的请求就是FastCGI编码的,这里是一个个的键值对,又有头部和尾部的一个编码,然后PHP-FPMFastCGI的协议的一个解码,然后走到FPM的生命周期里面,然后转到index.php或者其他PHP文件里面,然后进行脚本的执行,执行完以后,合成一个FastCGI协议返送给Nginx,返回的时候比较简单一些,按照HTTP协议的方式进行内容编码,但是还是FastCGI协议,Nginx再解析成HTTP协议发送给客户端。

总结:

整个FPM模式实际上是一个多进程模式,首先由calling process进程forkmaster进程,然后master进程会创建Socket,然后forkworker进程,worker进程会在accept处阻塞等待,请求过来时,由其中一个worker进程处理,按照FastCGI模式进行各阶段读取,另外,FPM建立计分板机制,关注全部和每个worker工作情况,方便使用者监控。

3.3.1 FastCGI 协议实战

通过抓包工具抓包理解:tcpdump -i lo port 9000 -XX -S

img

3.4 运行原理

3.4.1 常见的4种PHP运行模式

CGI 模式

CGI就是将Web服务器和PHP执行程序连接起来,把接收的指令传递给PHP执行程序,每有一个用户请求,都会先要创建CGI的子进程,然后处理请求,用户请求数量非常多会大量挤占系统的资源,造成效率低下,所以有多少连接请求就有多少CGI子进程,子进程反复加载是导致CGI性能低下的主要原因。

Fast-CGI 模式

CGI的升级版本,FastCGI 像是一个常驻型的 CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去fork 一次,也是一种协议。FastCGI的工作原理是:

  • Web Server启动时载入FastCGI进程管理器【PHPFastCGI进程管理器是PHP-FPM(php-FastCGI Process Manager)】(IISISAPIApacheModule);
  • FastCGI进程管理器自身初始化,启动多个CGI解释器进程 (在任务管理器中可见多个php-cgi.exe)并等待来自Web Server的连接。
  • 当客户端请求到达Web Server时,FastCGI进程管理器选择并连接到一个 CGI解释器。Web serverCGI环境变量和标准输入发送到FastCGI子进程php-cgi
  • FastCGI子进程完成处理后将标准输出和错误信息从同一连接返回Web Server。当FastCGI子进程关闭连接时,请求便处理完成。FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在 Web Server中)的下一个连接。在正常的CGI模式中,php-cgi.exe在此便退出了。

CGI模式中,可以想象 CGI通常有多慢。每一个Web请求PHP都必须重新解析php.ini、重新载入全部dll扩展并重新初始化全部数据结构。使用FastCGI,所有这些都只在进程启动时发生一次。

CLI 模式

模块模式

  • Apache + mod_php

  • lighttp + spawn-fcgi

  • nginx + PHP-FPMphpNginx中运行模式)

3.4.2 运行原理

PHP-CGIcgi是一种协议,而php-cgi是实现了这种协议的进程。不过这种实现比较烂。它是单进程的,一个进程处理一个请求,处理结束后进程就销毁。

PHP-FPM:是对php-cgi的改进,它直接管理多个php-cgi进程/线程。也就 是说,php-fpmphp-cgi的进程管理器,因此它也算是fast-cgi协议的实现。

php的运行原理:就是在服务器启动时,自动载入PHP-FPM进程管理器,从而管理多个PHP-CGI进程来准备响应用户的请求,如下图所示:

img

由于php-cgi是随服务器启动载入的,所以初始化变量只会发生一次。

3.4.3 SAPI简介

SAPI相当于PHP外部环境的代理器。PHP可以应用在终端上(CLI SAPI),也可以应用在Web服务器上(CGI SAPI)。

4. PHP代码的编译与执行

1.解释型语言也需要编译?

编译型语言

解释型语言

2.词法分析入门

2.1 NFA(不确定有穷自动机)

  • 正则表达式 (a|b)*abb

img

  • abb/aabb/babb/aababb

  • a/ab/bb/acabb

2.2 DFA(确定有穷自动机)

img

2.3 实战:使用 re2c 做词法分析

例子:/home/git/study/php_bottom/chapter5/integer.l

3.语法分析入门

  • a = b + c * 2

img

3.1 巴科斯范式

3.2 实战:使用 bison 做语法分析

例子:/home/git/study/php_bottom/chapter5/calc.y

4.PHP7中词法和语法分析

  • Zend/zend_language_scanner.l

  • Zend/zend_language_parser.y

4.1 ast 相关数据结构

  • _zend_ast

  • _zend_ast_list

  • _zend_ast_zval

  • _zend_ast_decl

4.2 实战:php7 词法分析过程

例子:/home/git/study/php_bottom/chapter5/a.php

4.3 实战:PHP7 语法分析过程

例子:/home/git/study/php_bottom/chapter5/a.php

5. AST的编译

  • $a = 1;

img

5.1 opcode 相关的数据结构

  • Struct _zend_op

  • Struct _zend_op_array

  • Struct _zend_execute_data

  • Struct _zend_vm_stack

5.2 AST 编译成指令集

6.Zend虚拟机

6.1 基础

img

6.2 Zend虚拟机的执行

5. 基本语法实现的原理

1.break 语法

1.1 生成的 AST

1.2 栈、符号表和常量

img

1.3 指令集

img

1.4 执行过程

实例:/home/git/study/php_bottom/chapter6/break.php

2. include 语法

2.1 生成的 AST

2.2 栈、符号表和常量

img

2.3 指令集

img

2.4 执行过程

例子:/home/git/study/php_bottom/chapter6/2.php

生成的 AST

指令集

img

3.if 语法

3.1 生成的 AST

img

3.2 栈、符号表和常量

img

3.3 指令集

img

img

3.4 执行过程

例子:/home/git/study/php_bottom/chapter6/condition.php

4.foreach 语法

4.1 生成的 AST

img

4.2 栈、符号表和常量

img

4.3 指令集

img

img

4.4 执行过程

6. PHP扩展的编写

实现 hello world 扩展