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
}

此属性新增用于管理向后兼容性问题。具体示例可参阅此处


细微变更


弃用项与破坏性变更


以上是PHP 8.5中值得关注的功能与变更;完整变更列表请参阅此处

您对PHP 8.5有何看法?欢迎在评论区留言分享!

共有 73 条评论

  1. 真不敢相信在GTA6发布前,PHP居然先实现了array_first、array_last和致命错误堆栈跟踪功能。

  2. 自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编译器并不适合这项任务,它会给本已复杂的代码库引入大量复杂性,且会造成显著的内存/性能开销。

        • 没错,这基本就是我记忆中的样子。类型系统虽然极其“不智能”,却复杂到泛型根本没法实现。

  3. 我依然热爱PHP。23年前我们为它开发过加密软件,至今仍在运行。我还运营着一份PHP通讯。这个社区依然充满活力,虽然我也使用其他语言(Python、Node.js),但面对快速简单的任务时,我总会不自觉地选择PHP。

    唯一的问题在于,这把“双刃剑”——自PHP 5发布后,语言复杂度大幅提升,初学者入门难度远超从前。

    • > 23年前我们曾为它开发过加密软件

      ZEND?

      记得00年代中期“破解”软件时,Zend总是让人头疼不已。

      若这是你的项目,那可算得上是极高的赞誉了。:)

  4. 许多人因自视甚高而不愿与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中极具反直觉性,更是初学者的常见陷阱:

                    String a = new String();
                    String b = new String();
                    a = “test”;
                    b = a + “”;
                    
                    if (a == “test”)
                    {
                        // true
                    }
                
                    if (b == “test”)
                    {
                        // false
                    }
                
                    if (a == b)
                    {
                        // false
                    }
                        
                

                就像PHP一样,你必须阅读文档才能正确使用它。

                • 这是十年前就存在的PHP辩护谬误。没人说其他语言没有问题,所以“反驳”这种说法本身就是谬误。PHP的问题和陷阱确实多得多。或许现在有所减少,但依然如此。多得多。

                • 所以你要忽略我写的内容?那我默认你认同我的观点和评论,只是不愿承认罢了。我倒无所谓。

    • 后端开发我宁可选PHP也不要JS/TS加时下流行的框架。好吧,PHP通常也得搭配框架( 咳咳 Laravel 咳咳 ),但至少那套体系更稳定成熟。可惜决策权不在我手里…

      • 若你追求十年后仍能开箱即用的代码,PHP是合理选择。

        当然这前提是你所在的团队能预见一年后的需求,更别说十年了。

    • https://github.com/AzuraCast/AzuraCast

      选择AzuraCast是因为我喜欢通过代码学习,还想自建电台/音乐平台

    • > 许多人因自负而不愿与PHP扯上关系。

      为何如此?

  5. PHP每次更新都变得更复杂。有何意义?其应用范围仍主要局限于网络领域。

    • 大量C#和Java代码同样面向Web后端开发,这些系统规模庞大且结构复杂。因此同类设计范式(传统面向对象)的语言趋同功能实属自然。如今唯一例外或许是Go语言。

      如今这句话可以改写为:“任何语言都能实现Spring式的代码结构”……

    • 显然你没听说过NativePhp 🙂

      即便它“只是”用于网络,为什么不能进化?这可是个优秀的语言,拥有庞大的用户群,总有改进空间和提升开发者体验的余地

    • 网络世界只会越来越复杂。

      • 我不认为增加语言复杂度能解决这个问题。复杂语言适用于系统编程场景,那里需要榨取性能极限。

      • 完全正确,我甚至认为网络开发(前后端)是软件开发领域迄今为止最大的工作/产业。

    • 未读完这篇精彩文章就给你投了反对票,特此修正。

      新增的array_first()和array_last()函数很实用,其余要么是现有功能的复刻,要么是会降低可维护性的“新特性”。管道运算符就是典型例子。我并不需要它——在我接触过的所有代码库中,嵌套方法从未构成实质性问题。新语法仅适用于一元函数,高阶函数必须用箭头函数包裹。这反而比直接嵌套函数更混乱且更易出错。

  6. 官方发布说明:https://www.php.net/releases/8.5/en.php

  7. PHP 真的该专注于完善核心功能了。

    至今仍需使用 mb_real_uppercase($name) 处理Unicode字符实在令人恼火。另一个痛点是标准库过于混乱。PHP 5.3本是千载难逢的契机,既可清理标准库,又可为内置函数引入命名空间API,还可选择性推出统一的函数调用语法:

        “foo”->strtoupper();
    

    开发PHP项目时,并发需求终将到来。完全缺乏并发支持令人失望。Fiber API本身毫无作用,迫使开发者依赖第三方运行时,这对遗留项目而言通常是不可行的方案。

    PHP虽已从PHP 4.0时代发展至今,但在多个领域仍显不足,我认为它在2025年仍难以成为绿地项目的首选。

    • 确实,尽管某些方面有所改进,其核心易用性仍显著落后于多数后端语言。我甚至为此撰写过文章https://waspdev.com/articles/2025-06-12/my-honest-opinion-ab…。

      至于并发/异步处理,虽然能通过curl_multi_*实现请求并行化,其他异步/并行操作也可行,但相较于支持Promise和异步的JS等语言,实现方式往往更为复杂。

  8. PHP 8.5发布最令人振奋之处在于其稳定性和成熟度

  9.     反引号作为shell_exec()别名的用法已弃用
    

    在shell脚本中常用此功能,通过php-cli实现。

    例如mkdir $dirname;

    • 这属于反模式,因其存在shell元字符注入(及参数注入)风险。PHP自带mkdir函数,应优先使用。而pcntl_exec()才是正确的多进程执行API。

  10. 看到新管道语法时…

        $output = $input
            |> trim(...)
            |> (fn (string $string) => str_replace(‘ ’, ‘-’, $string))
            |> (fn (string $string) => str_replace([‘.’, ‘/’, ‘…’], ‘’, $string))
            |> strtolower(...);
    

    …不禁想:为何不直接采用如下写法?

        $output = $input
            |> trim($)
            |> str_replace(‘ ’, ‘-’, $)
            |> str_replace([‘.’, ‘/’, ‘…’], ‘’, $)
            |> strtolower($);
    
  11. 自从90年代我在第一个小网站上使用PHP以来,它确实取得了长足进步。

  12. 期待真正的异步功能落地——目前这些特性还不足以让我兴奋。

  13. 为何PHP这类语言,连同TypeScript在内,都变得像解不开的谜题般难以阅读?泛型、类型等语言特性往往导致软件架构过度复杂。如今目睹太多同事苦于理解代码库,前端开发者几乎需要博士级头脑才能胜任。

    • 正如我某位兄弟的评论所言,我确信这与某种社会压力有关。

      人们总指责“过时”语言缺乏某些最时髦的语法结构。

      管道运算符无疑是这类特性之一——它提供了更多实现相同功能的方式,却带来模糊不清的收益。

      我这辈子从未遇到过“要是用管道运算符就能省下数小时调试/阅读/编写代码”的场景。

    • 我认为这源于多方面的自卑心理。PHP曾因过于简单、功能不足和不一致而遭人嘲笑,如今却矫枉过正——每版更新都堆砌类型系统、注解机制并破坏向后兼容性,导致旧代码库无法完整保留。前端开发者渴望被视为真正的开发者,在他们的语境中这意味着构建笨重复杂的企业级垃圾,于是有了TypeScript等工具。后端同样存在这种机制:开发者必须证明自己不是新手,因此在架构师指导下滥用设计模式,而非规避抽象化带来的复杂性。

      不,我并不愤世嫉俗。

    • 看到这个帖子里其他人也有同样的不安,倒让我稍感安慰

  14. 感谢PHP在2025年引入管道运算符。

  15. 至今记得当年在#php频道倒数PHP3发布时刻的场景

    • 记得当时关于文件扩展名的争论——真不敢相信.php3竟能胜出。我记得自己曾率先提议PHP4回归.php格式,不再包含版本号。

  16. 2025年的PHP开发者仍需应对str_replace、htmlspecialchars、nl2br等函数(此类函数不胜枚举)。

    (不,我不希望IDE像保姆般照看我,理想状态下我甚至不想使用IDE)

    • 你指开发者必须处理这类函数?它们只是按需使用,和其他函数无异。根本不需要IDE。我实在不明白。

      • > 根本不需要IDE。

        还能说什么呢,祝你好运。

        你没明白这个典故,大概是因为你没用过PHP(至少没用够久)。

        我给你个提示:不一致性。

  17. 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中,我虽可继承字符串类,但代码其他部分并不会自动采用我的字符串类型。

          因此我必须创建独立函数实现(这反而更优,因无需对象内部状态,外部封装更符合设计原则)。

          于是我的字符串函数与其他字符串函数运作方式不同:

              my_casing($string->trim())->substr(3);
          

          (示例当然毫无意义且可重新排序,但我们讨论的是语法)

          将所有功能都简化为函数能实现统一性。

          当然还有其他方案。C++多年来一直倡导“统一调用语法”,该方案始终支持“对象风格”函数调用,还能识别首参数类型兼容的非memwbr函数,但这需要更严格(甚至静态)的类型系统,在PHP中行不通。

  18. PHP应当进行真正重大的兼容性断裂,移除变量名中的$符号。这番痛苦绝对值得!

    • 你好Perl 6!

      此类变革将使语言彻底蜕变且完全不兼容。所有旧代码将失效,且无便捷迁移路径(尤其对希望过渡期同时支持两版的库而言)。

发表回复

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

链接收藏


京ICP备12002735号