PHP7实践指南:O2O网站与App后台开发
上QQ阅读APP看书,第一时间看更新

1.1 快速认识PHP

网页的本质是超文本标记语言,即HTML,通过结合Web技术可以创建出功能强大的网站应用。我们所能看到的一个个网页可以通过浏览器查看源代码的方式看到这些超文本标记语言。在网页之间通过超链接的方式进行切换,使用一些HTML标记来表现网页形式装载资源等。一个网页也是一个文件,一般是以HTML或者HTM作为文件扩展名,可以使用Windows下的记事本或者其他专业编辑器编写HTML代码,比如Dreamweaver、Notepad++、Sublime等常用软件。一些编辑器还提供代码审查的功能,在编写过程中可以提示语法信息并提供自动补全机制,极大地提高了工作效率。

网络技术的发展非常迅速,在Web 1.0时代,人们主要是阅读网站上的内容,而网站内容一般由一些具体的组织生产,用户不参与网站内容的制作,这是由网站到用户的单向行为。在这一时期网站的表现形式多以门户网站为主。门户网站的出现极大地改变了人们获取信息的方式,用户可以免费从网站上获取信息,而网站可以通过出售广告位进行盈利。这一时期的门户网站代表有新浪、雅虎、网易等,用户可以免费从网站上获取信息。进入Web 2.0时代,CGI的出现给网站增多了许多动态特性,CGI可以通过接受HTML表单的数据,在服务器端进行处理,并可以将其写入硬盘存储下来,然后将处理结果返回给Web浏览器。这时候用户也可以参与到网站内容的创造中来,用户可以通过填写表单数据提交给网站服务器,这样其他人就可以通过互联网访问到这个用户创建的内容。在Web 2.0时代,实现了网站和用户之间的互动,网站的内容可以基于用户提供,实现了两者的双向交流。人们热衷于创建自己的博客,积极地在网络世界里创造内容。互联网上的内容丰富起来了,开发网站的技术也在不断地演进,JavaScript的广泛应用使得开发者可以在网站上实现绚丽且更优秀的用户体验效果。Ajax可以在不更新整个页面的情况下维护数据,减少了客户端和服务器之间的数据交换量,通过JavaScript结合CSS实现的网页样式变化使得网页看起来更加美观。

大家对于现在处于Web 2.0还是Web 3.0时代有着很大争议,笔者更倾向于我们正处在Web 3.0时代的初期阶段。在这个时代,HTML 5和CSS 3的发布使得网页的效果更加绚丽,同时人们不仅可以生产网站内容,还可以通过简单的类似搭积木的形式生产程序,移动互联网发展迅速,各种移动应用层出不穷,Web App的出现加快了这一进程,开发者可以通过编写HTML代码开发出媲美原生应用的移动应用程序。大数据和云计算作为基础服务得到广泛应用,人工智能技术也成为人们热衷研究的方向,其最终目的是建立一个可以模仿人类进行学习思辨的网络。在Web 3.0时代,随着数据的极速增加,网站的访问速度成为人们首要关心的问题,我们需要从庞大的数据量中找到有用的数据,这时对数据库存储的要求加大,出现了非关系型数据库、缓存数据库,负载均衡技术被广泛用来解决网站并发量问题。

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、Lite等多种数据库,其中与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 Frameworl、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程序执行的最基本单位。

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

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的Hash Table、改进了函数的调用机制等。这些底层结构的改进大幅提升了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优化了数组的Hash Table实现,PHP 5的数组存储形式是一个支持双向链表的HashTable,不仅支持通过数组的key来做Hash映射访问元素,也能通过foreach以访问双向链表的方式遍历数组元素。当我们通过key值访问一个元素内容的时候,有时需要3次的指针跳跃才能找对需要的内容。最重要的一点是这些数组元素的存储分散在各个不同的内存区域,在CPU读取的时候,因为它们就很可能不在同一级缓存中,会导致CPU不得不到下级缓存甚至内存区域查找,也就是引起CPU缓存命中下降,进而增加更多的耗时。优化后的Zend Array最大的特点是整块的数组元素和hash映射表全部连接在一起,被分配在同一块内存中。如果是遍历一个整型的简单类型数组,效率会非常快,因为数组元素(Bucket)本身是连续分配在同一块内存里,并且数组元素的Zval会把整型元素存储在内部,也不再有指针外链,全部数据都存储在当前内存区域内。当然,最重要的是它能够避免CPU缓存命中率下降。另外,PHP 7还改进了函数的调用机制,通过优化参数传递的环节减少了一些指令,提高执行效率。