Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Existenz von Methoden pruefen (inkl. use)

0 views
Skip to first unread message

Stefan Froehlich

unread,
May 12, 2022, 6:53:18 AM5/12/22
to
Eine Fortsetzung des vorherigen Postings bezüglich automatischer
Code-Prüfung: Bis dato habe ich Klassen-, Konstanten- und
Methodennamen geprüft, indem ich einen ggf. aktiven Namespace vor
den Namen gehängt (=> $fqcn) und dann wahlweise eine dieser drei
Prüfungen durchgeführt habe, wobei $identifier die im Quelltext
gefundene Konstante oder Methode benennt.

is_subclass_of($fqcn, 'dummy');
constant("$fqcn::$identifier");
method_exists($fqcn, $identifier);

Das läuft seit Jahren und hat sich als hilfreich erwiesen. Ein
bisher ignoriertes Manko ist, dass ggf. vorhandene use-Anweisungen
nicht berücksichtigt werden - kommt bei mir nur an wenigen Stellen
vor und war daher nie wirklich dringend.

Die Idee war, die aktuelle Umgebung als String zusammenzustellen und
dann mit eval() zu prüfen, d.h. für Klassenbezeichner sinngemäß:

#v+
$eval = $this->createCurrentNamespace();
$eval .= $this->createUseEnvironment();
$eval .= sprintf('("dummy" instanceof %s);', $fqcn);
eval($eval);
#v-

Funktioniert gut - spricht mein ans Ende der Chain gehängter
Autoloader an, wurde die Klasse nicht gefunden => Fehler.

Gleiches mache ich für Konstanten, dort mit einer Zuweisung,
funktioniert ebenfalls gut:

#v+
$eval .= "\n" . sprintf('$dummy=%s::%s;', $fqcn, $identifier);
#v-

Nur bei den Methoden bin ich ratlos: Der einzige Ansatz, der mir
bislang einfällt ist ein Aufruf:

#v+
$eval .= "\n" . sprintf('$dummy=%s::%s();', $fqcn, $identifier);
#v-

Fehler wegen falscher Parameter lassen sich abfangen, mich
interessiert dzerit nur die Existenz. Aber: dummerweise rufe ich die
Methode damit auch auf, was in einigen Fällen absolut unerwünscht
ist (und sei es nur, weil die Methode mit "die," verendet und damit
auch das Prüfprogramm in den Abgrund reisst).

Wie also tue ich so, als ob ich eine (statische) Methode aufrufe,
ohne das dann tatsächlich zu tun? Bzw. lassen sich die
use-Statements irgendwie doch beim Aufruf von method_exists()
berücksichtigen (IMO nicht, weil die Aliases ja schon beim
compilieren aufgelöst werden)?

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich
Offizieller Erstbesucher(TM) von mmeike

Eleganz ohne Grenzen - drücken mit Stefan!
(Sloganizer)

Karl Pflästerer

unread,
May 12, 2022, 9:39:33 AM5/12/22
to
Stefan...@Froehlich.Priv.at (Stefan Froehlich) writes:

> Eine Fortsetzung des vorherigen Postings bezüglich automatischer
> Code-Prüfung: Bis dato habe ich Klassen-, Konstanten- und
> Methodennamen geprüft, indem ich einen ggf. aktiven Namespace vor
> den Namen gehängt (=> $fqcn) und dann wahlweise eine dieser drei
> Prüfungen durchgeführt habe, wobei $identifier die im Quelltext
> gefundene Konstante oder Methode benennt.
>
> is_subclass_of($fqcn, 'dummy');
> constant("$fqcn::$identifier");
> method_exists($fqcn, $identifier);
>...
> Wie also tue ich so, als ob ich eine (statische) Methode aufrufe,
> ohne das dann tatsächlich zu tun? Bzw. lassen sich die
> use-Statements irgendwie doch beim Aufruf von method_exists()
> berücksichtigen (IMO nicht, weil die Aliases ja schon beim
> compilieren aufgelöst werden)?


Reflection https://www.php.net/manual/en/book.reflection.php ist keine
Option?

KP

Stefan Froehlich

