2010-04-29 11 views
10

Quiero acceder a métodos privados y variables desde fuera de las clases en casos específicos muy raros.Llamar a métodos privados y propiedades privadas desde fuera de una clase en PHP

He visto que esto no es posible aunque se usa la introspección.

El caso específico es el siguiente:

me gustaría tener algo como esto:

class Console 
{ 
    final public static function run() { 

     while (TRUE != FALSE) { 
      echo "\n> "; 
      $command = trim(fgets(STDIN)); 

      switch ($command) { 
       case 'exit': 
       case 'q': 
       case 'quit': 
        echo "OK+\n"; 
        return; 
       default: 
        ob_start(); 
        eval($command); 
        $out = ob_get_contents(); 
        ob_end_clean(); 

        print("Command: $command"); 
        print("Output:\n$out");   

        break; 
      } 
     } 
    } 
} 

Este método debe ser capaz de ser inyectada en el código como el siguiente:

Class Demo 
{ 
    private $a; 

    final public function myMethod() 
    { 
     // some code 
     Console::run(); 
     // some other code 
    } 

    final public function myPublicMethod() 
    { 
     return "I can run through eval()"; 
    } 

    private function myPrivateMethod() 
    { 
     return "I cannot run through eval()"; 
    } 
} 

(esto es solo una simplificación. El real pasa por un socket e implementa un montón de cosas más ...)

Entonces ...

Si usted instancia la clase Demo y se llama a $ demo-> myMethod(), obtendrá una consola: la consola se puede acceder al primer método de escribir un comando como:

> $this->myPublicMethod(); 

Pero no se puede ejecutar correctamente la segunda:

> $this->myPrivateMethod(); 

¿alguno de ustedes tiene alguna idea, o si hay alguna biblioteca para PHP que le permite hacer esto?

¡Muchas gracias!

+1

Erm ... ¿Quién querría que los métodos etiquetados como privados sean de acceso público? Quiero decir ... si necesitas acceder desde afuera, solo usa el público. Además: su clase de consola no tiene sentido de la forma en que la agregó aquí. No hace un solo uso de OOP y es básicamente una función forzada en una clase. – lamas

+0

FYI 'while (true)' o 'for (;;)' son métodos de bucle un tanto más sucintos y comunes hasta que se encuentra un 'break' o' return 'explícito. – meagar

+0

@lamas: Como dije anteriormente, he hecho esto más como un POC que como un ejemplo real. La clase de consola real tiene más o menos aproximadamente 1k líneas y se extiende a otras por composición.El mantenimiento del código no es un problema ya que se usará como un componente aislado fuera del proyecto principal para el que estamos trabajando, de modo que no solo se trata de "una función forzada en una clase", sino un extracto de una clase que no se publicará aquí para evitar que la gente se enoje. :) @meagar: jeje, hice el tiempo (¡VERDADERO! = FALSO) como una broma, ya que PHP valida FALSO! = 0 como FALSO. gracias de todos modos;) –

Respuesta

43

Simplemente haga que el método sea público. Pero si se quiere conseguir difícil puede probar esto (PHP 5.3):

class LockedGate 
{ 
    private function open() 
    { 
     return 'how did you get in here?!!'; 
    } 
} 

$object = new LockedGate(); 
$reflector = new ReflectionObject($object); 
$method = $reflector->getMethod('open'); 
$method->setAccessible(true); 
echo $method->invoke($object); 
+0

que es exactamente lo que estaba buscando. Actualmente estoy usando PHP 5.2.3 en el entorno de desarrollo, pero estamos en el proceso de migración, ¡eso me ayuda mucho! +1 –

+0

@webbiedave: Hm, también tengo la clase final LockedGate {private function __construct() {} ...} y parece que no funciona. Me pregunto si "final" impide que se cambie el acceso. –

+2

@webbiedave: Ahh, creo que mi problema puede haber sido que era una clase estática. Esto funcionó para mí '$ method = $ reflector-> getMethod ('myStaticPrivate');' '$ method-> setAccessible (true);' '$ method-> invoke (NULL);' –

4

La primera pregunta que debes formular es, si necesitas acceder desde fuera de la clase, ¿por qué la declaraste privada? Si no es su código, es probable que el creador haya tenido una buena razón para declararlo privado, y acceder a él directamente es una práctica incorrecta muy (y prácticamente imposible de mantener).

EDIT: Como señala Adam V. en los comentarios, debe hacer accesible el método privado antes de invocarlo. Ejemplo de código actualizado para incluir esto. Aunque no lo he probado, solo agregué aquí para mantener la respuesta actualizada.

