更改子类中父方法的可见性范围
Posted
技术标签:
【中文标题】更改子类中父方法的可见性范围【英文标题】:Changing the visibility scope of parent methods in child classes 【发布时间】:2014-08-17 01:08:21 【问题描述】:我有一个 Validator
类和一个从它扩展而来的 UserValidator
类。
我的Validator
有一个公共方法setRule(...)
具有公开可见性。
当我从它扩展时,我想将 setRule(...)
父方法的可见性更改为私有/受保护 在 子级,以便它仅对子级可见,并且没有外人可以调用此方法来自孩子。
这可能吗?如果是这样,我该如何实现?
【问题讨论】:
使用protected
可见性而不是public
但这意味着,当我的Validator
类被使用时,setRule()
方法不能被访问,因为该方法将只对类本身可见并且它只是子类。跨度>
抱歉,我以为你要的就是这个
不,你不能随意改变方法的可见性......虽然反射可以模拟这一点,如果你需要使用它是一个肮脏的黑客
【参考方案1】:
从架构的角度来看,不建议这样做。如 cmets 中所述,干净的方法是将您的方法设置为 protected
,以便只有孩子可以访问它。
我想不出一个用例会让我需要在父类上调用公共方法,但不允许我在子类上调用它。
这违反了开放/封闭原则。类应该对扩展开放,而不是对修改开放。
既然这不是问题,我将提供一种方法来实现这一点。但请注意:
此方法使用了一个负责实例化的额外类 这是一个黑客。此解决方案在引发可访问性错误时不会使用 php 的本地语言功能。首先让我们定义你已经拥有的类
<?php
class Validator
public function setRule()
echo "Hello World";
class UserValidator extends Validator
public $prop = 'PROPERTY';
这里没有什么特别的。所以让我们继续为可见性错误创建一个自定义异常类。
<?php
class MethodNotAccessibleException extends Exception
当我们尝试在子类上调用“伪私有”方法时,将引发此异常。
现在我们要创建负责实例化您的子类的类。它基本上只是一个包装器,它定义了一个 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 完全不可能。我只是为了完整起见而提到它。 仍然不推荐使用
【讨论】:
以上是关于更改子类中父方法的可见性范围的主要内容,如果未能解决你的问题,请参考以下文章