PCRE – der Stackspeicher Fresser

Als erstes möchte ich euch mitteilen, dass ich mich sehr freue diesen Beitrag zu schreiben. Die letzten drei Monate war ich in Elternzeit und bin nicht zum Schreiben geschweige zum Programmieren gekommen. Es war aber eine sehr schöne Zeit für mich und die Familie. Nach dieser langen Programmier-Abstinenz wollte ich eine neue LAMP Entwicklungsumgebung Installieren. Kurz darauf habe ich einen in den Jahren gekommenen Laptop platt gemacht und eine neue Version von Ubuntu installiert. Das ging ganz schnell und einfach. Danach habe ich mittels „aptitude“, eine Erweiterung der Paketverwaltung APT welche auf allen Debian-basierten Systemen zum Einsatz komm, einen Apache2 Server, eine MySQL Datenbank, PHP, PEAR und PHPUnit installiert. Um das ganze zu testen habe ich ein im PHP5 erstelltes Projekt in der neuen LAMP Umgebung kopiert und die PHPUnit Tests gestartet. Alles lief recht schnell und ohne Probleme. Die Tests waren erfolgreich. Nun bin ich auch neugierig geworden und habe ein etwas älteres Projekt, das von PHP4 auf PHP5 umgeschrieben habe, ausprobiert. Hier handelt es sich um eine Schnittstelle die sehr viele Text-Dateien verarbeitet um den Inhalt anschließend in der Datenbank zu importiert. So, ab hier fangen die Probleme an.

Kurz nach dem ich den Test gestartet habe, meldete sich PHP mit einem PCRE Rekursionslimit-Fehler. Das Rekursionslimit sei 128 und darf nicht überschritten werden. Nach einer kurzen Recherche im Internet, habe ich erfahren, dass seit PHP5.2.0 zwei neue Einstellungen „pcre.recursion_limit“ und „pcre.backtrack_limit“ eingeführt worden sind. Diese sollen PHP vor komplexe oder fehlerhafte reguläre Ausdrücke schützen. Da sie zu viel Stackspeicher benötigen und PHP zum Absturz bringen können.

Lösungsansatz 1:
Da es sich hier um eine Einstellungen im PHP handelt, können diese direkt in der php.ini oder gleich mit ini_set() auf einen höheren Wert gesetzt werden. Die php.ini lässt man besser in Ruhe, denn der Standartwert von hunderttausend ist ganz gut gewählt. Dafür sollten man besser darauf achten, keine Stackspeicher-Fresser zu programmieren. Eventuell nach Alternativen suchen oder native PHP String-Funktionen kombinieren. Nun, zurück zum Thema. Je nach dem, ist es auch möglich den Wert auch auf zehn Millionen zu setzen, aber nicht bevor man sich mit dem Teamkollegen oder Administrator konsultiert hat. Man sollte eben nicht den ganzen Speicher für PCRE Operationen reservieren.

ini_set('pcre.recursion_limit',10000000);
ini_set('pcre.backtrack_limit',10000000);

Lösungsansatz 2:
Ich gebe es zu, der erste Lösungsansatz war nicht der beste. Wenn man die Einstellungen zur Scriptlaufzeit ändert, riskiert man dadurch einen PHP Absturz, wenn der reguläre Ausdruck doch zu viel Speicher verbraucht. Eleganter ist es, wenn man es durch eine Fehlerprüfung absichert. Ganz besonders wenn man mit komplexen regulären Ausdrücken arbeitet oder reguläre Ausdrücke auf große Datenmengen anwendet. Der Abbruch im Script passiert also wenn ein regulärer Ausdruck das gesetzte Limit überschreitet. PHP liefert keine Fehlermeldung indem Fall, und der reguläre Ausdruck liefert NULL als Ergebnis. Dabei muss man unmittelbar nach der PCRE-Funktion mittels preg_last_error() prüfen, welcher Fehler aufgetreten ist. Dabei wird ein integer-Wert zurück geliefert. Die Werte sind ebenfalls als PHP-Konstanten vertreten.

< ?php

preg_match('/(?:\D+|<\d+>)*[!?]/', '...big text....');

if (preg_last_error() == PREG_BACKTRACK_LIMIT_ERROR
	|| preg_last_error() == PREG_RECURSION_LIMIT_ERROR)
{
    throw new Exception('Regular Expression limit was exhausted!');
}

?>

Im den oberen Beispiel, wirft PHP eine Exception wenn der Speicher-Limit erreicht ist. So kann man besser den Fehler abfangen um entsprechend reagieren. Das Problem und die Folgefehler wurden somit behoben.

4 thoughts on “PCRE – der Stackspeicher Fresser

    1. @Oliver:
      Der Grund für die Falsche Darstellung der Zeichen ist der WP-Cache. Wenn der abgelaufen ist, schafft er es nicht immer die Seite neu dynamisch zu laden. Du musst dein Browser-Cache leeren und die Seite neu laden. Wenn ich mal Zeit habe, schaue ich mir das WP-Cache Plugin genauer an.

  1. Ich habe gar keinen Browsercache. :-)

    Aber normal ist das nicht, weil ich hab es ausprobiert, auf ISO funktionieren die Umlaute und die waren auch nur im Artikel selbst falsch.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.