加入收藏 | 设为首页 | 会员中心 | 我要投稿 上海站长网 (https://www.021zz.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

问世 20 多年的 PHP 还是最好的编程语言吗?

发布时间:2022-10-14 05:01:05 所属栏目:PHP教程 来源:转载
导读: 点击上方“CSDN”,选择“置顶公众号”
关键时刻,第一时间送达!
作者简介:Panda,一个热爱技术,喜欢刨根问底,热爱分享, 热爱开源的程序猿,活跃于开源社区,乐于分享交流。PHP 开源项

点击上方“CSDN”,选择“置顶公众号”

关键时刻,第一时间送达!

作者简介:Panda,一个热爱技术,喜欢刨根问底,热爱分享, 热爱开源的程序猿,活跃于开源社区,乐于分享交流。PHP 开源项目贡献者,对网络安全、PHP 内核、Nginx 内核、MySQL有一些研究。

本文来自作者在 GitChat 上分享 「2017 年 PHP 社区总结,2018 PHP 发展展望」主题内容。

php内核探索_php内核_深入理解php内核 pdf

一、2017 PHP 社区总结:回顾 PHP 语言本身的升级和变化

首先一起回顾下 PHP 的发展史:

PHP 作为 Web 开发领域性价比最高的语言,已经问世 20 多年了。

php内核探索_php内核_深入理解php内核 pdf

注:一个 Web 站点可以会使用多种语言作为它的开发语言,本文含有不少从鸟哥 PPT 里的截图,图片版权归鸟哥所有。

PHP 开始于 1994 年,最初产生于 Rasmus Lerdorf 的一个简单的想法,当时 Rasmus 用 C 语言写了一个应用程序,这个程序就是用来追踪和维护自己的个人主页的。

并且 Rasmus 对其又进行了扩展,使其可以应用于 web 表单还可以和数据库进行交互。就这样 PHP 的第一个版本就诞生了。Rasmus 称其为 “Personal Home Page/Forms Interpreter” 简称 PHP/FI。

用 Rasmus 自己的话说:起初并不想开发一门新的编程语言,但是随着 PHP/FI 的发展,渐渐的就不再受他的控制了。就这样一个开发团队行程了,并且在 1997 年的 11 月发布了第二个版本 PHP/FI 2。

再往后,Zeev Suraski 和 Andi Gutmans 两个人的出现,更是使得 PHP 的发展走向了一个新的里程碑。

1997 年,两人重新写了 PHP 的解释器,形成了 PHP 的第三个版本 PHP3,也就是在此时正式名字由 PHP/FI 改为 PHP(Hypertext Preprocessor 超文本预处理器)。

时隔一年,两人在 1998 年又重新写了 PHP 的核心代码,用了将近一年的时间,Zend 引擎在 1999 年诞生了。接着在 2000 年 5 月,带有第一代 zend 引擎的 PHP4 正式发布了。

随后其发展进入了一个平缓的阶段,带有第一代 Zend 引擎的 PHP4 在 2008 年 8 月达到 4.4.9 以后就再没有进行后续开发,也没有任何的安全更新。

我用的最早的一个 PHP4 的程序应该是 DEDECMS 了。这时 PHP 还是面向过程的编程方式。

在 2004 年 6 月份的时候,PHP 的发展到达了第二个里程碑。带有 Zend Engine II 的 PHP5 正式发布,在这 PHP5 中开始支持面向对象,而且性能明显增强。

直到 2008 年很多程序都不再支持 PHP4 版本了,取而代之的是 PHP5。

接着,下一个人物该出场了,他的出现使 PHP 从 5 又上升了一格成为了 PHP6。他的名字叫 Andrei Zmievski。

当时 PHP5 发布以后,PHP 收到了各种各样的反馈,反馈的内容就是在 PHP 中缺少编码转换的支持。所以在 2005 年的时候,由 Andrei 领导在 PHP 中嵌入了 ICU 库。并且使文本字符串以 unicode-16 的方式呈现。

这一举动,对于 PHP 本身以及用户的编码方式都产生了大的改变,所以 PHP6 应运而生了。

虽说这一改变跨越很大,但是由于开发人员不能很好的理解所做的这些改变,并且向 unicode-16 编码(这一编码方式在 web 环境中很少被用到)转换会导致性能的下降,种种原因导致这一工程停滞下来。

而且在 2009 年发布的 PHP5.3 还有 2010 年发布的 5.4 几乎涵盖了所有从 PHP6 移植来的功能。因此在 2010 年这项工程停止了,直到 2014 年也没有被人们所接受。

在 2014-2015 年期间,PHP7 正式发布了。最初对于 PHP7 的这个版本是存在一些争议的,因为先前的 PHP6 并没有正式发布,就夭折了,所以直接到 7 这个版本并不是很合适。

但是在一些学术论文还有书籍中已经引用了 PHP6 这个名称,所以说最终人们将其定位 7。

对于 PHP7 其主要的目标就是通过重构 Zend 引擎,使 PHP 的性能更加的优化,同时保留语言的兼容性。由于是对其引擎的重构,因此 PHP7 的引擎目前已是第三代 Zend Engine 3。

今天 PHP7 已经正式发布,纵观其从诞生到发展壮大,有成功也有失败,而今天的成功又仅仅源于昨天的一个简单的想法。

作为一名程序员,如果自己在现在的一个想法,多少年后也能产生如此大的成就,那岂是一个 “自豪” 所能表达的。类似的情况也发生在另一个人的身上,linux 的奠基者 linus。

不管怎么说,作为一名 PHP 程序员,看到 PHP 今天的成绩自然感到高兴,自己也会在 PHP 的路上一直走下去,希望 PHP 的发展越来越好。

1.PHP 7.0 的优化

PHP 的各种类型的变量php内核,其实,真正存储的载体就是 Zval,它特点是海纳百川,有容乃大。从本质上看,它是 C 语言实现的一个结构体(struct)。

对于写 PHP 的同学,可以将它粗略理解为是一个类似 array 数组的东西。

PHP5 的 Zval,内存占据 24 个字节:

深入理解php内核 pdf_php内核_php内核探索

PHP7 的 Zval,内存占据 16 个字节:

php内核_php内核探索_深入理解php内核 pdf

Zval 从 24 个字节下降到 16 个字节,为什么会下降呢?

这里需要补一点点的 C 语言基础,辅助不熟悉 C 的同学理解。struct 和 union(联合体)有点不同,Struct 的每一个成员变量要各自占据一块独立的内存空间,而 union 里的成员变量是共用一块内存空间。

也就是说修改其中一个成员变量,公有空间就被修改了,其他成员变量的记录也就没有了。因此,虽然成员变量看起来多了不少,但是实际占据的内存空间却下降了。

除此之外,还有被明显改变的特性,部分简单类型不再使用引用。

Zval 结构图:

php内核_深入理解php内核 pdf_php内核探索

图中 Zval 的由 2 个 64bits(1 字节 =8bit,bit 是 “位”)组成,如果变量类型是 long、bealoon 这些长度不超过 64bit 的,则直接存储到 value 中,就没有下面的引用了。

当变量类型是 array、objec、string 等超过 64bit 的,value 存储的就是一个指针,指向真实的存储结构地址。

对于简单的变量类型来说,Zval 的存储变得非常简单和高效。

不需要引用的类型:NULL、Boolean、Long、Double;需要引用的类型:String、Array、Object、Resource、Reference。

(1)内部类型 zend_string

Zend_string 是实际存储字符串的结构体,实际的内容会存储在 val(char,字符型)中,而 val 是一个 char 数组,长度为 1,方便成员变量占位)。

