PHP7新特性整理

版本

本文基于PHP版本:7.0~7.1.x

新特性

全局

define() 定义常量数组

define('ANIMALS', [
    'dog',
    'cat',
    'bird'
]);

echo ANIMALS[1]; // 输出 "cat"

  1. 匿名类:通过new class来实例化一个匿名类,可以创建一次性的简单对象

     // PHP 7 之前的代码
     class Logger
     {
         public function log($msg)
         {
             echo $msg;
         }
     }
    
     $util->setLogger(new Logger());
    
     // 使用了 PHP 7+ 后的代码
     $util->setLogger(new class {
         public function log($msg)
         {
             echo $msg;
         }
     });
  2. 常量可见性 1. public(默认) 2. protected 3. private

     class ConstDemo
     {
         const PUBLIC_CONST_A = 1;
         public const PUBLIC_CONST_B = 2;
         protected const PROTECTED_CONST = 3;
         private const PRIVATE_CONST = 4;
     }

函数

  1. 类型声明

    参数

    标量类型

    其他

    字符串(string)

    类名(ClassName)

    整型(int)

    接口(interface)

    浮点数(float)

    数组(array)

    布尔值(bool)

    回调(callable)

    ----

    object

    ----

    iterable伪类

    ----

    Throwable

    返回值:支持上面的类型

    1. 引入void

  2. 可为空类型Nullable(?):返回特定类型或NULL

  3. 新增函数 1. 整数除法函数:intdiv 2. 随机字符串:random_bytes 3. 随机整数:random_int

  4. 函数调整

    unserialize 提供过滤:这个特性旨在提供更安全的方式解包不可靠的数据。它通过白名单的方式来防止潜在的代码注入。

运算符

  1. null合并运算符(??):如果变量存在且值不为NULL, 它就会返回自身的值,否则返回它的第二个操作数

  2. 太空船操作符(<=>):用于比较两个表达式。当$a小于、等于或大于$b时它分别返回-1、0或1

语言结构

  • 生成器 1. yield不再需要括号

    1. 返回表达式:Generator::getReturn(),只能在生成器完成产生工作以后调用一次

    2. 委派:只需在最外层生成其中使用 yield from, 就可以把一个生成器自动委派给其他的生成器、traverable、array

  • 允许在克隆表达式上访问对象成员:(clone $foo)->bar()

  • list短数组语法([]),可嵌套:[$id, $name] = $student;

    一个有趣的例子:交换两个变量

  • list元素顺序

  • 带键的list

  • 断言:不符合预期将抛异常

  • 命名空间:从同一 namespace 导入的类、函数和常量现在可以通过单个use 语句一次性导入

会话

lazy_write:默认开启

它的作用是控制 PHP 只有在会话中的数据发生变化的时候才写入会话存储文件,如果会话中的数据没有发生改变,那么 PHP 会在读取完会话数据之后, 立即关闭会话存储文件,不做任何修改,可以通过设置 read_and_close 来实现。

cache_limiterarrow-up-right指定会话页面所使用的缓冲控制方法(none/nocache/private/private_no_expire/public)。默认为 nocache

异常

  1. 多异常捕获:catch (FirstException | SecondException $e),用来处理不同类的不同异常

  2. 异常捕获高级抽象(同时可捕获Error或Exception):Throwable

字符串

负数偏移:"abcdef"[-2]

PCNTL

pcntl_async_signals(),用于启用无需 ticks (这会带来很多额外的开销)的异步信号处理

PHP5

Tick(时钟周期)是一个在 declare 代码段中解释器每执行 N 条可计时的低级语句就会发生的事件。

不是所有语句都可计时。通常条件表达式和参数表达式都不可计时。

这段代码在执行pcntl_signal前,先加入了declare(ticks = 1)。因为PHP的函数无法直接注册到操作系统信号设置中,所以pcntl信号需要依赖ticks机制。

