本文将用约15分钟的时间,对PHP v7.x版本的改动进行简要的回顾。

PHP 7.3 版本一经推出,我为了深入掌握这门广泛应用的编程语言的新增功能和改进亮点,便下定决心对PHP的开发领域进行了深入探究:究竟在开发哪些内容,以及其未来的发展方向。

我的官方群点击进入

说到做到,150粉丝福利安排PHP进阶资料,免费获取

在浏览了PHP在PHP 7.x版本开发阶段所引入的一系列特性的概述之后,我决定将这份列表进行整合,作为一项有益的补充。我坚信,这样的做法同样会受到其他人的认可和欢迎。

以 PHP 5.6 为起点,我们将探讨新增或调整了哪些特性。此外,我还为每个提及的特性附上了通往官方文档的链接,以便有兴趣的读者进一步查阅。

PHP 7.0

匿名类的支持

在下面两种情况下,匿名类可能会被使用在命名类中:

new class($i) {
此函数构造器接受一个参数,即变量$i。
        $this->i = $i;
    }
}

整除函数 — 安全的除法 (即使是被 0 整除)

此函数执行的操作是提取第一个参数除以第二个参数所得结果的整数部分。若第二个参数即除数为零,函数将触发一个 E_WARNING 类型的错误,并输出 FALSE 值。

该函数执行整数除法操作,对两个整数值进行除法运算,并返回结果。

增加了新的空合并操作赋 — 也就是 “??”

$x = NULL;
$y = NULL;
$z = 3;
执行var_dump操作,输出变量$x、$y或$z的值,若$x存在则显示其值,若$x不存在则显示$y的值,若$x和$y均不存在则显示$z的值,结果显示为整数3。
$x是一个数组,其中包含一个键值对,键为字符串“c”,值为“meaningful_value”。
执行var_dump操作,输出$x数组中键为"a"、"b"或"c"的元素值,若"a"不存在则输出"b"的值,若"b"也不存在则输出"c"的值,结果显示为字符串"meaningful_value",长度为16个字符。

添加新的操作符 — 飞船符 ()

飞船符用于优化和简化比较操作。

// 使用  (飞船符)前
function order_func($a, $b) {
    return ($a  $b) ? 1 : 0);
}
// 使用  (飞船符)之后
function order_func($a, $b) {
    return $a  $b;
}

标量类型声明

这只是迈向在 PHP — v0.5 版本中引入更高级别类型编程特性的初始步骤。

定义一个函数,该函数接收两个浮点数作为参数,名为add,执行加法运算,并返回一个浮点数结果。
    return $a + $b;
}
add(1, 2); // float(3)

返回类型声明

新增了除继承标量类以外的特性,遗憾的是,这些特性并未被设定为可选项(相关说明将在v7.1版本中阐述)。

interface A {
    static function make(): A;
}
class B implements A {
    static function make(): A {
        return new B();
    }
}

组使用声明

// 显式使用语法:
引入FooLibrary库中的Bar模块下的Baz子模块中的ClassA类;
引入FooLibrary库中的Bar模块下的Baz子模块中的ClassB类;
引入FooLibrary库中的Bar模块下的Baz子模块中的ClassC类。
引入FooLibrary库中的Bar模块Baz子模块的ClassD类,将其命名为Fizbo。
// 分组使用语法:
引入FooLibrary库中的Bar模块下的Baz子模块,定义了ClassA、ClassB、ClassC和ClassD四个类,其中ClassD被重命名为Fizbo。

生成器委托

生成器函数体中允许使用如下新的语法:

yield from 

性能提升

PHP 7 比 PHP 5.6 快上两倍。

显著降低内存占用

图表显示,PHP 7.0 在性能提升和降低内存消耗方面实现了显著进步。对于包含数据库查询的页面,7.0.0 版本在开启opcache的情况下,其速度是5.6版本的3倍,而在未开启opcache的情况下,速度也提升了2.7倍。此外,在内存消耗方面,两者之间的差距同样十分显著。