php内核探索_php内核_深入理解php内核 pdf

结构体最后一个成员变量采用 char 数组,而不是使用 char*,这里有一个小优化技巧,可以降低 CPU 的 cache miss。

如果使用 char 数组,当 malloc 申请上述结构体内存,是申请在同一片区域的,通常是长度是 sizeof(_zend_string) + 实际 char 存储空间。

但是,如果使用 char*,那个这个位置存储的只是一个指针,真实的存储又在另外一片独立的内存区域内。

使用 char[1] 和 char* 的内存分配对比:

php内核探索_php内核_深入理解php内核 pdf

从逻辑实现的角度来看,两者其实也没有多大区别,效果很类似。

而实际上,当这些内存块被载入到 CPU 的中,就显得非常不一样。前者因为是连续分配在一起的同一块内存,在 CPU 读取时,通常都可以一同获得(因为会在同一级缓存中)。

而后者,因为是两块内存的数据,CPU 读取第一块内存的时候,很可能第二块内存数据不在同一级缓存中,使 CPU 不得不往 L2(二级缓存)以下寻找,甚至到内存区域查到想要的第二块内存数据。

这里就会引起 CPU Cache Miss,而两者的耗时最高可以相差 100 倍。

另外,在字符串复制的时候,采用引用赋值,zend_string 可以避免的内存拷贝。

