## 概述
> 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)
- 前言
- PHP生成对象之设计模式—单例模式
- PHP生成对象之设计模式—工厂方法模式
- PHP之设计模式—适配器模式
- PHP之设计模式—建造者模式(通过选择mysql,mongo数据库链接类型做说明)
- PHP之设计模式—委托模式
- PHP面向对象学习一:对象基础实践
- PHP面向对象学习之二:深入了解面向对象高级特性
- PHP面向对象学习之三:抽象类和接口类的实际作用
- PHP解决问题进化论(整理笔记)
- PHP7新特性整理介绍篇
- php-fpm 与 Nginx优化总结
- Centos+Nginx+PHP7.0编译安装(和PHP5.6老版本共存)
- PHP7:Mongodb API使用
- PHP之include/require深入了解
- PHP内核了解:生命周期及运行模式