Throwable 接口 **

重构后的异常类别采用了不太直观的命名方式,但这样的设计有助于降低误解,尤其是对于刚接触的初学者来说。

现在,Errors与Exceptions已成功转化为Throwable。

这是 Throwable 层次结构:

interface Throwable
|- Error implements Throwable
算术错误是错误的一个子类。
零除错误是算术错误的一个子类。
断言错误是错误的一种子类
    |- ParseError extends Error
    |- TypeError extends Error
ArgumentCountError 是 TypeError 的一种子类。
异常类继承自错误处理类。
ClosedGeneratorException 是 Exception 的子类。
DOM异常是异常类的一个子类。
错误异常类继承自异常类。
IntlException 是 Exception 的子类
逻辑异常类继承自异常类。
BadFunctionCallException 是 LogicException 的一种子类。
BadMethodCallException 是 BadFunctionCallException 的一种子类。
领域异常是逻辑异常的一种子类。
InvalidException作为LogicException的一个子类被定义,继承了LogicException的特性。
长度异常是逻辑异常的一个子类。
OutOfRangeException 是 LogicException 的一种子类。
PharException 是 Exception 的子类
反射异常是异常类的一个子类。
运行时异常是异常类的一个子类,它继承自异常类。
异常类型“OutOfBoundsException”是“RuntimeException”的子类。
OverflowException 是 RuntimeException 的一个子类。
PDO异常是RuntimeException的子类。
RangeException 是 RuntimeException 的子类。
下溢异常是运行时异常的一种子类。
UnexpectedValueException 是 RuntimeException 的一个子类。

* 警告!你只能

通过继承Error与Exception类,Throwable得以实现。

换言之,只有 Exception 或 Error 的子类能够实现继承自 Throwable 的接口。

Unicode码点转义规则表现为——“u{xxxxx}”形式。

echo "输出:反转后的文本";
输出 "mañana"; // "mañana"
输出 "mañana" 字符串;其中 "n" 字符与 ~ 符号(Unicode 编号 U+0303)相结合,形成 "ñ"。

上下文敏感的语法分析器

* 全局保留的单词都成为了 * 半保留:

可调用的类特性继承并实现了,具有静态、抽象、最终和公开、受保护、私有、常量等属性。
终止声明,结束循环,结束遍历,结束条件,结束while循环,并且,全局,跳转至,类型为,代替。
在命名空间中,不允许使用诸如new、xor、try、use、var、exit、list、clone、include、include_once、throw或array等关键字。
输出显示  反射输出  包含需求  包含需求一次  返回结果  否则  否则如果  默认值  跳出循环  继续执行  切换语句  生成值
函数、如果、结束选择、最终、对于、遍历、声明、情况、执行、当、作为、捕获、终止、自身、父类。

除了继续禁止设定一个名为“class”的类常量之外,还需注意,类名解析中的“::class”同样受到限制。

生成器返回表达式

统一变量语法

dirname () 函数的级别支持

PHP 7.1

可空类型

function answer(): ?int  {
    return null; //成功
}
function answer(): ?int  {
    return 42; // 成功
}
function answer(): ?int {
返回一个新的stdclass对象;// 错误
}
function say(?string $msg) {
    if ($msg) {
        echo $msg;
    }
}
执行代码`say("hello");`,结果显示为“成功”,随后在屏幕上显示信息“hello”。
say(null); // 成功 -- 不打印
say(); // 错误 -- 参数丢失
调用新创建的stdclass对象时出现错误;--错误类别

Void 返回

该函数执行完毕后不应返回任何内容。
执行返回1操作;然而,这表示存在严重问题:一个void类型的函数是不允许返回任何值的。
}

此类型与在函数调用过程中强制规定的其他返回类型存在差异,它会在编译阶段进行验证,从而使得错误在函数未被实际调用时便已显现。

函数若具备无返回值类型或定义为void,则其可进行隐式返回操作,亦或通过不携带任何值的返回语句来实现。

