Как функция автоматического вызова в php для вызова любой другой функции

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 

Теперь мой вопрос:

Как я могу вызвать другую функцию перед выполнением любой вызываемой функции? В приведенном выше случае, как я могу автоматически вызвать функцию «test1» для каждого другого вызова функции, чтобы я мог получить результат как,

 test1 test2 test1 test3 

в настоящее время я получаю

 test2 test3 

Я не могу назвать функцию «test1» в каждом определении функции, так как может быть много функций. Мне нужен способ автоматического вызова функции перед вызовом любой функции класса.

Любой альтернативный способ также будет делать.

Related of "Как функция автоматического вызова в php для вызова любой другой функции"

Ваш лучший выбор – волшебный метод __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 */ 

Обратите внимание, что test2 и test3 не отображаются в контексте, где они вызываются из-за protected и private . Если методы общедоступны, приведенный выше пример не будет выполнен.

test1 не должен быть объявлен private .

Пример ideone.com можно найти здесь

Обновлено : добавьте ссылку на ideone, добавьте пример с возвращаемым значением.

Все предыдущие попытки в основном ошибочны из-за http://ocramius.github.io/presentations/proxy-pattern-in-php/#/71

Вот простой пример, взятый из моих слайдов:

 class BankAccount { /* ... */ } 

И вот наша «бедная» логика перехватчика:

 class PoorProxy { public function __construct($wrapped) { $this->wrapped = $wrapped; } public function __call($method, $args) { return call_user_func_array( $this->wrapped, $args ); } } 

Теперь, если мы вызываем следующий метод:

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

Тогда это не сработает:

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

Это относится ко всем решениям, предлагающим внедрение «прокси».

Решения, предлагающие явное использование других методов, которые затем называют ваш внутренний API, являются ошибочными, поскольку они заставляют вас менять свой общедоступный API для изменения внутреннего поведения и уменьшают безопасность типов.

Решение, предоставленное Kristoffer, не учитывает public методы, что также является проблемой, так как вы не можете переписать свой API, чтобы сделать его private или protected .

Вот решение, которое частично решает эту проблему:

 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() { /**/ } } 

Вот как вы его используете:

 $account = new BankAccountProxy(new BankAccount()); pay($account); // WORKS! 

Это безопасное по типу, чистое решение, но оно требует много кодирования, поэтому, пожалуйста, используйте его только в качестве примера.

Написание этого кода шаблона НЕ весело, поэтому вы можете использовать разные подходы.

Чтобы дать вам представление о том, насколько сложна эта категория проблем, я могу просто сказать вам, что я написал целую библиотеку для их решения, а некоторые более умные, более мудрые, пожилые люди даже пошли и изобрели совершенно другую парадигму под названием «Ориентированный на ориентацию Программирование "(AOP) .

Поэтому я предлагаю вам изучить эти 3 решения, которые, как я думаю, могут решить вашу проблему гораздо более чистым способом:

  • Используйте « перехватчик доступа » ProxyManager , который в основном является прокси-типом, который позволяет запускать закрытие при вызове других методов ( пример ). Ниже приведен пример того, как прокси-сервер ALL вызывает публичный API-интерфейс $object :

     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 , которое, как я считаю, не является хорошим решением, в основном потому, что GO-AOP-PHP решает эту проблему более элегантно / отлаживаемо, а потому, что расширения в PHP по сути являются беспорядком (то есть приписывается внутренним функциям PHP, а не разработчикам расширений). Кроме того, используя расширение, вы делаете свое приложение как можно более переносимым (попробуйте убедить sysadmin установить скомпилированную версию PHP, если вы посмеете), и вы не можете использовать свое приложение для новых новых движков, таких как HHVM.

Может быть, это немного устарело, но вот приходят мои 2 цента …

Я не думаю, что предоставление доступа к приватным методам через __call () – хорошая идея. Если у вас есть метод, который вы действительно не хотите вызывать за пределами своего объекта, у вас нет способа избежать его.

Я думаю, что еще одним элегантным решением должно быть создание универсального прокси-декодера и использование __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 } } 

Теперь просто класс тестирования:

 class Test { public function methodOne() { echo 'Method one'; } public function methodTwo() { echo 'Method two'; } private function methodThree() { echo 'Method three'; } } 

И все, что вам нужно сделать сейчас, это:

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

Преимущества:

1) Вам просто нужен один прокси-класс, и он будет работать со всеми вашими объектами. 2) Вы не будете неуважительно относиться к правилам доступности. 3) Вам не нужно менять проксированные объекты.

Недостаток: вы потеряете приземление / контракт после обертывания исходного объекта. Если вы используете тип hinting с частотой, возможно, это проблема.

 <?php class test { public function __call($name, $arguments) { $this->test1(); // Call from here return call_user_func_array(array($this, $name), $arguments); } // methods here... } ?> 

Попробуйте добавить этот метод в класс …

Если вы действительно, действительно смелый, вы можете сделать это с расширением runkit. ( http://www.php.net/manual/en/book.runkit.php ). Вы можете играть с runkit_method_redefine (вам может понадобиться Reflection также для извлечения определения метода) или, может быть, комбинация runkit_method_rename (старая функция) / runkit_method_add (новая функция, которая обертывает вызовы вашей функции test1 и старую функцию)

Возможно, наилучшим способом до сих пор является создание вашего собственного вызывающего метода и обтекание всего, что вам нужно до и после метода:

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

И вот результат:

 callMethod: Я буду исполнять эту строку, а затем вызвать MyClass-> functionAA ()
 functionAA: меня вызвали
 callMethod: Я закончил с MyClass-> functionAA (), и теперь я выполняю эту строку 

 callMethod: я буду исполнять эту строку, а затем вызвать MyClass-> functionBB ()
 functionBB: Я был вызван с этими аргументами (1, 2, 3)
 callMethod: Я закончил с MyClass-> functionBB (), и теперь я выполняю эту строку 

 callMethod: метод MyClass-> functionCC () не существует

 callMethod: Указанный метод не указан!

Вы даже можете пойти дальше и создать белый список методов, но я оставляю его таким образом для более простого примера.

Вы больше не будете вынуждены делать частные методы и использовать их через __call (). Я предполагаю, что могут быть ситуации, когда вы захотите вызвать методы без оболочки или вы хотите, чтобы ваша среда IDE все еще автозаполняла методы, которые, скорее всего, не произойдут, если вы объявите методы как частные.

Единственный способ сделать это – использовать волшебный __call . Вам нужно сделать все методы частными, чтобы они не были доступны извне. Затем определите метод __call для обработки вызовов метода. В __call вы можете выполнить любую функцию, которую хотите, до вызова функции, которая была намеренно вызвана.

Давайте поговорим об этом:

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

То, что я буду делать