unread,
May 12, 2022, 3:26:47 PM5/12/22
to
On Thu, 12 May 2022 15:39:31 Karl Pflästerer wrote:
> Stefan...@Froehlich.Priv.at (Stefan Froehlich) writes:
>> Wie also tue ich so, als ob ich eine (statische) Methode aufrufe,
>> ohne das dann tatsächlich zu tun? Bzw. lassen sich die
>> use-Statements irgendwie doch beim Aufruf von method_exists()
>> berücksichtigen (IMO nicht, weil die Aliases ja schon beim
>> compilieren aufgelöst werden)?

> Reflection https://www.php.net/manual/en/book.reflection.php ist keine
> Option?

Gute Frage, arg viel habe ich mit Reflection noch nicht gemacht.

Soweit ich das sehe, gibt es damit das gleiche Problem, dass die
use-Umgebung nicht berücksichtigt wird, d.h. ich komme nur so weit,
wie ich ohnehin bereits bin.

Ich jage z.B. die folgenden Zeilen durch den AST-Parser:

#v+
use PhpOffice\PhpSpreadsheet\Cell as Cell;
$errorCodes = Cell\DataType::getErrorCodes();
#v-

bekomme dann u.a. ein Token \AST\AST_STATIC_CALL mit der Komponente
children:

#v+
array(3) {
'class' =>
class ast\Node#1643 (4) {
public $kind =>
int(2048)
public $flags =>
int(1)
public $lineno =>
int(66)
public $children =>
array(1) {
'name' =>
string(13) "Cell\DataType"
}
}
'method' =>
string(13) "getErrorCodes"
'args' =>
class ast\Node#1644 (4) {
public $kind =>
int(128)
public $flags =>
int(0)
public $lineno =>
int(66)
public $children =>
array(0) {
}
}
}
#v-

Wenn class->flags \AST\AST_NAME ist (d.h. keine Variable für die
Klasse verwendet wird), dann möchte ich herausfinden, ob:

#v+
$class=$node->children['class']->children['name'];
$method=$node->children['method'];
$eval=sprintf('use PhpOffice\PhpSpreadsheet\Cell as Cell; %s::%s();', $class, $method);
eval($eval);
#v-

...existiert, ohne dabei die Methode tatsächlich auszuführen
(Prüfung der Argumente käme vielleicht irgendwann später), und zwar
- erst das macht es für mich interessant - unter genau der aktuellen
Laufzeitumgebung inklusive Autoloader, die von Installation zu
Installation abweichen kann.

Ok, ich könnte natürlich alle use-Statements iterativ durchgehen und
schauen, ob ich wenigstens mit einem davon zu einer erfolgreichen
Ausführung kommen. Hm, wenn's gar nicht anders geht, ist das
vielleicht noch eine Option.

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich
Offizieller Erstbesucher(TM) von mmeike

Stefan: Eine Symphonie der Schönheit.
(Sloganizer)

Karl Pflästerer

unread,
May 13, 2022, 3:04:40 AM5/13/22
to
Stefan...@Froehlich.Priv.at (Stefan Froehlich) writes:

> On Thu, 12 May 2022 15:39:31 Karl Pflästerer wrote:
>> Stefan...@Froehlich.Priv.at (Stefan Froehlich) writes:
>>> Wie also tue ich so, als ob ich eine (statische) Methode aufrufe,
>>> ohne das dann tatsächlich zu tun? Bzw. lassen sich die
>>> use-Statements irgendwie doch beim Aufruf von method_exists()
>>> berücksichtigen (IMO nicht, weil die Aliases ja schon beim
>>> compilieren aufgelöst werden)?
>
>> Reflection https://www.php.net/manual/en/book.reflection.php ist keine
>> Option?
>
> Gute Frage, arg viel habe ich mit Reflection noch nicht gemacht.
>
> Soweit ich das sehe, gibt es damit das gleiche Problem, dass die
> use-Umgebung nicht berücksichtigt wird, d.h. ich komme nur so weit,
> wie ich ohnehin bereits bin.
>
> Ich jage z.B. die folgenden Zeilen durch den AST-Parser:
>
> #v+
> use PhpOffice\PhpSpreadsheet\Cell as Cell;
> $errorCodes = Cell\DataType::getErrorCodes();
> #v-
>
>
> bekomme dann u.a. ein Token \AST\AST_STATIC_CALL mit der Komponente
> children:
> ...
>
> Wenn class->flags \AST\AST_NAME ist (d.h. keine Variable für die
> Klasse verwendet wird), dann möchte ich herausfinden, ob:
>
> #v+
> $class=$node->children['class']->children['name'];
> $method=$node->children['method'];
> $eval=sprintf('use PhpOffice\PhpSpreadsheet\Cell as Cell; %s::%s();', $class, $method);
> eval($eval);
> #v-
>
> ...existiert, ohne dabei die Methode tatsächlich auszuführen
> (Prüfung der Argumente käme vielleicht irgendwann später), und zwar
> - erst das macht es für mich interessant - unter genau der aktuellen
> Laufzeitumgebung inklusive Autoloader, die von Installation zu
> Installation abweichen kann.
>
> Ok, ich könnte natürlich alle use-Statements iterativ durchgehen und
> schauen, ob ich wenigstens mit einem davon zu einer erfolgreichen
> Ausführung kommen. Hm, wenn's gar nicht anders geht, ist das
> vielleicht noch eine Option.