Una vez dicho esto, puede usar Reflection para lograr esto. Cree una instancia en ReflectionClass, llame al getMethod para el método que desea invocar, y luego llame al invoke en el ReflectionMethod devuelto.

Un ejemplo de código (aunque no lo he probado, lo que puede haber errores) podría parecerse a

$demo = new Demo(); 
$reflection_class = new ReflectionClass("Demo"); 
$reflection_method = $reflection_class->getMethod("myPrivateMethod"); 
$reflection_method->setAccessible(true); 
$result = $reflection_method->invoke($demo, NULL); 
+0

Esto debería dar como resultado una ReflectionException ('Intentando invocar el método privado desde el alcance ReflectionMethod'). – webbiedave

+0

Doh! Tienes razón, mis disculpas. – Dathan

+0

PHP Reflection API * does * admite la invocación de métodos privados. Solo necesita un '$ reflection_method-> setAccessible (true)' después del '$ reflection_method = $ reflection_class-> getMethod (" myPrivateMethod ")' –

2

tengo estos problemas también a veces, sin embargo yo conseguir alrededor de él a través de mis estándares de codificación. Las funciones privadas o protegidas se denotan con un subrayado del prefijo, por ejemplo,

private function _myPrivateMethod() 

Luego hago que la función sea pública.

public function _myPrivateMethod() 

Así que, aunque la función pública es la convención de nombres da la notificación que, si bien es público es privado y no debe ser utilizado realmente.

0

supongo que la ReflectionClass es la única alternativa si realmente desea ejecutar algunos métodos privados. De todos modos, si sólo tiene acceso de lectura a Privat o bienes protegidos, se puede utilizar este código:

<?php 
class Demo 
{ 
    private $foo = "bar"; 
} 

$demo = new Demo(); 

// Will return an object with public, private and protected properties in public scope. 
$properties = json_decode(preg_replace('/\\\\u([0-9a-f]{4})|'.get_class($demo).'/i', '', json_encode((array) $demo))); 

?> 
3

Aquí es una variación de las otras respuestas que se pueden utilizar para hacer este tipo de llamadas de una línea:

public function callPrivateMethod($object, $methodName) 
{ 
    $reflectionClass = new \ReflectionClass($object); 
    $reflectionMethod = $reflectionClass->getMethod($methodName); 
    $reflectionMethod->setAccessible(true); 

    $params = array_slice(func_get_args(), 2); //get all the parameters after $methodName 
    return $reflectionMethod->invokeArgs($object, $params); 
} 
0

Si puede agregar un método en la clase donde está definido el método, puede agregar un método que use call_user_method() internamente. Esto funciona también con PHP 5.2.x

<?php 
class SomeClass { 
    public function callprivate($methodName) { 
     call_user_method(array($this, $methodName)); 
    } 

    private function somePrivateMethod() { 
     echo 'test'; 
    } 
} 


$object = new SomeClass(); 
$object->callprivate('somePrivateMethod'); 
0

respuesta pública se pone al método. Cualquier truco que vayas a hacer no sería comprensible para otros desarrolladores. Por ejemplo, no saben que en otro código se ha accedido a esta función como pública mirando la clase Demo.

Una cosa más. esa consola puede acceder al primer método escribiendo un comando como:. ¿Cómo puede esto ser posible? La consola no puede acceder a las funciones de clase de demostración usando $ this.

4

A partir de PHP 5.4 , puede utilizar la clase predefinida Closure para unir un método/propiedad de una clase de funciones delta a que tiene acceso ni siquiera a los miembros privados.

The Closure class

Por ejemplo, tenemos una clase con una variable privada y queremos acceder a él fuera de la clase:

class Foo { 
    private $bar = "Foo::Bar"; 
} 

PHP 5.4+

$foo = new Foo; 
$getFooBarCallback = function() { 
    return $this->bar; 
}; 

$getFooBar = $getFooBarCallback->bindTo($foo, 'Foo'); 
echo $getFooBar(); // Prints Foo::Bar 

A partir de PHP 7, puede usar el nuevo Closure::call conocido Hod, para unir cualquier método/propiedad de un obect a una función de devolución de llamada, incluso para los miembros privados:

PHP 7+

$foo = new Foo; 
$getFooBar = function() { 
    return $this->bar; 
} 

echo $getFooBar->call($foo); // Prints Foo::Bar 
0

¿Por qué no utilizar Protected? Y extiéndalo

Cuestiones relacionadas