Rendimiento
Ciertos elementos que pueden aparecer en patrones son más eficientes
que otros. Es más eficiente usar una clase de caracteres como [aeiou] que un conjunto de alternativas como (a|e|i|o|u).
En general, la construcción más simple que proporciona el
comportamiento requerido suele ser la más eficiente. El libro de Jeffrey
Friedl contiene mucha discusión sobre cómo optimizar expresiones regulares para un rendimiento eficiente.
Cuando un patrón comienza con .* y la opción PCRE_DOTALL está
establecida, el patrón está implícitamente anclado por PCRE, ya que
solo puede coincidir al inicio de una cadena de sujeto. Sin embargo, si
PCRE_DOTALL no está establecida, PCRE no puede hacer esta optimización,
porque el metacarácter . no coincide entonces con un salto de línea,
y si la cadena de sujeto contiene saltos de línea, el patrón puede
coincidir desde el carácter inmediatamente después de uno de ellos
en lugar de desde el principio. Por ejemplo, el patrón
(.*) second
coincide con la cadena de sujeto "first\nand second" (donde \n representa
un carácter de nueva línea) con la primera subcadena capturada siendo
"and". Para hacer esto, PCRE tiene que volver a intentar la coincidencia
comenzando después de cada salto de línea en el sujeto.
Si está utilizando un patrón como este con cadenas de sujeto que no contienen saltos de línea, el mejor rendimiento se obtiene estableciendo PCRE_DOTALL,
o comenzando el patrón con ^.* para
indicar anclaje explícito. Esto ahorra a PCRE tener que escanear a lo largo del sujeto buscando un salto de línea para reiniciar.
Tenga cuidado con los patrones que contienen repeticiones indefinidas anidadas.
Estos pueden tardar mucho tiempo en ejecutarse cuando se aplican a una cadena
que no coincide. Considere el fragmento de patrón
(a+)*
Esto puede coincidir con "aaaa" de 33 maneras diferentes, y este número
aumenta muy rápidamente a medida que la cadena se alarga. (La repetición *
puede coincidir 0, 1, 2, 3 o 4 veces, y para cada uno de
esos casos excepto 0, las repeticiones + pueden coincidir con diferentes
números de veces.) Cuando el resto del patrón es tal que
toda la coincidencia va a fallar, PCRE en principio
tiene que probar cada variación posible, y esto puede tomar un
tiempo extremadamente largo.
Una optimización atrapa algunos de los casos más simples como
(a+)*b
donde un carácter literal sigue. Antes de embarcarse en el
procedimiento de coincidencia estándar, PCRE verifica que hay una "b"
más adelante en el sujeto, y si no la hay, falla la coincidencia inmediatamente. Sin embargo, cuando no hay un carácter literal siguiente, esta optimización no puede usarse. Puede ver la diferencia comparando el comportamiento de
(a+)*\d
con el patrón anterior. El primero da un fallo casi
instantáneamente cuando se aplica a una línea completa de caracteres "a",
mientras que el segundo tarda un tiempo apreciable con cadenas
más largas que unas 20 caracteres.
arthur200126 at gmail dot com ¶1 year 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!