PHP不需要前向声明。
$core = new Core();
class Core {
工作正常
$test = new ParamSet(new IntParam(1));
echo($test->asString());
interface ParamType {
/*snipped*/
}
class IntParam implements ParamType {
/*snipped*/
}
class ParamSet implements ParamType {
/*snipped*/
}
没有,我明白了:
Fatal error: Class 'ParamSet' not found in
不知道为什么,我已经包含了层次结构,因为它很重要。将$ test移到定义之下会使它工作。
答案 0 :(得分:7)
被视为为bug(发送此帖子时出错,但后来标记为不是错误 - 顺便说一下,感谢这个问题。&#34 ; bug"已关闭)。
事情是 - 即使class_exists()
(即标准指标功能)也会显示它实际上并不存在:
//false, true
var_dump(class_exists('ParamSet'), class_exists('IntParam'));
interface ParamType {
/*snipped*/
}
class IntParam {
/*snipped*/
}
class ParamSet implements ParamType {
/*snipped*/
}
解决方案 - 是的,在类定义之后在接口 - 和 - 偶数 - 上定义之后创建类实例。
重要更新
实际上,虽然bugs.php.net
说它是一个错误,但在与PHP内部人员聊天之后 - 我已经意识到它不是bug(好吧,绝对不是&#意义上的#) 34;错误&#34)。这是预期的行为。
实际原因是:实现接口会将类声明转换为条件声明。因此,将在运行时评估,而不是在解释阶段。这就是class_exists()
和其他东西在这种情况下不能确定类定义的原因。
因此,虽然解决方案将保持不变(即在声明之后实例化) - 更新我认为这种行为的所有原因并不正确。
答案 1 :(得分:1)
Alec,你问题的结尾与PHP如何实现类常量,属性和方法等的绑定以及继承有关。 PHP源代码是根据需要在源文件(或eval类指令的字符串)量程中编译成一个称为op_arrays的中间代码,而这个op_array就是由PHP解释器在运行时执行的。
手头的问题是PHP何时何地为给定的类或函数执行此绑定。 (函数绑定的工作方式相同,所以我在下面引用 class 时可以互换地读取它作为类或函数)。基本上有两种选择:
第一种是在编译时绑定类,在这种情况下,它会在请求编译的include
(或等效)指令之后立即全局可见 - 或者更具体地说是在执行此语句编译的ZEND_INCLUDE_OR_EVAL
操作码。
第二个 - 知道为后期绑定 - 位于类声明的执行顺序中。
这第二个需要更多解释。如果需要这样,那么PHP解释器使用一个特殊的受损名称编译该类并注册这个受损的名称;受损的名称是在有效符号类名称范围之外的设计,因此不能直接调用,因此在源应用程序级别是不可见的。然后,编译器在源流中的类语句的位置生成DECLARE_CLASS
操作码,以将受损的名称重新绑定为真正的类名。执行此DECLARE_CLASS
操作码后,该类即可供应用程序使用。
有些东西总会强制后期绑定,例如,如果你已经加载并启用了OPcache,这个扩展会设置一个内部Zend标志来强制所有类的后期绑定。另一个例子是如果你在有条件执行的语句中有声明,例如在函数内或在if块中,例如:
if (!function_exists('fred') {
function fred ($arg) {
...
}
}
如果考虑实现接口的类,接口定义了一组方法,任何实现接口的类必须实现。 PHP运行时系统不能强制执行此要求,除非接口在之前绑定任何实现它的类。
现在提供给你的示例:在这种情况下,Core
在编译时被绑定,因此该类已经可用于以下new
。但是ParamSet
是后期绑定的,而DECLARE_CLASS
是ParamSet
新语句的执行点,该类仍然在后者处于未绑定状态,因此提升了错误。这是语言的一个特征,如果你不理解这个规则就很明显。我的简单建议是:
始终代码假设类和函数是后期绑定的。
如果PHP在编译时绑定,则此约定不会受到损害,但相反的 - 即假设编译时绑定 - 可能会导致错误,具体取决于运行时配置。