PHP Conference Nagoya 2025

Rendimiento

Ciertos elementos que pueden aparecer en patrones son más eficientes que otros. Es más eficiente usar una clase carácter como [aeiou] en vez de un conjunto de alternativas como (a|e|i|o|u). En general, la construcción más simple que proporciona el comportamiento requerido es normalmente la más eficiente. El libro de Jeffrey Friedl contiene muchas discusiones sobre la optimización de expresiones regulares para un rendimiento eficiente.

Cuando un patrón comienza con .* y la opción PCRE_DOTALL está establecida, el patrón es anclado implícitamente por PCRE, ya que sólo puede coincidir con el inicio de una cadena objetivo. Sin embargo, si PCRE_DOTALL no está establecido, PCRE no puede realizar esta optimización, ya que el metacarácter . no coincide entonces con una nueva línea, y si la cadena objetivo contine nuevas líneas, el patrón puede coincidir con el carácter inmediatemente siguiente a ellas en vez de con el inicio absoluto. Por ejemplo, el patrón (.*) segundo coincide con el sujeto "primero\ny segundo" (donde \n significa un carácter de nueva línea), siendo "y" la primera subcadena capturada. Para llevar a cabo esto, PCRE ha de re-intentar la comparación comenzando después de cada nueva línea del sujeto.

Si está usando un patrón similar con cadenas objetivo que no contienen nuevas líneas, el mejor rendimiento se obtiene estableciendo PCRE_DOTALL, o comenzando el patrón con ^.* para indicar el anclado explícito. Esto salva a PCRE de tener que escanear el sujeto buscando una nueva línea desde donde reiniciar.

Tenga cuidado con los patrones que contienen repeticiones anidadas indefinidas. Éstos pueden tomar mucho tiempo al ejecutarse cuando se aplican a cadenas que no coinciden. Considere el fragmento de patrón (a+)*

Esto puede coincidir con "aaaa" de 33 maneras diferentes, y este número se incrementa muy rápidamente mientras la cadena se hace más larga. (La repetición * puede coincidir 0, 1, 2, 3, o 4 veces, y para cada uno de estos casos distintos de 0, las repeticiones de + pueden coincidir diferente número de veces.) Cuando el resto del patrón es tal que la comparación completa falla, PCRE tiene como principio el intentar cada posible variación, y esto puede tomar un tiempo extremadamente largo.

Una optimización toma algunos de los casos más simples como (a+)*b donde sigue un carácter literal. Antes de emprender el procedimiento de comparación estándar, PCRE verifica que hay una "b" posterior en la cadena objetivo, y si no la hay, falla la comparación inmediatamente. Sin embargo, cuando no hay un literal siguiente, no se puede usar esta optimización. Puede ver la diferencia comparando el comportamiento de (a+)*\d con el patrón anterior. El primero otorga un fallo casi instantáneamente cuando se aplica a una línea llena de caracteres "a", mientras que el segundo toma un tiempo apreciable con cadenas con más de de 20 caracteres.

add a note

User Contributed Notes 1 note

up
1
arthur200126 at gmail dot com
9 months ago
> Beware of patterns that contain nested indefinite repeats. These can take a long time to run when applied to a string that does not match.

To say that it takes a "long time" is an understatement: the time taken would be exponential, specifically 2^n, where n is the number of "a" characters. This behavior could lead to a "regular expression denial of service" (ReDoS) if you run such a expression on user-provided input.

To not be hit by ReDoS, do one (or maybe more than one) of the three things:

* Write your expression so that it is not vulnerable. https://www.regular-expressions.info/redos.html is a good resource (both the "atomic" and "possessive" options are available in PHP/PCRE). Use a "ReDoS detector" or "regex linter" if your eyeballs can't catch all the issues.
* Set up some limits for preg_match. Use `ini_set(...)` on the values mentioned on https://www.php.net/manual/en/pcre.configuration.php. Reducing the limits might cause regexes to fail, but that is usually better than stalling your whole server.
* Use a different regex implementation. There used to be an RE2 extension; not any more!
To Top