了解升级为警告的通知
在 PHP 7 之前的 PHP 版本中,有许多被认为对运行时 PHP 引擎的稳定性不太重要的情况被低估了。不幸的是,新的(或者说懒惰的!)PHP 开发人员在急于将代码投入生产时,通常会忽略 "通知"。
这些年来,PHP 标准大幅收紧,导致 PHP 核心团队将某些错误条件从 "通知" 升级为 "警告"。这两种错误报告级别都不会导致代码停止工作。不过,PHP 核心团队认为,从 "通知" 到 "警告" 的升级将使不良的编程实践变得清晰可见。警告被忽略的可能性会大大降低,最终会产生更好的代码。
以下是在 PHP 早期版本中导致发出 "通知" 的错误条件的简要列表,在 PHP 8 中,同样的条件现在会产生 "警告":
-
尝试访问不存在的对象属性
-
尝试访问不存在的静态属性
-
尝试使用不存在的键访问数组元素
-
滥用资源作为数组偏移
-
不明确的字符串偏移
-
字符串偏移不存在或未初始化
让我们先来看看涉及对象的情况下的通知提升。
不存在的对象属性访问处理
在早期的 PHP 版本中,当尝试访问不存在的属性时会发出通知。唯一的例外是在自定义类中定义了神奇的 __get()
和/或 __set()
方法。
在下面的代码示例中,我们定义了一个带有两个属性的 Test
类,其中一个被标记为静态属性:
// /repo/ch03/php8_warn_undef_prop.php
class Test {
public static $stat = 'STATIC';
public $exists = 'NORMAL';
}
$obj = new Test();
然后,我们尝试 echo 现有和不存在的属性,如下所示:
echo $obj->exists;
echo $obj->does_not_exist;
不出所料,当尝试使用不存在的属性 echo 时,PHP 7 的输出会返回一个 "通知",如图所示:
NORMAL
PHP Notice: Undefined property: Test::$does_not_exist in
/repo/ch03/php8_warn_undef_prop.php on line 14
在 PHP 8 中,相同的代码文件现在返回警告,如下所示:
NORMAL
PHP Warning: Undefined property: Test::$does_not_exist in /
repo/ch03/php8_warn_undef_prop.php on line 14
|
我们现在添加几行代码,试图访问一个不存在的静态属性,如下所示:
try {
echo Test::$stat;
echo Test::$does_not_exist;
} catch (Error $e) {
echo __LINE__ . ':' . $e;
}
有趣的是,PHP 7 和 PHP 8 现在都会出现致命错误,如图所示:
STATIC
22:Error: Access to undeclared static property Test::$does_not_
exist in /repo/ch03/php8_warn_undef_prop.php:20
任何时候,如果代码块以前发出的是警告,而现在发出的是错误,都值得关注。如果可能,请扫描代码中对静态类属性的静态引用,并确保它们已被定义。否则,在 PHP 8 升级后,您的代码就会失效。
现在让我们来看看不存在的偏移处理。
不存在的偏移处理
如上一节所述,一般来说,在读取数据时,"通知" 会被升级为 "警告",而在写入数据时,"警告" 会被升级为 "错误"(可能导致数据丢失)。对不存在的偏移量的处理也遵循这一逻辑。
在下面的示例中,数组键是从字符串中提取的。在这两种情况下,偏移量都不存在:
// /repo/ch03/php8_warn_undef_array_key.php
$key = 'ABCDEF';
$vals = ['A' => 111, 'B' => 222, 'C' => 333];
echo $vals[$key[6]];
在 PHP 7 中,结果是一个通知,如图所示:
PHP Notice: Uninitialized string offset: 6 in /repo/ch03/php8_
warn_undef_array_key.php on line 6
PHP Notice: Undefined index: in /repo/ch03/php8_warn_undef_
array_key.php on line 6
在 PHP 8 中,结果是警告,如图所示:
PHP Warning: Uninitialized string offset 6 in /repo/ch03/php8_
warn_undef_array_key.php on line 6
PHP Warning: Undefined array key "" in /repo/ch03/php8_warn_
undef_array_key.php on line 6
这个示例进一步说明了 PHP 8 错误处理增强的一般原理:如果代码将数据写入一个不存在的偏移量,以前的警告在 PHP 8 中就变成了错误。前面的输出显示,在 PHP 8 中,如果尝试从一个不存在的偏移量读取数据,现在就会发出警告。下一个要检查的 "通知" 升级涉及滥用资源 ID。
滥用资源 ID 作为数组偏移量
资源是在创建与应用程序代码外部服务的连接时生成的。这种数据类型的一个典型例子就是文件句柄。在下面的代码示例中,我们将打开一个文件句柄(从而创建 resource
)到 gettysburg.txt
文件:
// /repo/ch03/php8_warn_resource_offset.php
$fn = __DIR__ . '/../sample_data/gettysburg.txt';
$fh = fopen($fn, 'r');
echo $fh . "\n";
请注意,我们在最后一行直接 echo resource。这显示了资源 ID 号。但是,如果我们现在尝试使用资源 ID 作为数组偏移量,PHP 7 会生成一个通知,如图所示:
Resource id #5
PHP Notice: Resource ID#5 used as offset, casting to integer
(5) in /repo/ch03/php8_warn_resource_offset.php on line 9
PHP 8 如期生成了警告,如图所示:
Resource id #5
PHP Warning: Resource ID#5 used as offset, casting to integer
(5) in /repo/ch03/php8_warn_resource_offset.php on line 9
请注意,在 PHP 8 中,许多以前产生资源的函数现在会产生对象。第 7 章 "使用 PHP 8 扩展时避免陷阱" 将介绍这一主题。
最佳实践:不要将资源 ID 用作数组偏移量! |
现在,我们将注意力转移到与字符串相关的 "通知" 上,在字符串偏移量不明确的情况下,将 "通知" 升级为 "警告"。
不明确的字符串偏移量转换
将注意力转移到字符串处理上,我们再次重温使用数组语法识别字符串中单个字符的想法。如果 PHP 需要执行内部类型转换以评估字符串偏移量,但类型转换的位置并不明确,那么可能会出现字符串偏移量不明确的情况。
在这个非常简单的示例中,我们定义了一个包含所有字母的字符串。然后,我们定义了一个数组,其中的键值包括 NULL;布尔值 TRUE;浮点数 22/7(Pi 的近似值)。然后,我们循环查看键值,并尝试将键值用作字符串偏移量,如图所示:
// /repo/ch03/php8_warn_amb_offset.php
$str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$ptr = [ NULL, TRUE, 22/7 ];
foreach ($ptr as $key) {
var_dump($key);
echo $str[$key];
}
正如您所预料的,在 PHP 7 中运行的输出会产生 A、B 和 D,以及一系列通知,如图所示:
NULL
PHP Notice: String offset cast occurred in /repo/ch03/php8_
warn_amb_offset.php on line 8
A
/repo/ch03/php8_warn_amb_offset.php:7:
bool(true)
PHP Notice: String offset cast occurred in /repo/ch03/php8_warn_amb_offset.php on line 8
B
/repo/ch03/php8_warn_amb_offset.php:7:
double(3.1428571428571)
PHP Notice: String offset cast occurred in /repo/ch03/php8_
warn_amb_offset.php on line 8
D
PHP 8 始终会产生相同的结果,但在这里,"警告" 取代了 "通知":
NULL
PHP Warning: String offset cast occurred in /repo/ch03/php8_
warn_amb_offset.php on line 8
A
bool(true)
PHP Warning: String offset cast occurred in /repo/ch03/php8_
warn_amb_offset.php on line 8
B
float(3.142857142857143)
PHP Warning: String offset cast occurred in /repo/ch03/php8_
warn_amb_offset.php on line 8
D
现在让我们来看看不存在的偏移处理。
未初始化或不存在的字符串偏移量
这类错误的设计目的是对使用偏移量访问字符串(偏移量越界)进行陷阱处理。下面是一个非常简单的代码示例,可以说明这种情况:
// /repo/ch03/php8_warn_un_init_offset.php
$str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
echo $str[27];
在 PHP 7 中运行此代码会得到一个通知。下面是 PHP 7 的输出结果:
PHP Notice: Uninitialized string offset: 27 in /repo/ch03/
php8_warn_un_init_offset.php on line 5
可以预见的是,PHP 8 的输出会产生一个警告,如下所示:
PHP Warning: Uninitialized string offset 27 in /repo/ch03/
php8_warn_un_init_offset.php on line 5
本节中的所有示例都证实了 PHP 8 在执行最佳编码实践方面的总体趋势。
有关升级通知和警告的更多信息,请查看本文: https://wiki.php.net/rfc/engine_warnings 。 |
现在,我们将注意力转向(臭名昭著的)@
警告抑制器。