遇到了这么多次,不知道为什么,所以让我感到好奇。有些类在声明之前起作用,而另一些则没有。
示例1
$test = new TestClass(); // top of class
class TestClass {
function __construct() {
var_dump(__METHOD__);
}
}
输出
string 'TestClass::__construct' (length=22)
示例2
当一个类扩展另一个类或实现任何接口(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));
}
}
输出
Fatal error: Class 'TestClass' not found
示例3
让我们尝试上面相同的类,但是改变位置
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
输出
string 'TestClass::__construct' (length=22)
示例4(我也用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
一旦实现
JsonSerializable
(或任何其他)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
还检查了操作码
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
还检查了操作码
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
问题
Example 3
可以工作,因为该类是在初始化之前声明的,但是为什么Example 1
首先可以工作? Opcodes
本来可以使事情变得清楚,但只是使其变得更加复杂,因为class_exists
在TestClass
之前被调用,但情况恰恰相反。 最佳答案
我找不到关于PHP类定义的文章;但是,我想它与您的实验表明的User-defined functions完全相同。
无需在引用函数之前就定义它们,除外,当有条件定义函数时,除外,如下面的两个示例所示。以条件方式定义功能时;其定义必须在被调用之前处理。
<?php
$makefoo = true;
/* We can't call foo() from here
since it doesn't exist yet,
but we can call bar() */
bar();
if ($makefoo) {
function foo()
{
echo "I don't exist until program execution reaches me.\n";
}
}
/* Now we can safely call foo()
since $makefoo evaluated to true */
if ($makefoo) foo();
function bar()
{
echo "I exist immediately upon program start.\n";
}
?>
对于类也是如此:
JsonSerializable
为条件。 通过实现接口(interface)或从另一个文件(
require
)扩展另一个类,使该类成为有条件的。我称它为有条件的,因为该定义现在依赖于另一个定义。想象一下,PHP解释器首先查看了该文件中的代码。它看到了无条件的类和/或函数,因此它继续并将它们加载到内存中。它会看到一些有条件的条件,并跳过它们。
然后,解释器开始解析该页面以执行。在示例4中,它到达
class_exists("TestClass")
指令,检查内存,然后说不,我没有那个。如果没有,因为它是有条件的。它继续执行指令,请参阅条件类,并执行指令以将该类实际加载到内存中。然后,它下降到最后一个
class_exists("TestClass")
,并看到该类确实存在于内存中。在阅读您的操作码时,不会在
TestClass
之前调用class_exist
。您所看到的是SEND_VAL,它将发送值TestClass,以便它在内存中用于下一行,该行实际上在class_exists
上调用DO_FCALL然后,您可以看到它如何处理类定义本身:
正是第二部分 ZEND_ADD_INTERFACE 似乎阻止了PHP Engine仅在该类的初始峰值处加载该类。
我想我们已经回答了您所有的问题。
最佳做法:将每个类放在自己的文件中,然后根据需要autoload进行分类,如@StasM在他的回答中所述,请使用明智的文件命名和自动加载策略-例如PSR-0或类似的内容。当您执行此操作时,您不再需要担心引擎加载它们的顺序,它会自动为您处理。
关于php - 类扩展或接口(interface)如何工作?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15688642/