假设我们有一个具有多个受保护和/或公共方法的类。 每次调用方法时我都需要执行检查。我可以在每次调用方法时进行检查:
class Object
{
// Methods
}
$o = new Object();
if($mayAccess) $o->someMethod();
或
if($mayAccess) $this->someMethod();
但我希望开发人员不必考虑它,也不必编写它。我考虑过使用 __call 来做:
class Object
{
public function __call($methodName, $args)
{
if($mayAccess) call_user_func_array($this->$methodName, $args);
}
}
不幸的是,如果我从类内部调用该方法,__call 将不会被调用,因为它仅在调用不可见方法时才有效。
是否有一种干净的方法来隐藏内部和外部调用的此检查?同样,我们的目标是确保开发人员在调用方法时不会忘记执行此操作。
提前致谢:)
EDIT :
我还有另一种方法:
class Object
{
public function __call($methodName, $args)
{
if($mayAccess) call_user_func_array($methodName, $args);
}
}
function someMethod() { }
但是我将无法再使用 $this,这意味着没有受保护的方法,而我确实需要。
不,我不这么认为。你可以做的是写一个代理:
class MayAccessProxy {
private $_obj;
public function __construct($obj) {
$this->_obj = $obj;
}
public function __call($methodName, $args) {
if($mayAccess) call_user_func_array(array($this->_obj, $methodName), $args);
}
}
这意味着您必须为要检查的每个对象实例化一个代理:
$obj = new MayAccessProxy(new Object());
$obj->someMethod();
当然,您还希望代理的行为与对象本身完全相同。所以你还必须定义其他魔法方法。
为了让开发人员更容易一些,你可以这样做:
class Object {
/**
* Not directly instanciable.
*/
private __construct() {}
/**
* @return self
*/
public static function createInstance() {
$obj = new MayAccessProxy(new self());
return $obj;
}
}
$obj = Object::createInstance();
如果您将所有方法设置为受保护或私有会怎样? (我知道这是一个古老且“已回答”的问题)
__call 魔术方法拦截所有不存在和非公共方法,因此所有方法不公开将允许您拦截所有这些方法。
public function __call( $func, $args )
{
if ( !method_exists( $this, $func ) ) throw new Error("This method does not exist in this class.");
Handle::eachMethodAction(); // action which will be fired each time a method will be called
return $this->$func( ...$args );
}
感谢您不需要对代码执行任何操作(期望添加 __call 并快速执行
replace all
),并且如果您的类有共同的父级,那么您可以将其添加到父级而不再关心。
该解决方案产生了两个主要问题:
您可以添加所有受保护/私有方法的列表,并在调用之前检查该方法是否可以返回公共:
public function __call( $func, $args )
{
$private = [
"PrivateMethod" => null
];
if ( !method_exists( $this, $func ) ) throw new Error("This method does not exist in this class.");
if ( isset( $private[$func] ) ) throw new Error("This method is private and cannot be called");
Handle::eachMethodAction(); // action which will be fired each time a method will be called
return $this->$func( ...$args );
}
对于许多人来说,这可能会破坏交易,但我个人只在只有公共方法(我将其设置为受保护)的类中使用这种方法。因此,如果可以的话,您可以将方法分为
publicClass
和 privateClass
并消除此问题。
为了更好的错误,我创建了这个方法:
/**
* Get parent function/method details
*
* @param int counter [OPT] The counter allows to move further back or forth in search of methods detalis
*
* @return array trace It contains those elements :
* - function - name of the function
* - file - in which file exception happend
* - line - on which line
* - class - in which class
* - type - how it was called
* - args - arguments passed to function/method
*/
protected function getParentMethod( int $counter = 0 ) {
$excep = new \Exception();
$trace = $excep->getTrace();
$offset = 1;
if ( sizeof( $trace ) < 2 ) $offset = sizeof( $trace ) - 1;
return $trace[$offset - $counter];
}
这将返回有关调用受保护方法的先前方法/函数的详细信息。
public function __call( $func, $args )
{
$private = [
"PrivateMethod" => null
];
if ( !method_exists( $this, $func ) ) {
$details = (object) $this->getParentMethod();
throw new Error("Method $func does not exist on line " . $details->line . ", file: " . $details->file . " invoked by " . get_class($this) . $details->type . $func . " () ");
}
if ( isset($private[$func]) ) {
$details = (object) $this->getParentMethod();
throw new Error("Method $func is private and cannot be called on line " . $details->line . ", file: " . $details->file . " invoked by " . get_class($this) . $details->type . $func . " () ");
}
return $this->$func( ...$args );
}
这不是什么大问题,但可能会在调试时导致一些混乱。
此解决方案允许您控制类外部对私有/受保护方法的任何调用。任何
this->Method
都会省略 __call
并且通常会调用私有/受保护的方法。
class Test {
public function __call( $func, $args )
{
echo "__call! ";
if ( !method_exists( $this, $func ) ) throw new Error("This method does not exist in this class.");
return $this->$func( ...$args );
}
protected function Public()
{
return "Public";
}
protected function CallPublic()
{
return "Call->" . $this->Public();
}
}
$_Test = new Test();
echo $_Test->CallPublic(); // result: __call! Call->Public - it uses two methods but __call is fired only once
如果你想向静态方法添加类似的东西,请使用
__callStatic
魔术方法。
我认为对于你的问题有最好的答案,当然还有例子