Rufen Sie private Methoden und private Eigenschaften von außerhalb einer Klasse in PHP auf
Lesezeit: 9 Minuten
Ich möchte in sehr seltenen Einzelfällen von außerhalb der Klassen auf private Methoden und Variablen zugreifen.
Ich habe gesehen, dass dies nicht möglich ist, obwohl Introspektion verwendet wird.
Der Spezialfall ist der nächste:
Ich hätte gerne sowas:
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;
}
}
}
}
Diese Methode sollte wie folgt in den Code eingefügt werden können:
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()";
}
}
(Dies ist nur eine Vereinfachung. Die echte geht durch einen Socket und implementiert eine Reihe weiterer Dinge …)
Damit…
Wenn Sie die Klasse Demo instanziieren und $demo->myMethod() aufrufen, erhalten Sie eine Konsole: Diese Konsole kann auf die erste Methode zugreifen und einen Befehl schreiben wie:
> $this->myPublicMethod();
Aber Sie können den zweiten nicht erfolgreich ausführen:
> $this->myPrivateMethod();
Hat jemand von euch eine Idee oder gibt es eine Bibliothek für PHP, mit der Sie dies tun können?
Vielen Dank!
Ähm … Wer würde jemals Methoden, die als privat gekennzeichnet sind, öffentlich zugänglich machen wollen? Ich meine … wenn Sie von außen darauf zugreifen müssen, verwenden Sie einfach public. Außerdem: Ihre Konsolenklasse ergibt keinen Sinn, wie Sie sie hier hinzugefügt haben. Es macht keinen einzigen Gebrauch von OOP und ist im Grunde nur eine Funktion, die in eine Klasse gezwungen wird.
– Lamas
29. April 2010 um 15:50 Uhr
FYI while(true) oder for(;;) sind etwas prägnantere und gebräuchlichere Schleifenmethoden bis zu einem expliziten break oder returnangetroffen wird.
– mager ♦
29. April 2010 um 16:13 Uhr
@lamas: Wie ich bereits sagte, habe ich dies eher als POC als als echtes Beispiel getan. Die echte Console-Klasse hat mehr oder weniger etwa 1.000 Zeilen und erweitert einige andere durch Zusammensetzung. Die Wartbarkeit des Codes ist kein Problem, da er als isolierte Komponente außerhalb des Hauptprojekts, an dem wir arbeiten, verwendet wird, also nicht nur “eine Funktion, die in eine Klasse gezwungen wird”, sondern ein Auszug aus einer Klasse, die dies tut hier nicht gepostet werden, damit die Leute sich nicht ärgern. 🙂 @meagar: hehe, ich habe das while(TRUE != FALSE) aus Spaß gemacht, da PHP FALSE != 0 als FALSE validiert. trotzdem danke 😉
– Pablo Lopez Torres
29. April 2010 um 18:38 Uhr
@meagar: und ich habe vergessen, es zu kommentieren, ich habe nie eine Rückkehr oder eine Fortsetzung innerhalb einer Funktion geschrieben, es sei denn, es handelt sich um einen PoC. Ich denke, das ist keine gute Praxis und solche Dinge könnten die Lesbarkeit des Codes stören.
– Pablo Lopez Torres
29. April 2010 um 18:45 Uhr
@lamas: Ich stimme vollkommen mit dir überein Re: die Wahl des Umfangs. Aber ich stimme Ihnen nicht zu, dass eine Klasse keinen Sinn machen würde, wenn sie keinen einzigen OOP-Aufruf und rein statische Funktionen enthalten würde: Es ist eine Möglichkeit, eine Bibliothek aufzubauen und trotzdem davon zu profitieren von Autoloader-Funktionen, oder auch nur, um den Code sauberer zu machen, und fügen Sie eine Katalogisierungsebene für Ihre Funktionen hinzu.
– Fabien Haddadi
29. Oktober 2019 um 0:45 Uhr
webbiedave
Machen Sie die Methode einfach öffentlich. Aber wenn Sie knifflig werden wollen, können Sie dies versuchen (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);
das ist genau das, wonach ich gesucht habe. Ich verwende derzeit PHP 5.2.3 in der Entwicklungsumgebung, aber wir befinden uns im Migrationsprozess, das hilft mir sehr!! +1
– Pablo Lopez Torres
29. April 2010 um 18:40 Uhr
@webbiedave: Hm, ich habe auch die letzte Klasse LockedGate { private function __construct() {} … } und es scheint nicht zu funktionieren. Ich frage mich, ob “final” verhindert, dass die Zugänglichkeit geändert wird.
– Dustin Graham
23. Januar 2013 um 11:30 Uhr
@webbiedave: Ahh, ich denke, mein Problem war vielleicht, dass es eine statische Klasse war. Das hat bei mir funktioniert $method = $reflector->getMethod('myStaticPrivate');$method->setAccessible(true);$method->invoke(NULL);
– Dustin Graham
23. Januar 2013 um 11:48 Uhr
Versuchen Sie dies mit PHP 5.3.22 auf der Shell $ php -a scheitert mit: PHP Fatal error: Call to private method LockedGate::open() from context '' in php shell code on line 1PHP Stack trace:PHP 1. {main}() php shell code:0Fatal error: Call to private method LockedGate::open() from context '' in php shell code on line 1Call Stack:5.3208 641904 1. {main}() php shell code:0
– Rico Sonntag
6. März 2013 um 7:47 Uhr
Ich habe den Reflexionscode aktualisiert. Meine primäre Antwort bleibt jedoch bestehen Machen Sie die Methode einfach öffentlich.
– Webbiedave
5. Oktober 2015 um 6:05 Uhr
Christos Lytras
BEARBEITEN:
Aktualisiert, um Beispiele für private Funktionsaufrufe mit Parametern aufzunehmen.
Ab PHP5.4können Sie die vordefinierten verwenden Closure Klasse, um eine Methode/Eigenschaft einer Klasse an eine Delta-Funktion zu binden, die sogar Zugriff auf private Mitglieder hat.
Zum Beispiel haben wir eine Klasse mit einer privaten Variablen und möchten außerhalb der Klasse darauf zugreifen:
class Foo {
private $bar = "Foo::Bar";
private function add_ab($a, $b) {
return $a + $b;
}
}
PHP5.4+
$foo = new Foo;
// Single variable example
$getFooBarCallback = function() {
return $this->bar;
};
$getFooBar = $getFooBarCallback->bindTo($foo, 'Foo');
echo $getFooBar(); // Prints Foo::Bar
// Function call with parameters example
$getFooAddABCallback = function() {
// As of PHP 5.6 we can use $this->fn(...func_get_args()) instead of call_user_func_array
return call_user_func_array(array($this, 'add_ab'), func_get_args());
};
$getFooAddAB = $getFooAddABCallback->bindTo($foo, 'Foo');
echo $getFooAddAB(33, 6); // Prints 39
Ab PHP 7 können Sie die neue verwenden Closure::call Methode, um jede Methode/Eigenschaft eines Objekts an eine Callback-Funktion zu binden, auch für private Mitglieder:
PHP7+
$foo = new Foo;
// Single variable example
$getFooBar = function() {
return $this->bar;
};
echo $getFooBar->call($foo); // Prints Foo::Bar
// Function call with parameters example
$getFooAddAB = function() {
return $this->add_ab(...func_get_args());
};
echo $getFooAddAB->call($foo, 33, 6); // Prints 39
Abschnitt PHP 7+ ist großartig! Toll!
– Nurbol Alpysbayev
23. Mai 2018 um 6:08 Uhr
@NurbolAlpysbayev danke. Ich habe gerade festgestellt, dass die Antwort keinen Funktionsaufruf mit Parametern abdeckt, sondern nur private Variablen! Aktualisierte und hinzugefügte Beispiele für private Funktionsaufrufe für PHP 5.4+ und PHP 7+.
– Christos Lytras
23. Mai 2018 um 9:16 Uhr
Das funktioniert super! Aber denken Sie, dass dies die beste Vorgehensweise ist, da sie es wahrscheinlich aus einem bestimmten Grund privat gemacht haben?
– David Jarrin
19. Juni 2018 um 14:49 Uhr
@DavidJarrin Natürlich ist es das NICHT die beste Praxis. Dies sollte nach Möglichkeit vermieden werden; Es kann verwendet werden, wenn wir diesen Zugriff auf dieses private Mitglied haben möchten und 1) Wir wollen die Quelldateien der Klasse nicht ändern (vielleicht eine fertige Bibliothek, die nach dem Aktualisieren kaputt geht) und 2) Wenn es sich um eine der Klassen Ihres Projekts handelt, ändern Sie die Eigenschaft/Funktion in öffentlich oder erstellen Sie eine andere Funktion, um dieses private Element abzurufen.
– Christos Lytras
19. Juni 2018 um 15:05 Uhr
@ChristosLytras ja, das ist die Situation für mich, ich möchte die Bibliothek nicht wirklich ändern, weil sie beim Update kaputt geht, danke für die Antwort.
– David Jarrin
19. Juni 2018 um 17:58 Uhr
Dathan
Die erste Frage, die Sie stellen sollten, lautet: Wenn Sie von außerhalb der Klasse darauf zugreifen müssen, warum haben Sie es als privat deklariert? Wenn es nicht Ihr Code ist, hatte der Urheber wahrscheinlich einen guten Grund, ihn als privat zu deklarieren, und der direkte Zugriff darauf ist a sehr schlechte (und weitgehend unhaltbare) Praxis.
BEARBEITEN: Wie Adam V. in den Kommentaren betont, müssen Sie die private Methode zugänglich machen, bevor Sie sie aufrufen. Codebeispiel wurde aktualisiert, um dies einzuschließen. Ich habe es jedoch nicht getestet – nur hier hinzugefügt, um die Antwort auf dem neuesten Stand zu halten.
Davon abgesehen können Sie verwenden Betrachtung um das zu erreichen. Instanziieren ReflectionClassAnruf getMethod für die Methode, die Sie aufrufen möchten, und rufen Sie dann auf invoke auf dem Retournierten ReflectionMethod.
Ein Codebeispiel (obwohl ich es nicht getestet habe, daher können Fehler auftreten) könnte so aussehen
$demo = new Demo();
$reflection_class = new ReflectionClass("Demo");
$reflection_method = $reflection_class->getMethod("myPrivateMethod");
$reflection_method->setAccessible(true);
$result = $reflection_method->invoke($demo, NULL);
Dies sollte zu einer ReflectionException führen („Trying to invoke private method from scope ReflectionMethod“).
– Webbiedave
29. April 2010 um 16:06 Uhr
PHP-Reflection-API tut unterstützt das Aufrufen privater Methoden. Sie brauchen nur eine $reflection_method->setAccessible(true) nach dem $reflection_method = $reflection_class->getMethod("myPrivateMethod")
– Adam v.
4. Januar 2012 um 17:20 Uhr
Hier ist eine Variation der anderen Antworten, die verwendet werden können, um solche Anrufe auf einer Leitung zu tätigen:
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);
}
Ich habe diese Probleme auch manchmal, aber ich umgehe sie durch meine Codierungsstandards. Private oder geschützte Funktionen werden mit einem vorangestellten Unterstrich gekennzeichnet, dh
private function _myPrivateMethod()
Dann mache ich die Funktion einfach öffentlich.
public function _myPrivateMethod()
Obwohl die Funktion öffentlich ist, gibt die Namenskonvention die Benachrichtigung, dass während öffentlich ist, privat ist und nicht wirklich verwendet werden sollte.
Thomas Hünziker
Wenn Sie in der Klasse, in der die Methode definiert ist, eine Methode hinzufügen können, können Sie eine Methode hinzufügen, die intern call_user_method() verwendet. Dies funktioniert auch mit 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');
Shiva Kiran
Die Antwort wird der Methode öffentlich zugänglich gemacht. Welchen Trick Sie auch immer machen werden, es wäre für andere Entwickler nicht verständlich. Zum Beispiel wissen sie nicht, dass bei einem anderen Code auf diese Funktion als öffentlich zugegriffen wurde, indem sie sich die Demo-Klasse ansehen.
Eine Sache noch. Diese Konsole kann auf die erste Methode zugreifen und einen Befehl schreiben wie:. Wie kann das überhaupt möglich sein? Die Konsole kann mit $this nicht auf Funktionen der Demoklasse zugreifen.
Ich denke, alle Leute hier, die das tun wollen, sind die “Mitentwickler” 🙂
– beppe9000
10. Mai 2019 um 20:22 Uhr
9956900cookie-checkRufen Sie private Methoden und private Eigenschaften von außerhalb einer Klasse in PHP aufyes
Ähm … Wer würde jemals Methoden, die als privat gekennzeichnet sind, öffentlich zugänglich machen wollen? Ich meine … wenn Sie von außen darauf zugreifen müssen, verwenden Sie einfach public. Außerdem: Ihre Konsolenklasse ergibt keinen Sinn, wie Sie sie hier hinzugefügt haben. Es macht keinen einzigen Gebrauch von OOP und ist im Grunde nur eine Funktion, die in eine Klasse gezwungen wird.
– Lamas
29. April 2010 um 15:50 Uhr
FYI
while(true)
oderfor(;;)
sind etwas prägnantere und gebräuchlichere Schleifenmethoden bis zu einem explizitenbreak
oderreturn
angetroffen wird.– mager
♦
29. April 2010 um 16:13 Uhr
@lamas: Wie ich bereits sagte, habe ich dies eher als POC als als echtes Beispiel getan. Die echte Console-Klasse hat mehr oder weniger etwa 1.000 Zeilen und erweitert einige andere durch Zusammensetzung. Die Wartbarkeit des Codes ist kein Problem, da er als isolierte Komponente außerhalb des Hauptprojekts, an dem wir arbeiten, verwendet wird, also nicht nur “eine Funktion, die in eine Klasse gezwungen wird”, sondern ein Auszug aus einer Klasse, die dies tut hier nicht gepostet werden, damit die Leute sich nicht ärgern. 🙂 @meagar: hehe, ich habe das while(TRUE != FALSE) aus Spaß gemacht, da PHP FALSE != 0 als FALSE validiert. trotzdem danke 😉
– Pablo Lopez Torres
29. April 2010 um 18:38 Uhr
@meagar: und ich habe vergessen, es zu kommentieren, ich habe nie eine Rückkehr oder eine Fortsetzung innerhalb einer Funktion geschrieben, es sei denn, es handelt sich um einen PoC. Ich denke, das ist keine gute Praxis und solche Dinge könnten die Lesbarkeit des Codes stören.
– Pablo Lopez Torres
29. April 2010 um 18:45 Uhr
@lamas: Ich stimme vollkommen mit dir überein Re: die Wahl des Umfangs. Aber ich stimme Ihnen nicht zu, dass eine Klasse keinen Sinn machen würde, wenn sie keinen einzigen OOP-Aufruf und rein statische Funktionen enthalten würde: Es ist eine Möglichkeit, eine Bibliothek aufzubauen und trotzdem davon zu profitieren von Autoloader-Funktionen, oder auch nur, um den Code sauberer zu machen, und fügen Sie eine Katalogisierungsebene für Ihre Funktionen hinzu.
– Fabien Haddadi
29. Oktober 2019 um 0:45 Uhr