PHP运行原理和机制

虽然说 PHP 学起来相对简单,但是要精通也不是一件简单的事,我们除了要知道怎么使用之外,还得知道它的工作原理。

PHP 是一种适用于 Web 开发的脚本语言,可以将它看做是一个用C语言实现的包含大量组件的软件框架。

了解 PHP 的底层实现,有助于我们更好的运用它,优化我们程序的性能,从而实现更加强大的功能

1) Zend 引擎(核心)

Zend 引擎整体用C语言实现,是 PHP 的内核部分,它负责将 PHP 代码翻译(词法、语法解析等一系列编译过程)为可执行的 opcode 操作码,并实现相应的处理方法、基本的数据结构(如 hashtable、oo)、内存分配及管理、提供相应的 API 方法供外部调用。

Zend 是一切的核心,所有的外围功能均围绕 Zend 实现。

2) Extensions(扩展)

围绕着 Zend 引擎,Extensions 通过组件化的方式提供各种基础服务,我们常见的各种内置函数(例如变量操作函数、字符串操作函数等)以及标准库等都是通过 Extensions 来实现。用户也可以根据需要实现自己的 Extension 组件以达到功能扩展、性能优化等目的,这就是高手常说的“编写 PHP 扩展”。

3) SAPI(服务器应用程序编程接口)

SAPI 全称是 Server Application Programming Interface,译为“服务器应用程序编程接口”。

SAPI 通过一系列钩子函数,使得 PHP 可以和外围交互数据,这是 PHP 非常优雅和成功的一个设计,通过 SAPI 成功的将 PHP 本身和上层应用解耦隔离,PHP 可以不再考虑如何针对不同应用进行兼容,而应用本身也可以针对自己的特点实现不同的处理方式。

4) Application(上层应用)

这就是我们平时编写的 PHP 程序,通过不同的 SAPI 方式得到各种各样的应用模式,例如通过 Web 服务器实现网站后台、在命令行下以脚本方式运行等。

总结

如果将 PHP 看作一辆汽车,那么车的框架就是 PHP 本身,Zend 是车的引擎(发动机),Ext 下面的各种组件就是车的轮子,SAPI 可以看做是公路,车可以跑在不同类型的公路上,而一次 PHP 程序的执行就是汽车真正跑在公路上。要想让汽车跑得快,性能优异的引擎+合适的车轮+正确的跑道都是缺一不可的。

PHP 常见的运行模式

SAPI 即服务器应用程序编程接口,是 PHP 与其他应用交互的接口,PHP 脚本要执行有很多方式,比如通过 Web 服务器、命令行下或者嵌入在其他程序中。SAPI 提供了一个和外部通信的接口,

常见的 SAPI 有:cgi、fast-cgi、cli、apache 模块的 DLL、isapi 等。

CGI

CGI 即通用网关接口(Common Gateway Interface),它是一段程序,通俗的讲 CGI 就象是一座桥,把网页和 WEB 服务器中的执行程序连接起来,它把 HTML 接收的指令传递给服务器的执行程序,再把服务器执行程序的结果返还给 HTML。

CGI 的跨平台性能极佳,几乎可以在任何操作系统上实现。

CGI 在遇到连接请求后,会先要创建 CGI 的子进程,激活一个 CGI 进程,然后处理请求,处理完后结束这个子进程,这就是 fork-and-execute 模式。

综上所述,使用 CGI 方式的服务器有多少连接请求就会有多少 CGI 子进程,子进程反复加载 会导致 CGI 性能低下。当用户请求数量非常多时,会大量挤占系统的资源,如内存、CPU 时间等,造成性能低下。

FastCGI

fast-cgi 是 CGI 的升级版本,FastCGI 像是一个常驻(long-live)型的 CGI,它激活后可以一直执行着。

FastCGI 的工作原理:

Web Server 启动时载入 FastCGI 进程管理器(IIS ISAPI 或 Apache Module);

FastCGI 进程管理器自身初始化,启动多个 CGI 解释器进程(可见多个 php-cgi)并等待来自 Web Server 的连接;

当客户端请求到达 Web Server 时,FastCGI 进程管理器选择并连接到一个 CGI 解释器。Web server 将 CGI 环境变量和标准输入发送到 FastCGI子进程 php-cgi;

FastCGI 子进程完成处理后将标准输出和错误信息从同一连接返回 Web Server。当 FastCGI 子进程关闭连接时,请求便处理完成了。FastCGI 子进程接着等待并处理来自 FastCGI 进程管理器(运行在 Web Server 中)的下一个连接。 在 CGI 模式中,php-cgi 在此便退出了。

APACHE2HANDLER

PHP作为Apache 的模块,Apache 服务器在系统启动后,预先生成多个进程副本驻留在内存中,一旦有请求出现,就立即使用这些空余的子进程进行处理,这样就不存在生成子进程造成的延迟了。这些服务器副本在处理完一次 HTTP 请求之后并不立即退出,而是停留在计算机中等待下次请求。对于客户浏览器的请求反应更快,性能较高。

apache模块的DLL

该运行模式是我们以前在 windows 环境下使用 apache 服务器经常使用的,而在模块化(DLL)中,PHP 是与 Web 服务器一起启动并运行的。(是 apache 在 CGI 的基础上进行的一种扩展,可以加快 PHP 的运行效率)

ISAPI

ISAPI 即 Internet Server Application Program Interface,是微软提供的一套面向 Internet 服务的 API 接口。一个 ISAPI 的 DLL,可以在被用户请求激活后长驻内存,等待用户的另一个请求,还可以在一个 DLL 里设置多个用户请求处理函数,此外 ISAPI 的 DLL 应用程序和 WWW 服务器处于同一个进程中,效率要显著高于 CGI。

