更改子类中父方法的可见范围

时间:2014-06-26 08:47:22

标签: php methods parent-child visibility extends

我有一个Validator班级和一个UserValidator班级。

我的Validator有公开方法setRule(...),具有公众可见度。

当我从中扩展时,我想将setRule(...)父方法的可见性更改为中的私有/受保护,以便它只对子女可见没有外人可以称这个方法来自孩子。

这可能吗?如果是这样,我怎么能实现它?

1 个答案:

答案 0 :(得分:2)

从架构的角度来看,不建议这样做。正如评论中已经说明的那样,干净的方法是将您的方法设置为protected,这样只有孩子才能访问它。

我想不出一个单一的用例会让我需要在父类上调用公共方法,但是我不允许在子类上调用它。

这违反了开放/封闭原则。类应该是可以扩展的,但不能用于修改。

由于这不是问题,我将提供一种如何实现这一目标的方法。但请注意:

  • 此方法使用一个额外的类来负责实例化
  • 这是一个黑客。在抛出可访问性错误时,此解决方案将不会使用PHP的本机语言功能。

首先让我们定义你已经拥有的类

<?php

class Validator {

    public function setRule()
    {
        echo "Hello World";
    }

}

class UserValidator extends Validator {

    public $prop = 'PROPERTY';

}

这里没什么特别的。因此,让我们继续为可见性错误创建一个自定义异常类。

<?php

class MethodNotAccessibleException extends Exception {}

当我们尝试在子类上调用“伪私有”方法时,将抛出此异常。

现在我们要创建负责实例化子类的Class。它基本上只是一个包装器,它定义了一个lock属性,它保存了不应该访问的方法名称。

<?php

class PrivateInstanceCreator {

    protected $reflectionClass;
    protected $lock = [];
    protected $instance;

    public function __construct($classname, $args = []) 
    {
        // We'll store an instance of the reflection class
        // and an instance of the real class
        $this->reflectionClass = new ReflectionClass($classname);
        $this->instance = $this->reflectionClass->newInstanceArgs($args);
        return $this;
    }

    // The lock method is able to make a method on the
    // target class "pseudo-private"
    public function lock($method)
    {
        $this->lock[] = $method;
        return $this;
    }

    // Some real magic is going on here
    // Remember. This class is a wrapper for the real class
    // if a method is invoked we look for the method
    // in the real instance and invoke it...
    public function __call($method, $args)
    {
        // ... but as soon as this method is defined as
        // locked, we'll raise an exception that the method
        // is private
        if(in_array($method, $this->lock))
        {
            $reflectionMethod = $this->reflectionClass->getMethod($method);
            if($reflectionMethod->isPublic())
                throw new MethodNotAccessibleException('Method: __' . $method . '__ is private and could not be invoked');
        }

        return call_user_func_array([$this->instance, $method], $args);
    }

    // The same goes for properties
    // But in this case we'll do no protection
    public function __get($prop)
    {
        return $this->instance->{$prop};
    }

}

我们的最后一步是实例化。

<?php

$userValidator = new PrivateInstanceCreator('UserValidator', []);
$userValidator->lock('setRule');

$userValidator->setRule(); //Will throw an exception

不是直接实例化类,而是使用我们的自定义包装类来实现。 当然你可以在子类本身中处理它,但这是一种完成任务而不直接触及类的方法。

话虽如此,仍然是一个肮脏的黑客,如果可能的话应该避免使用。如果要直接实例化子类,则继承的方法仍然是公共的。

因此,如果开发人员对包装类没有任何了解,那么他将很难弄清楚如何正确地实例化子类。

<强>更新

要直接使子类不可实例化,可以将构造函数设置为private并从反射类调用newInstanceWithoutConstructor(),这甚至更脏,因为这会使类Dependency Injection成为类完全不可能。我只是为了完整性而提到它。 仍然不推荐使用