Navigation
 Startseite
 Fachbücher
 Anzeigenmarkt
 Forum
 Webmaster News
 Script Newsletter
 Push Meldungen
 Kontakt
 Script Installation
 Php
 Php Tutorials
 Lernpfade
 Webhoster Vergleich
 Impressum

Community-Bereich
 kostenlos Registrieren
 Anmelden
 Benutzerliste

Script Datenbank
 Script Archiv
 Script Top 20
 Screenshots
 Testberichte

Suche
 

Unsere Php Scripts
 Counter Script
 Umfrage Script
 Bilder Upload Script
 Terminverwaltung
 Simple PHP Forum
 RSS Grabber

Tools und Generatoren
 .htpasswd Generator
 md5 Generator
 base64 Generator
 Markdown to HTML
 Colorpicker
 Unix timestamp Tool
 Unit Test Generator
 TLD Liste
 Webkatalog‑Verzeichnis

Artfiles.de
Bietet Serviceorientierte Internetdienstleistungen...
https://www.Artfiles.de
Hosterplus.de
Bekommen Sie Speicherplatz (Webspace), Domains und...
https://www.Hosterplus.de
 
 
 

PHP Tokenizer: Quellcode maschinell auswerten

Sie befinden sich: Home > Php Tutorial > PHP Tokenizer: Quellcode ma...

PHP Tokenizer: Quellcode maschinell auswerten
Veröffentlicht am:
03.09.2026
Hits / Besucher:
6
Sprache:
  Deutsch
Tutorial Art:
eigenes
Eingetragen von:
Merkliste:
 
Beschreibung

Ein Werkzeug soll alle Klassennamen eines Projekts einsammeln. Der erste Versuch mit einem regulären Ausdruck liefert prompt zu viele Treffer: das Wort class steht auch in Kommentaren, in Zeichenketten und in einem CSS-Schnipsel mitten im Code.

Illustration zum Tutorial: PHP Tokenizer: Quellcode maschinell auswerten

Im Bild zerfällt ein Codeblock in beschriftete Bausteine, und der Kommentar-Baustein liegt abseits. Genau diese Trennung macht den Unterschied.

Was der PHP Tokenizer macht

Bevor PHP Code ausführt, zerlegt es ihn in die kleinsten bedeutungstragenden Einheiten. Dieser Schritt heißt lexikalische Analyse, und sein Ergebnis sind Token. Ein Token weiß, was es ist, welchen Text es trägt und in welcher Zeile es steht. Der PHP tokenizer macht genau dieses Zwischenergebnis für eigene Skripte zugänglich.

Die Erweiterung ist in aller Regel vorhanden. Bricht ein Aufruf mit einer Meldung über eine undefinierte Funktion ab, gibt extension_loaded('tokenizer') Auskunft; liefert das true, steht die Funktion vermutlich auf der Liste der abgeschalteten Funktionen in der Serverkonfiguration.

Warum ein regulärer Ausdruck hier scheitert

Der Unterschied lässt sich in einem kurzen Beispiel zeigen. Dieselbe Datei, zwei Verfahren, zwei Ergebnisse.

<?php

$quelle = <<<'CODE'
<?php
/* Diese class ist nur ein Kommentar */
$hinweis = 'hier steht class im String';
class Rechnung {}
CODE;

/* Der Regex findet drei Treffer, zwei davon falsch. */
preg_match_all('/\bclass\s+(\w+)/', $quelle, $treffer);
print_r($treffer[1]);
/* Array ( [0] => ist [1] => im [2] => Rechnung ) */

Zwei der drei Treffer sind Unsinn, weil der Ausdruck den Kontext nicht kennt. Er sieht Zeichen, keine Sprache. Der PHP tokenizer dagegen hat den Kommentar und die Zeichenkette bereits als eigene Token abgelegt, bevor irgendjemand danach sucht.

Mit dem PHP Tokenizer Token sichtbar machen

Der klassische Weg in den PHP tokenizer ist token_get_all(). Die Funktion liefert ein Array, dessen Einträge entweder ein Array aus Kennung, Text und Zeilennummer sind oder ein einzelner String für Zeichen wie Semikolon und Klammer.

<?php

$quelle = '<?php $summe = 3 + 4;';

foreach (token_get_all($quelle) as $token) {
if (is_array($token)) {
printf("%-22s %s\n", token_name($token[0]), trim($token[1]));
} else {
printf("%-22s %s\n", 'Zeichen', $token);
}
}

/* T_OPEN_TAG <?php
T_VARIABLE $summe
T_WHITESPACE
Zeichen =
T_LNUMBER 3
Zeichen +
T_LNUMBER 4
Zeichen ; */

