Have come across this so many times and am not sure why so it got me curious. Some classes work before they are declared and others don't;
Example 1
$test = new TestClass(); // top of class
class TestClass {
function __construct() {
var_dump(__METHOD__);
}
}
Output
string 'TestClass::__construct' (length=22)
Example 2
When a class extends another class or implements any interface
$test = new TestClass(); // top of class
class TestClass implements JsonSerializable {
function __construct() {
var_dump(__METHOD__);
}
public function jsonSerialize() {
return json_encode(rand(1, 10));
}
}
Output
Fatal error: Class 'TestClass' not found
Example 3
Let's try the same class above but change the position
class TestClass implements JsonSerializable {
function __construct() {
var_dump(__METHOD__);
}
public function jsonSerialize() {
return json_encode(rand(1, 10));
}
}
$test = new TestClass(); // move this from top to bottom
Output
string 'TestClass::__construct' (length=22)
Example 4 ( I also tested with class_exists )
var_dump(class_exists("TestClass")); //true
class TestClass {
function __construct() {
var_dump(__METHOD__);
}
public function jsonSerialize() {
return null;
}
}
var_dump(class_exists("TestClass")); //true
as soon as it implements JsonSerializable
( Or any other)
var_dump(class_exists("TestClass")); //false
class TestClass implements JsonSerializable {
function __construct() {
var_dump(__METHOD__);
}
public function jsonSerialize() {
return null;
}
}
var_dump(class_exists("TestClass")); //true
Also Checked Opcodes without
JsonSerializable
line # * op fetch ext return operands
---------------------------------------------------------------------------------
3 0 > SEND_VAL 'TestClass'
1 DO_FCALL 1 $0 'class_exists'
2 SEND_VAR_NO_REF 6 $0
3 DO_FCALL 1 'var_dump'
4 4 NOP
14 5 > RETURN 1
Also Checked Opcodes with
JsonSerializable
line # * op fetch ext return operands
---------------------------------------------------------------------------------
3 0 > SEND_VAL 'TestClass'
1 DO_FCALL 1 $0 'class_exists'
2 SEND_VAR_NO_REF 6 $0
3 DO_FCALL 1 'var_dump'
4 4 ZEND_DECLARE_CLASS $2 '%00testclass%2Fin%2FaDRGC0x7f563932f041', 'testclass'
5 ZEND_ADD_INTERFACE $2, 'JsonSerializable'
13 6 ZEND_VERIFY_ABSTRACT_CLASS $2
14 7 > RETURN 1
Question
- I know
Example 3
worked because the class was declared before its initiated but why wouldExample 1
work in the first place ? - How does this entire process of extending or interface work in PHP to make one valid and the other invalid?
- What Exactly is happening in Example 4?
Opcodes
was supposed to make things clear but just made it more complex becauseclass_exists
was called beforeTestClass
but the reverse is the case.