Desempenho
Certos itens que podem aparecer nas expressões são mais eficientes
que outros. É mais eficiente usar uma classe de caracteres
como [aeiou] do que um conjunto de alternativas como (a|e|i|o|u).
Em geral, a construção mais simples que proporciona o
comportamento requerido é normalmente a mais eficiente. O livro
de Jeffrey Friedl contém muita discussão sobre como otimizar
expressões regulares para um desempenho eficiente.
Quando uma expressão começa com .* e a opção PCRE_DOTALL está
definida, a expressão é implicitamente ancorada pelo PCRE, pois ela
pode corresponder apenas ao início de um string de entrada. Porém, se
PCRE_DOTALL
não estiver definida, o PCRE não poderá fazer esta otimização,
pois o metacaractere . não corresponderá a uma nova linha
e, se a string de entrada contiver novas linhas, a expressão poderá
corresponder a partir do caractere imediatamente após um deles,
em vez de desde o início. Por exemplo, a expressão
(.*) second
corresponde à string "first\nand second" (onde \n representa
um caractere de nova linha) com a primeira substring capturada igual a
"and". Para fazer isso, o PCRE precisa tentar a correspondência novamente
iniciando após cada caractere de nova linha na string.
Se uma expressão como essa estiver sendo usada com uma string de entrada que
não contêm novas linhas, o melhor desempenho será obtido
definindo PCRE_DOTALL
ou iniciando a expressão com ^.* para
indicar ancoragem explícita. Isso evita que o PCRE tenha que
varrer a string em busca de uma nova linha para reiniciar.
Cuidado com expressões que contêm repetições indefinidas aninhadas.
Elas podem levar muito tempo para serem executadas quando aplicados a uma string
que não corresponde. Considere o fragmento de expressão
(a+)*
Isso pode corresponder a "aaaa" de 33 maneiras diferentes, e esse número
aumenta muito rapidamente à medida que a string fica mais longa. (A repetição
* pode corresponder a 0, 1, 2, 3 ou 4 vezes e, para cada um
desses casos diferentes de 0, as repetições + podem corresponder a diferentes
números de vezes.) Quando o restante da expressão é tal
que toda a correspondência irá falhar, o PCRE tem que, em princípio,
tentar todas as variações possíveis, e isso pode levar
muito tempo.
Uma otimização captura alguns dos casos mais simples
como
(a+)*b
,
onde um carectere literal aparece na sequência. Antes de embarcar no
procedimento padrão de correspondência, o PCRE verifica se existe um "b"
mais à frente na string de entrada, e se não houver, ele falha
a correspondência imediatamente. Porém, quando não há um caractere literal
na sequência, esta otimização não pode ser usada. Pode-se ver a
deferença comparando o comportamento de
(a+)*\d
com a expressão mais acima. A anterior falha quase
instantaneamente quando aplicada a uma linha inteira de caracteres "a",
enquanto que a última leva um tempo apreciável com strings
mais longas que aproximadamente 20 caracteres.
arthur200126 at gmail dot com ¶10 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!