2010-09-15 16 views
44
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 

Ahora mi pregunta es,Cómo activarse la función de llamada en php para cada otra llamada a la función

¿Cómo puedo llamar a otra función llamada antes de cualquier ejecución de la función? En caso anterior, la salida cómo puedo automática llamada a la función 'test1' por cada otra llamada a la función, de modo que pueda obtener la salida como,

test1 
test2 
test1 
test3 

Actualmente estoy recibiendo como

test2 
test3 

No puedo llamar a la función 'test1' en cada definición de función ya que puede haber muchas funciones. Necesito una forma de llamar a una función antes de llamar al cualquier función de una clase.

Cualquier forma alternativa también sería hacer.

Respuesta

50

Su mejor opción es el método mágico __call, véase más adelante, por ejemplo:

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

Por favor, observe que test2test3 y no son visibles en el contexto donde se les llama debido a protected y private. Si los métodos son públicos, el ejemplo anterior fallará.

test1 no tiene que ser declarado private.

ideone.com example can be found here

Actualizado: Añadir enlace a Ideone, añadir ejemplo, con valor de retorno.

+0

En realidad, a) también puede tener los métodos como protegidos, simplemente no es público yb) no necesita el _ – ktolis

+0

Tiene razón, protegido funcionará también, he eliminado el '_' en el ejemplo y hecho 'test3'' protected'. –

+0

Debe 'return'in' __call' – Alex2php

3
<?php 

class test 
{ 

    public function __call($name, $arguments) 
    { 
     $this->test1(); // Call from here 
     return call_user_func_array(array($this, $name), $arguments); 
    } 

    // methods here... 

} 

?> 

Trate de añadir este método imperiosa de ... clase

+0

gracias por la sugerencia, pero no puedo llamar a la función 'test1' en cada definición de función, ya que puede haber muchas funciones. Necesito una forma de llamar automáticamente una función antes de llamar a cualquier función de una clase. – Nik

+0

+1 ya que esta es la mejor manera de hacerlo sin violar los principios de OOP al especificar los métodos públicos como privados, aunque todavía se puede acceder como públicos. – poke

+0

@Yogesh, mira la respuesta actualizada por favor ... – Otar

0

La única manera de hacer esto es usar la magia __call. Necesita hacer todos los métodos privados para que no sean accesibles desde el exterior. Luego defina el método __call para manejar las llamadas a métodos. En __call puede ejecutar cualquier función que desee antes de llamando a la función que se llamó intencionadamente.

-2

Deja para tener un ir en éste:

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

Eso es lo que haría

2

Si usted es muy, muy valiente, puedes hacerlo con extensión runkit. (http://www.php.net/manual/en/book.runkit.php). Puede jugar con runkit_method_redefine (también puede necesitar Reflection para recuperar la definición de método) o quizás la combinación runkit_method_rename (old function)/runkit_method_add (nueva función que envuelve las llamadas a su función test1 y una función anterior)

5

Quizás sea un poco un poco anticuado, pero aquí vienen mis 2 centavos ...

No creo que dar acceso a métodos privados a través de __call() es una buena idea. Si tiene un método que realmente no quiere que se llame fuera de su objeto, no tiene forma de evitarlo.

Creo que una solución más elegante debería ser crear algún tipo de proxy/decorador universal y usar __call() dentro de él. Te voy a enseñar cómo:

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 
    } 
} 

Ahora, una clase simple prueba:

class Test 
{ 
    public function methodOne() 
    { 
     echo 'Method one'; 
    } 

    public function methodTwo() 
    { 
     echo 'Method two'; 
    } 

    private function methodThree() 
    { 
     echo 'Method three'; 
    } 

} 

Y todo lo que hay que hacer ahora es:

$obj = new Proxy(new Test()); 

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

Ventajas:

1) solo necesita una clase de proxy y funcionará con todos sus objetos. 2) No faltará el respeto a las reglas de accesibilidad. 3) No necesita cambiar los objetos proxificados.

