Performance
Certaines séquences de recherches sont plus efficaces que d'autres.
Ainsi, il est plus efficace d'utiliser une classe de caractères
telle que [aeiou]
plutôt qu'une alternative
(a|e|i|o|u)
.
En général, le masque le plus simple, qui permette
la recherche désirée est le plus efficace. Le livre
de Jeffrey Friedl's contient de nombreuses études à
propos de l'optimisation des expressions régulières.
Lorsqu'un masque commence par.* et que l'option
PCRE_DOTALL est
activée, le masque est implicitement ancré par PCRE,
étant donné qu'il ne peut que rechercher au début
de la chaîne. Cependant, si l'option
PCRE_DOTALL n'est pas
activée, PCRE ne peut faire aucune optimisation, car le
métacaractère point ".
"
ne remplace pas une nouvelle ligne, et si la chaîne
sujet contient des nouvelles lignes, le masque peut trouver une
solution qui serait située juste après une
de ces nouvelles lignes, et non pas seulement au début
de la chaîne sujet. Par exemple, le masque,
(.*)second
acceptera la chaîne "premier \net second
"
(avec "\n
" qui remplace la nouvelle ligne),
et la première chaîne capturée sera "et
".
Afin d'effectuer la recherche, PCRE va essayer d'appliquer le masque
à partir de chaque début de ligne.
Si vous utilisez un tel masque avec des chaînes qui ne contiennent
pas de caractères de nouvelle ligne, les meilleures performances
seront atteintes avec l'option
PCRE_DOTALL, ou en ancrant le
masque avec ^.*
. Cela évite à PCRE
de scanner toute la chaîne pour rechercher un caractère
de nouvelle ligne et recommencer la recherche.
Attention aux masques qui contiennent des quantificateurs infinis
imbriqués. Ils peuvent demander un temps de calcul très
long, lorsque appliqués à une chaîne qui ne
correspond pas à ce masque. Par exemple,
(a+)*
Ce masque peut accepter "aaaa
" de 33 manières
différentes, et ce nombre croit rapidement avec la taille
de la chaîne (le quantificateur *
peut prendre
les valeurs de 0, 1, 2, 3, ou 4, et pour chaque cas non nul, le
quantificateur +
peut prendre différentes
valeurs). Lorsque le reste de la chaîne est tel que l'on s'achemine
vers un échec, PCRE doit en principe vérifier
toutes les possibilités, et cela prend un temps
extrêmement long.
Un optimiseur repère les cas les plus simples, tel que
(a+)*b
où un caractère simple suit les quantificateurs.
Avant de partir dans les procédures standards de recherche, PCRE
s'assure qu'il y a au moins un "b
" dans la
chaîne, et si ce n'est pas le cas, l'échec est
annoncé immédiatement. Sinon, il n'y a pas
d'optimisation dans la recherche. Vous pouvez voir la
différence de comportement avec le masque suivant :
(a+)*\d
.
Le premier retourne un échec quasi-immédiatement, s'il est appliqué à
une ligne de "a
", alors que le second masque
prend un temps significatif pour une chaîne de plus de
20 caractères.
arthur200126 at gmail dot com ¶8 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!