我有一个Validator
班级和一个UserValidator
班级。
我的Validator
有公开方法setRule(...)
,具有公众可见度。
当我从中扩展时,我想将setRule(...)
父方法的可见性更改为中的私有/受保护,以便它只对子女可见没有外人可以称这个方法来自孩子。
这可能吗?如果是这样,我怎么能实现它?
答案 0 :(得分:2)
从架构的角度来看,不建议这样做。正如评论中已经说明的那样,干净的方法是将您的方法设置为protected
,这样只有孩子才能访问它。
我想不出一个单一的用例会让我需要在父类上调用公共方法,但是我不允许在子类上调用它。
这违反了开放/封闭原则。类应该是可以扩展的,但不能用于修改。
由于这不是问题,我将提供一种如何实现这一目标的方法。但请注意:
首先让我们定义你已经拥有的类
<?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成为类完全不可能。我只是为了完整性而提到它。 仍然不推荐使用