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

Jan*_*s T 80 migration null deprecated php-8.1

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

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

我使用这种方法的主要问题是,该功能rename_function(PECL apd)不再起作用,最后一次更新是从 2004 1开始。

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

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

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


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

IMS*_*SoP 83

首先,需要记住两件事:

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

接下来,一些选项:

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

  • @geoffrey a) 对于 8.0 中更改的内容,您实际上有不同的消息;或者b)你_do_实际上有一些“花哨的配置” - 最有可能的是,一个[自定义错误处理函数](https://www.php.net/set_error_handler),它(不合逻辑地)将所有弃用提升为致命错误。 (2认同)

Mic*_*lli 8

Rector有一条规则NullToStrictStringFuncCallArgRector来解决这个问题:

-     mb_strtolower($value);
+     mb_strtolower((string) $value);
Run Code Online (Sandbox Code Playgroud)

  • @hakre 一个很大的区别是,如果您使用 `strict_mode=1` 运行,那么 `htmlspecialchars(42)` 已经是一个错误;`htmlspecialchars(42 ?? '')` 保留该错误,`htmlspecialchars((string)42)` 抑制它。类似地,`(string)$object`会调用`$object->__toString()`,`$object ?? '''不会。另一方面,“htmlspecialchars($mistypedVar ?? '')”会抑制警告,而“htmlspecialchars((string)$mistypedVar)”则不会,因此两者都不是完美的替代品。 (2认同)

hak*_*kre 8

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

\n

鉴于前提:

\n
\n

我的主要问题是像htmlspecialchars(php)and 这样的函数trim(php),它null不再默默地转换为空字符串。

\n
\n

那么这(首先)对我来说看起来是一个报告问题。可以通过不报告来使代码保持沉默E_DEPRECATED

\n

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

\n

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

\n

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

\n

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

\n
trim($mixed);   #1  ->     @trim($mixed);   #2\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
@trim($mixed);  #3  ->     @@trim($mixed);  #4\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n

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

\n

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

\n

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

\n

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

\n

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

\n

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

\n
@trim($mixed);     ->     trim((string)$mixed)\n@@trim($mixed);    ->     @trim((string)$mixed)\n
Run Code Online (Sandbox Code Playgroud)\n

模板(输出)保持稳定。

\n

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

\n

这样的trim($mixed)安全措施可能是一个被遗忘了多年、从未进行过升级的安全措施(有更好的安全措施可用)。对于这样的代码,我非常确定$mixed在我$string 使用trim(). 原因很简单,至少有两件事直接浮现在脑海中:

\n
    \n
  • a) 不再trim()需要 - 比它可以被删除(我最喜欢的修复之一:删除代码!) - 或 -
  • \n
  • b)它正在做字符串工作,然后我遇到了一个问题,因为我不希望那里有任何非字符串的东西。问题在于,它通常不适用于霰弹枪方法(Gie\xc3\x9fkanne 有人吗?)。
  • \n
\n

$mixed ?? \'\' 如果最初使用的是 string 或null only,则修补是完全有效的。

\n
@trim($mixed);     ->     trim($mixed ?? \'\')\n@@trim($mixed);    ->     @trim($mixed ?? \'\')\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n

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

\n

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

\n

备忘单

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

错误处理程序

\n

错误处理没有什么神奇之处,它是 PHP.net 上记录的标准(与示例 #1error_reporting(php)相比),它作为错误事件的观察者,可以通过/ error_reporting(php-ini)at来区分抑制和非抑制的错误如果需要区分,则至少达到通常必要的水平(在生产环境中E_DEPRECATED通常不属于报告的一部分)。这个示例性处理程序会抛出所有报告的错误,因此对于弃用事件也会抛出,E_ALL因此需要@抑制运算符不抛出:

\n
trim($mixed);   #1  ->     @trim($mixed);   #2\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

E_USER_DEPRECATED

\n

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

\n

然而,对其的控制较少,并且它可能已被项目依赖项中已有的第三方代码使用。找到类似于以下内容的代码并不罕见:

\n
@trim($mixed);  #3  ->     @@trim($mixed);  #4\n
Run Code Online (Sandbox Code Playgroud)\n

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

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


chi*_*aos 6

针对您想要迁移到 PHP8+ 的具有大量页面的现有项目的解决方案:

就我而言,大多数问题都与接收空值的“trim”函数有关。在这种情况下,您可以创建自定义“trim”函数,然后在现有代码中替换“custom_trim”函数的“trim”:

public function custom_trim(?string $value)
{
    return trim($value ?? '') ;
} 
Run Code Online (Sandbox Code Playgroud)

或者像这样传递参数

   trim((string) $value);
Run Code Online (Sandbox Code Playgroud)

  • 为什么要修剪空字符串?这样更好吗:`returnempty($value)?'' : 修剪($值)`? (4认同)
  • 是的,这很聪明。_“哦,这个闪亮的红色升级按钮看起来真漂亮。我想知道当我按下它时会发生什么。”_让我猜猜,没有快照吗? (2认同)