Like a function of automatic call in php to call any other function

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 executing any called function? In the above case, how can I automatically call the function "test1" for every other function call, so that I can get the output like,

 test1 test2 test1 test3 

I am currently getting output as

 test2 test3 

I cannot name the function "test1" in every function definition, since there are many functions. I need a way to automatically call a function before calling any function of the class.

Any alternative method may also be performed.

+44
php
Sep 15 '10 at 10:13
source share
8 answers

It is best to use the __ call magic method, see below, for example:

 <?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 */ 

Note that test2 and test3 do not appear in the context where they are called due to protected and private . If the methods are publicly available, the above example will not be executed.

test1 does not need to declare private .

An example of ideone.com can be found here.

Updated . Add a link to ideone, add an example with a return value.

+49
Sep 15 '10 at 10:27
source share
— -

All previous attempts are mostly flawed due to http://ocramius.imtqy.com/presentations/proxy-pattern-in-php/#/71

Here is a simple example taken from my slides:

 class BankAccount { /* ... */ } 

And here is 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 call the following method:

 function pay(BankAccount $account) { /* ... */ } 

Then this will not work:

 $account = new PoorProxy(new BankAccount()); pay($account); // KABOOM! 

This applies to all solutions offering to implement a "proxy".

Solutions that explicitly use other methods that then call your internal API are erroneous because they force you to change your public API to change internal behavior and reduce type safety.

The solution provided by Kristoffer does not take public methods into account, which is also a problem, since you cannot rewrite your API to make it all private or protected .

Here is a solution that partially solves this problem:

 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's 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 as an example only.

Writing this boilerplate code is NOT fun, so you can use different approaches.

To give you an idea of ​​how complex this category of problems is, I can just tell you that I wrote the entire library to solve them, and some smarter, older people even went and invented a completely different paradigm called Aspect Oriented Programming (AOP) .

Therefore, I suggest you study these 3 solutions, which I think can solve your problem in a much cleaner way:

  • Use the ProxyManager " access interceptor ", which is basically a proxy type that allows you to trigger closure when other methods are called ( example ). The following is an example of how to proxy ALL calls to the $object 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 a real AOP library written entirely in PHP, but will apply this logic to ALL instances of the classes for which you define dot reductions. This may or may not be what you want, and if your $callOnMethod should be applied only to specific instances, then AOP is not what you are looking for.

  • Use the PHP AOP extension, which I believe is not a good solution, mainly because GO-AOP-PHP solves this problem in a more elegant / debugable way and because extensions in PHP are essentially a mess (which should be related with internal PHP components, not extension developers). In addition, using the extension, you make your application as portable as possible (try convincing sysadmin to install a compiled version of PHP if you dare), and you cannot use your application for new new engines, such as HHVM.

+16
Aug 10 '14 at 4:01
source share

Maybe this is a little outdated, but here come my 2 cents ...

I do not think that providing access to private methods via __call () is a good idea. If you have a method that you really don't want to call outside of your object, you have no way to avoid this.

I think another elegant solution is to create some kind of universal proxy decoder and use __call () inside it. Let me show you how:

 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 just a 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 have to do is:

 $obj = new Proxy(new Test()); $obj->methodOne(); $obj->methodTwo(); $obj->methodThree(); // This will fail, methodThree is private 

Benefits:

1) You just need one proxy class, and it will work with all your objects. 2) You will not disrespect accessibility rules. 3) You do not need to modify proxied objects.

Disadvantage: you will lose touchdown / contract after wrapping the original object. If you use a type hint with a frequency, this may be the problem.

+5
Dec 29 '12 at 23:00
source share
 <?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 to the class ...

+3
Sep 15 '10 at 10:15
source share

If you are really, really brave, you can do it with the runkit extension. ( http://www.php.net/manual/en/book.runkit.php ). You can play with runkit_method_redefine (you may also need Reflection to retrieve the method definition), or maybe a combination of runkit_method_rename (old function) / runkit_method_add (new function that wraps calls to your function test1 and old function)

+2
15 Sep '10 at 11:04 on
source share

Perhaps the best way so far is to create your own calling method and wrap everything 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 is the conclusion:

 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 this way for a simpler example.

You no longer have to do private methods and use them through __call (). I suppose there may be situations where you want to call methods without a shell or you want your IDE to still autocomplete methods that are most likely not to happen if you declare the methods as private.

+2
Oct 05
source share

The only way to do this is to use __call magic. You need to make all methods private so that they are not accessible externally. Then define the __call method to handle method calls. In __call, you can execute any function you want before calling a function that was intentionally called.

0
Sep 15 '10 at 10:23
source share

Goes to the following:

 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')); 

Here is what i will do

-2
Sep 15 '10 at 10:36
source share



All Articles