token_name() ist dabei unverzichtbar, denn ohne diese Umwandlung stehen dort nur Zahlen. Wichtig: Diese Zahlen ändern sich zwischen PHP-Versionen. Im eigenen Code gehören deshalb immer die Konstanten hin, nie die Zahlenwerte.

Beide Wege im Vergleich, mit der Beschreibung aus dem Handbuch im Original:

token_get_all(string $code, int $flags = 0): array

PhpToken::tokenize(string $code, int $flags = 0): array

/* code - the php source code to be tokenized, opening tag included
flags - TOKEN_PARSE runs the additional checks of the parser
return - token_get_all() returns arrays and plain strings mixed,
PhpToken::tokenize() returns PhpToken objects instead

Both functions use the same lexer that the php parser runs before
executing a script. The token constants are listed in the manual,
their numeric values change between versions. */

Der letzte Satz ist der Grund für die Regel oben: Weil die Zahlenwerte versionsabhängig sind, wäre jeder Vergleich gegen eine feste Zahl beim nächsten Versionswechsel falsch.

PhpToken: der moderne Weg seit PHP 8

Im PHP tokenizer macht die Mischung aus Arrays und Strings jeden Durchlauf umständlich, weil ständig geprüft werden muss, womit man es gerade zu tun hat. Seit PHP 8.0 gibt es dafür eine Klasse, die durchgängig Objekte liefert.

<?php

$quelle = '<?php $summe = 3 + 4;';

foreach (PhpToken::tokenize($quelle) as $token) {
printf("%-22s %-8s Zeile %d\n",
$token->getTokenName(),
trim($token->text),
$token->line
);
}

Jedes Objekt hat id, text, line und pos, dazu die Methoden is() zum Prüfen gegen eine oder mehrere Kennungen und isIgnorable(), das Leerraum, Kommentare und offene Tags in einem Aufruf erkennt. Die Fallunterscheidung entfällt damit vollständig. Für neuen Code ist das die bessere Wahl, während token_get_all() in jedem gewachsenen Projekt weiterhin anzutreffen ist und unverändert funktioniert.

Anwendung 1: Klassennamen ohne include ermitteln

Jetzt dieselbe Aufgabe wie oben, diesmal richtig. Gesucht wird das Schlüsselwort, genommen wird der nächste bedeutungstragende Baustein.

<?php

function klassenNamen(string $datei): array
{
$token = PhpToken::tokenize((string) file_get_contents($datei));
$namen = [];
$wartend = false;

foreach ($token as $t) {
if ($t->isIgnorable()) {
continue;
}

if ($t->is([T_CLASS, T_INTERFACE, T_TRAIT, T_ENUM])) {
$wartend = true;
continue;
}

if ($wartend) {
$namen[] = $t->text;
$wartend = false;
}
}

return $namen;
}

print_r(klassenNamen('src/Rechnung.php'));
/* Array ( [0] => Rechnung ) */

Der Zwischenschritt mit der Merkvariablen ist nötig, weil zwischen Schlüsselwort und Namen immer Leerraum steht. Und weil isIgnorable() vorher aussortiert, kommen Kommentare gar nicht erst in die Prüfung. Die Datei wird dabei nie eingebunden, ihr Code also nie ausgeführt. Wer ein ganzes Projektverzeichnis durchgehen will, kombiniert das mit PHP scandir zum Auslesen von Verzeichnissen.

Anwendung 2: unerwünschte Funktionsaufrufe finden

Ein Prüfwerkzeug soll melden, wenn Debug-Aufrufe in einen Commit rutschen. Der PHP tokenizer ist dafür die verlässliche Grundlage. Der PHP tokenizer liefert dabei keine Fehlalarme aus Kommentaren.

<?php

function verboteneAufrufe(string $quelle, array $sperrliste): array
{
$token = PhpToken::tokenize($quelle);
$funde = [];
$vorher = null;

foreach ($token as $t) {
if ($t->isIgnorable()) {
continue;
}

/* Methodenaufrufe wie $log->dump() sind erlaubt,
deshalb wird das vorherige Token geprueft. */
$istMethode = $vorher !== null
&& $vorher->is([T_OBJECT_OPERATOR, T_DOUBLE_COLON]);

if ($t->is(T_STRING) && !$istMethode
&& in_array(strtolower($t->text), $sperrliste, true)) {
$funde[] = $t->text . ' in Zeile ' . $t->line;
}

$vorher = $t;
}

return $funde;
}

