【问题标题】:Access protected method from child class in PHP从 PHP 中的子类访问受保护的方法
【发布时间】:2012-11-25 16:26:43
【问题描述】:

我可以使用至少两种基本方法从子类访问受保护的类方法:

parent::myMethod();

$this->myMethod();

如果我不需要在子类中覆盖它,在这种情况下我必须这样做:

function myMethod() {
   ...
   parent::myMethod();
   ...
}

哪个是最推荐的调用方式?我个人觉得使用 parent::myMethod() 而不是 $this->myMethod 更舒服,因为第一个立即告诉我这个方法正在被继承。但我不确定在性能和最佳实践方面采用哪种方式。

编辑:

检查一下,这是我的问题的真实情况。它使用 CodeIgniter,但即使你不熟悉它,你也可能会得到它:

class Admin_Controller extends CI_Controller {

    protected function validate_form($validation) {
            $this->load->library('form_validation');
            // This will validate the form sent against the validation rules group specified in $validation
            if ($this->form_validation->run($validation) == false) {   
                    throw new Exception('There are errors in the form');
            }
    }

}

class Articles extends Admin_Controller {

    function save($id) {
            $this->validate_form(strtolower(get_class($this));
            // OR
            parent::validate_form(strtolower(get_class($this));
            // Other actions
            ....
    }

}

class Login extends Admin_Controller {

    function signIn() {
            $this->validate_form(strtolower(get_class($this));
            // OR
            parent::validate_form(strtolower(get_class($this));
            // Other actions
            ....
    }

}

【问题讨论】:

  • 你的意思是它们都不被认为是不好的做法?
  • 技术上讲parent::myMethod()$this->myMethod()是两个不同的东西,可能返回不同的结果……第二个是指子类。
  • ... 部分是什么?如果您不覆盖父方法,为什么需要其中任何一个?你能举一个更现实的例子吗?
  • @Wesley Murch:是的,它们在技术上是不同的,因为 parent::myMethod() 是父类中的方法,而 this->myMethod() 是子类中的继承方法。但是,如果这个方法没有被覆盖,我能期待不同的结果吗?忽略'...'部分,这是我做的一个例子,以防我需要覆盖该方法,但事实并非如此。
  • 但是,您正在在您的示例中覆盖myMethod,并在您的孩子myMethod 中调用父myMethod。尝试一个更好的例子,或者尝试以某种方式澄清你的问题,使其更具体或更现实。只调用父方法而不做任何修改是没有意义的。

标签: php codeigniter inheritance protected


【解决方案1】:

下面是从子类调用受保护的父方法的示例。

这样做的一个很好的理由是重用父类的功能,然后在子类中添加该功能。

class A{

    protected $a;

    public function __construct(){
        $this->setValueOfStringInA();

    }
    protected function setValueOfStringInA(){
        $this->a = "a set from A::setValueOfStringInA";
    }
    public function getValueOfA(){
        echo 'a of ' . __CLASS__ . ' = ' . $this->a . '<br>';
    }
}

class B extends A{

    protected $b;

    public function __construct(){
        parent::__construct();
    }
    protected function setValueOfStringInA(){
        parent::setValueOfStringInA();
        $this->b = "b set from B::setValueOfStringInA";
    }
    public function getValueOfA(){
        echo 'a of ' . __CLASS__ . ' = ' . $this->a . '<br>';
    }    
    public function getValueOfB(){
        echo 'b of ' . __CLASS__ . ' = ' . $this->b . '<br>';
    }
}

$objA = new A();
//the following call will not work since it is a protected method, it can not be called from outside, hence commented out.
//$objA->setValueOfStringInA();
$objA->getValueOfA();

$objB = new B();
//$objB->setValueOfStringInA();
$objB->getValueOfB();
$objB->getValueOfA();

检查输出:

a of A = a set from A::setValueOfStringInA
b of B = b set from B::setValueOfStringInA
a of B = a set from A::setValueOfStringInA

【讨论】:

    【解决方案2】:

    据我了解,这是两件不同的事情。

    $this 用于引用类的实例,并且在创建对象后可用。因此它将引用子类对象,而不是父类。

    parent 是在子类中扩展父类方法的正确方法。它引用父类,并具有额外的优点,即不需要在多个级别上更改类名。 self 类似。

    有关示例,请参见下面的两个实现。

    1. Accessing with parent keyword
    2. Accessing with this leads to unexpected behaviour.

    希望我能有所帮助。

    【讨论】:

    • 这在技术上是不正确的。 parent::method() 是对父类的直接转发调用(这意味着即使该方法被该类覆盖,它也会被转发到其父类)。 $this-&gt;method() 很可能会在孩子没有覆盖其父母的方法的情况下被转发给它的父母。但是,我同意您的观点,即使用 parent 是确保您使用 parent 方法而不是 child 方法的明确方式。但我还想补充一点,self 不允许使用 LSB (Late Static Binding),这也会给您带来一些麻烦;)
    • 我试图找出父母走了多远,找不到任何信息。我想说的最重要的一点是 $this 引用对象并开始从当前上下文向上查找,而 parent 直接指向父级。但是,您的答案是正确的,因为他特别不想覆盖父母的方法。
    • @Awemo:这些示例解释了覆盖。我不想覆盖。关于第二个例子,我不会使用 $this 在被覆盖的方法中调用父方法。从您提供的示例中检查此修改示例:codepad.org/EkHXwrHs
    • @Awemo 如果孩子没有声明该方法,它们都会直接转到父级。尽管我只是想澄清我在您的信息中发现的技术性,但您的观点已被理解。抱歉,我是一个迂腐的 PHP 开发人员 :)
    【解决方案3】:

    他们做两种不同的事情。 parent 关键字的语义使得它对其父类进行转发调用。但是,$this-&gt;method() 的语义是,它只会在调用该方法的实例未声明该方法的情况下将其调用转发给父类。同样重要的是要注意,孩子可能有父母和祖父母,因此在设计原型时也必须仔细考虑。

    如果您不希望方法被覆盖,您应该在方法原型声明中使用final 关键字。

    class MyClass {
        final protected function myMethod() {
        }
    }
    

    这确保了任何扩展类都无法覆盖该方法。如果扩展类尝试像下面的代码那样重写此方法,您将收到 Fatal error: Cannot override final method MyClass::myMethod() 的致命错误。

    class MyOtherClass extends MyClass {
        public function myMethod() {
        }
    }
    

    此外,这使您的代码更具可读性并且不易出错,因为您清楚地向阅读代码的人声明该方法是最终的并且不能被覆盖。

    此外,您的方法的根本问题(如果您想使用 parent::myMethod()$this-&gt;myMethod())是您没有考虑到当班级是 孙子 其父级的,例如...

    class Father {
        protected function myMethod() {
            return __METHOD__;
        }
    }
    
    class Child extends Father {
        public function myMethod() {
            return __METHOD__;
        }
    }
    
    class GrandChild extends Child {
        public function myOtherMethod() {
            echo parent::myMethod(), "\n"; // Child::myMethod
            echo $this->myMethod(), "\n";  // Child::myMethod
        }
    }
    
    $obj = new GrandChild;
    $obj->myOtherMethod();
    

    如您所见,即使您打算使用父亲的方法,它们也会给您子方法。如果您从不打算在扩展类中重写该方法,只需将该方法声明为 final,并且在从对象上下文调用 $this->myMethod() 时应该始终没问题。

    【讨论】:

    • 对不起,如果我似乎有点慢:),但我不确定我是否理解你的解释,因为你的例子清楚地表明两种方式都会做同样的事情,但我认为你不是说哪一个应该是正确的方法。如果我没记错的话,你说既然我不会重写myMethod(),我应该在父类中声明它为final,然后呢?
    • 我做了,通过声明他们都做不同的事情parent::method() 意味着总是调用 parent 方法,无论实例对象是否定义它。 $this-&gt;method() 表示如果该方法在实例中定义,则始终调用该方法,或者将调用转发给其父级,直到找到该方法或达到未定义对象的致命错误。我还在最后一段中解释过,如果你将方法声明为 final,那么从对象上下文调用 $this->myMethod() 时总是没问题。我不确定我能说得清楚多少。
    • 好的,那么,看看我在编辑部分添加的代码。这就是我提出问题的代码。我应该在父类中声明 final protected function validate_form($validation),并在子类中使用 $this->validate_form(...); 吗?我在子类中使用 parent::validate_form(...) 吗?删除 final 键?因为任何一种方式都适用于我的情况。事实上,我没有使用超过一层的继承。也许您的意思是 2 级或更高级别的扩展会很方便?
    • 我的问题中的另一个观点:如果我没记错的话,在子类中不重新声明此方法的情况下执行 parent::validate_form() 从技术上讲会更快,因为 php 引擎不会必须搜索继承的方法,或者将父方法声明为最终方法并使用 $this->validate_form() 也会直接转发给父类....真是一团糟!呵呵我觉得最后差别不大! :)
    • 我不确定我还能在这里重复多少...如果您不希望在子项中覆盖该方法,请执行以下两件事 1) 声明父方法最后,2) 在对象上下文中简单地从所有和任何扩展类调用 $this->method。如果您没有将父方法声明为最终方法,则必须调用 parent::method() 以确保您正在调用父方法。但是,我在上面为您指出了多级继承的注意事项,并举例说明了如果其中一个父级覆盖该方法会发生什么。
    【解决方案4】:

    当你调用 parent::method() 时,它是不同于 $this->method() 的方法。如果你在子类中覆盖它,它可能会返回不同的值

    【讨论】:

    • 他确实提到了“如果我不需要在子类中覆盖它”
    • 但他当然是在调用myMethod 来自子方法myMethod,这让问题变得非常混乱。
    • 好吧,如果你不覆盖它,你就不能用 $this 来调用它吗?
    • @Wesley Murch:使用 $this-> 调用除父类外不存在的方法?是的,我有点困了:)
    • 是的,是的,我知道不要乱用 :)
    猜你喜欢
    • 2017-01-12
    • 2013-04-06
    • 2015-08-14
    • 2014-04-17
    • 2014-05-01
    • 2015-11-04
    • 1970-01-01
    • 2017-12-09
    • 2020-07-26
    相关资源
    最近更新 更多