Du wirst bei einer dynamischen Sprache wie PHP, nur bis zu einem
gewissen Punkt kommen. Man kann zur Laufzeit extrem viel machen (dein
Autoloader mit dem eval() ist ein gutes Beispiel (haben wir
lustigerweise an einer Stelle vor Jahren auch so gemacht, um dynamisch
Default Klassen zu erzeugen)).
Ich denke, wenn man an so einen Punkt kommt, sollte man mal einen
Schritt zurücktreten und die eigene Architektur überdenken. Irgendwann
sind zu viele Erker und Wasserspeier am Haus; sieht zu Anfang sehr gut
aus, aber wenn du einmal putzen musst ...

KP

Stefan Froehlich

unread,
May 13, 2022, 3:11:45 AM5/13/22
to
On Fri, 13 May 2022 09:04:38 Karl Pflästerer wrote:
> Stefan...@Froehlich.Priv.at (Stefan Froehlich) writes:
>> #v+
>> $class=$node->children['class']->children['name'];
>> $method=$node->children['method'];
>> $eval=sprintf('use PhpOffice\PhpSpreadsheet\Cell as Cell; %s::%s();', $class, $method);
>> eval($eval);
>> #v-

>> Ok, ich könnte natürlich alle use-Statements iterativ durchgehen
>> und schauen, ob ich wenigstens mit einem davon zu einer
>> erfolgreichen Ausführung kommen. Hm, wenn's gar nicht anders
>> geht, ist das vielleicht noch eine Option.

> Du wirst bei einer dynamischen Sprache wie PHP, nur bis zu einem
> gewissen Punkt kommen. Man kann zur Laufzeit extrem viel machen
> (dein Autoloader mit dem eval() ist ein gutes Beispiel (haben wir
> lustigerweise an einer Stelle vor Jahren auch so gemacht, um
> dynamisch Default Klassen zu erzeugen)).
> Ich denke, wenn man an so einen Punkt kommt, sollte man mal einen
> Schritt zurücktreten und die eigene Architektur überdenken. Irgendwann
> sind zu viele Erker und Wasserspeier am Haus; sieht zu Anfang sehr gut
> aus, aber wenn du einmal putzen musst ...

Eleganz ist für dieses Stück Code - wiewohl nicht zu verachten -
nicht das oberste Designziel. Die zu prüfende Software läuft in
mehreren Installationen, bei denen auf Kundenwunsch zusätzliche
Klassen ergänzt oder (via Autoloader) bestehende überladen werden.

Die bestehende Absicherung hat schon... naja, nicht unbedingt viele
Leben gerettet, aber immerhin Tippfehler ans Licht gebracht.
Eigentlich stören mich daran nur das halbe Dutzend Warnungen wegen
genau solcher use-Konstrukte wie im vorigen Posting gezeigt; die
kenne ich zwar längst auswendig und kann sie daher gut ignorieren,
aber jetzt wäre gerade etwas Zeit übrig um zu schauen, ob ich die
nicht auch noch wegbekommen.

Sieht leider schwieriger aus, als ich mir das vorgestellt habe.

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich
Offizieller Erstbesucher(TM) von mmeike

Die letzte Steigerungsform von "super", oder warum Stefan so grenzenlos ballert!
(Sloganizer)
0 new messages