Für jede Art von grammatikbasiertem Parsing sind reguläre Ausdrücke in der Regel eine schreckliche Lösung. Selbst einfache Grammatiken (wie Arithmetik) haben Verschachtelungen, und gerade bei Verschachtelungen fallen reguläre Ausdrücke einfach um.
Glücklicherweise bietet PHP eine viel, viel bessere Lösung für Sie, indem es Ihnen Zugriff auf denselben lexikalischen Analysator gibt, der vom PHP-Interpreter über die Option Funktion token_get_all() . Geben Sie ihm einen Zeichenstrom von PHP-Code, und es wird ihn in Token ("Lexeme") zerlegen, die Sie mit einem ziemlich einfachen Parsing-Programm endlicher Automat .
Führen Sie dieses Programm aus (es wird als test.php ausgeführt, damit es sich selbst ausprobiert). Die Datei ist absichtlich schlecht formatiert, damit Sie sehen können, dass es das mit Leichtigkeit handhabt.
<?
define('CONST1', 'value' );
define (CONST2, 'value2');
define( 'CONST3', time());
define('define', 'define');
define("test", VALUE4);
define('const5', //
'weird declaration'
) ;
define('CONST7', 3.14);
define ( /* comment */ 'foo', 'bar');
$defn = 'blah';
define($defn, 'foo');
define( 'CONST4', define('CONST5', 6));
header('Content-Type: text/plain');
$defines = array();
$state = 0;
$key = '';
$value = '';
$file = file_get_contents('test.php');
$tokens = token_get_all($file);
$token = reset($tokens);
while ($token) {
// dump($state, $token);
if (is_array($token)) {
if ($token[0] == T_WHITESPACE || $token[0] == T_COMMENT || $token[0] == T_DOC_COMMENT) {
// do nothing
} else if ($token[0] == T_STRING && strtolower($token[1]) == 'define') {
$state = 1;
} else if ($state == 2 && is_constant($token[0])) {
$key = $token[1];
$state = 3;
} else if ($state == 4 && is_constant($token[0])) {
$value = $token[1];
$state = 5;
}
} else {
$symbol = trim($token);
if ($symbol == '(' && $state == 1) {
$state = 2;
} else if ($symbol == ',' && $state == 3) {
$state = 4;
} else if ($symbol == ')' && $state == 5) {
$defines[strip($key)] = strip($value);
$state = 0;
}
}
$token = next($tokens);
}
foreach ($defines as $k => $v) {
echo "'$k' => '$v'\n";
}
function is_constant($token) {
return $token == T_CONSTANT_ENCAPSED_STRING || $token == T_STRING ||
$token == T_LNUMBER || $token == T_DNUMBER;
}
function dump($state, $token) {
if (is_array($token)) {
echo "$state: " . token_name($token[0]) . " [$token[1]] on line $token[2]\n";
} else {
echo "$state: Symbol '$token'\n";
}
}
function strip($value) {
return preg_replace('!^([\'"])(.*)\1$!', '$2', $value);
}
?>
Ausgabe:
'CONST1' => 'value'
'CONST2' => 'value2'
'CONST3' => 'time'
'define' => 'define'
'test' => 'VALUE4'
'const5' => 'weird declaration'
'CONST7' => '3.14'
'foo' => 'bar'
'CONST5' => '6'
Dabei handelt es sich im Grunde um einen endlichen Zustandsautomaten, der nach dem Muster sucht:
function name ('define')
open parenthesis
constant
comma
constant
close parenthesis
im lexikalischen Strom einer PHP-Quelldatei und behandelt die beiden Konstanten als ein (Name,Wert)-Paar. Dabei behandelt es verschachtelte define()-Anweisungen (gemäß den Ergebnissen) und ignoriert Leerzeichen und Kommentare sowie die Arbeit über mehrere Zeilen hinweg.
Anmerkung: Ich habe es absichtlich so gemacht, dass der Fall ignoriert wird, wenn Funktionen und Variablen konstante Namen oder Werte sind, aber Sie können es nach Belieben erweitern.
Es ist auch erwähnenswert, dass PHP ziemlich nachsichtig ist, wenn es um Strings geht. Sie können mit einfachen Anführungszeichen, doppelten Anführungszeichen oder (unter bestimmten Umständen) ganz ohne Anführungszeichen deklariert werden. Dies kann (wie von Gumbo hervorgehoben) ein mehrdeutiger Verweis auf eine Konstante sein und Sie haben keine Möglichkeit zu wissen, um welche es sich handelt (jedenfalls keine garantierte Möglichkeit), was Ihnen die Möglichkeit gibt:
- Ignorieren dieser Art von Strings (T_STRING);
- Prüfen, ob eine Konstante mit diesem Namen bereits deklariert wurde, und Ersetzen ihres Wertes. Es gibt keine Möglichkeit zu wissen, welche anderen Dateien aufgerufen wurden, noch können Sie Defines verarbeiten, die bedingt erstellt wurden, so dass Sie nicht mit Sicherheit sagen können, ob etwas definitiv eine Konstante ist oder nicht oder welchen Wert es hat; oder
- Sie können einfach mit der Möglichkeit leben, dass es sich um Konstanten handelt (was unwahrscheinlich ist) und sie einfach als Zeichenketten behandeln.
Ich persönlich würde mich für (1) und dann für (3) entscheiden.
0 Stimmen
Wie sieht es mit nicht-literalen Werten aus, z. B.
define('NOW', time())
?0 Stimmen
Ich habe es mit einer Sprachdatei zu tun, die nur Text in einfachen Anführungszeichen auf beiden Seiten enthält. Konstanter Name und Wert.
0 Stimmen
Die Namen und Werte sind also immer feste Zeichenfolgen?