若函数名为“lacks_return”,则其定义应包含“void”关键字,表示不返回任何值。
    // valid
}

可迭代的伪类型

函数一般会接收或输出数组,或者提供一个可与 foreach 一起使用的可遍历对象。然而,由于数组属于基本数据类型,而可遍历对象是一个接口,因此在参数或返回值的类型声明中,目前尚不能明确指出该值是否支持迭代操作。

定义函数foo,该函数接受一个可迭代对象作为参数。
遍历可迭代对象中的每个元素,对每个值进行处理。
        // ...
    }
}

可迭代对象同样适用于函数的返回类型,用以指示函数输出的结果是一个可遍历的数据。若函数输出的结果并非可遍历的数组或其实例,系统将抛出类型错误异常。

function bar(): iterable {
    return [1, 2, 3];
}

声明iterable类型的参数,其默认值可以是空值或数组。

定义函数foo,其中参数iterable默认为空数组[]。
    // ...
}

可调用的闭包

class Closure {
    ...
定义一个静态方法,该方法接受一个可调用对象作为参数,并返回一个闭包:public static function createClosureFromCallable(callable $callable) : Closure {...}
    ...
}

数组结构赋值的方括号语法

$array = [1, 2, 3];
对 $a,$b 和 $c 进行按键值排序,从 0 开始依次将对应的值赋给 $array 数组的各个元素。
[$a, $b, $c] = $array;
请通过按“a”、“b”和“c”这三个键来为变量$a$、$b$和$c$分别赋予数组$arry$中相应元素的数值。
将数组$array中的元素分别赋值给变量$a、$b和$c,具体为$a获取数组中的"a"键对应的值,$b获取数组中的"b"键对应的值,$c获取数组中的"c"键对应的值。

list() 的方括号语法

定义数组powersOfTwo,其键值对分别为:1对应2,2对应4,3对应8。
$powersOfTwo数组中,1对应的是$oneBit,2对应的是$twoBit,3对应的是$threeBit。

类常量的可见性

class Token {
    // 常量默认为 public
    const PUBLIC_CONST = 0;
        // 常量也可以定义可见性
定义了一个私有常量,其名为PRIVATE_CONST,并为其赋值为0。
定义了一个受保护的常量,命名为PROTECTED_CONST,其值为0。
公开定义常量PUBLIC_CONST_TWO的值为0。
        //常量只能有一个可见性声明列表
定义了一个私有常量FOO,其值为1,以及另一个私有常量BAR,其值为2。
}

捕获多个异常类型

try {
// 部分代码...
捕捉到ExceptionType1或ExceptionType2类型的异常对象$e。
// 处理异常的代码
} catch (Exception $e) {
// ...
}

PHP 7.2参数类型扩大

<?php
class ArrayClass {
public function foo(array $foo) { /* ... */ }
}
// 这个 RFC 提议允许类型被扩大为无类型,也就是任何类型。
// 类型可以作为参数传递。
// 任何类型的限制都可以通过用户写在方法体中的代码来实现。
class EverythingClass extends ArrayClass {
public function foo($foo) { /* ... */ }
}

不可数对象的计数

当对象为标量或未实现 Countable 接口时,执行 count() 方法将输出数字1,这一结果显然是不合理的。

PHP 7.2 版本对使用标量、null或未实现 Countable 接口的对象作为 count() 方法的参数进行了处理,并引入了新的 WARNING 警告提示。

在命名空间的 列表用法中使用尾随逗号

严禁对FooBar结构体中的Foo、Bar、Baz成员进行修改,结构体定义如下:FooBar{ Foo, Bar, Baz, }。

Argon2 密码散列算法

目前通用的密码函数为密码散列提供了向后兼容的简易接口。本 RFC 建议采用 Argon2i(版本1.3)作为密码函数,以替换原有的 Bcrypt 密码散列算法。

调试 PDO 预处理语句模拟

