首页  >  问答  >  正文

迁移到 PHP 8.1 - 如何修复已弃用的将 null 传递给参数错误 - 重命名函数中的构建

PHP 8.1 已弃用将 null 作为参数传递给许多核心函数。我的主要问题是 htmlspecialchars(php)trim(php) 等函数,其中 null 不再默默转换为空字符串。

为了在不使用大量代码的情况下解决此问题,我尝试重命名原始内置函数,并将它们替换为将输入从 null 转换为(空)字符串的包装器。

这种方法的主要问题是,函数 rename_function(PECL apd) 不再起作用,上次更新是在 2004 年1

我需要对内置函数进行某种重写,以避免每次调用函数时都编写空检查,从而使我的所有代码变大两倍。

我能想到的唯一其他解决方案是仅使用我的自定义函数,但这仍然需要遍历我拥有的所有代码和第三方库。

在 PHP 8.1 中,当将 null 传递给内置函数时,它不再默默地转换为空字符串。


  1. https://pecl.php.net/package/apd

P粉420868294P粉420868294257 天前428

全部回复(2)我来回复

  • P粉811329034

    P粉8113290342024-01-11 15:36:30

    我想(作为补充,现有的答案得到了我的支持)就如何看待和解决此类“问题”描绘了一幅不同的图画。它并没有减少所概述的方法的正确性或错误性,而只是一种希望互惠互利的附加观点。每个项目都是不同的。

    给定前提:

    那么这对我来说(首先)看起来是一个报告问题。通过不报告 E_DEPRECATED

    这样做的好处是(不仅是您的代码),现在知道您的代码带有弃用通知。报告确实有效

    另一方面,压制弃用通知可能会让它们消失。如果您丢失了代码库带有弃用通知的信息,从技术上讲,从信息丢失中恢复可能仍然很容易(再次报告弃用通知),但是如果更改的时间延长了,现在可能会出现压倒性的噪音 (E_TOO_MUCH_NOISE)。

    那么代码不沉默实际上是一件坏事吗?或者说可以转化为利益吗?我宁愿选择后者。不管怎样,我们已经在处理这些信息了。

    因此,在这种情况中,我的想法是一般不抑制弃用通知,而是“静默”函数调用。这很容易,但无论从好的方面还是从坏的方面来说,这都是愚蠢的:

    trim($mixed);   #1  ->     @trim($mixed);   #2

    这当然是一个可以使用标准文本工具应用于代码库的操作。它还会向您显示过去已经使用过 @ 抑制运算符的位置:

    @trim($mixed);  #3  ->     @@trim($mixed);  #4

    如果您是一名 PHP 开发人员,在编辑器中查看此类代码(对于情况#2-#4),他们会立即向您尖叫,并且对于所有四种情况至少都会引起您的注意($mixed)。

    感谢您没有保持沉默,我们让这些地方尖叫,只是不是在运行时1

    与第一种通过不报告 E_DEPRECATED 来保持沉默的方法不同,这种方法很容易丢失信息,而信息是通过使用所有 @ 符号来保存的。

    它对解决噪音问题有帮助吗?如果我们停止在这里工作,那就完全不行了。现在我们会在代码上涂上@-符号,决定不采取进一步的操作,这样我们就可以使用第一个解决方案(不报告弃用消息)来完成它,而无需触及代码。

    那么它的好处是什么?好吧,尽管代码现在静默运行,PHP 仍然提供诊断消息。也就是说,现在可以将 PHP 错误处理程序注册为侦听器(在执行代码时)。

    仅在代码级别,很容易检查这些位置,因为 @ 符号(通常)也很容易在代码中发现。

    第二部分很重要,因为尽管多个地方可能会受到弃用的影响,但一定不能有一个解决方案来解决所有问题(我更喜欢远离“一刀切” '解决方案'”(如果可能的话),但特别是在问题上下文中 PHP 8.1 发生了变化,我可以想象根据使用地点会有不同的需求。

    例如,在模板代码(输出)中,具体类型不是一个问题,因此转换为字符串很可能是首选解决方案:

    @trim($mixed);     ->     trim((string)$mixed)
    @@trim($mixed);    ->     @trim((string)$mixed)

    模板(输出)保持稳定。

    但对于实际的输入处理,弃用通知可能会发现值得修复的实际潜在缺陷,例如缺少默认值(使事情变得过于复杂)、值的处理不明确(空与空、字符串、布尔与数字)与 PHP 中的数组与对象)或一般的 $mixed 混淆。

    这样的 trim($mixed) 可能是一个被遗忘多年的安全防护,从未进行过升级(有更好的安全防护可用)。对于这样的代码,我很确定我已经想要并要求 $mixed 实际上是 $string before 我使用 trim ()。原因很简单,至少直接想到两件事:

    • a) 不再需要 trim() - 它可以被删除(我最喜欢的修复之一:删除代码!) - 或 -
    • b)它正在进行字符串工作,那么我有一个问题,因为我不希望有任何非字符串的东西存在。问题在于,它通常不适用于霰弹枪方法(Gießkanne 有人吗?)。

    使用 $mixed 进行修补是完全有效的? ''如果原始使用是字符串或null

    @trim($mixed);     ->     trim($mixed ?? '')
    @@trim($mixed);    ->     @trim($mixed ?? '')

    但除此之外,例如像 42 这样的数字,将抛出 TypeError,而不是弃用消息。这可以区分正在运行的代码和未运行的代码。

    因此,这里还有更多需要维护的地方,例如检查位置,如果可能的话进一步聚类,然后应用更多专用修复程序。它可能会揭示缺失的测试或断言,需要一些时间来稳定整个应用程序流程等。

    在这种情况下,要完成代码的迁移,进行集群,处理空合并运算符,并为真正的修复做适当的文书工作。一旦完成了使用空合并运算符的非明显错误抑制并删除了 @ 抑制运算符,如果修复计划未捕获这些信息,您可能会丢失这些信息。

    当我在这些地方看起来受过更多教育时,当我发现自己挠头或揉眼睛时,我并不感到惊讶。然后我提醒自己,这些错误不是因为 PHP 8.1 版本造成的,版本更改只是让它们(再次)出现,有时我什至会通过维护 PHP 版本来获得完整的错误集群作为副渔获物。

    备忘单

    • (string)$mixed - 之前的行为
    • $mixed ?? '' - 仅在 null 上抑制 TypeError 错误
    • @ - 完全错误抑制。您应该在适用的地方记录您的代码库。
    • @@ - 如果出现这种情况,这可能是一个值得研究的有趣地方。
    • 空($mixed)? '' : xxx($mixed) - 把垃圾带出去,典型的空瘫/混合混乱,寻找集群,有机会大大简化代码库。迁移到标量类型(PHP 7),从最内向外引入严格的类型处理,在适用的情况下使用 PHP“经典”和“严格”类型处理。 PHP 7.0 断言和 PHP 8.1 弃用消息可以很好地支持这里。

    错误处理程序

    错误处理没有什么魔力,它是 PHP.net 上记录的标准(与 Example #1),它作为错误事件的观察者,可以区分受抑制的错误和非受抑制的错误通过 error_reporting(php) / error_reporting(php-ini) 至少达到通常需要的级别,如果需要进行区分(在生产环境中,E_DEPRECATED 通常不是报告的一部分)。此示例性处理程序会抛出所有报告的错误,对于弃用事件以及 E_ALL 也会抛出此类错误,因此需要 @ 抑制运算符不抛出:

    set_error_handler(static function ($type, $message, $file, $line) use (&$deprecations) {
        if (!(error_reporting() & $type)) {
            // This error code is not included in error_reporting, so let it fall
            // through to the standard PHP error handler
    
            // capture E_DEPRECATED
            if ($type === E_DEPRECATED) {
                $deprecations[] =
                    ['deprecations' => count($deprecations ?: [])]
                    + get_defined_vars();
            }
    
            return false;
        }
    
        // throwing error handler, stand-in for own error reporter
        // which may also be `return false;`
        throw new ErrorException($message, $type, error_reporting(), $file, $line);
    });
    

    类似的错误处理程序可以在 3v4l.org 上的扩展示例中找到,包括要报告的已弃用代码上。

    E_USER_DEPRECATED

    从技术上讲,错误抑制运算符可以与 E_USER_DEPRECATED 结合使用,与上面 E_DEPRECATED 概述的相同。

    但是,对它的控制较少并且它可能已被项目依赖项中已有的第三方代码使用。类似下面的代码并不罕见:

    @trigger_error('this. a message.', E_USER_DEPRECATED);
    

    它的作用完全相同:发出弃用事件,但将它们从 PHP 报告中排除。订阅这些内容可能会让您陷入噪音之中。使用 E_DEPRECATED,您总是可以直接从 PHP 获得“好的、原创的”。


    1. 当考虑使用 @ 错误抑制运算符的方法并对其进行评论时,IMSoP 立即举起红/黑旗(正确!),很容易将婴儿与洗澡水一起倒掉@ 抑制运算符。在我的回答中,它的目的只是抑制弃用通知但是使用的结果是它抑制所有诊断消息和错误,在某些 PHP 版本中甚至是致命的消息和错误,因此 PHP 退出 255,无需任何进一步的诊断 - 不仅要小心,还要处理。这个运营商很强大。跟踪它在代码库中的使用情况并不断检查它是否符合您的基线/期望。对于合法情况,请考虑使用消音器。为了移植/维护代码,首先使用它来标记。完成批量编辑后,再次将其删除。

    回复
    0
  • P粉592085423

    P粉5920854232024-01-11 00:24:47

    首先,要记住两件事:

    1. PHP 8.1 弃用这些调用,它不会使它们出错。弃用的目的是提前通知作者修复他们的代码,因此您和您使用的库的作者可以在 PHP 9.0 发布之前修复问题。因此,不要惊慌,因为并非所有问题都能立即修复,并对库维护人员保持耐心,他们会在自己的时间解决这个问题。
    2. 大多数情况下的快速解决方法是使用 空合并运算符来提供适当的默认值,因此您不需要在每次使用时进行长时间的空检查。例如,htmlspecialchars($something) 可以替换为 htmlspecialchars($something ?? '')

    接下来,一些选项:

    • 根据您的案例数量,您也许可以一次手动修复几个问题,或者添加 ?? '' 或修复一个逻辑错误,无论如何你都不希望出现 null。
    • 创建 nullable_htmlspecialchars 等自定义函数,并在代码中直接查找和替换。
    • 创建自定义命名空间函数,例如 nullableoverride\htmlspecialchars;然后在添加 use function nullableoverride\htmlspecialchars; 的任何文件中,将使用该函数而不是内置函数。不过,这必须添加到每个文件中,因此您可能需要一个工具来自动添加它。
    • 使用Rector自动添加?? '' 到适当的函数调用,因此您不必手动编辑它们。不幸的是,似乎还没有这方面的内置规则,因此您必须学会编写自己的规则。
    • 可能更简单,根据您的技能,使用正则表达式查找和替换来添加 ?? ''到简单的情况。

    回复
    0
  • 取消回复