(2)PHP 数组的变化(HashTable 和 Zend Array)

在编写 PHP 程序过程中,使用最频繁的类型莫过于数组,PHP5 的数组采用 HashTable 实现。

如果用比较粗略的概括方式来说,它算是一个支持双向链表的 HashTable,不仅支持通过数组的 key 来做 hash 映射访问元素,也能通过 foreach 以访问双向链表的方式遍历数组元素。

php内核_php内核探索_深入理解php内核 pdf

这个图看起来很复杂,各种指针跳来跳去,当我们通过 key 值访问一个元素内容的时候,有时需要 3 次的指针跳跃才能找对需要的内容。

而最重要的一点,就在于这些数组元素存储,都是分散在各个不同的内存区域的。

同理可得,在 CPU 读取的时候,因为它们就很可能不在同一级缓存中,会导致 CPU 不得不到下级缓存甚至内存区域查找,也就是引起 CPU 缓存命中下降,进而增加更多的耗时。

PHP7 的 Zend Array:

php内核_php内核探索_深入理解php内核 pdf

新版本的数组结构,非常简洁,让人眼前一亮。最大的特点是,整块的数组元素和 hash 映射表全部连接在一起,被分配在同一块内存内。

如果是遍历一个整型的简单类型数组,效率会非常快,因为,数组元素(Bucket)本身是连续分配在同一块内存里,并且,数组元素的 zval 会把整型元素存储在内部,也不再有指针外链,全部数据都存储在当前内存区域内。

当然,最重要的是,它能够避免 CPU Cache Miss(CPU 缓存命中率下降)。

Zend Array 的变化:

(3)函数调用机制(Function Calling Convention)

PHP7 改进了函数的调用机制,通过优化参数传递的环节,减少了一些指令,提高执行效率。

PHP5 的函数调用机制:

php内核_深入理解php内核 pdf_php内核探索

图中,在 vm 栈中的指令 send_val 和 recv 参数的指令是相同,PHP7 通过减少这两条重复,来达到对函数调用机制的底层优化。

PHP7 的函数调用机制:

深入理解php内核 pdf_php内核探索_php内核

通过宏定义和内联函数(inline),让编译器提前完成部分工作 C 语言的宏定义会被在预处理阶段(编译阶段)执行,提前将部分工作完成,无需在程序运行时分配内存。

能够实现类似函数的功能,却没有函数调用的压栈、弹栈开销,效率会比较高。

内联函数也类似,在预处理阶段,将程序中的函数替换为函数体,真实运行的程序执行到这里,就不会产生函数调用的开销。

PHP7 在这方面做了不少的优化,将不少需要在运行阶段要执行的工作,放到了编译阶段。例如参数类型的判断(Parameters Parsing),因为这里涉及的都是固定的字符常量。

因此,可以放到到编译阶段来完成,进而提升后续的执行效率。例如下图中处理传递参数类型的方式,从左边的写法,优化为右边宏的写法。

php内核_php内核探索_深入理解php内核 pdf

2. PHP7.1 的优化

3. PHP7.2 的变化

PHP 周边优秀开源项目

1. 框架篇

2. 组件篇

3. 工具篇

4. 项目篇

PHP 在 Web 生态中的变化

一句话总结 PHP 在 Web 生态中的变化:更快更高更强。

国内的 PHP 社区

二、2018PHP 发展展望

PHP 周边生态的发力

编程语言最重要的生态,社区活跃则语言繁荣,社区衰落则语言式微,就好比水与鱼的关系。

正在开发中的 libpkd => 拓展语言原生能力,构建运行时标准库,目前已经基于 Swoole 生态涌出很多优秀的框架。

PHP 程序猿的努力

随着开源的流行 越来越多的 PHPer 在贡献自己的一份力量 GitHub 上 PHP 的开源项目越来越多,例如 packagist 上的组件越来越丰富,而且 PHP 语言本身的性能也在不断提升,让 PHP 语言换发出新的生命力。

参考资料

声明:封面图为付费下载自视觉中国。

(编辑:上海站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!