它可能不是最佳实践,但在PHP中我们可以在文件底部定义类但是,
$t = new Test();
$t->foo();
class Test extends FakeInvalidClass {
public function foo(){
echo "arrived in foo.";
}
}
Run Code Online (Sandbox Code Playgroud)
生成错误消息:
Fatal error: Class 'Test' not found in /mysite/test.php on line 4
这很奇怪......类'Test'在文件的底部定义,PHP应该失败,因为FakeInvalidClass没找到,不是Test
<?php
// test.php
class Test extends FakeInvalidClass {
public function foo(){
echo "arrived in foo.";
}
}
$t = new Test();
$t->foo();
Run Code Online (Sandbox Code Playgroud)
产生更易读的错误
Fatal error: Class 'FakeInvalidClass' not found in /mysite/test.php on line 4
作为参考,这很好用:
<?php
// test.php
$t = new Test();
$t->foo();
class Test {
public function foo(){
echo "arrived in foo.";
}
}
Run Code Online (Sandbox Code Playgroud)
我认为当你看到Zend Engine为每个例子生成的操作码时(对我来说无论如何)都会对你有意义.
例1:
compiled vars: !0 = $t
line # * op fetch ext return operands
---------------------------------------------------------------------------------
4 0 > EXT_STMT
1 ZEND_FETCH_CLASS :0 'Test'
2 EXT_FCALL_BEGIN
3 NEW $1 :0
4 DO_FCALL_BY_NAME 0
5 EXT_FCALL_END
6 ASSIGN !0, $1
6 7 EXT_STMT
8 ZEND_INIT_METHOD_CALL !0, 'foo'
9 EXT_FCALL_BEGIN
10 DO_FCALL_BY_NAME 0
11 EXT_FCALL_END
7 12 EXT_STMT
13 ZEND_FETCH_CLASS :6 'FakeInvalidClass'
14 ZEND_DECLARE_INHERITED_CLASS $7 '%00test%2Fhome%2Fflacroix%2Ftest.php0x7f756fea4055', 'test'
12 15 > RETURN 1
Run Code Online (Sandbox Code Playgroud)
正如你所看到的那样,#1获得了这个Test类,而后者又进入了#13来获取FakeInvalidClass(参见return :6那里).由于后者未定义,#13失败并返回#1,这也失败,因为Test未定义.
它们(#13和#1)都将调用zend_error(如PHP源代码所示),但zend_error具有全局状态(即:错误未堆叠),因此任何后续调用都将使用新错误覆盖错误消息.所以,在伪代码中:
ZEND_FETCH_CLASS('Test')
ZEND_FETCH_CLASS('FakeInvalidClass')
zend_error('Class FakeInvalidClass not found')
return
zend_error('Class Test not found')
return
Run Code Online (Sandbox Code Playgroud)
例2:
compiled vars: !0 = $t
line # * op fetch ext return operands
---------------------------------------------------------------------------------
4 0 > EXT_STMT
1 ZEND_FETCH_CLASS :0 'FakeInvalidClass'
2 ZEND_DECLARE_INHERITED_CLASS $1 '%00test%2Fhome%2Fflacroix%2Ftest2.php0x7fe2c1461038', 'test'
10 3 EXT_STMT
4 ZEND_FETCH_CLASS :2 'Test'
5 EXT_FCALL_BEGIN
6 NEW $3 :2
7 DO_FCALL_BY_NAME 0
8 EXT_FCALL_END
9 ASSIGN !0, $3
12 10 EXT_STMT
11 ZEND_INIT_METHOD_CALL !0, 'foo'
12 EXT_FCALL_BEGIN
13 DO_FCALL_BY_NAME 0
14 EXT_FCALL_END
13 15 > RETURN 1
Run Code Online (Sandbox Code Playgroud)
这里#1是一个ZEND_FETCH_CLASS 'FakeInvalidClass'代码,但该类不存在,因此它返回一个FakeInvalidClass not found消息,就像它应该的那样.
例3:
compiled vars: !0 = $t
line # * op fetch ext return operands
---------------------------------------------------------------------------------
4 0 > EXT_STMT
1 ZEND_FETCH_CLASS :0 'Test'
2 EXT_FCALL_BEGIN
3 NEW $1 :0
4 DO_FCALL_BY_NAME 0
5 EXT_FCALL_END
6 ASSIGN !0, $1
6 7 EXT_STMT
8 ZEND_INIT_METHOD_CALL !0, 'foo'
9 EXT_FCALL_BEGIN
10 DO_FCALL_BY_NAME 0
11 EXT_FCALL_END
8 12 EXT_STMT
13 NOP
13 14 > RETURN 1
Run Code Online (Sandbox Code Playgroud)
Zend获取ZEND_FETCH_CLASS 'Test'代码并正常执行.
这可以解释为PHP将在执行代码之前解析它在代码中遇到的第一级类.当您创建扩展另一个类或实例化对象的类定义时,将在代码中的该点为该类插入操作码.它实际上是懒惰初始化.ZEND_FETCH_CLASS
这也可以通过以下方式得到证明:
<?php
exit;
class Test extends FakeInvalidClass {
public function foo(){
echo "arrived in foo.";
}
}
Run Code Online (Sandbox Code Playgroud)
结论:
不同的错误消息由ZEND_FETCH_CLASS操作码的不同参数解释.
现在,如果你想知道为什么ZE会生成这样的操作码,它可能是一个设计选择,它可能更容易维护.但说实话,我不知道.
逻辑结论是,如果您尝试创建类的对象,它将继续扫描文件的其余部分以获取此类声明.如果它无法"解析"该类(由于您扩展了一个不存在的类),它将返回到您尝试创建该对象的行并告诉您该类不存在.
那么为什么不在找不到你扩展的课程时给你一个错误呢?
好吧,PHP不知道你以后是否会包含一个包含的文件,FakeInvalidClass在第4行说这是错误的Fatal error: Class 'FakeInvalidClass' not found.
编辑:
但是,您FakeInvalidClass以后不能包含,因为Test已经解析过了.因此,仍然存在PHP将在无法执行的行上给出错误.但是一个更有用的错误信息就是堆叠它们:
Class 'Test' not found (Class 'FakeInvalidClass' not found in /mysite/test.php on line 8) in /mysite/test.php on line 4
但PHP不会堆叠错误.