单个文件中 PHP class 扩展名的顺序

Order of PHP class extensions in a single file

在某些情况下,乱序定义 PHP class 扩展会导致致命错误,而在某些情况下则不会。我正在尝试了解潜在的行为。

例如,两者

<?php
class BaseClass {}
class FirstExt extends BaseClass {}

<?php
class FirstExt extends BaseClass {}
class BaseClass {}

很好,所以简单地定义子class乱序不会导致问题。

但是,当涉及三个 classes 时,会出现错误,但 仅在一种特定情况下 ,即 classes 的链是以相反的顺序定义。也就是说,以下代码会导致致命错误:

<?php
class SecondExt extends FirstExt {}
class FirstExt extends BaseClass {}
class BaseClass {}

如果你尝试从命令行运行这个(比如main.php),你会得到

PHP Fatal error:  Class 'FirstExt' not found in /path/to/main.php on line 2

然而,三个 classes 运行 的其他五个排序中的任何一个都没有错误。我很惊讶甚至

<?php
class SecondExt extends FirstExt {}
class BaseClass {}
class FirstExt extends BaseClass {}

工作正常。区别因素是在给出错误的情况下,所有三对可能的 classes 都是乱序的,而在所有其他情况下,三对中最多有两对是乱序的。

产生这种行为的幕后原因是什么?

行为不直观,但我不认为这是错误,这只是 PHP 加载 classes 的方式的结果。

为了让 class 扩展父 class,必须在定义子 class 时定义父 class。

根据我的观察,似乎在解析文件并开始执行后,定义了以下 classes:

  • 内置classes
  • 所有用户定义的class在文件被解析之前定义的
  • 用户定义的基础class在该文件中
  • 该文件中的用户定义 class 扩展另一个 class 已经定义的,在该文件的前面或在该文件被解析之前

基本上任何可以在编译时定义的 class 都会被定义,而任何其他在那个时候没有定义的 class 将(试图被)定义在 运行时间.

所以在这个例子中:

<?php
echo class_exists('A') ? "Yes" : "No";   // No
echo class_exists('B') ? "Yes" : "No";   // Yes
echo class_exists('C') ? "Yes" : "No";   // Yes

class A extends B {}
class C {}
class B extends C {}

classB是在classA试图扩展它的时候定义的,因为它是在解析文件时定义的,因为在文件中classC是在它之前定义的。

但在这个例子中:

<?php
echo class_exists('A') ? "Yes" : "No";   // No
echo class_exists('B') ? "Yes" : "No";   // No
echo class_exists('C') ? "Yes" : "No";   // Yes

class A extends B {}
class B extends C {}
class C {}

class B is not defined when class A tries to extend it, because it was not defined when the file was parsed, 因为class C was not defined before it in文件。

PHP 试图找到它,但它不会再次签入同一个文件,它会尝试自动加载它。那是你得到 "not found".

的时候

添加第四个 class,您可以看到它不仅发生在 classes 以相反顺序定义时:

echo class_exists('A') ? "Yes" : "No";   // No
echo class_exists('B') ? "Yes" : "No";   // No
echo class_exists('C') ? "Yes" : "No";   // Yes
echo class_exists('D') ? "Yes" : "No";   // Yes

class A extends B {}
class D {}
class B extends C {}
class C extends D {}