ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
## 概述 > PHP7将在2015年10月正式发布,PHP7 ,将会是PHP脚本语言的重大版本更新,同时将带来大幅的性能改进和新的特性,以及改进一些过时功能。 该 发布版本将会专注在性能加强,源自PHP版本树中的phpng分支。在硅谷公司的ZendCon会议,PHP工具厂商Zend技术官方讨论phpng和 PHP7的进度。“(本次升级)真正专注于帮助业界的应用程序显著加强执行速度,再加上,我们在PHP中的其他改进,”Zend的首席执行官安迪特曼斯 (曾参与了PHP语言的持续开发和发展)表示。 ## 新特性 ### PHP7引擎( What will be in PHP 7 / PHPNG ) - Performance Improvements with the addition of PHPNG engine.(使用PHPNG引擎来提升性能) - PHPNG不再使用zval的二级指针。大多数出现的zval**变量和参数都将改变成zval*。相应的,使用在这些变量上的宏Z_*_PP()也需要变成Z_*_P()。 - More -> [https://wiki.php.net/phpng-upgrading](https://wiki.php.net/phpng-upgrading) - JIT - Just in Time compiler - 即时编译 ([参考百度百科介绍](http://baike.baidu.com/link?url=sap20L79nr0CnNELWfluVvtP5v3-U3RBbDD-Q3RDKHhGZwQtSHJhAzmk5YPLyVfJPoFG7O-e2KutCXEZN2VuLK)) - Just In Time(即时编译)是一种软件优化技术,指在运行时才会去编译字节码为机器码。从直觉出发,我们都很容易认为,机器码是计算机能够直接识别和执行的,比起Zend读取opcode逐条执行效率会更高。其中,HHVM(HipHop Virtual Machine,HHVM是一个Facebook开源的PHP虚拟机)就采用JIT,让他们的PHP性能测试提升了一个数量级,放出一个令人震惊的测试结果,也让我们直观地认为JIT是一项点石成金的强大技术。 - 通过JIT,可以降低VM的开销,同时,通过指令优化,可以间接降低内存管理的开发,因为可以减少内存分配的次数。然而,对于真实的WordPress项目来说,CPU耗时只有25%在VM上,主要的问题和瓶颈实际上并不在VM上。因此,JIT的优化计划,最后没有被列入该版本的PHP7特性中。不过,它很可能会在更后面的版本中实现,这点也非常值得我们期待哈。 - Abstract Syntax Tree for compilation(抽象语法树编译) - Asynchronous refactoring of the I/O layer. 对I/O层的异步重构 - Multi-threaded build in Web Server多线程构建Web服务器 - Expanded use of ->, [], (), {}, and :: operators 扩展使用 ->, [], (), {}, 和 :: 符号 - 100% increase in performance性能提升 100% (应该是QPS) - Cool Name: PHPNG 酷名:PHPNG引擎 ### Zval的改变 > PHP的各种类型的变量,其实,真正存储的载体就是Zval,它特点是海纳百川,有容乃大。从本质上看,它是C语言实现的一个结构体(struct)。对于写PHP的同学,可以将它粗略理解为是一个类似array数组的东西。 PHP5的Zval,内存占据24个字节,PHP7的Zval,内存占据16个字节。 Zval从24个字节下降到16个字节,为什么会下降呢,这里需要补一点点的C语言基础,辅助不熟悉C的同学理解。struct和union(联合体)有点不同,Struct的每一个成员变量要各自占据一块独立的内存空间,而union里的成员变量是共用一块内存空间(也就是说修改其中一个成员变量,公有空间就被修改了,其他成员变量的记录也就没有了)。因此,虽然成员变量看起来多了不少,但是实际占据的内存空间却下降了。 > 不需要引用的类型:NULL、Boolean、Long、Double 需要引用的类型:String、Array、Object、Resource、Reference ### 内部类型zend_string Zend_string是实际存储字符串的结构体,实际的内容会存储在val(char,字符型)中,而val是一个char数组,长度为1(方便成员变量占位)。 ### PHP数组的变化(HashTable和Zend Array) 在编写PHP程序过程中,使用最频繁的类型莫过于数组,PHP5的数组采用HashTable实现。如果用比较粗略的概括方式来说,它算是一个支持双向链表的HashTable,不仅支持通过数组的key来做hash映射访问元素,也能通过foreach以访问双向链表的方式遍历数组元素。 当我们通过key值访问一个元素内容的时候,有时需要3次的指针跳跃才能找对需要的内容。而最重要的一点,就在于这些数组元素存储,都是分散在各个不同的内存区域的。同理可得,在CPU读取的时候,因为它们就很可能不在同一级缓存中,会导致CPU不得不到下级缓存甚至内存区域查找,也就是引起CPU缓存命中下降,进而增加更多的耗时。 新版本的数组结构,非常简洁,让人眼前一亮。最大的特点是,整块的数组元素和hash映射表全部连接在一起,被分配在同一块内存内。如果是遍历一个整型的简单类型数组,效率会非常快,因为,数组元素(Bucket)本身是连续分配在同一块内存里,并且,数组元素的zval会把整型元素存储在内部,也不再有指针外链,全部数据都存储在当前内存区域内。当然,最重要的是,它能够避免CPU Cache Miss(CPU缓存命中率下降)。 ### Zend Array的变化: (1) 数组的value默认为zval。 (2) HashTable的大小从72下降到56字节,减少22%。 (3) Buckets的大小从72下降到32字节,减少50%。 (4) 数组元素的Buckets的内存空间是一同分配的。 (5) 数组元素的key(Bucket.key)指向zend_string。 (6) 数组元素的value被嵌入到Bucket中。 (7) 降低CPU Cache Miss。 ### 函数调用机制(Function Calling Convention) PHP7改进了函数的调用机制,通过优化参数传递的环节,减少了一些指令,提高执行效率。 ### 通过宏定义和内联函数(inline),让编译器提前完成部分工作 PHP7在这方面做了不少的优化,将不少需要在运行阶段要执行的工作,放到了编译阶段。例如参数类型的判断(Parameters Parsing),因为这里涉及的都是固定的字符常量,因此,可以放到到编译阶段来完成,进而提升后续的执行效率。 ### AST(Abstract Syntax Tree,抽象语法树) > AST在PHP编译过程作为一个中间件的角色,替换原来直接从解释器吐出opcode的方式, 让解释器(parser)和编译器(compliler)解耦,可以减少一些Hack代码,同时,让实现更容易理解和可维护。 more -> [https://wiki.php.net/rfc/abstract_syntax_tree](https://wiki.php.net/rfc/abstract_syntax_tree) ![这里写图片描述](https://box.kancloud.cn/2016-04-06_5704a6b2022c8.jpg "") ### TLS(Native Thread local storage,原生线程本地存储) PHP在多线程模式下(例如,Web服务器Apache的woker和event模式,就是多线程),需要解决“线程安全”(TS,Thread Safe)的问题,因为线程是共享进程的内存空间的,所以每个线程本身需要通过某种方式,构建私有的空间来保存自己的私有数据,避免和其他线程相互污染。而PHP5采用的方式,就是维护一个全局大数组,为每一个线程分配一份独立的存储空间,线程通过各自拥有的key值来访问这个全局数据组。 而这个独有的key值在PHP5中需要传递给每一个需要用到全局变量的函数,PHP7认为这种传递的方式并不友好,并且存在一些问题。因而,尝试采用一个全局的线程特定变量来保存这个key值。 ### Combined comparison Operator (<=>) 结合比较运算符 (<=>) ~~~ // PHP 7之前的写法:比较两个数的大小 function order_func($a, $b) { return ($a < $b) ? -1 : (($a > $b) ? 1 : 0); } // PHP新增的操作符 <=>,perfect function order_func($a, $b) { return $a <=> $b; } ~~~ ### Return Type Declarations 返回类型声明 和Scalar Type Declarations 标量类型声明 PHP语言一个非常重要的特点就是“弱类型”,它让PHP的程序变得非常容易编写,新手接触PHP能够快速上手,不过,它也伴随着一些争议。支持变量类型的定义,可以说是革新性质的变化,PHP开始以可选的方式支持类型定义。除此之外,还引入了一个开关指令declare(strict_type=1);,当这个指令一旦开启,将会强制当前文件下的程序遵循严格的函数传参类型和返回类型。 ~~~ declare(strict_type=1); function add(int $a, int $b): int{ return $a + $b } ~~~ 如果不开启strict_type,PHP将会尝试帮你转换成要求的类型,而开启之后,会改变PHP就不再做类型转换,类型不匹配就会抛出错误。对于喜欢“强类型”语言的同学来说,这是一大福音。 更为详细的介绍: [https://wiki.php.net/rfc/scalar_type_hints_v5](https://wiki.php.net/rfc/scalar_type_hints_v5) PHP7标量类型声明RFC ## 新特性语法 ### 标量类型声明 > 标量类型声明 有两种模式: 强制 (默认) 和 严格模式。 现在可以使用下列类型参数(无论用强制模式还是严格模式): 字符串(string), 整数 (int), 浮点数 (float), 以及布尔值 (bool)。它们扩充了PHP5中引入的其他类型:类名,接口,数组和 回调类型。 ~~~ <?php // Coercive mode function sumOfInts(int ...$ints) { return array_sum($ints); } var_dump(sumOfInts(2, '3', 4.1)); ~~~ 输出:int(9) 要使用严格模式,一个 declare 声明指令必须放在文件的顶部。这意味着严格声明标量是基于文件可配的。 这个指令不仅影响参数的类型声明,也影响到函数的返回值声明(参见 返回值类型声明, 内置的PHP函数以及扩展中加载的PHP函数) **完整的标量类型声明文档和示例参见** [http://php.net/manual/zh/functions.arguments.php#functions.arguments.type-declaration](http://php.net/manual/zh/functions.arguments.php#functions.arguments.type-declaration) ### 返回值类型声明 > PHP 7 增加了对返回类型声明的支持。 类似于参数类型声明,返回类型声明指明了函数返回值的类型。可用的类型与参数声明中可用的类型相同。 ~~~ <?php function arraysSum(array ...$arrays): array { return array_map(function(array $array): int { return array_sum($array); }, $arrays); } print_r(arraysSum([1,2,3], [4,5,6], [7,8,9])); ~~~ 输出: > Array ( [0] => 6 [1] => 15 [2] => 24 ) ### null合并运算符 > 由于日常使用中存在大量同时使用三元表达式和 isset()的情况, 我们添加了null合并运算符 (??) 这个语法糖。如果变量存在且值不为NULL, 它就会返回自身的值,否则返回它的第二个操作数。 ~~~ <?php // Fetches the value of $_GET['user'] and returns 'nobody' // if it does not exist. $username = $_GET['user'] ?? 'nobody'; // This is equivalent to: $username = isset($_GET['user']) ? $_GET['user'] : 'nobody'; // Coalesces can be chained: this will return the first // defined value out of $_GET['user'], $_POST['user'], and // 'nobody'. $username = $_GET['user'] ?? $_POST['user'] ?? 'nobody'; ?> ~~~ ### Spaceship operator 结合比较运算符 (<=>) ~~~ <?php // Integers echo 1 <=> 1; // 0 echo 1 <=> 2; // -1 echo 2 <=> 1; // 1 // Floats echo 1.5 <=> 1.5; // 0 echo 1.5 <=> 2.5; // -1 echo 2.5 <=> 1.5; // 1 // Strings echo "a" <=> "a"; // 0 echo "a" <=> "b"; // -1 echo "b" <=> "a"; // 1 ?> ~~~ ### Constant arrays using define() 的常量数组 > 现在可以用 define() 定义数组常量。在 PHP 5.6,只可以被定义与 const。 ~~~ <?php define('ANIMALS', [ 'dog', 'cat', 'bird' ]); echo ANIMALS[1]; // outputs "cat" ?> ~~~ ### Anonymous classes 匿名类 > 通过新的类添加了用于匿名类的支持。这些可以用于代替完整的类定义一次性的对象 ~~~ <?php interface Logger { public function log(string $msg); } class Application { private $logger; public function getLogger(): Logger { return $this->logger; } public function setLogger(Logger $logger) { $this->logger = $logger; } } $app = new Application; $app->setLogger(new class implements Logger { public function log(string $msg) { echo $msg; } }); var_dump($app->getLogger()); ?> ~~~ 输出: > object(class@anonymous)#2 (0) { } ### Unicode codepoint escape syntax Unicode编码转移语法 > 这 Unicode 编码以十六进制格式,并输出该编码在 UTF-8 为双引号括起来的字符串或定界符。任何有效的编码被接受。 ~~~ echo "\u{aa}"; echo "\u{0000aa}"; echo "\u{9999}"; ~~~ ### Closure::call() ~~~ <?php class A {private $x = 1;} // Pre PHP 7 code $getXCB = function() {return $this->x;}; $getX = $getXCB->bindTo(new A, 'A'); // intermediate closure echo $getX(); // PHP 7+ code $getX = function() {return $this->x;}; echo $getX->call(new A); ~~~ 待续… 更多参考:[http://php.net/manual/zh/migration70.new-features.php](http://php.net/manual/zh/migration70.new-features.php)