$db = new PDO(...);
// 生成没有绑定值的语句
执行查询操作以检索数据库中的数据,查询语句为 `SELECT 1`,该操作通过 `$db` 对象的 `query` 方法完成。
执行var_dump操作,对$stmt对象的activeQueryString方法返回的结果进行输出,结果显示为字符串“SELECT 1”,长度为8个字符。
$db->prepare() 方法调用来执行一个包含占位符的查询,其中占位符为 :string;该查询被设置为从数据库中检索特定字符串。
$stmt->将冒号加字符串作为占位符绑定,具体值为'foo'。
// 返回执行前,未解析的查询
执行var_dump操作,结果显示:$stmt对象的activeQueryString方法返回的字符串为"SELECT :string",长度为14个字符。
// 返回执行后,已解析的查询
$stmt->execute();
执行var_dump操作以输出$stmt对象的活跃查询字符串,结果显示为:字符串长度为11,内容为"SELECT 'foo'"。

PHP 7.3JSON_THROW_ON_ERROR

在长时间内,对于JSON的使用,缺乏有效的错误处理手段,这一现象被全球的开发者们普遍视为该语言的一大不足之处。

在 PHP 7.2 之前,我们得采取特定手段从 JSON 数据中提取错误信息,尽管这种方法并不稳定,且技术含量不高。

例子如下:

json_decode("{");
json_last_error()的返回值等于JSON_ERROR_NONE,表明处理结果存在错误。
调用json_last_error_msg()函数,其输出信息为“语法错误”。

那么让我们看看如何使用这种新语法糖:

use JsonException;
try {
执行json_encode函数,将"{ "作为参数传入,并设置JSON_THROW_ON_ERROR选项,以确保在编码过程中遇到错误时抛出异常。
    return base64_encode($json);
} catch (JsonException $e) {
抛出加密异常,信息为“无法加密数据”,错误代码为0,并附带异常对象$e。
}

从上述代码中我们可以观察到,json_encode 函数新增了一个可选的参数 JSON_THROW_ON_ERROR,该参数的作用是捕捉到错误,并通过以下异常处理方式来展示:,

执行 $e->getMessage(); 操作等同于调用 json_last_error_msg() 函数。
获取异常代码的方法是调用 $e->getCode();这一操作与执行 json_last_error() 函数所达到的效果相同。

添加 is_countable 函数

// 之前:
若$foo是一个数组,或者$foo实现了Countable接口,{
    // $foo is countable
}
// 之后
if (is_countable($foo)) {
    // $foo is countable
}

新增数组函数,包括array_key_first()和array_key_last()。

获取数组中的第一个键值,赋值给变量$firstKey。
获取数组中的最后一个键值,变量$lastKey被赋予该键名。

原生支持同站点 Cookie 判断

判断同站点 Cookie 的使用方法有两种,分别是 Lax 和 Strict。这两种方法在跨域 HTTP GET 请求中 Cookie 的访问权限上存在差异。具体来说,采用 Lax 方式的 Cookie 可以实现跨域 GET 请求的访问,而采用 Strict 方式的 Cookie 则不允许进行跨域 GET 请求的访问。而采用 POST 方法时,并无差异存在:这是因为浏览器不允许用户在涉及不同域名的 POST 请求中获取 Cookie 信息。

禁止设置Cookie,其键值对为key=value,作用范围为网站根目录,域名限定为example.org,具有HttpOnly属性,同时遵循SameSite的Lax或Strict策略。

从 PCRE 迁移至 PCRE2

Argon2 哈希密码功能增强

现有的 password_* 函数为密码散列提供了向后兼容的简化接口,本 RFC 提案建议在 password_* 函数中采纳 Argon2id,将其作为原先提出的 Argon2i 的安全性更强的替代选择。

在函数调用中允许尾随逗号

$newArray = array_merge(
    $arrayOne,
    $arrayTwo,
在函数的调用过程中,可以使用逗号作为句尾的符号。
);

