PHP 8的新功能展望:JIT以及其他
moboyou 2025-07-01 19:27 3 浏览
万众瞩目的PHP 8,预计将于今年年12月份发布。根据已经批准和实施的RFC预案,可以PHP将带来许多强大的功能和出色的语言改进。作为尝鲜,让我们一起来展望一下PHP8的新功能和改进。
PHP JIT(即时编译器)
很多人可能已经了解,PHP 8中最令人期望功能是JIT功能。在此我们先介绍一下JIT,根据PHP RFC提案:
"PHP JIT为Opcache部分的独立实现。它可以在PHP编译时和运行时启用/禁用。启用后,PHP文件的本机代码将存储在OPcache共享内存的附加区域中,并且op_array→opcodes []。handler指针指向JIT版本代码的入口点。"
为了更好地理解什么是JIT for PHP,我review一下如何从源代码执行到最终结果。
PHP执行过程分为四个阶段:
Lexing/Tokenizing:解释器读取PHP代码并构建一组令牌。
语法解析:解释器检查脚本是否与语法规则匹配,并使用标记来构建抽象语法树(AST),AST是源代码结构的分层表示。
编译:解释器遍历树并将AST节点转换为低级Zend操作码,这些操作码是确定Zend VM执行的指令类型的数字标识符。
解释:操作码将在Zend VM上解释并运行。
基本PHP执行过程的直观表示如下:
那么,OPcache如何让PHP执行的更快?JIT执行过程中有哪些变化?
OPcache扩展
由于PHP是一种解释型语言,当运行PHP脚本时,解释器将在每次请求时都会重复地解析,编译和执行代码的过程。这会导致CPU浪费和其他资源耗费,让执行时间增加。
"OPcache通过将预编译的脚本字节码存储在共享内存中来提高PHP性能,从而消除了PHP在每个请求上加载和解析脚本的需要。"
启用OPcache后,PHP解释器仅在脚本首次运行时才进行4个阶段的过程。由于PHP字节码存储在共享内存中,因此可以作为低级中间表示形式缓存(OP)来被重复使用,可以立即在Zend VM上执行。
从PHP 5.5开始,Zend OPcache扩展在默认情况下启动,可以通过可以phpinfo()在Zend OPcache来查看Opcache配置情况。
预加载(Reload)
预加载是PHP 7.4新增的OPcache新功能。预加载提供了一种"在运行任何应用程序代码之前"将指定的脚本集存储到OPcache内存中的方法,但是对于典型的基于Web的应用程序而言,它不会带来明显的性能提升。
JIT —及时编译器
即使操作码采用低级中间表示码形式,仍然需要将其编译为机器代码。JIT"不引入任何其他IR(中间表示)形式",使用DynASM(用于代码生成引擎的动态汇编程序)直接从PHP字节码生成本机代码。
简而言之,JIT将中间码的热门部分转换为机器代码。绕过编译,它将能够显著的提高性能和内存使用率。
实时Web应用的JIT
根据JIT RFC,即时编译器实现应提高PHP性能。但是,我们真的会在WordPress等现实应用中体验到这种改进吗?
早期测试表明,JIT可以使CPU密集型工作负载的运行速度大大提高,但是对WordPress等应用并不能带来显著性能提高。
启用JIT后,代码将不会由Zend VM运行,而是由CPU本身运行,这将提高计算速度。诸如WordPress之类的Web应用程序还依赖于TTFB,数据库优化,HTTP请求等其他因素。
因此,当涉及到WordPress和类似的应用程序时,不应该期望PHP的执行速度会大大提高。但是,JIT可以为开发人员带来一些好处。
数字代码的性能明显更好。
"典型" PHP Web应用程序代码的性能略好。
将更多代码从C转移到PHP的潜力,因为PHP现在已经足够快了。"
因此,尽管JIT几乎不会给WordPress性能带来巨大的改善,但它将把PHP升级到一个新的水平,从而使它成为一种可以直接编写许多功能的语言。
不过,JIT的引入将会导致更大的复杂性,它可能导致维护,稳定性和调试成本增加。
PHP 8改进和新功能
除了JIT之外,还值得期望的PHP 8新功能和改进还有很多,它们将使PHP更加可靠和高效。
构造器属性增强
关于如何改进PHP中的对象人体工程学的持续讨论的结果是,构造器属性增强RFC提出了一种新的,更简洁的语法,该语法将简化属性声明,使其更短,更少冗余。
该提议仅与提升的参数有关,即以public,protected和private可见性关键字为前缀的那些方法参数。
目前所有属性必须重复几次(至少四次),然后才能将其与对象一起使用。下面是一个RFC的示例:
class Point {
public int $x;
public int $y;
public int $z;
public function __construct(
int $x = 0,
int $y = 0,
int $z = 0,
) {
$this->x = $x;
$this->y = $y;
$this->z = $z;
}
}
从PHP 8开始,将有一种更有用的声明参数的方法,上述代码可以简单写为:
class Point {
public function __construct(
public int $x = 0,
public int $y = 0,
public int $z = 0,
) {}
}
对比可以看出,新的属性语法更易读且不易出错。
抽象Trait方法验证
Trait是一种在单一继承语言中代码重用的机制。通常,它们用于声明可在多个类中使用的方法。
特征也可以包含抽象方法。这些方法只是声明方法的签名,但是方法的实现必须使用trait在类中完成。
trait T {
abstract public function test(int $x);
}
class C {
use T;
public function test(string $x) {}
}
如果实现方法与抽象特征方法不兼容,将会抛出致命错误:
Fatal error: Declaration of C::test(string $x) must be compatible with T::test(int $x) in /path/to/your/test.php on line 10
不兼容的方法签名
在PHP中,由于方法签名不兼容而导致的继承错误会引发致命错误或警告,具体取决于导致错误的原因。
如果类正在实现接口,则不兼容的方法签名将引发致命错误。根据对象接口文档:
实现接口的类必须使用与LSP(Liskov替换原理)兼容的方法签名。不这样做将导致致命错误。下面是一个带接口的继承错误的示例:
interface I {
public function method(array $a);
}
class C implements I {
public function method(int $a) {}
}
在PHP 7.4中,上面的代码将引发以下错误:
Fatal error: Declaration of C::method(int $a) must be compatible with I::method(array $a) in /path/to/your/test.php on line 7
子类中具有不兼容签名的函数将引发警告。
class C1 {
public function method(array $a) {}
}
class C2 extends C1 {
public function method(int $a) {}
}
在PHP 7.4中,上面的代码只会发出警告:
Warning: Declaration of C2::method(int $a) should be compatible with C1::method(array $a) in /path/to/your/test.php on line 7
在PHP 8,都会抛出致命错误。
Fatal error: Declaration of C2::method(int $a) must be compatible with C1::method(array $a) in /path/to/your/test.php on line 7
数组索引支持负数
在PHP中,如果数组以负索引(start_index < 0)开头,则以下索引自动从0开始。看下面的例子:
$a = array_fill(-5, 4, true);
var_dump($a);
在PHP 7.4中,结果如下:
array(4) {
[-5]=>
bool(true)
[0]=>
bool(true)
[1]=>
bool(true)
[2]=>
bool(true)
}
在PHP 8中,上面的代码将结果如下:
array(4) {
[-5]=>
bool(true)
[-4]=>
bool(true)
[-3]=>
bool(true)
[-2]=>
bool(true)
}
联合类型2.0
联合类型接受可以是不同类型的值。目前,PHP不支持联合类型,但?Type语法和特殊iterable类型除外。
在PHP 8之前,联合类型只能在phpdoc批注中指定:
class Number {
/**
* @var int|float $number
*/
private $number;
/**
* @param int|float $number
*/
public function setNumber($number) {
$this->number = $number;
}
/**
* @return int|float
*/
public function getNumber() {
return $this->number;
}
}
根据联合类型2.0 RFC提议在函数签名中添加对联合类型的支持,这样我们就不再依赖内联文档,而是使用T1|T2|...语法来定义联合类型:
class Number {
private int|float $number;
public function setNumber(int|float $number): void {
$this->number = $number;
}
public function getNumber(): int|float {
return $this->number;
}
}
联合类型支持所有可用类型,但有一些限制:
void类型不能是并集的一部分,因为void意味着函数不返回任何值。
null类型仅支持union的类型,但它的使用作为一个独立的类型是不允许的。
(?T)也可以使用可为null的类型表示法,表示T|null,但不允许在联合类型中包含该表示法(不允许使用?T1|T2,应该用T1|T2|null改用)。
尽可能多的功能(即strpos(),strstr(),substr()等等)包括false可能的返回类型中,false伪类型也支持。
内部函数的一致类型错误
传递非法类型的参数时,内部函数和用户定义函数的行为会有所不同。
用户定义的函数会引发TypeError,但是内部函数会根据多种情况以多种方式运行。无论如何,典型的行为是发出警告并返回null。比如下面的代码在PHP 7.4会导致告警
var_dump(strlen(new stdClass));
Warning: strlen() expects parameter 1 to be string, object given in /path/to/your/test.php on line 4
NULL
如果启用strict_types,或参数信息指定类型,则行为将有所不同。在这种情况下,将检测到类型错误并导致TypeError。
为了消除这些不一致之处,RFC建议使内部参数解析API在参数类型不匹配的情况下抛出ThrowError。
在PHP 8中,上面的代码将抛出致命错误:
Fatal error: Uncaught TypeError: strlen(): Argument #1 ($str) must be of type string, object given in /path/to/your/test.php:4
Stack trace:
#0 {main}
thrown in /path/to/your/test.php on line 4
抛出表表达式
在PHP中,throw是一个语句,因此无法在只允许使用表达式的地方使用它。
建议将throw语句转换为表达式,这样就可以在允许表达式的任何上下文中使用它。例如,箭头符号,空合并运算符,三元判断和合并运算符等:
$callable = fn() => throw new Exception();
// $value is non-nullable.
$value = $nullableValue ?? throw new InvalidArgumentException();
// $value is truthy.
$value = $falsableValue ?: throw new InvalidArgumentException();
WeakMap
WeakMap是弱引用键的数据(对象)的集合,这意味着不会阻止对它们的垃圾回收。
PHP 7.4添加了对弱引用的支持,以此作为保留对对象的引用的一种方式,这种引用不会阻止对象本身被破坏。
在长时间运行的进程中,这将防止内存泄漏并提高性能。:
$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 42;
var_dump($map);
使用PHP 8,上面的代码执行结果如下:
object(WeakMap)#1 (1) {
[0]=>
array(2) {
["key"]=>
object(stdClass)#2 (0) {
}
["value"]=>
int(42)
}
}
如果取消设置对象,则键会自动从弱Map中删除:
unset($obj);
var_dump($map);
现在的结果如下:
object(WeakMap)#1 (0) {
}
参数列表中的尾部逗号
尾随逗号是附加到不同上下文中的项目列表的逗号。PHP 7.2在列表语法中引入了结尾逗号,PHP 7.3在函数调用中引入了结尾逗号。
PHP 8现在在参数列表中以函数,方法和闭包形式引入尾部逗号,如以下示例所示:
class Foo {
public function __construct(
string $x,
int $y,
float $z,
) {
// do something
}
}
在对象上允许:: class语法
为了获取类的名称,可以使用Foo\Bar::class语法。建议将相同的语法扩展到对象,以便现在可以获取给定对象的类的名称,如下例所示:
$object = new stdClass;
var_dump($object::class); // "stdClass"
$object = null;
var_dump($object::class); // TypeError
使用PHP 8,$object::class提供与相同的结果get_class($object)。如果$object不是对象,则抛出TypeError异常。
属性v2
属性,也称为注释,是结构化元数据的一种形式,可用于指定对象,元素或文件的属性。
在PHP 7.4之前,文档注释是将元数据添加到类,函数等的声明中的唯一方法。Attributes v2 RFC引入了PHP属性,这些属性将它们定义为结构化的语法元数据的形式,可以将其添加到类,属性,函数,方法,参数和常量。
将属性添加到它们所引用的声明之前。示例:
<<ExampleAttribute>>
class Foo
{
<<ExampleAttribute>>
public const FOO = 'foo';
<<ExampleAttribute>>
public $x;
<<ExampleAttribute>>
public function foo(<<ExampleAttribute>> $bar) { }
}
$object = new <<ExampleAttribute>> class () { };
<<ExampleAttribute>>
function f1() { }
$f2 = <<ExampleAttribute>> function () { };
$f3 = <<ExampleAttribute>> fn () => 1;
可以在文档块注释之前或之后添加属性:
<<ExampleAttribute>>
/** docblock */
<<AnotherExampleAttribute>>
function foo() {}
每个声明可以具有一个或多个属性,并且每个属性可以具有一个或多个关联值:
<<WithoutArgument>>
<<SingleArgument(0)>>
<<FewArguments('Hello', 'World')>>
function foo() {}
新的PHP函数
PHP 8为该语言带来了几个新功能,str_contains,str_starts_with(),str_ends_with()和get_debug_type
str_contains
在PHP 8之前,strstr和strpos是开发人员在给定字符串中搜索针的典型选择。问题是,这两个函数并不是很直观,它们的用法可能会使新人员感到困惑。
$mystring = 'Managed WordPress Hosting';
$findme = 'WordPress';
$pos = strpos($mystring, $findme);
if ($pos !== false) {
echo "The string has been found";
} else {
echo "String not found";
}
在上面的示例中,使用了!==比较运算符,该运算符还检查两个值是否属于同一类型。如果针的位置为0,这可以防止我们出错:
"此函数可能返回布尔FALSE,但也可能返回非布尔值,其值为FALSE。[…]使用===运算符测试此函数的返回值。"
此外,一些框架提供了帮助程序功能来搜索给定字符串内的值(比如Laravel)。
RFC建议引入一个新功能,该功能允许在字符串内部进行搜索:str_contains。
str_contains ( string $haystack , string $needle ) : bool
它的用法非常简单。str_contains检查是否$needle在中找到$haystack并返回true或false相应地返回。使用str_contains可以使用如下语法:
$mystring = 'Managed WordPress Hosting';
$findme = 'WordPress';
if (str_contains($mystring, $findme)) {
echo "The string has been found";
} else {
echo "String not found";
}
这更易读,更不容易出错。目前str_contains它区分大小写,但是将来可能会改变。
str_starts_with()和str_ends_with()
除此str_contains功能外,还有两个新功能允许在给定的规则搜索:str_starts_with和str_ends_with。检查给定字符串是否以另一个字符串开头或结尾:
str_starts_with (string $haystack , string $needle) : bool
str_ends_with (string $haystack , string $needle) : bool
如果$needle大于$haystack,则两个函数都将返回false。这两个函数都区分大小写:
$str = "WordPress";
if (str_starts_with($str, "Word")) echo "Found!";
if (str_starts_with($str, "word")) echo "Not found!";
get_debug_type
get_debug_type是一个新的PHP函数,它返回变量的类型。新函数的工作方式与gettype函数非常相似,但是get_debug_type返回本机类型名称并解析类名称。对于语言来说,这是一个很好的改进,gettype()对类型检查没有用。
RFC提供了两个有用的示例,可以更好地理解新get_debug_type()功能和的区别gettype()。第一个示例显示gettype了工作方式:
$bar = [1,2,3];
if (!($bar instanceof Foo)) {
throw new TypeError('Expected ' . Foo::class . ', got ' . (is_object($bar) ? get_class($bar) : gettype($bar)));
}
在PHP 8中,可以使用get_debug_type,而不是:
if (!($bar instanceof Foo)) {
throw new TypeError('Expected ' . Foo::class . ' got ' . get_debug_type($bar));
}
下面显示了get_debug_type和gettype的返回值对比:
总结
在本文中,我们介绍了PHP 8发行版中预期的所有关键更改和改进。其中最值得期待的肯定是JIT编译器,还PHP 8还有很多其他功能。希望我们能早日运行新的版本,替换到满是bug的PHP 5.2系统(群里一个PHP 5.2老内存泄露,还不敢升级的人问怎么解决,有感!)。
相关推荐
- 验证IronPython的ScriptEngine和ScriptScope的兼容性验证机制?
-
测试和验证IronPython兼容性机制的完整指南IronPython的ScriptEngine和ScriptScope兼容性验证机制需要通过多层次测试确保其正确性。以下是系统化的测试方法和实践...
- C#+Python 热更新技术在游戏开发中的核心应用场景
-
一、核心玩法逻辑动态更新1.战斗系统实时调优技能逻辑热更新:通过Python动态修改技能伤害公式、特效触发条件,例如:python#实时调整法师大招伤害系数defupdate_firest...
- 不到100行代码制作各种证件照
-
文|某某白米饭来源:Python技术「ID:pythonall」生活中经常需要使用各种版本的电子版证件照,如:红底、蓝底、白底、一寸、两寸等等。在Python中替换图片背景色可以用Ima...
- python从入门到脱坑 输入与输出——print()函数
-
大家好今天开始系统的讲解一些入门课程,遇到不会的也不用想太多,跟着写一遍,学习到新内容是你就会明白.以下是针对Python初学者的print()函数详解,从基础到实用技巧,配合清晰示例:一、最基础用法...
- 外婆都能学会的Python教程(七):Python中循环语句
-
前言Python是一个非常容易上手的编程语言,它的语法简单,而且功能强大,非常适合初学者学习,它的语法规则非常简单,只要按照规则写出代码,Python解释器就可以执行。下面是Python的入门教程循环...
- 解释一下Python脚本中版本号声明的作用
-
在Python脚本中声明版本号(如__version__变量)是一种常见的元数据管理实践,在IronPython的兼容性验证机制中具有重要作用。以下是版本号声明的核心作用及实现原理:一、版本号...
- 除了版本号声明,还有哪些元数据可以用于Python脚本的兼容性管理
-
在Python脚本的兼容性管理中,除了版本号声明外,还有多种元数据可以用于增强脚本与宿主环境的交互和验证。以下是一些关键的元数据类型及其应用场景:一、环境依赖声明1.Python版本要求pyth...
- 使用python实现人脸检测
-
一,准备dlib库下载:提取码1111dlib环境配置数据集下载python3.8opencv3.4.11二,代码老规矩,先导入包#导入包importnumpyasnpimport...
- Python
-
fromdjango.shortcutsimportrenderfromdjango.httpimportHttpResponseRedirectfromdjango.core.ur...
- 如何用Python画一个简单的笑脸
-
写在前面Python画画,必不可少的要用到小乌龟turtle库函数。对所用到的函数进行说明一下:fromturtleimport*#包含turtle库里面所有的函数,这样写可以不用标名hid...
- 习惯了各种框架的文件上传,php原生上传图片你还记得吗?
-
序言:如今各种框架层出不穷,如thinkphp、laravel、yii等,对于功能的封装也是各显其能,以至于很多开发者离开了框架之后就不会开发了,今天我以实际的例子介绍最基本的图片上传功能,希望对一些...
- php源码开发的商城系统有什么优点
-
1、php是一种流行的脚本语言,它编写的程序更容易被人理解。2、php的函数非常丰富,可以通过这些函数来进行开发,而不需要关注代码本身。3、php是一种面向对象的程序语言,它不像Java和...
- php宝塔搭建实战Dinner订餐系统php源码
-
大家好啊,欢迎来到web测评。本期给大家带来一套php开发的Dinner订餐系统php源码,上次是谁要的系统项目啊,帮你找到了,还说不会搭建,让我帮忙录制一期教程,趁着今天有空,简单的录制测试了一下,...
- php宝塔搭建实战美食小吃网站系统php源码
-
大家好啊,我是测评君,欢迎来到web测评。本期给大家带来一套pbootcms开发的美食小吃网站系统php源码,感兴趣的朋友可以自行下载学习。技术架构PHP7.0+nginx+sqlite+...
- php中assert和eval的详细介绍(代码示例)
-
本篇文章给大家带来的内容是关于php中assert和eval的详细介绍(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。assert判断一个表达式是否成立。返回trueo...
- 一周热门
- 最近发表
- 标签列表
-
- curseforge官网网址 (16)
- 外键约束 oracle (36)
- oracle的row number (32)
- 唯一索引 oracle (34)
- oracle in 表变量 (28)
- oracle导出dmp导出 (28)
- oracle 数据导出导入 (16)
- oracle两个表 (20)
- oracle 数据库 字符集 (20)
- oracle安装补丁 (19)
- matlab归一化 (16)
- matlab化简多项式 (20)
- 多线程的创建方式 (29)
- 多线程 python (30)
- java多线程并发处理 (32)
- 宏程序代码一览表 (35)
- c++需要学多久 (25)
- c语言编程小知识大全 (17)
- css class选择器用法 (25)
- css样式引入 (30)
- html5和css3新特性 (19)
- css教程文字移动 (33)
- php简单源码 (36)
- php个人中心源码 (25)
- 网站管理平台php源码 (19)