PHP超类在不知道它们的情况下调用子类方法?

时间:2013-01-29 12:56:15

标签: php oop subclass superclass

在尝试调试某些PHP类时,我遇到了一些在我看来非常奇怪的行为。

我构建了以下行为的演示:

class BaseClass {
   public function baseMethod () {
      echo (implode (' ', $this -> childMethod ()) . PHP_EOL);
   }
}

class ChildClass extends BaseClass {
   protected function childMethod () {
      return array ('What', 'The', 'Actual', 'Fork!');
   }
}

$a = new ChildClass ();
$a -> baseMethod ();

现在,在我看来,基类根本不应该对子类做任何假设,除了它通过声明(或继承)抽象方法或通过实现接口对子类强制执行的假设。但是,上面的代码实际上输出一个字符串,并没有抛出任何错误!

  

实际的叉子!

这对我来说似乎是破碎的行为。除非基类声明abstract protected function childMethod();,否则它应该无法调用它,如果它?

我一直在网上搜索,试图找到一些证明这是预期行为的东西。到目前为止,我设法找到的是PHP's manual

中的以下内容
  

来自其他对象的可见性

     

相同类型的对象可以私密访问彼此   受保护的成员,即使它们不是相同的实例。这是   因为实现具体细节已经知道了   在这些对象中

我在这里看到的行为是正确的还是PHP中的错误?这肯定不是我依赖的行为,因为它对我来说似乎不对。

仅供参考,我们在实际代码中发现的问题是子类声明了超类试图调用的私有方法。超类没有声明方法是抽象的(如果它已经完成它必须至少受到保护)。

3 个答案:

答案 0 :(得分:7)

  

现在,在我看来,基类不应该做任何   关于子类的假设,除了它强制执行的假设   通过声明(或继承)抽象方法或通过   实现一个接口。

这就是静态类型语言的工作原理。在PHP(和许多其他人)中,您可以随意调用任何值的任何方法(它甚至不需要是一个对象)。如果调用没有意义,则会出现运行时错误。

当然,如果您希望派生类实现它,那么声明该方法是个好主意;这只是一种很好的做法。此外,许多着名的PHP IDE都会检测到这样的遗漏,并标记调用以引起您的注意,因为编译器不能。

  

这对我来说似乎是破碎的行为。除非基类声明   abstract protected function childMethod();,它不应该   叫它,是吗?

如上所述,它应该。如果您发现此行为不受欢迎,则PHP不是您应该使用的语言。

除此之外:在一个密切相关的说明中,您可能还会发现,反直觉基类可以毫无困难地访问派生类中定义的protected成员。这是documented。事实上,这正是你的例子中发生的事情,但我特别提到它,因为它是一种不同的反直觉(如果方法是public,你的例子也会令人费解。)

考虑PHP允许的无穷无尽的构造列表,从静态类型的角度来看也“应该不可能”,其中包括:

// #1
$varName = 'foo';

// How do you know $object has a property named "foo"?
// How do you know that "foo" is a valid property name in the first place?
// How do you know that $object is an object to begin with?
echo $object->$varName;

// #2
$object = new SomeObject;
$methodName = 'someMethod';

// it's practically impossible to reason about this before runtime
call_user_func(array($object, $methodName));

答案 1 :(得分:1)

这不是一个错误。它应该像那样工作。让我尝试评论方法调用:

class BaseClass {
   public function baseMethod () {
      echo (implode (' ', $this -> childMethod ()) . PHP_EOL);
   }
}

class ChildClass extends BaseClass {
   protected function childMethod () {
      return array ('What', 'The', 'Actual', 'Fork!');
   }
}

/****/

$a = new ChildClass ();
/** $a is now instance of ChildClass, that is a sublass of BaseClass
 *  so the Object $a has 2 methods: baseMethod() inherited from BaseClass and childMethod() from ChildClass.
 */

$a -> baseMethod ();
/** now you're calling $a->baseMethod(), which will do a method call on $this->childMethod(). 
 *  As $this is refering $a here, $a has indeed a method named childMethod(), that will be called.
 */

您想知道的是protected的可见度。但由于$a是ChildClass的类型,受保护的方法肯定可以访问它和继承的baseMethod()。 每种OOP语言都应该相同。

代码的不好部分是调用baseMethod() childMethod()内的假设。如果没有这样的方法,那将始终以错误结束。像Java这样的OOP语言会在这里引发编译错误,但PHP不会,因为PHP中没有预运行的编译器。如果您更改初始化,例如$a = new ChildClass ();$a = new BaseClass ();,你也会在PHP中遇到运行时错误。

答案 2 :(得分:0)

PHP是非常动态的语言,它允许您在没有继承的情况下使用多态。在这种情况下,它只是在运行时查找方法'childMethod',因为它存在,它将调用它。你如何得到那种方法并不重要。您还可以使用__call magic方法动态生成该方法。你永远不能用C ++或Java做到这一点,但是在PHP或Javascript这样的语言中它很好,它为你提供了更多的选择。