PHP Conference Nagoya 2025

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.

adicione uma nota

Notas Enviadas por Usuários (em inglês) 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