购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

1.1 快速认识PHP

本节首先介绍PHP语言的优势、运行机制和原理,以及PHP 7的新特性,帮助读者对PHP有一个初步的认识。

1.1.1 PHP语言的优势

PHP语言主要有以下几点优势:

(1)PHP学习入门快、开发成本低,语法相对简单,并且提供了丰富的类库,如用于图像处理的GD库、各种加密扩展(如OpenSSL和Mcrypt等),可以很方便地直接使用。很多库默认在安装PHP环境的时候都是自带的。

(2)PHP结合Linux、Nginx或Apache、MySQL可以方便快捷地搭建一套系统,PHP还支持直接调用系统命令,这样便可以用代码完成许多操作Linux的工作,如打包压缩、复制粘贴、重命名、执行Linux中grep查询筛选等。Nginx是一个非常优秀的Web服务器软件,Nginx可接收客户端请求,将PHP文件发送给PHP程序执行,Nginx中的PHP采用fastCGI的形式运行脚本。

(3)PHP支持使用MySQL、MSSQL、SQLite等多种数据库,其中与MySQL的结合使用最为流行。PHP提供了3种连接MySQL的扩展,包括MySQL扩展、MySQLi扩展和PDO扩展,MySQL扩展在PHP 5.5及以后的版本中不再支持,MySQLi是PHP推出的专门用于链接MySQL的更加安全高效的扩展,并且提供了更高级的一些操作,完全支持面向对象。PDO扩展是PHP推出的链接MySQL和其他类型的数据库的一种统一解决方案,可移植性很高,使用它可以灵活方便地切换不同类型的数据库,而不需变动更多的代码。

(4)PHP是解释执行的脚本语言,写完程序以后可以立即执行,不像C、Java、C++等其他语言需要编译再执行,这使得PHP的开发效率更高。

(5)PHP中使用到的配置文件相对简单,与PHP运行有关的配置文件常用的有php-fpm.conf和php.ini两个,并且配置参数也简单易懂。更改了PHP的配置文件不需要重新启动即可继续运行,因为PHP每次运行程序前都会主动加在配置文件中,这比Java等其他语言方便多了。

(6)PHP作为最流行、使用最为广泛的Web开发语言,有着丰富的生态圈,有许多著名的开源框架可供使用,如官方的Zend Framework、CakePHP、Yaf、Symfony等,开源论坛有Discuz!、PHPwind等,开源博客WordPress,开源网店系统如Ecshop、ShopEx等,开源的SNS系统如UCHome、ThinkSNS等。基于这些优秀的开源系统,你可以方便快速地搭建一套Web站点。另外,活跃的社区氛围也能帮助你快速解决开发中遇到的问题。

(7)结合LVS负载均衡、消息队列、数据库主从等技术,PHP能够支持一般大型网站的应用,满足绝大多数场景下的应用开发。

(8)PHP本身是由C语言开发的,在一些对性能有严苛要求的情况下,还可以使用C语言编写PHP的扩展来提升程序的执行速度,使用PHP完成主要业务的代码编写,使用C完成性能提升的需求,这使得可以保证软件开发效率的同时兼顾执行效率。在这种对软件开发速度和程序执行性能有极致追求的情况下,如果是其他语言,可能会让你束手无策,或者推倒重来。

(9)国内的许多大公司,如百度、淘宝、360等公司都广泛地使用PHP作为开发语言,在具体实践中已经取得了很大成功,有许多成功的经验可供借鉴。

1.1.2 PHP的运行机制和原理

PHP由内核Zend引擎和扩展层组成,PHP内核负责处理请求、完成文件流错误处理等操作,Zend引擎可以将PHP程序文件转换成可在虚拟机上运行的机器语言,扩展层提供一些应用层操作需要的函数类库等,比如数组和MySQL数据库的操作等。

Zend引擎是用C语言实现的,将PHP代码通过词法语法解析成可执行的opcode并实现相应的处理方法、基本的数据结构内存分配和管理等,对外提供相应的可供调用的API方法。Zend引擎是PHP的核心,所有的外围功能都是围绕它实现的。扩展层通过组件的方式提供各种基础服务、内置函数,标准库都是通过它实现的。用户也可以编写自己的扩展来实现特定的需求。服务端应用编程接口(Server Application Programming Interface,SAPI),通过一系列钩子函数使得PHP可以和外围交互数据。我们平时编写的PHP程序就是通过不同的SAPI方式得到不同的应用模式,如通过WebServer实现的Web应用和在命令行下运行的脚本等。

一段PHP程序被执行的时候会先被解析成opcode指令,然后在虚拟机中按顺序执行,由于PHP本身是用C语言开发的,所以其在执行的时候调用的都是C的函数。opcode是PHP程序执行的最基本单位。