list () 使用参考

$array = [1, 2];
list($a, &$b) = $array;

相当于:

$array = [1, 2];
$a = $array[0];
$b = &$array[1];

不建议使用不区分大小写的常量

PHP 7.4(开发中)

参数类型(Typed properties)

class User {
    public int $id;
    public string $name;
本函数用于创建对象,其中接收一个整型参数作为标识符,以及一个字符串参数作为名称。
        $this->id = $id;
        $this->name = $name;
    }
}

外部函数接口,通常简称为FFI,是一种允许不同编程语言之间相互调用的技术手段。

外部函数接口,简称为 FFI,是 Python 与 LuaJIT 在快速原型开发中的一项极为实用的特性。它允许纯脚本语言直接调用 C 语言编写的函数和数据类型,极大地提升了系统代码的开发效率。此外,FFI 在 PHP 中还提供了一种独特的方式,允许开发者利用 PHP 语言来编写 PHP 扩展,并将其与 C 语言库进行绑定。

非空合并赋值运算符,也被称为空值合并赋值运算符。

// 下面几行代码完成相同功能
在请求的数据中,若评论部分的用户ID未指定,则将其默认设置为'value'。
// 使用非空赋值运算符,替代上面的方法
若请求中的数据数组中不存在键为'user_id'的'comments'键对应的值,则将其默认设置为'value'。

预加载(Preloading)

PHP长久以来都采用了操作码缓存机制,如APC、Turck MMCache、Zend OpCache等。这些机制几乎完全消除了PHP代码重新编译的成本,从而带来了显著的性能飞跃。而新增的预加载功能,只需通过调整php.ini文件中的opcache.preload配置项即可轻松实现。通过此配置,可以指定一个PHP文件,该文件负责执行预加载任务,随后,通过引入其他文件或调用opcache_compile_file()函数,实现其他文件的预加载。

该哈希扩展功能始终处于可用状态。

这将确保hash扩展(ext/hash)始终处于可用状态,与date扩展相似。该扩展不仅提供了众多实用功能,还涵盖了多种哈希算法,这对PHP开发者的工作大有裨益,同时也促进了PHP语言的进一步发展。

在去 PHP 8.0 的旅途中

JIT.

简言之,在启动 PHP 程序的过程中,Zend 引擎首先将代码分解成抽象语法树(AST),接着将其转化为操作码。这些操作码构成了 **Zend 虚拟机的执行基础**。由于操作码位于较低层次,其转化为机器代码的速度远超原始 PHP 代码。PHP的内核中包含一个叫做OPcache的模块,该模块的功能是缓存这些操作码。

JIT技术是一种在程序执行过程中动态编译部分代码的方法,这使得我们能够利用编译后的版本进行运行。

这乃是目前热议中的最大规模PHP性能提升方案之一。PHP开发者们正热切期盼,这个新特性能多大程度上提升他们应用的运行效率。我个人对此也充满期待,渴望亲眼见证这一变革。

内部函数的一致类型错误

若参数解析未能成功,那么内部参数解析接口将持续抛出 TypeError 错误。特别指出,这类错误涵盖了因参数传递数量不足或过多而引发的 ArgumentCountError(它是 TypeError 的一个子类)。

性能比较

我设计了一个简易的测试工具,旨在便捷地对比不同PHP版本的运行效率(借助Docker技术)。通过简单地为容器赋予新的名称,我们便能够轻松地检测到新PHP版本的性能表现。

在搭载2.5 GHz Intel Core i7处理器的Macbook pro设备上运行。

