Why does PHP produce different error messages for these two cases?

This is not a good practice, but in PHP we are allowed to define classes at the bottom of files. But,

$t = new Test(); $t->foo(); class Test extends FakeInvalidClass { public function foo(){ echo "arrived in foo."; } } 

It produces an error message:

Fatal error: Class 'Test' not found in /mysite/test.php on line 4

This odd ... class "Test" is defined at the bottom of the file, PHP should have failed because FakeInvalidClass not found, not Test

 <?php // test.php class Test extends FakeInvalidClass { public function foo(){ echo "arrived in foo."; } } $t = new Test(); $t->foo(); 

Creates a more user readable error

Fatal error: Class 'FakeInvalidClass' not found in /mysite/test.php on line 4

For reference, this works fine:

 <?php // test.php $t = new Test(); $t->foo(); class Test { public function foo(){ echo "arrived in foo."; } } 
+7
source share
4 answers

I think this will make sense to you when you see the Zend Engine opcodes for each example (this is all the same to me).

Example 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 

As you can see, # 1 gets the Test class, which in turn goes to # 13 to get a FakeInvalidClass (see return :6 there). Since the latter is undefined, # 13 fails and returns to # 1, which also fails, because Test remains undefined.

Both of them (# 13 and # 1) will zend_error (as shown in the PHP source), but zend_error has a global state (that is: errors are not stacked), so any subsequent call will overwrite the error message with a new one. So in the pseudo code:

 ZEND_FETCH_CLASS('Test') ZEND_FETCH_CLASS('FakeInvalidClass') zend_error('Class FakeInvalidClass not found') return zend_error('Class Test not found') return 

Example 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 

Here # 1 is the code ZEND_FETCH_CLASS 'FakeInvalidClass' , but the class does not exist, so it returns the message FakeInvalidClass not found , as it should be.

Example 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 

Zend receives the code ZEND_FETCH_CLASS 'Test' and runs normally.

This is because PHP will analyze the first-level classes that it encounters in the code before it executes it. When you create a class definition that extends another class or instantiates an object, the operation code ZEND_FETCH_CLASS will be inserted for that class at this point in the code. This is actually lazy initialization.

This is also evidenced by the fact that this works great:

 <?php exit; class Test extends FakeInvalidClass { public function foo(){ echo "arrived in foo."; } } 

Output:

Different error messages are explained by different parameters of the operation code ZEND_FETCH_CLASS .

Now, if you are wondering why ZE generates such operation codes, this is probably the design choice, it is probably easier to maintain. But honestly, I have no idea.

+6
source

The logical conclusion is that if you try to create an object of the class, it will continue to scan the rest of the file for this class declaration. If it fails to β€œparse” the class (due to the fact that you are expanding a non-existing class), it will return to the line in which you tried to create the object and inform that the class does not exist.

So why not give you an error if you have not found a class that you are extending?

Well, PHP doesn't know if it will include a file that will contain FakeInvalidClass , and it would be wrong to say on line 4 that Fatal error: Class 'FakeInvalidClass' not found .

Edit: However, you cannot enable FakeInvalidClass later, since Test will already be parsed. Thus, it still remains that PHP will give you an error in the string that it cannot execute. But a more informative error message would be for their stack:

Class 'Test' not found (Class 'FakeInvalidClass' not found in /mysite/test.php on line 8) in /mysite/test.php on line 4

But PHP does not pose errors.

+5
source

It's just that in the first case, the Test class does not load. In the second case, the class is a load, but the extension is not valid.

+3
source

Case 1: you are trying to create an object of the Test class, so when it tries to create an object, the extension from the base class is FakeInvalid, therefore, an error occurred while creating the Test class saying that it was not found.

Case 2: it first tries to extend the base class and does not find it, so it gives such an error that FakeInvalidClass is not found.

0
source