HashTable是Zend的核心数据结构,实现了PHP里几乎所有的功能,支持key->value查询,添加删除的复杂度是O(1),支持线性遍历和混合类型。在HashTable中既有key->value形式的散列结构,也有双向链表模式,使得它能够非常方便地支持快速查找和线性遍历。Zend的散列结构是典型的hash表模型,通过链表的方式来解决冲突。Zend的HashTable是一个自增长的数据结构,当hash表数目满了之后,其本身会动态地以2倍的方式扩容并重新布置元素位置,初始大小均为8。另外,在进行key->value快速查找的时候,Zend本身还做了一些优化,通过空间换时间的方式加快速度。比如在每个元素中都会用一个变量nKeyLength标识key的长度以做快速判定。Zend HashTable通过一个链表结构实现了元素的线性遍历。理论上,做遍历使用单向链表就够了,使用双向链表的主要目的是为了快速删除链表元素,避免遍历。

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(写时复制)。

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

1.1.3 PHP 7的新特性

相较于以前的版本,PHP 7在语言语法层面和底层架构层面都有一些改进。在语法层面的改进主要是增加了一些新特性、移除了一些扩展、改变了错误异常处理等。在底层结构方面,改变了存储各种变量的Zval和Zend_String结构体、优化了Zend Array的HashTable、改进了函数的调用机制等。这些底层结构的改进大幅提升了PHP的执行效率,使得其执行速度比PHP 5高出一倍左右。

PHP是一个弱类型的语言,不过在PHP 7中支持变量类型的定义,引入了一个开关指令declare(strict_type=1);。这个指令一旦开启,就会强制当前文件下的程序遵循严格的函数传参类型和返回类型。不开启strict_type,PHP将会尝试转换成要求的类型;开启之后,PHP不再做类型转换,类型不匹配就会抛出错误。要使用严格模式,一个declare声明指令必须放在文件的顶部。这意味着严格声明标量是基于文件可配的。这个指令不仅影响参数的类型声明,还影响函数的返回值声明。

PHP 7中的新特性主要有以下几点:

(1)标量类型声明。

(2)函数返回值类型声明。

(3)新增null合并运算符。

(4)新增组合比较符。

(5)支持通过define()定义常量数组。

(6)新增支持匿名类。

(7)支持Unicode codepoint转译语法。

(8)更好的闭包支持。

(9)为unserialize()提供过滤。

(10)新增加IntlChar类。

(11)支持use语句从同一namespace导入类、函数和常量。

(12)新增整除函数intdiv()。

(13)session_start()支持接收数组参数。

除了以上列举的13点新特性之外,还有其他一些变更,读者可到http://php.net/manual/zh/migration70.new-features.php查看有关PHP 7新特性的详细变更和示例。

另外,在PHP 7中,很多致命错误以及可恢复的致命错误都被转换为异常来处理了。这些异常继承自Error类,此类实现了Throwable接口(所有异常都实现了这个基础接口)。

这也意味着,当发生错误的时候,以前代码中的一些错误处理的代码将无法被触发。因为在PHP 7版本中,已经使用抛出异常的错误处理机制了。(如果代码中没有捕获Error异常,就会引发致命错误)。

在2013年的时候,惠新宸和Dmitry(PHP语言内核开发者之一)就曾经在PHP 5.5的版本上做过一个JIT(Just In Time,即时编译,一种软件优化技术)的尝试。PHP 5.5原来的执行流程是将PHP代码通过词法和语法分析编译成opcode字节码,然后Zend引擎读取这些opcode指令,逐条解析执行。他们在opcode环节后又引入了类型推断(TypeInf),然后通过JIT生成ByteCodes再执行。采用这种技术优化,PHP的效率在实际项目中并没有取得明显的提升,于是他们重新设计了PHP的底层语言结构。Zval是存储PHP中变量的载体,是一个C语言实现的结构体(struct),PHP 5的Zval在内存中占据24个字节,而在PHP 7中优化后的Zval只占16个字节,这样变量的存储变得非常简单和高效。PHP 7优化了数组的HashTable实现,PHP 5的数组存储形式是一个支持双向链表的HashTable,不仅支持通过数组的key来做hash映射访问元素,也能通过foreach以访问双向链表的方式遍历数组元素。当我们通过key值访问一个元素内容的时候,有时需要3次的指针跳跃才能找对需要的内容。最重要的一点是,这些数组元素的存储是分散在各个不同的内存区域的,在CPU读取的时候,因为它们很可能不在同一级缓存中,导致CPU不得不到下级缓存甚至内存区域查找,从而引起CPU缓存命中下降,进而增加更多的耗时。优化后的Zend Array最大的特点是整块的数组元素和hash映射表全部连接在一起,被分配在同一块内存中。如果是遍历一个整型的简单类型数组,效率会非常快,因为数组元素(Bucket)本身是连续分配在同一块内存里的,并且数组元素的Zval会把整型元素存储在内部,也不再有指针外链,全部数据都存储在当前内存区域内。当然,最重要的是它能够避免CPU缓存命中率下降。另外,PHP 7还改进了函数的调用机制,通过优化参数传递的环节减少了一些指令,提高执行效率。 G5Xqiy32tPxNBMV01E1kPrW5CatO6c0b0u7+VTH3Dw9ezU/deKNzo9qs7+C3Su8t

点击中间区域
呼出菜单
上一章
目录
下一章
×