Desventaja: Perderá la inferfaz/contrato después de envolver el objeto original. Si usa la sugerencia Tipo con frecuencia tal vez sea un problema.

16

Todos los intentos anteriores son básicamente defectuosa debido http://ocramius.github.io/presentations/proxy-pattern-in-php/#/71

Aquí está el ejemplo sencillo, tomado de mis diapositivas:

class BankAccount { /* ... */ } 

Y aquí está nuestra "mala" lógica interceptor:

class PoorProxy { 
    public function __construct($wrapped) { 
     $this->wrapped = $wrapped; 
    } 

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

Ahora si tenemos el siguiente método para llamar:

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

Entonces esto no funcionará:

$account = new PoorProxy(new BankAccount()); 

pay($account); // KABOOM! 

Esto se aplica a todas las soluciones que sugieren que aplican un "proxy".

Las soluciones que sugieren el uso explícito de otros métodos que luego llaman a su API interna son defectuosas, porque le obligan a cambiar su API pública para cambiar un comportamiento interno y reducen la seguridad de tipo.

La solución proporcionada por Kristoffer no tiene en cuenta public métodos, que también es un problema, ya que no puede reescribir su API para que todo private o protected.

Aquí es una solución que resuelve este problema en parte:

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

Aquí es cómo lo usa:

$account = new BankAccountProxy(new BankAccount()); 

pay($account); // WORKS! 

Esta es una solución de tipo seguro, limpio, pero se trata de una mucha codificación, así que tómenlo solo como ejemplo.

Escribir este código repetitivo NO es divertido, por lo que es posible que desee utilizar diferentes enfoques.

para darle una idea de lo complicado de esta categoría de problemas está, sólo puede decirle que escribí an entire library para resolverlos, y algo más inteligente, más prudente, las personas mayores, incluso fue y se inventó un paradigma totalmente diferente, llamado "Aspect Oriented Programming" (AOP) .

Por tanto, propongo que investigue estas 3 soluciones que creo que puede ser capaz de resolver su problema de una manera mucho más limpia:

  • Uso ProxyManager 's 'access interceptor', que es básicamente un proxy tipo que le permite ejecutar un cierre cuando se llaman otros métodos (example). Aquí hay un ejemplo de cómo proxy de todas las llamadas a la API pública una $object 's:

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

    continuación, sólo tiene que utilizar build_wrapper como desee.

  • Utilice GO-AOP-PHP, que es una biblioteca de AOP real, completamente escrita en PHP, pero aplicará este tipo de lógica a TODAS las instancias de clases para las que defina cortes de punto. Esto puede ser o no lo que usted desea, y si su $callOnMethod se debe aplicar solo para casos particulares, entonces AOP no es lo que está buscando.

  • Uso del PHP AOP Extension, que no creo ser una buena solución, sobre todo porque GO-AOP-PHP resuelve este problema de una manera más elegante/depurable, y porque las extensiones de PHP son inherentemente un desastre (que ha de ser atribuido a internos de PHP, no a los desarrolladores de extensiones). Además, al usar una extensión, la aplicación será lo menos portátil posible (intente convencer a un administrador de sistemas para que instale una versión compilada de PHP, si se atreve), y no podrá usar su aplicación en motores nuevos y geniales como HHVM.

+0

También hay otra biblioteca independiente https://github.com/koriym/Ray.Aop que usa Bear.Sunday. –

3

Quizás la mejor manera hasta ahora es crear su propio método de llamadas y envolver alrededor de cualquier cosa que necesite antes y después del método:

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

Y aquí está la salida:

 
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! 

Incluso puede ir más allá y crear una lista blanca de métodos, pero lo dejo así por un ejemplo más simple.

Ya no tendrá que hacer que los métodos sean privados y usarlos a través de __call(). Supongo que puede haber situaciones en las que desee llamar a los métodos sin el derivador o si desea que su IDE continúe autocompletando los métodos, lo que probablemente no ocurra si declara que los métodos son privados.

Cuestiones relacionadas