php - regular - preg_match()
RegExp en la funciĆ³n preg_match que devuelve el error del navegador (4)
La siguiente función se rompe con la expresión regular que he proporcionado en la variable $ pattern. Si cambio la expresión regular, estoy bien, entonces creo que ese es el problema. Sin embargo, no veo el problema y no recibo un error estándar de PHP aunque estén activados.
function parseAPIResults($results){
//Takes results from getAPIResults, returns array.
$pattern = ''//[(.|/n)+/]/'';
$resultsArray = preg_match($pattern, $results, $matches);
}
Firefox 6: la conexión se reinició
Chrome 14: Error 101 (net :: ERR_CONNECTION_RESET): se restableció la conexión.
IE 8: Internet Explorer no puede mostrar la página web
ACTUALIZAR:
Apache / PHP puede estar fallando. Aquí está el registro de errores de Apache cuando ejecuto el script:
[Sáb 01 oct 11:41:40 2011] [aviso] Proceso padre: hijo salido con estado 255 - Reiniciar.
[Sáb. 01 de octubre 11:41:40 2011] [aviso] Apache / 2.2.11 (Win32) PHP / 5.3.0 configurado - reanudación de las operaciones normales
Ejecutando WAMP 2.0 en Windows 7.
preg_match devuelve el número de coincidencias encontradas para el patrón. Cuando tiene una coincidencia, está causando un error fatal en php ( print_r(1)
, por ejemplo, causa el error). print_r (0) (para cuando cambia el patrón y no tiene coincidencias) no lo hace y simplemente imprime 0.
Desea print_r($matches)
Como un aparte, su patrón no se escapó correctamente. El uso de comillas dobles significa que debe escapar de las barras diagonales delante de sus corchetes.
Pregunta simple. Respuesta compleja!
Sí, esta clase de expresiones regulares repetidamente (y silenciosamente) bloqueará Apache / PHP con una falla de segmentación no controlada debido a un desbordamiento de la pila.
Fondo:
La familia PHP preg_*
de funciones regex usa la potente biblioteca PCRE de Philip Hazel. Con esta biblioteca, hay una cierta clase de expresión regular que requiere muchas llamadas recursivas a su función interna de match()
y esto consume mucho espacio de pila, (y el espacio de pila utilizado es directamente proporcional al tamaño de la secuencia del sujeto siendo emparejado). Por lo tanto, si la cadena del sujeto es demasiado larga, se producirá un desbordamiento de la pila y la correspondiente falla de segmentación. Este comportamiento se describe en la documentación de PCRE al final en la sección titulada: pcrestack .
PHP Bug 1: PHP establece: pcre.recursion_limit
too large.
La documentación de PCRE describe cómo evitar un error de segmentación de desbordamiento de pila limitando la profundidad de recursión a un valor seguro aproximadamente igual al tamaño de la pila de la aplicación vinculada dividido por 500. Cuando la profundidad de recursión está debidamente limitada como se recomienda, la biblioteca no genera un desbordamiento de pila y en su lugar sale con gracia con un código de error. En PHP, esta profundidad máxima de recursión se especifica con la variable de configuración pcre.recursion_limit
y (desafortunadamente) el valor predeterminado se establece en 100,000. ¡Este valor es DEMASIADO GRANDE! Aquí hay una tabla de valores seguros de pcre.recursion_limit
para una variedad de tamaños de pila ejecutables:
Stacksize pcre.recursion_limit
64 MB 134217
32 MB 67108
16 MB 33554
8 MB 16777
4 MB 8388
2 MB 4194
1 MB 2097
512 KB 1048
256 KB 524
Por lo tanto, para la compilación de Win32 del servidor web Apache ( httpd.exe
), que tiene un tamaño de pila (relativamente pequeño) de 256 KB, el valor correcto de pcre.recursion_limit
debe establecerse en 524. Esto se puede lograr con la siguiente línea de Código PHP:
ini_set("pcre.recursion_limit", "524"); // PHP default is 100,000.
Cuando este código se agrega al script PHP, el desbordamiento de la pila NO ocurre, sino que genera un código de error significativo. Es decir, ¡ DEBERÍA generar un código de error! (Pero desafortunadamente, debido a otro error de PHP, preg_match()
no lo hace).
Error 2 de PHP: preg_match()
no devuelve FALSE en caso de error.
La documentación de PHP para preg_match()
dice que devuelve FALSE en caso de error. Desafortunadamente, las versiones de PHP 5.3.3 y siguientes tienen un error ( # 52732 ) donde preg_match()
NO devuelve FALSE
en caso de error (en su lugar devuelve int(0)
, que es el mismo valor devuelto en el caso de una falta de coincidencia) . Este error se corrigió en PHP versión 5.3.4.
Solución:
Asumiendo que continuará usando WAMP 2.0 (con PHP 5.3.0), la solución debe tener en cuenta los dos errores anteriores. Esto es lo que recomendaría:
- Necesidad de reducir
pcre.recursion_limit
a un valor seguro: 524. - Necesidad de comprobar explícitamente un error PCRE siempre que
preg_match()
devuelva algo que no seaint(1)
. - Si
preg_match()
devuelveint(1)
, la coincidencia fue exitosa. - Si
preg_match()
devuelveint(0)
, la coincidencia no fue exitosa o hubo un error.
Aquí hay una versión modificada de su secuencia de comandos (diseñada para ejecutarse desde la línea de comandos) que determina la longitud de la secuencia del sujeto que da como resultado el error de límite de recursión:
<?php
// This test script is designed to be run from the command line.
// It measures the subject string length that results in a
// PREG_RECURSION_LIMIT_ERROR error in the preg_match() function.
echo("Entering TEST.PHP.../n");
// Set and display pcre.recursion_limit. (set to stacksize / 500).
// Under Win32 httpd.exe has a stack = 256KB and 8MB for php.exe.
//ini_set("pcre.recursion_limit", "524"); // Stacksize = 256KB.
ini_set("pcre.recursion_limit", "16777"); // Stacksize = 8MB.
echo(sprintf("PCRE pcre.recursion_limit is set to %s/n",
ini_get("pcre.recursion_limit")));
function parseAPIResults($results){
$pattern = "//[(.|/n)+/]/";
$resultsArray = preg_match($pattern, $results, $matches);
if ($resultsArray === 1) {
$msg = ''Successful match.'';
} else {
// Either an unsuccessful match, or a PCRE error occurred.
$pcre_err = preg_last_error(); // PHP 5.2 and above.
if ($pcre_err === PREG_NO_ERROR) {
$msg = ''Successful non-match.'';
} else {
// preg_match error!
switch ($pcre_err) {
case PREG_INTERNAL_ERROR:
$msg = ''PREG_INTERNAL_ERROR'';
break;
case PREG_BACKTRACK_LIMIT_ERROR:
$msg = ''PREG_BACKTRACK_LIMIT_ERROR'';
break;
case PREG_RECURSION_LIMIT_ERROR:
$msg = ''PREG_RECURSION_LIMIT_ERROR'';
break;
case PREG_BAD_UTF8_ERROR:
$msg = ''PREG_BAD_UTF8_ERROR'';
break;
case PREG_BAD_UTF8_OFFSET_ERROR:
$msg = ''PREG_BAD_UTF8_OFFSET_ERROR'';
break;
default:
$msg = ''Unrecognized PREG error'';
break;
}
}
}
return($msg);
}
// Build a matching test string of increasing size.
function buildTestString() {
static $content = "";
$content .= "A";
return ''[''. $content .'']'';
}
// Find subject string length that results in error.
for (;;) { // Infinite loop. Break out.
$str = buildTestString();
$msg = parseAPIResults($str);
printf("Length =%10d/r", strlen($str));
if ($msg !== ''Successful match.'') break;
}
echo(sprintf("/nPCRE_ERROR = /"%s/" at subject string length = %d/n",
$msg, strlen($str)));
echo("Exiting TEST.PHP...");
?>
Cuando ejecuta este script, proporciona una lectura continua de la longitud actual de la cadena del sujeto. Si pcre.recursion_limit
se deja en su valor predeterminado demasiado alto, esto le permite medir la longitud de la cadena que hace que el ejecutable se bloquee.
Comentarios:
- Antes de investigar la respuesta a esta pregunta, no sabía acerca de la falla de PHP donde
preg_match()
no devuelveFALSE
cuando ocurre un error en la biblioteca PCRE. ¡Este error ciertamente cuestiona MUCHOS códigos que usanpreg_match
! (Definitivamente voy a hacer un inventario de mi propio código PHP). - En Windows, el ejecutable del servidor web Apache (
httpd.exe
) está construido con un stack de 256 KB. El ejecutable de la línea de comando de PHP (php.exe
) está construido con un stack de 8MB. El valor seguro parapcre.recursion_limit
debe establecerse de acuerdo con el ejecutable bajo el que se ejecuta el script (524 y 16777, respectivamente). - En los sistemas * nix, el servidor web Apache y los ejecutables de la línea de comandos se crean normalmente con un stack de 8 MB, por lo que este problema no se encuentra con tanta frecuencia.
- Los desarrolladores de PHP deben establecer el valor predeterminado de
pcre.recursion_limit
en un valor seguro. - Los desarrolladores de PHP deben aplicar la
preg_match()
errorespreg_match()
a PHP versión 5.2. - El tamaño compacto de un ejecutable de Windows se puede modificar manualmente utilizando el programa gratuito CFF Explorer . Puede usar este programa para aumentar el tamaño de pila del
httpd.exe
ejecutablehttpd.exe
Apache. (Esto funciona bajo XP pero Vista y Win7 pueden quejarse).
Tuve el mismo problema. Muchas gracias por la respuesta publicada por ridgerunner.
Aunque es útil saber por qué php se cuelga, para mí esto no soluciona realmente el problema. Para resolver el problema, tengo que ajustar mi expresión regular para poder guardar la memoria, de modo que php no se bloquee por más tiempo.
Entonces la pregunta es cómo cambiar la expresión regular. El enlace al manual de PCRE publicado arriba ya describe una solución para un ejemplo de expresión regular que es bastante similar al suyo.
Entonces, ¿cómo arreglar tu expresión regular? Primero, dices que quieres hacer coincidir "a. O newline". Tenga en cuenta que "." es un personaje especial en una expresión regular que no solo coincide con un punto, sino con cualquier carácter, por lo que debes escapar de eso. (Espero no haberlo equivocado aquí y esto fue intencionado).
$pattern = ''//[(/.|/n)+/]/'';
A continuación, podemos copiar el cuantificador dentro de los corchetes:
$pattern = ''//[(/.+|/n+)+/]/'';
Esto no cambia el significado de la expresión. Ahora usamos cuantificadores posesivos en lugar de los normales:
$pattern = ''//[(/.++|/n++)++/]/'';
Por lo tanto, esto debería tener el mismo significado que tu expresión regular original, pero trabaja en php sin colgarlo. ¿Por qué? Los cuantificadores posesivos "devoran" a los personajes y no permiten retroceder. Por lo tanto, PCRE no tiene que usar la recursión y la pila no se desbordará. Usarlos dentro de los corchetes parece ser una buena idea ya que no necesitamos la cuantificación de la alternativa tan a menudo.
En resumen, la mejor práctica parece ser:
- usar cuantificadores posesivos cuando sea posible. Esto significa: ++, * +,? + {} + En lugar de +, *,?, {}.
- mover cuantificadores dentro de corchetes alternativos cuando sea posible
Siguiendo estas reglas, pude solucionar mi problema y espero que esto ayude a otros.
Tuve el mismo problema y necesitas cambiar el patrón a algo así como
$pattern = ''|/your pattern/|s'';
La ''s'' en el extremo básicamente significa tratar la cuerda como una sola línea.