在查看一些PHP代码时,我发现了一件奇怪的事情。下面是一个简单的例子:
文件A.php:
<?php
class A{
public function methodA(){
echo $this->B;
}
}
?>
文件B.php:
<?php
class B extends A{
public $B = "It's working!";
}
?>
文件测试.php:
<?php
require_once("A.php");
require_once("B.php");
$b = new B();
$b->methodA();
?>
运行test.php会打印出"它在工作!",但问题是为什么它在工作?:)这是一个功能还是一个bug?类A中的方法methodA也可以调用类B中不应在OOP中工作的方法。
您只是在实例化类B
。暂时忽略A
,并假设methodA()
是类B
的一部分。
当类B
扩展A
时,它得到了A
的所有函数。$this->B
在代码运行之前不会求值,而不是在之前求值。因此,不会发生错误,也不会发生,因为$this->B
存在于类B
中。
PHP是一种动态语言。方法和数据成员在运行时进行评估。当你调用一个方法或访问一个成员时,PHP实际上会查找一个排序的哈希表,以确定这个方法或成员是否可以在继承层次结构中的任何地方访问。
不仅是继承,您还可以在运行时将任意数据分配给对象,并且类内的代码仍然可以使用$this->something访问它,而类中甚至不存在"something"。
$this
只是一个对象变量——一个特殊的变量,因为它是当前的变量,但它仍然只是一个目标变量。
因为$B::B
是一个公共成员变量,所以可以从任何可以访问B
实例的引用的地方访问它,例如使用对象变量。
由于公共成员在任何地方都可以访问,任何函数,即使是在A::methodA()
中也可以访问。
因此,实际上并没有什么值得怀疑的。示例中的类继承仅与调用A::methodA()
时以$this
"参数"形式传递对象变量(不可见)有关。
请参阅以下可能使其更显眼的示例:
function methodA($object) {
echo $object->B;
}
class B {
public $B = "It's working!";
public function methodA() {
methodA($this);
}
}
由于PHP是一种动态语言,调用使用它的实例(在本例中是子类B的实例)上可能存在的属性或方法没有错。
PHP是一种动态语言。当对B的实例调用methodA()时,B的成员$B确实存在。
$a = new A();
$a->methodA();
不会起作用。
在某些动态语言中,您甚至可以在运行时定义方法。
这对我来说很有意义$B由于出现在类B中而在结果对象中可用。由于$this->B是在运行时求值的,并且该值是在此之前设置的(当类B实例化时),因此发现它设置正确。
您也可以对方法执行此操作,因为在执行它们之前不会检查它们的存在性(尽管在方法的情况下,通常在父类中声明它们为抽象的,从而迫使子类实现它们)。
OOP就是这样工作的。一开始这可能看起来有点奇怪,因为你可能认为a必须首先知道$this->B
是什么(事实上,在某些语言中,这会产生编译器警告),但行为是正确的,因为你使用的子类定义了函数要查找的变量。如果您从A()
的实例调用methodA()
,则会得到一个"未定义"错误。
现在,奇怪的是(读起来:错误)如果THIS有效:
class A {
public $b = "Derp";
}
class B extends A {
function sayIt() { echo $this->b; }
}
$a = new A();
$a->sayIt();
类B从类A扩展而来,因此它继承了来自类A的方法methodA()
。