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 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.
Any alternative way would also be do.
Your best bet is the magic method __call, 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 */
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.
test1
does not have to be declared private
.
ideone.com example can be found here
Updated: Add link to ideone, add example with return value.
All previous attempts are basically flawed because of 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.
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
.
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 library to solve them, and some smarter, wiser, older people even went and invented an entirely different paradigm, called "Aspect Oriented Programming" (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:
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-PHP solves 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With