CLI

CLI(全称:command-line interface)命令行界面,是在图形用户界面得到普及之前使用最为广泛的用户界面,它通常不支持鼠标,用户通过键盘输入指令,计算机接收到指令后,予以执行。也有人称之为字符用户界面(CUI)。

PHP 的执行流程和 opcode

我们再来看看 PHP 代码执行所经过的流程。


v2-d2154b05e6a5487f3542fa84563c7342_b.png

图:PHP 的执行流程

一段PHP代码会经过词法解析、语法解析等阶段,会被翻译成一个个指令(opcode),然后 zend 虚拟机会顺序执行这些指令。PHP 本身是用C语言实现的,因此最终调用的也是C语言的函数,实际上我们可以把 PHP 看做一个C语言开发的软件。

PHP 执行的核心就是翻译出来的一条一条指令,也就是 opcode,opcode 是 PHP 程序执行的最基本单位。

在计算机科学领域中,操作码(Operation Code)被用于描述机器语言指令中,指定要执行某种操作的那部分机器码,构成 opcode 的指令格式和规范由处理器的指令规范指定。

一个 opcode 由两个参数(op1,op2)、返回值和处理函数组成。PHP 程序最终被翻译为一组 opcode 处理函数的顺序执行。

下面列举了几个常见的处理函数:

ZEND_ASSIGN_SPEC_CV_CV_HANDLER : 变量分配 (a=b);

ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER:函数调用;

ZEND_CONCAT_SPEC_CV_CV_HANDLER:字符串拼接 a.b;

ZEND_ADD_SPEC_CV_CONST_HANDLER: 加法运算 $a+2;

ZEND_IS_EQUAL_SPEC_CV_CONST:判断相等 $a==1;

ZEND_IS_IDENTICAL_SPEC_CV_CONST:判断相等 $a===1。

HashTable

HashTable是Zend的核心数据结构,在PHP里面几乎并用来实现所有常见功能,我们知道的PHP数组即是其典型应用,此外在zend内部,如函数符号表、全局变量等也都是基于HashTable。

HashTable具有如下特点:

支持典型的key->value查询;
可以当做数组使用;
添加、删除节点是O(1)复杂度;
key支持混合类型,同时存在关联数组合索引数组;
Value支持混合类型:array("string",2332);
支持线性遍历,如 foreach。

Zval

由于PHP 是一门弱类型语言,本身不严格区分变量的类型。PHP 在声明变量的时候不需要指定类型。PHP 在程序运行期间可能进行变量类型的隐式转换。和其他强类型语言一样,程序中也可以进行显式的类型转换。Zval 是 Zend 中另一个非常重要的数据结构,用来标识并实现 PHP 变量。

Zval 主要由以下 3 部分组成。

Type:指定了变量所述的类型(整数、字符串、数组等);
refcount&is_ref:用来实现引用计数;
value:是核心部分,存储了变量的实际数据。

Zval 用来保存一个变量的实际数据。因为要存储多种类型,所以 zval 是一个 union,也由此实现了弱类型。

引用计数在内存回收、字符串操作等地方使用得非常广泛。PHP 中的变量就是引用计数的典型应用。Zval 的引用计数通过成员变量 is_ref 和 ref_count 实现。通过引用计数,多个变量可以共享同一份数据,避免频繁复制带来的大量消耗。

在进行赋值操作时,Zend 将变量指向相同的 Zval,同时 ref_count++,在 unset 操作时,对应的 ref_count-1。只有 ref_count 为 0 时才会真正执行销毁操作。如果是引用赋值,Zend 就会修改 is_ref 为 1。

PHP 变量通过引用计数实现变量共享数据,当试图写入一个变量时,Zend 若发现该变量指向的 Zval 被多个变量共享,则为其复制一份 ref_count 为 1 的 Zval,并递减原 Zval 的 refcount,这个过程称为“Zval分离”。可见,只有在有写操作发生时,Zend 才进行复制操作,因此也叫 copy-on-write(写时复制)。

对于引用型变量,其要求和非引用型相反,引用赋值的变量间必须是捆绑的,修改一个变量就修改了所有捆绑变量。

点关注,不迷路

好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才。之前说过,PHP方面的技术点很多,也是因为太多了,实在是写不过来,写过来了大家也不会看的太多,所以我这里把它整理成了PDF和文档,如果有需要的可以点这里进阶PHP月薪30k>>>架构师成长路线【视频、面试文档免费获取】

24020938-0e1d4138a0a22218.png

24020938-7cffd24782dd56f9.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容

  • PHP说简单,但是要精通也不是一件简单的事。我们除了会使用之外,还得知道它底层的工作原理。 PHP是一种适用于we...
    flysharp阅读 282评论 0 3
  • PHP说简单,但是要精通也不是一件简单的事。我们除了会使用之外,还得知道它底层的工作原理。 PHP是一种适用于we...
    天天在此TTTT阅读 86评论 0 0
  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 8,521评论 28 53
  • 信任包括信任自己和信任他人 很多时候,很多事情,失败、遗憾、错过,源于不自信,不信任他人 觉得自己做不成,别人做不...
    吴氵晃阅读 6,181评论 4 8
  • 怎么对待生活,它也会怎么对你 人都是哭着来到这个美丽的人间。每个人从来到尘寰到升入天堂,整个生命的历程都是一本书,...
    静静在等你阅读 4,957评论 1 6