pcntl_signal的实现原理是,触发信号后先将信号加入一个队列中。然后在PHP的ticks回调函数中不断检查是否有信号,如果有信号就执行PHP中指定的回调函数,如果没有则跳出函数。

也就是说PHP解释器每执行N条可计时的语句(低级指令)就会触发一个ticks事件。PHP中这种ticks触发信号处理函数的机制导致了PHP在对信号处理时有很大的缺陷,如果PHP中有造成阻塞的语句,由于语句无法执行结束,无法触发tick事件,信号处理函数也就不会被回调。

PHP7

Socket

禁用 TCP NAGLEarrow-up-right 算法:tcp_nodelay=true

当我们通过 TCP socket 分多次发送较少的数据时,对端可能会很长时间收不到数据,导致本端应用程序认为超时报错,这时可能是受到了TCP NAGLE算法的影响。

内核

变量

zval改动

PHP5-zval

问题:

  1. 这个结构体的大小是(在64位系统)24个字节, zend_object_value导致整个value需要16个字节。

  2. 没有预留字段,如想存储一些zval相关的信息时,无法扩展使用。

  3. zval大部分类型是按值传递,写时复制(refcount__gc)。而对象和资源类型是按引用传递,所以需要在全局加一个引用计数才能保证内存回收。

  4. 大量重复的字符串计算无法存储起来。

  5. 变量引用造成写时复制,在一些大数组拷贝的时候容易导致性能问题。

  6. 临时变量在堆上分配zval(MAKE_STD_ZVAL/ALLOC_ZVAL),分配效率低,且对缓存不够友好。

PHP7-zval

64位环境下,现在只需要16个字节 1. value 2. 扩展字段

  • u1: 类型相关信息

  • u2:辅助字段

改动:

  1. 能直接保存的值,不再进行引用计数,而是直接拷贝赋值,如IS_LONG

  2. 无值的类型,也不需要引用计数,如IS_NULLIS_TRUE

  3. 需要引用计数的类型,如数组、对象,则通过另外的结构体保存,zval仅保存指针。

  4. 引用计数信息保存在zend_refcounted_h结构。

  5. zval预先内存分配不再从堆上申请,函数内部使用的zval要么来自外面输入, 要么使用在栈上分配的临时zval。

ZendVM

对语法的评价要基于其优点,而不是基于技术限制。

AST (Abstract Syntax Tree)

将 PHP 文件转换为 opcodes 的过程现在包括三个阶段。

  1. 编码:从源代码中生成一个token流。

  2. 解析:从token流生成抽象语法树。

  3. 编译:从抽象语法树生成op_array。

线程安全

问题:尽可能的减少线程storage的检索

PHP5处理方式:层层传递。只检索一次,把已获取的storage指针传给接下来调用的函数用,其它函数再一级级往下传,这样一来各函数如果发现storage通过参数传进来了就直接用,无需再检索了。(TSRMLS_DC、TSRMLS_CC)

在函数参数中必须加上这两个宏,容易遗漏,较为丑陋。

TLS (Thread Local Storage)

通过GCC的内置__thread定义,这样各线程更新这个变量就不会冲突

可以用于修饰全局变量,函数内的静态变量,不能修饰函数的局部变量或者class的普通成员变量,且__thread变量值只能初始化为编译器常量。

内存管理

  • chunk:2MB

  • page: 4KB

  • slot:8,16,24,32,...,3072B

HugePage

关于Hugepage是啥,简单的说下就是默认的内存是以4KB分页的,而虚拟地址和内存地址是需要转换的, 而这个转换是要查表的,CPU为了加速这个查表过程都会内建TLB(Translation Lookaside Buffer), 显而易见如果虚拟页越小,表里的条目数也就越多,而TLB大小是有限的,条目数越多TLB的Cache Miss也就会越高, 所以如果我们能启用大内存页就能间接降低这个TLB Cache Miss

参考

Last updated