2010-04-29 8 views
10

Desidero accedere a metodi e variabili private dall'esterno delle classi in casi molto rari.Chiama metodi privati ​​e proprietà private al di fuori di una classe in PHP

Ho visto che questo non è possibile anche se si utilizza l'introspezione.

Il caso specifico è la prossima:

Vorrei avere qualcosa di simile:

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

Questo metodo dovrebbe essere in grado di essere iniettato nel codice come questo:

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

(questa è solo una semplificazione, quella vera passa attraverso un socket e implementa un sacco di altre cose ...)

Quindi ...

Se si crea un'istanza della demo di classe e si chiama $ demo-> myMethod(), si otterrà una console: che console può accedere al primo metodo di scrittura un comando come:

> $this->myPublicMethod(); 

Ma non si può eseguire correttamente la seconda:

> $this->myPrivateMethod(); 

Qualcuno di voi ha qualche idea, o se c'è qualche libreria per PHP che ti permette di fare questo?

Grazie mille!

+1

Erm ... Chi vorrebbe mai rendere i metodi etichettati privati ​​accessibili al pubblico? Voglio dire ... se hai bisogno di accedervi dall'esterno, basta usare il pubblico. Inoltre: la classe della tua console non ha senso nel modo in cui l'hai aggiunta qui. Non fa un singolo uso di OOP ed è fondamentalmente solo una funzione forzata in una classe. – lamas

+0

FYI 'while (true)' o 'for (;;)' sono metodi un po 'più succinti e comuni di loop fino a quando non si incontra un 'break' o' return' esplicito. – meagar

+0

@lamas: Come ho detto in precedenza, ho fatto questo più come un POC di un esempio reale. La vera classe Console ha più o meno circa 1k linee e ne estende altre per composizione.La manutenibilità del codice non è un problema dato che verrà usato come componente isolato al di fuori del progetto principale per cui stiamo lavorando, quindi non è solo "una funzione forzata in una classe", ma un estratto da una classe che non essere pubblicato qui per evitare che la gente si infastidisca. :) @meagar: hehe, ho fatto il tempo (TRUE! = FALSE) come uno scherzo, dal momento che PHP convalida FALSE! = 0 come FALSE. grazie comunque;) –

risposta

43

Basta rendere pubblico il metodo. Ma se si vuole ottenere difficile si può provare questo (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

che è esattamente quello che stavo cercando. Attualmente sto usando PHP 5.2.3 nell'ambiente di sviluppo, ma siamo nel processo di migrazione e questo mi aiuta molto !! +1 –

+0

@webbiedave: Hm, ho anche la classe finale LockedGate {private function __construct() {} ...} e non sembra funzionare. Mi chiedo se "final" impedisca di modificare l'accessibilità. –

+2

@webbiedave: Ahh, penso che il mio problema potrebbe essere stato che si trattava di una classe statica. Questo ha funzionato per me $ $ method = $ reflector-> getMethod ('myStaticPrivate'); '' $ method-> setAccessible (true); '' $ method-> invoke (NULL); ' –

4

La prima domanda da porsi è, se è necessario accedervi dall'esterno della classe, perché l'ha dichiarata privata? Se non è il tuo codice, il mittente probabilmente ha avuto un buon motivo per dichiararlo privato, e accedervi direttamente è uno molto pratica sbagliata (e in gran parte non gestibile).

EDIT: Come Adam V. fa notare nei commenti, è necessario rendere accessibile il metodo privato prima di richiamarlo. Esempio di codice aggiornato per includerlo. Non l'ho ancora testato, ma aggiungo qui per mantenere aggiornata la risposta.

Ciò detto, è possibile utilizzare Reflection per ottenere ciò. Istanziare ReflectionClass, chiamare per il metodo che si desidera richiamare e quindi chiamare invoke sul reso ReflectionMethod.

Un esempio di codice (anche se non ho provato, quindi non ci possono essere errori) potrebbe essere simile

$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

Ciò dovrebbe comportare una ReflectionException ('Tentativo di richiamare il metodo privato dall'ambito ReflectionMethod'). – webbiedave

+0

Doh! Hai ragione - le mie scuse. – Dathan

+0

PHP Reflection API * supporta * invocando metodi privati. Hai solo bisogno di un '$ reflection_method-> setAccessible (true)' dopo il '$ reflection_method = $ reflection_class-> getMethod (" myPrivateMethod ")' –

2

ho questi problemi troppo a volte, però io ottenere intorno ad esso attraverso i miei standard di codifica. Le funzioni private o protette sono contrassegnate da un carattere di sottolineatura prefisso, ovvero

private function _myPrivateMethod() 

Quindi ho semplicemente reso pubblica la funzione.

public function _myPrivateMethod() 

Quindi, anche se la funzione è pubblica la convenzione di denominazione dà la notifica che pur pubblico è è privato e non in realtà dovrebbe essere utilizzato.

0

Credo che la reflectionClass è l'unica alternativa se si vuole veramente per eseguire alcuni metodi privati. In ogni caso, se avete solo bisogno di leggere l'accesso a privato o in protette, è possibile utilizzare questo codice:

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

Ecco una variazione delle altre risposte che possono essere utilizzati per rendere tali chiamate una riga:

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

Se è possibile aggiungere un metodo nella classe in cui è definito il metodo, è possibile aggiungere un metodo che utilizza internamente call_user_method(). Funziona anche 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

La risposta è pubblica per il metodo. Qualunque trucco tu faccia, non sarebbe comprensibile agli altri sviluppatori. Ad esempio, non sanno che in qualche altro codice questa funzione è stata consultata come pubblica guardando la classe Demo.

Un'altra cosa. quella console può accedere al primo metodo scrivendo un comando come:. Come può essere anche possibile? La console non può accedere alle funzioni della classe demo utilizzando $ this.

4

Dal PHP 5.4, è possibile utilizzare la Closure classe predefinita per associare un metodo/proprietà di una classe a un funzioni delta che ha accesso anche ai membri privati.

The Closure class

Per esempio abbiamo una classe con una variabile privata e vogliamo accedere al di fuori della classe:

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 partire da PHP 7, è possibile utilizzare il nuovo Closure::call incontrato hod, per legare qualsiasi metodo/proprietà di un obect a una funzione di callback, anche per i soci privati:

PHP 7+

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

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

Perché non si utilizza protetti? Ed estendilo