print_r(verboteneAufrufe($quelle, ['var_dump', 'print_r', 'dd']));
/* Array ( [0] => var_dump in Zeile 42 ) */

Die Prüfung auf das vorherige Token verhindert, dass eine gleichnamige Methode fälschlich gemeldet wird. Ohne sie würde jedes $logger->dump() als Verstoß gelten.

Anwendung 3: Codezeilen ohne Kommentare zählen

Zeilen zählen kann jeder Editor. Interessant wird es, wenn Kommentare und Leerzeilen herausfallen sollen.

<?php

function codeZeilen(string $quelle): int
{
$zeilen = [];

foreach (PhpToken::tokenize($quelle) as $t) {
if ($t->isIgnorable()) {
continue;
}

/* Ein Token kann sich ueber mehrere Zeilen erstrecken,
gezaehlt wird hier bewusst nur die Anfangszeile. */
$zeilen[$t->line] = true;
}

return count($zeilen);
}

echo codeZeilen((string) file_get_contents('src/Rechnung.php'));
/* 84 */

Das Ergebnis ist belastbarer als jede Schätzung, weil es nur Zeilen zählt, in denen tatsächlich Code steht.

Kommentare und Doc-Blöcke mit dem PHP Tokenizer trennen

Nicht jeder Kommentar ist gleich viel wert, und der PHP tokenizer trennt beide Sorten von sich aus. PHP trennt beide Sorten schon beim Zerlegen: Ein Block, der mit zwei Sternen beginnt, wird als T_DOC_COMMENT gemeldet, alle anderen als T_COMMENT.

<?php

foreach (PhpToken::tokenize($quelle) as $t) {
if ($t->is(T_DOC_COMMENT)) {
echo 'Dokumentation in Zeile ' . $t->line . PHP_EOL;
}
}

Damit lassen sich Dokumentationsblöcke gezielt einsammeln, ohne jeden beiläufigen Hinweis mitzunehmen. Wer strukturierte Angaben direkt im Code ablegen will, findet mit PHP Attributes für Metadaten im Code allerdings das modernere Mittel.

flowchart TD
  A[Quelldatei einlesen] --> B[PhpToken tokenize]
  B --> C[Leerraum verwerfen]
  C --> D{Token pruefen}
  D -->|T_CLASS| E[Naechsten Namen merken]
  D -->|T_STRING| F[Gegen Sperrliste pruefen]
  D -->|sonst| G[Ueberspringen]
  E --> H[Ergebnisliste]
  F --> H[Ergebnisliste]

Alle drei Anwendungen oben folgen diesem Muster. Nur der mittlere Zweig unterscheidet sich, der Rahmen bleibt derselbe.

Wo der PHP Tokenizer aufhört

Token kennen keine Struktur. Sie wissen nicht, ob eine Methode zu einer Klasse gehört, welche Argumente ein Aufruf bekommt oder ob eine Variable vorher zugewiesen wurde. Wer solche Fragen beantworten will, braucht einen Parser, der einen Syntaxbaum erzeugt, und dafür gibt es fertige Bibliotheken.

Eine zweite Grenze betrifft die Sicherheit. Ein Aufruf, der zur Laufzeit aus Teilen zusammengesetzt wird, taucht in keiner Token-Liste als solcher auf. Eine statische Prüfung findet, was im Klartext dasteht, und das ist wertvoll. Ein Beweis, dass eine Datei harmlos ist, entsteht daraus nicht. Reguläre Ausdrücke bleiben übrigens für vieles das richtige Werkzeug, nur eben nicht für Code, der selbst eine Sprache ist. Wer sie für Textmuster einsetzen will, findet in PHP Regex und reguläre Ausdrücke die Grundlagen.

Fazit

Der PHP tokenizer beantwortet Fragen über Quellcode, ohne ihn auszuführen, und er unterscheidet dabei sicher zwischen echtem Code, Zeichenketten und Kommentaren. Mit PhpToken::tokenize() und isIgnorable() sind die drei gezeigten Werkzeuge jeweils zwanzig Zeilen lang. Wer weiß, dass die Token-Liste die Struktur des Programms nicht kennt, setzt sie genau dort ein, wo sie unschlagbar ist, und greift für alles Weitere zum Syntaxbaum.

 

Tags:

 

 

Kommentare (0)

Noch keine Kommentare. Sei der Erste!

Melde dich an, um einen Kommentar zu schreiben.
Bücherregal mit drei Büchern: 'PHP 4 - Grundlagen und Profiwissen' von Hanser Verlag, 'Webdesign in a Nutshell' von O'Reilly Verlag, und 'Webgestaltung' von Galileo Computing.