PHP 版本 : 5.6.40
禁止对特定内容进行修改,确保专有名词不受影响,同时避免使用原文中已有的词汇,对句子结构和用词进行适当的调整和丰富,力求在最大程度上保持原句的含义。
测试数学项目耗时:1.101秒。
测试字符串处理耗时:1.144秒。
测试循环耗时:1.736秒。
test_ifelse:耗时1.122秒。
内存使用量:429.46 KB,峰值内存使用量:687.66 KB。
--------------------------------------
总时长:共计5.103秒
PHP 版本 : 7.0.33
--------------------------------------
测试数学部分耗时:0.344秒。
测试字符串操作耗时:0.516秒。
测试循环耗时:0.477秒。
执行测试ifelse所需时间:0.373秒。
内存使用量:421.0859375千字节,峰值内存使用量:422.2109375千字节。
--------------------------------------
总时长:1.71秒
PHP 版本 : 7.1.28
--------------------------------------
数学测试用时:仅0.389秒。
测试字符串处理耗时:0.514秒。
测试循环耗时:0.501秒。
执行测试:if-else结构,耗时0.464秒。
内存使用量达到了420.9375千字节,而峰值内存使用量则为421.3828125千字节。
--------------------------------------
总时长:1.868秒
PHP 版本 : 7.2.17
--------------------------------------
测试数学部分耗时:0.264秒。
测试字符串操作耗时:0.391秒。
测试循环耗时:0.182秒。
执行test_ifelse测试所需时间:0.252秒。
内存使用量:456.578125千字节,峰值内存使用量:457.0234375千字节。
--------------------------------------
总时长::1.089秒
PHP 版本 : 7.3.4
--------------------------------------
数学测试耗时:仅0.233秒。
测试字符串处理耗时:0.317秒。
测试循环耗时:0.171秒。
执行test_ifelse测试,耗时0.263秒。
内存使用量达到了459.953125千字节,而峰值内存使用量则为460.3984375千字节。
--------------------------------------
总时长:0.984秒
PHP 版本 : 7.4.0-dev
--------------------------------------
进行数学测试耗时仅为0.212秒。
测试字符串操作耗时:0.358秒。
测试循环耗时:0.205秒。
执行test_ifelse测试,耗时共计0.228秒。
内存使用量达到了459.6640625千字节,而峰值内存使用量为460.109375千字节。
--------------------------------------
总时长:1.003秒
若您想亲自进行测试,不妨在仓库中搜寻名为meskis/php-bench的相应代码。

PHP 5.6 及更高版本的基准测试

我对Servebolt.com——这家提供惊人快速托管服务的网站情有独钟,尤其喜欢用它来对5.6及以上所有主流版本进行性能编译的可视化操作。编译后的结果,请您参考下方的表格。

性能摘要

PHP 7.0.0 标志着重要的进展,其显著提升了运行效率并减少了内存消耗,然而,负责PHP维护的团队已无法对其进行进一步的优化。此外,还需关注的是即时编译技术,即JIT编译,这是PHP 8.0版本所引入的新特性。

发展方向

在 PHP 7.x 整个版本系列中,存在着一条清晰的发展轨迹,它指向了更加类型化(以及更加客观)的编程语言。即便如此,PHP 依然倾向于采纳其他编程语言中那些简洁且实用的特性。

很快我们就能看到一些更好的功能,例如:

凭借这些优势,PHP开发者将跻身于现代编程语言的行列之中。尽管没有任何一种编程语言能够做到完美无缺,但PHP已经为其未来的发展奠定了坚实的基础。

太长了,读不下去了

为了缩短内容长度,我已依据PHP 7.3的最新版本来筛选出关键的更新内容,具体包括:

参考

https://wiki.php.net/rfc

在比较PHP 5.6和PHP 7.0版本时,我们可以注意到它们在性能、安全性和功能方面存在显著差异。具体来说,PHP 7.0在执行速度上有了显著提升,同时引入了多项安全特性以增强系统的稳定性。此外,PHP 7.0还带来了许多新特性,如支持匿名函数和改进的异常处理机制,这些改进使得开发过程更加高效和便捷。

WordPress 5.0搭配PHP 7.2与PHP 7.3在性能与速度上的比较测试。

本网站每日更新互联网创业教程,一年会员只需98,全站资源免费下载点击查看会员权益

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注