php 如何在php中为每个其他函数调用自动调用函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3716649/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to auto call function in php for every other function call
提问by Nik
Class test{
function test1()
{
echo 'inside test1';
}
function test2()
{
echo 'test2';
}
function test3()
{
echo 'test3';
}
}
$obj = new test;
$obj->test2();//prints test2
$obj->test3();//prints test3
Now my question is,
现在我的问题是,
How can i call another function before any called function execution? In above case, how can i auto call 'test1' function for every another function call, so that i can get the output as,
如何在任何被调用的函数执行之前调用另一个函数?在上述情况下,我如何为每个另一个函数调用自动调用“test1”函数,以便我可以获得输出,
test1
test2
test1
test3
currently i am getting output as
目前我得到的输出为
test2
test3
I cannot call 'test1' function in every function definition as there may be many functions. I need a way to auto call a function before calling any function of a class.
我不能在每个函数定义中调用 'test1' 函数,因为可能有很多函数。我需要一种在调用类的任何函数之前自动调用函数的方法。
Any alternative way would also be do.
任何替代方式也可以。
回答by Kristoffer Sall-Storgaard
Your best bet is the magic method __call, see below for example:
您最好的选择是魔术方法__call,请参见下面的示例:
<?php
class test {
function __construct(){}
private function test1(){
echo "In test1", PHP_EOL;
}
private function test2(){
echo "test2", PHP_EOL;
}
protected function test3(){
return "test3" . PHP_EOL;
}
public function __call($method,$arguments) {
if(method_exists($this, $method)) {
$this->test1();
return call_user_func_array(array($this,$method),$arguments);
}
}
}
$a = new test;
$a->test2();
echo $a->test3();
/*
* Output:
* In test1
* test2
* In test1
* test3
*/
Please notice that test2
and test3
are not visible in the context where they are called due to protected
and private
. If the methods are public the above example will fail.
请注意,test2
而test3
不是在他们被称为由于上下文可见的protected
和private
。如果方法是公共的,上面的例子就会失败。
test1
does not have to be declared private
.
test1
不必声明private
。
ideone.com example can be found here
Updated: Add link to ideone, add example with return value.
更新:添加指向 ideone 的链接,添加带有返回值的示例。
回答by Ocramius
All previous attempts are basically flawed because of http://ocramius.github.io/presentations/proxy-pattern-in-php/#/71
由于http://ocramius.github.io/presentations/proxy-pattern-in-php/#/71,之前的所有尝试基本上都有缺陷
Here's the simple example, taken from my slides:
这是一个简单的例子,摘自我的幻灯片:
class BankAccount { /* ... */ }
And here's our "poor" interceptor logic:
这是我们“糟糕”的拦截器逻辑:
class PoorProxy {
public function __construct($wrapped) {
$this->wrapped = $wrapped;
}
public function __call($method, $args) {
return call_user_func_array(
$this->wrapped,
$args
);
}
}
Now if we have the following method to be called:
现在,如果我们要调用以下方法:
function pay(BankAccount $account) { /* ... */ }
Then this won't work:
那么这将不起作用:
$account = new PoorProxy(new BankAccount());
pay($account); // KABOOM!
This applies to all solutions that suggest implementing a "proxy".
这适用于所有建议实施“代理”的解决方案。
Solutions suggesting explicit usage of other methods that then call your internal API are flawed, because they force you to change your public API to change an internal behavior, and they reduce type safety.
建议显式使用其他方法然后调用您的内部 API 的解决方案是有缺陷的,因为它们迫使您更改公共 API 以更改内部行为,并且它们降低了类型安全性。
The solution provided by Kristoffer doesn't account for public
methods, which is also a problem, as you can't rewrite your API to make it all private
or protected
.
Kristoffer 提供的解决方案没有考虑public
方法,这也是一个问题,因为您无法重写 API 以使其全部private
或protected
.
Here is a solution that does solve this problem partially:
这是一个可以部分解决此问题的解决方案:
class BankAccountProxy extends BankAccount {
public function __construct($wrapped) {
$this->wrapped = $wrapped;
}
public function doThings() { // inherited public method
$this->doOtherThingsOnMethodCall();
return $this->wrapped->doThings();
}
private function doOtherThingsOnMethodCall() { /**/ }
}
Here is how you use it:
以下是您如何使用它:
$account = new BankAccountProxy(new BankAccount());
pay($account); // WORKS!
This is a type-safe, clean solution, but it involves a lot of coding, so please take it only as an example.
这是一个类型安全、干净的解决方案,但它涉及大量编码,因此请仅作为示例。
Writing this boilerplate code is NOT fun, so you may want to use different approaches.
编写此样板代码并不有趣,因此您可能需要使用不同的方法。
To give you an idea of how complicated this category of problems is, I can just tell you that I wrote an entire libraryto solve them, and some smarter, wiser, older people even went and invented an entirely different paradigm, called "Aspect Oriented Programming" (AOP).
为了让您了解这类问题的复杂程度,我可以告诉您,我编写了一个完整的库来解决这些问题,一些更聪明、更聪明、更年长的人甚至发明了一种完全不同的范式,称为“面向方面的编程”(AOP)。
Therefore I suggest you to look into these 3 solutions that I think may be able to solve your problem in a much cleaner way:
因此,我建议您研究以下 3 种解决方案,我认为它们可能能够以更简洁的方式解决您的问题:
Use ProxyManager's "access interceptor", which is basically a proxy type that allows you to run a closure when other methods are called (example). Here is an example on how to proxy ALL calls to an
$object
's public API:use ProxyManager\Factory\AccessInterceptorValueHolderFactory; function build_wrapper($object, callable $callOnMethod) { return (new AccessInterceptorValueHolderFactory) ->createProxy( $object, array_map( function () use ($callOnMethod) { return $callOnMethod; }, (new ReflectionClass($object)) ->getMethods(ReflectionMethod::IS_PUBLIC) ) ); }
then just use
build_wrapper
as you like.Use GO-AOP-PHP, which is an actual AOP library, completely written in PHP, but will apply this sort of logic to ALL instances of classes for which you define point cuts. This may or may not be what you want, and if your
$callOnMethod
should be applied only for particular instances, then AOP is not what you are looking for.Use the PHP AOP Extension, which I don't believe to be a good solution, mainly because GO-AOP-PHPsolves this problem in a more elegant/debuggable way, and because extensions in PHP are inherently a mess (that is to be attributed to PHP internals, not to the extension developers). Additionally, by using an extension, you are making your application as un-portable as possible (try convincing a sysadmin to install a compiled version of PHP, if you dare), and you can't use your app on cool new engines such as HHVM.
使用ProxyManager的“访问拦截器”,它基本上是一种代理类型,允许您在调用其他方法时运行闭包(示例)。这是一个关于如何将所有调用代理到
$object
的公共 API的示例:use ProxyManager\Factory\AccessInterceptorValueHolderFactory; function build_wrapper($object, callable $callOnMethod) { return (new AccessInterceptorValueHolderFactory) ->createProxy( $object, array_map( function () use ($callOnMethod) { return $callOnMethod; }, (new ReflectionClass($object)) ->getMethods(ReflectionMethod::IS_PUBLIC) ) ); }
然后随意使用
build_wrapper
。使用GO-AOP-PHP,这是一个实际的 AOP 库,完全用 PHP 编写,但会将这种逻辑应用于您定义切点的所有类实例。这可能是也可能不是你想要的,如果你
$callOnMethod
应该只应用于特定的实例,那么 AOP 不是你想要的。使用PHP AOP Extension,我认为这不是一个好的解决方案,主要是因为GO-AOP-PHP以更优雅/可调试的方式解决了这个问题,并且因为 PHP 中的扩展本质上是一团糟(也就是说归因于 PHP 内部结构,而不是扩展开发人员)。此外,通过使用扩展,您将尽可能使您的应用程序不可移植(如果您敢的话,请尝试说服系统管理员安装 PHP 的编译版本),并且您不能在很酷的新引擎上使用您的应用程序,例如HHVM。
回答by marcellorvalle
Maybe it is a little bit outdated but here come my 2 cents...
也许它有点过时了,但我的 2 美分来了……
I don't think that giving access to private methods via __call() is a good idea. If you have a method that you really don't want to be called outside of your object you have no way to avoid it happening.
我认为通过 __call() 访问私有方法不是一个好主意。如果你有一个你真的不想在你的对象之外被调用的方法,你就没有办法避免它发生。
I think that one more elegant solution should be creating some kind of universal proxy/decorator and using __call() inside it. Let me show how:
我认为一个更优雅的解决方案应该是创建某种通用代理/装饰器并在其中使用 __call() 。让我展示如何:
class Proxy
{
private $proxifiedClass;
function __construct($proxifiedClass)
{
$this->proxifiedClass = $proxifiedClass;
}
public function __call($methodName, $arguments)
{
if (is_callable(
array($this->proxifiedClass, $methodName)))
{
doSomethingBeforeCall();
call_user_func(array($this->proxifiedClass, $methodName), $arguments);
doSomethingAfterCall();
}
else
{
$class = get_class($this->proxifiedClass);
throw new \BadMethodCallException("No callable method $methodName at $class class");
}
}
private function doSomethingBeforeCall()
{
echo 'Before call';
//code here
}
private function doSomethingAfterCall()
{
echo 'After call';
//code here
}
}
Now a simply test class:
现在是一个简单的测试类:
class Test
{
public function methodOne()
{
echo 'Method one';
}
public function methodTwo()
{
echo 'Method two';
}
private function methodThree()
{
echo 'Method three';
}
}
And all you need to do now is:
您现在需要做的就是:
$obj = new Proxy(new Test());
$obj->methodOne();
$obj->methodTwo();
$obj->methodThree(); // This will fail, methodThree is private
Advantages:
好处:
1)You just need one proxy class and it will work with all your objects. 2)You won't disrespect accessibility rules. 3)You don't need to change the proxified objects.
1) 您只需要一个代理类,它就可以与您的所有对象一起使用。2)您不会不尊重可访问性规则。3)您不需要更改代理对象。
Disadvantage: You will lose the inferface/contract after wrapping the original object. If you use Type hinting with frequence maybe it is a problem.
缺点:包裹原始对象后,您将丢失接口/合同。如果您使用频繁的类型提示可能是一个问题。
回答by ntt
Perhaps the best way so far is to create your own method caller and wrap around whatever you need before and after the method:
也许到目前为止最好的方法是创建你自己的方法调用者并在方法之前和之后环绕你需要的任何东西:
class MyClass {
public function callMethod()
{
$args = func_get_args();
if (count($args) == 0) {
echo __FUNCTION__ . ': No method specified!' . PHP_EOL . PHP_EOL;;
} else {
$method = array_shift($args); // first argument is the method name and we won't need to pass it further
if (method_exists($this, $method)) {
echo __FUNCTION__ . ': I will execute this line and then call ' . __CLASS__ . '->' . $method . '()' . PHP_EOL;
call_user_func_array([$this, $method], $args);
echo __FUNCTION__ . ": I'm done with " . __CLASS__ . '->' . $method . '() and now I execute this line ' . PHP_EOL . PHP_EOL;
} else
echo __FUNCTION__ . ': Method ' . __CLASS__ . '->' . $method . '() does not exist' . PHP_EOL . PHP_EOL;
}
}
public function functionAA()
{
echo __FUNCTION__ . ": I've been called" . PHP_EOL;
}
public function functionBB($a, $b, $c)
{
echo __FUNCTION__ . ": I've been called with these arguments (" . $a . ', ' . $b . ', ' . $c . ')' . PHP_EOL;
}
}
$myClass = new MyClass();
$myClass->callMethod('functionAA');
$myClass->callMethod('functionBB', 1, 2, 3);
$myClass->callMethod('functionCC');
$myClass->callMethod();
And here's the output:
这是输出:
callMethod: I will execute this line and then call MyClass->functionAA() functionAA: I've been called callMethod: I'm done with MyClass->functionAA() and now I execute this line callMethod: I will execute this line and then call MyClass->functionBB() functionBB: I've been called with these arguments (1, 2, 3) callMethod: I'm done with MyClass->functionBB() and now I execute this line callMethod: Method MyClass->functionCC() does not exist callMethod: No method specified!
You can even go further and create a whitelist of methods but I leave it like this for the sake of a more simple example.
您甚至可以进一步创建方法的白名单,但为了更简单的示例,我将其保留为这样。
You will no longer be forced to make the methods private and use them via __call(). I'm assuming that there might be situations where you will want to call the methods without the wrapper or you would like your IDE to still autocomplete the methods which will most probably not happen if you declare the methods as private.
您将不再被迫将方法设为私有并通过 __call() 使用它们。我假设在某些情况下,您可能希望在没有包装器的情况下调用方法,或者您希望 IDE 仍然自动完成方法,如果您将方法声明为私有,这很可能不会发生。
回答by Otar
<?php
class test
{
public function __call($name, $arguments)
{
$this->test1(); // Call from here
return call_user_func_array(array($this, $name), $arguments);
}
// methods here...
}
?>
Try adding this method overriding in the class...
尝试在类中添加此方法覆盖...
回答by ts.
If you are really, really brave, you can make it with runkit extension. (http://www.php.net/manual/en/book.runkit.php). You can play with runkit_method_redefine (you can need Reflection also to retrieve method definition) or maybe combination runkit_method_rename (old function) / runkit_method_add (new function which wraps calls to your test1 function and an old function )
如果你真的非常勇敢,你可以使用 runkit 扩展来实现。(http://www.php.net/manual/en/book.runkit.php)。您可以使用 runkit_method_redefine (您也可以需要 Reflection 来检索方法定义)或者组合 runkit_method_rename (旧函数)/ runkit_method_add (新函数包装对您的 test1 函数和旧函数的调用)
回答by halfdan
The only way to do this is using the magic __call
. You need to make allmethods private so they are not accessable from the outside. Then define the __call method to handle the method calls. In __call you then can execute whatever function you want beforecalling the function that was intentionally called.
做到这一点的唯一方法是使用魔法__call
。您需要将所有方法设为私有,以便从外部无法访问它们。然后定义__call 方法来处理方法调用。在 __call 中,您可以在调用有意调用的函数之前执行您想要的任何函数。
回答by RobertPitt
Lets have a go at this one :
让我们来看看这个:
class test
{
function __construct()
{
}
private function test1()
{
echo "In test1";
}
private function test2()
{
echo "test2";
}
private function test3()
{
echo "test3";
}
function CallMethodsAfterOne($methods = array())
{
//Calls the private method internally
foreach($methods as $method => $arguments)
{
$this->test1();
$arguments = $arguments ? $arguments : array(); //Check
call_user_func_array(array($this,$method),$arguments);
}
}
}
$test = new test;
$test->CallMethodsAfterOne('test2','test3','test4' => array('first_param'));
Thats what I would do
那就是我会做的