PHP 8.5 新特性
PHP 8.5 于 2025年11月20日 正式发布。本次更新包含管道运算符、clone with 语法、全新 URI 解析器等多项功能。
管道运算符
PHP 8.5 引入的管道运算符极大简化了函数间输出链式传递。无需再使用深度嵌套的函数调用:
$input = ' Some kind of string. ';
$output = strtolower(
str_replace(['.', '/', '…'], '',
str_replace(' ', '-',
trim($input)
)
)
);
现在可改写为:
$output = $input
|> trim(...)
|> (fn (string $string) => str_replace(' ', '-', $string))
|> (fn (string $string) => str_replace(['.', '/', '…'], '', $string))
|> strtolower(...);
克隆时赋值
现在可以在克隆对象时 同时 为其赋予新值:
final class Book
{
public function __construct(
public string $title,
public string $description,
) {}
public function withTitle(string $title): self
{
return clone($this, [
'title' => $title,
]);
}
}
我认为这是个绝佳特性。唯一遗憾的是,当从外部克隆只读属性时无法生效(我认为这是常见场景)。要实现此功能,必须将属性的写入权限显式重置为public(set)。相关问题详见此处。
(void) 强制转换与 #[NoDiscard]
现在可为函数添加 #[NoDiscard] 属性,表明其返回值必须被使用。若未对返回值进行任何操作,系统将触发警告。
#[NoDiscard("you must use this return value, it's very important.")]
function foo(): string {
return 'hi';
}
// Warning:
// The return value of function foo() is expected to be consumed,
// you must use this return value, it's very important.
foo();
// This is ok:
$string = foo();
仍可通过新引入的 (void) 强制转换抑制警告:
(void) foo();
闭包增强
闭包和一等可调用对象现可用于常量表达式。这意味着您能在属性中定义闭包——这是项令人惊叹的新特性:
#[SkipDiscovery(static function (Container $container): bool {
return ! $container->get(Application::class) instanceof ConsoleApplication;
})]
final class BlogPostEventHandlers
{ /* … */ }
请注意此类闭包必须显式标记为static,因其不隶属于$this作用域,且无法通过use访问外部作用域变量。
致命错误回溯功能
一项微小却强大的改进:致命错误现在将包含回溯信息。
Fatal error: Maximum execution time of 1 second exceeded in example.php on line 6
Stack trace:
#0 example.php(6): usleep(100000)
#1 example.php(7): recurse()
#2 example.php(7): recurse()
#3 example.php(7): recurse()
#4 example.php(7): recurse()
#5 example.php(7): recurse()
#6 example.php(7): recurse()
#7 example.php(7): recurse()
#8 example.php(7): recurse()
#9 example.php(7): recurse()
#10 example.php(10): recurse()
#11 {main}
新增 array_first() 和 array_last()
虽然稍显迟缓(array_key_first() 和 array_key_last() 早在 PHP 7.3 便已加入),但我们终于拥有了从数组获取首尾元素的内置函数!因此无需再这样写:
$first = $array[array_key_first($array)] ?? null;
现在可以改写为:
$first = array_first($array);
URI解析
全新的URI实现大幅简化了URI操作:
use UriRfc3986Uri;
$uri = new Uri('https://tempestphp.com/2.x/getting-started/introduction');
$uri->getHost();
$uri->getScheme();
$uri->getPort();
// …
#[DelayedTargetValidation] 属性
某些内置属性(如#[Override])在编译时进行验证,而非通过反射调用时的运行时验证。#[DelayedTargetValidation] 允许将验证推迟至运行时执行:
class Child extends Base
{
#[DelayedTargetValidation]
#[Override]
public const NAME = 'Child';
// Note that this is an example, you cannot currently add #[Override] on constants
}
此属性新增用于管理向后兼容性问题。具体示例可参阅此处。
细微变更
- 支持静态属性的非对称可见性
- 新增对编译时非类常量属性的支持
- 构造函数属性提升现可用于 final 属性
#[Override]现可应用于属性- 新增 DomElement::$outerHTML 方法
- Exif 扩展现支持 HEIF 和 HEIC 图像格式
- 调用
filter_var()时新增FILTER_THROW_ON_FAILURE标志
弃用项与破坏性变更
- 非标准类型转换名称如
(boolean)和(integer)已弃用 - 反引号作为
shell_exec()的别名已被弃用 - 常量重新声明已被弃用
disabled_classesini 配置项已被移除- 所有破坏性变更与弃用项详见此处
以上是PHP 8.5中值得关注的功能与变更;完整变更列表请参阅此处。
您对PHP 8.5有何看法?欢迎在评论区留言分享!
真不敢相信在GTA6发布前,PHP居然先实现了array_first、array_last和致命错误堆栈跟踪功能。
自PHP 5以来,PHP的演进相当显著,我认为这确实是个问题。作为多年前学习这门语言的人,其变革速度(泛型、属性、模式匹配、类型化属性)让现代代码库变得难以理解。
我怀疑这影响了许多在PHP领域起步却未能跟进的开发者。这门语言已蜕变为截然不同的存在——这对社区是优势,却成了重新入门的门槛。
依我之见,新版PHP依然可读性极佳。我曾用C++编程十年,但现在完全无法理解现代C++代码库了。
我认为现在的PHP远比过去优秀。学好PHP 8就足够应对了。
其实大多数语言都如此,比如C# 14与C# 1.0、Java 25与Java 1.0、C 23(含常见编译器扩展)与K&R C的对比……
C语言其实变化不大,用C99编程的人只需30分钟就能跟上现代C23代码库的变更。这种保守性是众所周知的——近二十年来,抵制变革始终是该社区的主要阻力。
若翻出最早期的C语言示例,确实显得 怪异 。但 那种 C语言早在1989年就已过时。此后每隔十年左右(99、11、17、23年)仅进行小幅迭代(约五至八项新增/修改)。它是否改变了?当然。但能与C#、Java、C++等语言的迭代速度相提并论吗?绝无可能。
我确信若考察整个语言体系及编译器扩展(无论具体编译器),许多人根本答不出C语言相关的酒馆问答题。
我觉得他更像是指PHP 5到8.5的演变路径。
版本1到最新版的差异固然巨大,但这些都是存在数十年的语言,曾长期停滞不前,如今却都在引入新语法。
这种演进方向虽合理,但显然会让那些不热衷编程、只想投入最少精力学习语言的朝九晚五开发者被时代抛下。
朝九晚五的工作模式与编程热情或语言学习投入有何必然关联?
这种现象在多数语言中都存在,绝大多数语言都在持续维护和改进。作为Java领域的雇佣专家,我曾向同事讲解近期引入的新特性,但仅限于能真正提升可读性的功能。PHP或许略有不同,因为大量用户将其作为业余爱好创建首个网站的入门语言。
PHP没有泛型?我曾读到过在PHP领域实现泛型“太难”,主要是因为其类型系统过于原始。
这与“太难”无关,完全是因为它不符合类型系统的逻辑。PHP是弱类型语言且高度依赖反射机制(因此所有元素随时都知晓自身及彼此的类型)。
为PHP添加泛型或许能让计算机科学原教旨主义者稍感欣慰,但既无法改变PHP的核心设计,也无法提供泛型在强类型编译语言中固有的传统优势。更何况实现起来将极其棘手,还会进一步加重本已臃肿的虚拟机实现负担。
> 且实现过程将极其棘手
完全正确。该类型系统从未为稍复杂的场景设计,本质上只是对基本类型和类的注解。PHP始终采用弱类型系统,因此添加泛型几乎不可能实现。
> 为PHP添加泛型会让计算机科学原教旨主义者稍感欣慰
PHP实际上只有一种集合数据类型(臭名昭著的数组),因此泛型将极具实用价值——例如你无法从函数中返回类型化的数组,这简直糟糕透顶。
反观Python,它在保持动态语言特性的同时实现了泛型支持,尽管其类型系统比PHP更严格。
若对PHP泛型感兴趣,可参阅PHP基金会博文:https://thephp.foundation/blog/2024/08/19/state-of-generics-… 或 Nikita 的这个 PR:https://github.com/PHPGenerics/php-generics-rfc/issues/45。
简而言之:PHP编译器并不适合这项任务,它会给本已复杂的代码库引入大量复杂性,且会造成显著的内存/性能开销。
没错,这基本就是我记忆中的样子。类型系统虽然极其“不智能”,却复杂到泛型根本没法实现。
我依然热爱PHP。23年前我们为它开发过加密软件,至今仍在运行。我还运营着一份PHP通讯。这个社区依然充满活力,虽然我也使用其他语言(Python、Node.js),但面对快速简单的任务时,我总会不自觉地选择PHP。
唯一的问题在于,这把“双刃剑”——自PHP 5发布后,语言复杂度大幅提升,初学者入门难度远超从前。
> 23年前我们曾为它开发过加密软件
ZEND?
记得00年代中期“破解”软件时,Zend总是让人头疼不已。
若这是你的项目,那可算得上是极高的赞誉了。:)
许多人因自视甚高而不愿与PHP扯上关系。我坦然承认对这门语言一无所知,只知道它能创造出许多酷炫的东西。
目前我最爱的PHP产品是BookStack(https://www.bookstackapp.com/),这是个出色的维基系统。我为家人搭建了实例,效果极佳。
但它能实现的功能实在太多。我注意到许多我常用的网站…都是基于维护良好的PHP技术栈构建的。
PHP是种非常愉快且直截了当的语言。虽然也见过不少糟糕代码,但我确实享受过用它开发的时光。
我认为PHP真正的危险在于它能轻易引发*严重问题*。
这部分源于糟糕的培训(我大学的PHP课程竟用SQL注入漏洞的示例教学),更关键的是语言本身极易引发问题——经验不足的开发者(早期绝大多数都如此)往往在出错前根本意识不到隐患。
作为互联网早期工具,PHP因上述特性背负了“不安全且低劣”的恶名。
至少根据我的经验,PHP早期缺乏企业级用户;后来RoR(Ruby on Rails)的出现引发了一场小型革命,将MVC模式引入众多(网页)开发者视野——此前他们尚未形成明确的模式/架构偏好。
同期网络上充斥着大量平庸的教程和文档,包括PHP官网本身——该网站允许用户在评论区发布代码示例,但据我所知这些内容几乎无人审核。
最终许多人选择自行编写框架,一方面是技术能力允许,另一方面则是当时缺乏优质且广泛采用的框架——直到后来Zend Framework率先出现,随后Laravel崛起,后者如今已成为事实标准。
> 我认为PHP真正的危险在于它能轻易引发 极其糟糕的后果 。
难道还有语言能做到不会这样吗?
这就像在布满地雷的雷区行走,只是“雷密度”天差地别:使用更严格的语言时,每英亩土地可能只有一枚地雷;而PHP环境下,每英亩可能埋着十枚。
长期以来,该语言秉持着“即使出错也要继续运行”的开发理念,认为输出 任何内容 都比无操作退出更优。
这意味着要确保程序可靠运行,必须编写极具防御性的代码,检查所有可能的变量。虽然任何语言都应如此,但旧版PHP对此要求尤为严苛。
值得庆幸的是,过去十年间他们一直在改变这种状况,同时保持了与旧代码的良好兼容性。我最近刚完成一个大型项目(约200万行代码)从十年前的5.6版迁移到最新8.4版的任务,过程相当顺利。唯一出现问题的,是那些原本就未被正确实现、纯属侥幸运行的功能。
或许不会,但大多数语言同样不鼓励这样做。
若你能举出PHP诱导开发者写出糟糕代码的例子,我就能列出另外两种流行语言同样存在更恶劣的诱导行为 🙂
正如Bjarne Stroustrup所言: 语言分为两类:被抱怨的语言和无人使用的语言
PHP的@运算符。在Java这类语言中,要静默捕获所有异常且不做任何处理,至少需要写些冗余代码。
PHP竟为这种在正常代码库中绝不该做的事提供了运算符。
你知道Python追求的是让“good good”看起来更美观吗?
PHP的设计方式让糟糕的代码看起来不错。若想让软件工程成为一个严肃且不断发展的领域,我们必须坦诚面对现实:它是个糟糕的工具。优秀的程序员即便使用劣质工具也能写出好程序,但这并不意味着在有选择时不该避开劣质工具。
或许真有《PHP精粹》这本书。但JavaScript的核心设计其实相当出色,且具备其他语言无法复制的跨浏览器兼容性。那么PHP究竟占据着怎样的独特领域?在哪些场景下它能创造其他更优语言无法替代的价值?
在合理的代码库中完全可以使用@。你自己就举过例子:其他语言中常见的冗余代码就是抛出异常后直接忽略,因为某些边界情况无法合理处理,而你希望程序继续运行——毕竟你知道它最终会成功。这正是@的作用。
不过要注意,@在近期某些PHP版本中已被削弱功能。
正是如此。
@运算符的一个常见用法是在“解构”数组成员赋值给变量时。某些情况下,你无法确定成员是否存在,但缺失也无伤大雅。此时便可通过@抑制警告。
$array = [‘apple’, ‘pear’]; @list($mainFruit, $secondaryFruit, $tertiaryFruit);
由于我抑制了因第三个成员缺失而产生的警告,程序将持续执行而非终止。
原论点是“PHP助长糟糕代码”——但你的观点是“PHP可编写糟糕代码”,这完全是两回事。快速搜索@符号时发现https://stackoverflow.com/questions/136899/suppress-error-wi…,其中最高票回答是“请千万别用它”。过去十年间我遇到的所有用例,都未曾要求或暗示使用@符号。这是整个社区公认的禁忌古董。若你真想辩称这种现状反而“鼓励”使用@,倒值得探究一番。
每种语言都能做出疯狂的事。但在Java这类语言中,疯狂多体现在概念层面(工厂的工厂的工厂),而非基础层面——比如==的含义、弱类型和隐式转换的问题。现代PHP通过strict_types=1等机制能规避多数问题,但现实中我们很少能参与遵循最佳实践的项目。与其维护糟糕的PHP项目(我曾不幸经历过),我宁愿处理一个差劲的Java项目。
有趣的是你竟以==为例——这个运算符在Java中极具反直觉性,更是初学者的常见陷阱:
就像PHP一样,你必须阅读文档才能正确使用它。
这是十年前就存在的PHP辩护谬误。没人说其他语言没有问题,所以“反驳”这种说法本身就是谬误。PHP的问题和陷阱确实多得多。或许现在有所减少,但依然如此。多得多。
所以你要忽略我写的内容?那我默认你认同我的观点和评论,只是不愿承认罢了。我倒无所谓。
后端开发我宁可选PHP也不要JS/TS加时下流行的框架。好吧,PHP通常也得搭配框架( 咳咳 Laravel 咳咳 ),但至少那套体系更稳定成熟。可惜决策权不在我手里…
若你追求十年后仍能开箱即用的代码,PHP是合理选择。
当然这前提是你所在的团队能预见一年后的需求,更别说十年了。
https://github.com/AzuraCast/AzuraCast
选择AzuraCast是因为我喜欢通过代码学习,还想自建电台/音乐平台
> 许多人因自负而不愿与PHP扯上关系。
为何如此?
虚荣心作祟,这可是“个人主页语言”啊
PHP每次更新都变得更复杂。有何意义?其应用范围仍主要局限于网络领域。
大量C#和Java代码同样面向Web后端开发,这些系统规模庞大且结构复杂。因此同类设计范式(传统面向对象)的语言趋同功能实属自然。如今唯一例外或许是Go语言。
如今这句话可以改写为:“任何语言都能实现Spring式的代码结构”……
显然你没听说过NativePhp 🙂
即便它“只是”用于网络,为什么不能进化?这可是个优秀的语言,拥有庞大的用户群,总有改进空间和提升开发者体验的余地
网络世界只会越来越复杂。
我不认为增加语言复杂度能解决这个问题。复杂语言适用于系统编程场景,那里需要榨取性能极限。
完全正确,我甚至认为网络开发(前后端)是软件开发领域迄今为止最大的工作/产业。
未读完这篇精彩文章就给你投了反对票,特此修正。
新增的array_first()和array_last()函数很实用,其余要么是现有功能的复刻,要么是会降低可维护性的“新特性”。管道运算符就是典型例子。我并不需要它——在我接触过的所有代码库中,嵌套方法从未构成实质性问题。新语法仅适用于一元函数,高阶函数必须用箭头函数包裹。这反而比直接嵌套函数更混乱且更易出错。
管道运算符极大简化了自定义加密哈希函数的创建,就像2000年代初大家常做的那样:
哦,现在我信服了!
> 管道运算符[…]语法仅适用于一元函数,因此高阶函数必须用箭头函数包裹。
功能即将推出——但PHP RFC提案必须拆分成多个小块才能通过,否则会被否决。
https://wiki.php.net/rfc/partial_function_application_v2
官方发布说明:https://www.php.net/releases/8.5/en.php
PHP 真的该专注于完善核心功能了。
至今仍需使用 mb_real_uppercase($name) 处理Unicode字符实在令人恼火。另一个痛点是标准库过于混乱。PHP 5.3本是千载难逢的契机,既可清理标准库,又可为内置函数引入命名空间API,还可选择性推出统一的函数调用语法:
开发PHP项目时,并发需求终将到来。完全缺乏并发支持令人失望。Fiber API本身毫无作用,迫使开发者依赖第三方运行时,这对遗留项目而言通常是不可行的方案。
PHP虽已从PHP 4.0时代发展至今,但在多个领域仍显不足,我认为它在2025年仍难以成为绿地项目的首选。
确实,尽管某些方面有所改进,其核心易用性仍显著落后于多数后端语言。我甚至为此撰写过文章https://waspdev.com/articles/2025-06-12/my-honest-opinion-ab…。
至于并发/异步处理,虽然能通过curl_multi_*实现请求并行化,其他异步/并行操作也可行,但相较于支持Promise和异步的JS等语言,实现方式往往更为复杂。
PHP 8.5发布最令人振奋之处在于其稳定性和成熟度
在shell脚本中常用此功能,通过php-cli实现。
例如
mkdir $dirname;这属于反模式,因其存在shell元字符注入(及参数注入)风险。PHP自带mkdir函数,应优先使用。而pcntl_exec()才是正确的多进程执行API。
看到新管道语法时…
…不禁想:为何不直接采用如下写法?
trim(…) 中的三个点将函数转换为可调用对象,该功能已存在,因此目前最好沿用该语法。[1]
关于部分函数应用,已有相关 RFC 提案,但目前尚未定案。[2]
1: https://www.php.net/manual/en/functions.first_class_callable…
2: https://wiki.php.net/rfc/partial_function_application_v2
自从90年代我在第一个小网站上使用PHP以来,它确实取得了长足进步。
期待真正的异步功能落地——目前这些特性还不足以让我兴奋。
为何PHP这类语言,连同TypeScript在内,都变得像解不开的谜题般难以阅读?泛型、类型等语言特性往往导致软件架构过度复杂。如今目睹太多同事苦于理解代码库,前端开发者几乎需要博士级头脑才能胜任。
正如我某位兄弟的评论所言,我确信这与某种社会压力有关。
人们总指责“过时”语言缺乏某些最时髦的语法结构。
管道运算符无疑是这类特性之一——它提供了更多实现相同功能的方式,却带来模糊不清的收益。
我这辈子从未遇到过“要是用管道运算符就能省下数小时调试/阅读/编写代码”的场景。
我认为这源于多方面的自卑心理。PHP曾因过于简单、功能不足和不一致而遭人嘲笑,如今却矫枉过正——每版更新都堆砌类型系统、注解机制并破坏向后兼容性,导致旧代码库无法完整保留。前端开发者渴望被视为真正的开发者,在他们的语境中这意味着构建笨重复杂的企业级垃圾,于是有了TypeScript等工具。后端同样存在这种机制:开发者必须证明自己不是新手,因此在架构师指导下滥用设计模式,而非规避抽象化带来的复杂性。
不,我并不愤世嫉俗。
看到这个帖子里其他人也有同样的不安,倒让我稍感安慰
感谢PHP在2025年引入管道运算符。
至今记得当年在#php频道倒数PHP3发布时刻的场景
记得当时关于文件扩展名的争论——真不敢相信.php3竟能胜出。我记得自己曾率先提议PHP4回归.php格式,不再包含版本号。
2025年的PHP开发者仍需应对str_replace、htmlspecialchars、nl2br等函数(此类函数不胜枚举)。
(不,我不希望IDE像保姆般照看我,理想状态下我甚至不想使用IDE)
你指开发者必须处理这类函数?它们只是按需使用,和其他函数无异。根本不需要IDE。我实在不明白。
> 根本不需要IDE。
还能说什么呢,祝你好运。
你没明白这个典故,大概是因为你没用过PHP(至少没用够久)。
我给你个提示:不一致性。
tl;dr:没什么特别的,就是稳定和成熟
还有管道运算符,其他语言也在讨论这个特性。
管道运算符的引入,很大程度上是因为PHP数组和字符串缺乏“方法”特性。你无法用“面向对象”风格编写:“some_string”->str_replace(‘some’, “replacement”)->strtoupper()。而用PHP的数组/字符串过程化方式编写这类操作链,代码会显得臃肿得多。管道运算符虽能略微减少冗余代码,但原生的“OOP”风格仍更胜一筹。
虽然存在添加“方法”的提案,但我记不清具体链接了。
我并非盲目抵制PHP,但PHP社区成员有时会为新特性欢呼雀跃,而其他编程语言早已拥有类似功能多年。https://waspdev.com/articles/2025-06-12/my-honest-opinion-ab…
不,面向对象风格并非更优。面向对象可用的函数集是封闭的。
假设我需要实现一个自定义字符串函数,用于将每隔一个字母大写(因特定需求):这在面向对象风格下无法实现。
在OOP中,我虽可继承字符串类,但代码其他部分并不会自动采用我的字符串类型。
因此我必须创建独立函数实现(这反而更优,因无需对象内部状态,外部封装更符合设计原则)。
于是我的字符串函数与其他字符串函数运作方式不同:
(示例当然毫无意义且可重新排序,但我们讨论的是语法)
将所有功能都简化为函数能实现统一性。
当然还有其他方案。C++多年来一直倡导“统一调用语法”,该方案始终支持“对象风格”函数调用,还能识别首参数类型兼容的非memwbr函数,但这需要更严格(甚至静态)的类型系统,在PHP中行不通。
PHP应当进行真正重大的兼容性断裂,移除变量名中的$符号。这番痛苦绝对值得!
你好Perl 6!
此类变革将使语言彻底蜕变且完全不兼容。所有旧代码将失效,且无便捷迁移路径(尤其对希望过渡期同时支持两版的库而言)。