PHP para principiantes. Manejo de errores

imagen

Solo el que no hace nada no comete errores, y somos un ejemplo de esto: nos sentamos y trabajamos incansablemente, leemos el Habr :)

En este artículo, dirigiré mi historia sobre errores en PHP y cómo frenarlos.

Errores


Variedades en la familia del error


Antes de domesticar errores, recomendaría estudiar cada especie y prestar atención por separado a los representantes más destacados.

Para evitar que un solo error pase desapercibido, debe habilitar el seguimiento de todos los errores utilizando la función error_reporting () y la directiva display_errors para habilitar su visualización:

<?php error_reporting(E_ALL); ini_set('display_errors', 1); 

Errores fatales


Los tipos de errores más formidables son fatales, pueden ocurrir tanto durante la compilación como durante el trabajo del analizador o script PHP, mientras el script se interrumpe.

E_PARSE

Este error aparece cuando comete un error de sintaxis grave y el intérprete PHP no entiende lo que quiere de él, por ejemplo, si no cerró el rizado o el paréntesis:

 <?php /** * Parse error: syntax error, unexpected end of file */ { 

O escribieron en un lenguaje incomprensible:

 <?php /** * Parse error: syntax error, unexpected '...' (T_STRING) */     

También aparecen corchetes adicionales, y no tan importantes redondos o rizados:

 <?php /** * Parse error: syntax error, unexpected '}' */ } 

Observo un punto importante: el código del archivo en el que cometió el error de análisis no se ejecutará, por lo tanto, si intenta activar la visualización de errores en el mismo archivo donde ocurrió el error del analizador, esto no funcionará:

 <?php //     error_reporting(E_ALL); ini_set('display_errors', 1); // ..     

E_ERROR

Este error aparece cuando PHP entendió lo que desea, pero esto no funcionó por varias razones. Este error también interrumpe la ejecución del script y el código funcionará antes de que aparezca el error:

No se encontró el complemento:

 /** * Fatal error: require_once(): Failed opening required 'not-exists.php' * (include_path='.:/usr/share/php:/usr/share/pear') */ require_once 'not-exists.php'; 

Se lanzó una excepción (qué tipo de bestia, te contaré un poco más tarde), pero no se procesó:

 /** * Fatal error: Uncaught exception 'Exception' */ throw new Exception(); 

Al intentar llamar a un método de clase inexistente:

 /** * Fatal error: Call to undefined method stdClass::notExists() */ $stdClass = new stdClass(); $stdClass->notExists(); 

Falta de memoria libre (m√°s de lo prescrito en la directiva memory_limit ) o algo similar:

 /** * Fatal Error: Allowed Memory Size */ $arr = array(); while (true) { $arr[] = str_pad(' ', 1024); } 

Es muy com√ļn al leer o descargar archivos grandes, as√≠ que tenga cuidado con el problema del consumo de memoria.
Llamada de función recursiva. En este ejemplo, terminó en la iteración 256, porque está escrito en la configuración de xdebug (sí, este error puede aparecer en este formulario solo cuando la extensión xdebug está habilitada):

 /** * Fatal error: Maximum function nesting level of '256' reached, aborting! */ function deep() { deep(); } deep(); 

No fatal


Esta vista no interrumpe la ejecución del script, pero es el probador quien generalmente los encuentra. Son estos errores los que causan más problemas a los desarrolladores novatos.

E_ADVERTENCIA

A menudo ocurre cuando conecta un archivo usando include , pero no aparece en el servidor o si cometió un error al indicar la ruta al archivo:

 /** * Warning: include_once(): Failed opening 'not-exists.php' for inclusion */ include_once 'not-exists.php'; 

Ocurre si usa el tipo incorrecto de argumentos al llamar a funciones:

 /** * Warning: join(): Invalid arguments passed */ join('string', 'string'); 

Hay muchos de ellos, y enumerar todo no tiene sentido ...

E_NOTICE

Estos son los errores más comunes, además, hay ventiladores que apagan la salida de errores y los remachan todo el día. Hay una serie de errores triviales.

Al acceder a una variable indefinida:

 /** * Notice: Undefined variable: a */ echo $a; 

Al acceder a un elemento de matriz inexistente:

 /** * Notice: Undefined index: a */ $b = []; $b['a']; 

Al acceder a una constante inexistente:

 /** * Notice: Use of undefined constant UNKNOWN_CONSTANT - assumed 'UNKNOWN_CONSTANT' */ echo UNKNOWN_CONSTANT; 

Cuando los tipos de datos no se convierten:

 /** * Notice: Array to string conversion */ echo array(); 

Para evitar tales errores, tenga cuidado, y si el IDE le dice algo, no lo ignore:

PHP E_NOTICE en PHPStorm

E_STRICT

Estos son errores que le ense√Īar√°n a escribir el c√≥digo correctamente para que no se averg√ľence, especialmente porque el IDE le muestra inmediatamente estos errores. Por ejemplo, si llam√≥ a un m√©todo no est√°tico como est√°tico, el c√≥digo funcionar√°, pero de alguna manera es incorrecto, y pueden producirse errores graves si se cambia el m√©todo de clase en el futuro, y aparece una apelaci√≥n a $this :

 /** * Strict standards: Non-static method Strict::test() should not be called statically */ class Strict { public function test() { echo "Test"; } } Strict::test(); 


Este tipo de error es relevante para PHP versión 5.6, y casi todos fueron eliminados
7 partidos. Lea más en el RFC relevante . Si alguien sabe dónde quedaron estos errores, escriba los comentarios


E_DEPRECATED

Entonces PHP jurará si usa funciones obsoletas (es decir, aquellas que están marcadas como obsoletas, y no estarán en la próxima versión principal):

 /** * Deprecated: Function split() is deprecated */ //  ,   PHP 7.0 //    PHP 5.3 split(',', 'a,b'); 

En mi editor, se tachar√°n funciones similares:

PHP E_DEPRECATED en PHPStorm

Personalizado


Este tipo, que el desarrollador de código mismo "engendra", no los he visto en mucho tiempo, y no recomiendo que abusen de ellos:

  • E_USER_ERROR - error cr√≠tico
  • E_USER_WARNING : no es un error cr√≠tico
  • E_USER_NOTICE : mensajes que no son errores

Por separado, vale la pena se√Īalar E_USER_DEPRECATED : este tipo todav√≠a se usa con mucha frecuencia para recordarle al programador que el m√©todo o la funci√≥n est√°n desactualizados y es hora de volver a escribir el c√≥digo sin usarlo. La funci√≥n trigger_error () se usa para crear este y otros errores similares:

 /** * @deprecated Deprecated since version 1.2, to be removed in 2.0 */ function generateToken() { trigger_error('Function `generateToken` is deprecated, use class `Token` instead', E_USER_DEPRECATED); // ... // code ... // ... } 

Ahora que se ha familiarizado con la mayoría de los tipos y tipos de errores, es hora de expresar una breve explicación sobre el funcionamiento de la directiva display_errors :

  • si display_errors = on , entonces, en caso de error, el navegador recibir√° html con el texto de error y el c√≥digo 200
  • si display_errors = off , para errores fatales el c√≥digo de respuesta ser√° 500 y el resultado no ser√° devuelto al usuario, para otros errores: el c√≥digo no funcionar√° correctamente, pero no se lo dir√° a nadie


Domar


Hay 3 funciones para trabajar con errores en PHP:

  • set_error_handler () : establece un controlador para errores que no interrumpen el script (es decir, para errores no fatales)
  • error_get_last () - obtiene informaci√≥n sobre el √ļltimo error
  • register_shutdown_function () : registra un controlador que se iniciar√° cuando finalice el script. Esta funci√≥n no se aplica directamente a los controladores de errores, pero a menudo se usa para este prop√≥sito.

Ahora, algunos detalles sobre el manejo de errores usando set_error_handler() , como argumentos, esta función toma el nombre de la función que se le asignará la misión de manejo de errores y los tipos de errores que serán monitoreados. Un controlador de errores también puede ser un método de clase o una función anónima, lo principal es que toma la siguiente lista de argumentos:

  • $errno : el primer argumento contiene el tipo de error como un entero
  • $errstr : el segundo argumento contiene un mensaje de error
  • $errfile : un tercer argumento opcional contiene el nombre del archivo en el que ocurri√≥ el error
  • $errline : un cuarto argumento opcional contiene el n√ļmero de l√≠nea en el que ocurri√≥ el error
  • $errcontext : el quinto argumento opcional contiene una matriz de todas las variables existentes en el √°mbito donde ocurri√≥ el error

Si el controlador devuelve true , entonces el error se considerar√° procesado y el script continuar√° ejecut√°ndose; de ‚Äč‚Äčlo contrario, se llamar√° a un controlador est√°ndar que registra el error y, seg√ļn su tipo, continuar√° ejecutando el script o complet√°ndolo. Aqu√≠ hay un ejemplo de controlador:

 <?php //    ,  E_NOTICE error_reporting(E_ALL & ~E_NOTICE); ini_set('display_errors', 1); //    function myHandler($level, $message, $file, $line, $context) { //         switch ($level) { case E_WARNING: $type = 'Warning'; break; case E_NOTICE: $type = 'Notice'; break; default; //   E_WARNING   E_NOTICE //      //      PHP return false; } //    echo "<h2>$type: $message</h2>"; echo "<p><strong>File</strong>: $file:$line</p>"; echo "<p><strong>Context</strong>: $". join(', $', array_keys($context))."</p>"; // ,    ,      return true; } //   ,         set_error_handler('myHandler', E_ALL); 

No podrá asignar más de una función para manejar errores, aunque me gustaría registrar mi propio controlador para cada tipo de error, pero no, escriba un controlador y describa la lógica de visualización completa para cada tipo directamente en él.
Hay un problema importante con el controlador que está escrito anteriormente: no detecta errores fatales y, con dichos errores, en lugar del sitio, los usuarios verán solo una página en blanco o, lo que es peor, un mensaje de error. Para evitar este escenario, debe usar la función register_shutdown_function () y usarla para registrar una función que siempre se ejecutará al final del script:

 function shutdown() { echo '    '; } register_shutdown_function('shutdown'); 

¡Esta función siempre funcionará!

Pero volviendo a los errores, para rastrear la aparici√≥n del error en el c√≥digo de error, utilizamos la funci√≥n error_get_last () , con su ayuda puede obtener informaci√≥n sobre el √ļltimo error detectado, y dado que los errores fatales interrumpen la ejecuci√≥n del c√≥digo, siempre desempe√Īar√°n el papel de "√ļltimo":

 function shutdown() { $error = error_get_last(); if ( //       is_array($error) && //       in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR]) ) { //    (       ) while (ob_get_level()) { ob_end_clean(); } //    echo "    ,  "; } } register_shutdown_function('shutdown'); 

Me gustaría llamar la atención sobre el hecho de que este código incluso se produce para el manejo de errores, e incluso puede encontrarlo, pero ha perdido relevancia desde la séptima versión de PHP. Lo que vino a reemplazar lo contaré un poco más tarde.
Tarea
Complemente el controlador de errores fatales con la salida del código fuente del archivo donde se cometió el error, y también agregue el resaltado de sintaxis del código de salida.

Sobre la gula


Hagamos una prueba simple y descubramos cu√°ntos recursos preciosos come el error m√°s trivial:

 /** *      */ //     $time= microtime(true); define('AAA', 'AAA'); $arr = []; for ($i = 0; $i < 10000; $i++) { $arr[AAA] = $i; } printf('%f seconds <br/>', microtime(true) - $time); 

Como resultado de ejecutar este script, obtuve este resultado:

 0.002867 seconds 

Ahora agregue el error en el bucle:

 /** *     */ //     $time= microtime(true); $arr = []; for ($i = 0; $i < 10000; $i++) { $arr[BBB] = $i; //   ,      } printf('%f seconds <br/>', microtime(true) - $time); 

El resultado es peor, y un orden de magnitud (¡incluso dos órdenes de magnitud!):

 0.263645 seconds 

La conclusión es clara: los errores en el código conducen a una excesiva glotonería de los scripts, ¡así que encienda la visualización de todos los errores durante el desarrollo y las pruebas de la aplicación!
Las pruebas se llevaron a cabo en varias versiones de PHP, y en todas partes la diferencia es decenas de veces, así que esta es otra razón para corregir todos los errores en el código

¬ŅD√≥nde est√° enterrado el perro?


PHP tiene un símbolo especial "@": un operador de supresión de errores, se utiliza para no escribir el manejo de errores, pero se basa en el comportamiento correcto de PHP en cuyo caso:

 <?php echo @UNKNOWN_CONSTANT; 

En este caso, a√ļn se llamar√° al controlador de errores especificado en set_error_handler() , y el hecho de que se aplic√≥ la supresi√≥n al error puede rastrearse llamando a la funci√≥n error_reporting() dentro del controlador, en cuyo caso devolver√° 0 .
Si suprime los errores de esta manera, esto reduce la carga en el procesador en comparación con si simplemente los oculta (consulte la prueba comparativa anterior), pero en cualquier caso, suprimir los errores es malo
Tarea
Compruebe cómo la supresión de errores con @ afecta el ejemplo de bucle anterior.

Excepciones


En la era de PHP4 no hubo excepciones, todo fue mucho m√°s complicado, y los desarrolladores lucharon con los errores lo mejor que pudieron, fue una batalla no por la vida sino por la muerte ... Puedes sumergirte en esta fascinante historia de la confrontaci√≥n en el art√≠culo C√≥digo Excepcional. Parte 1 ¬ŅDeber√≠a leerlo ahora? No puedo dar una respuesta definitiva, solo quiero se√Īalar que esto lo ayudar√° a comprender la evoluci√≥n del lenguaje y revelar√° todo el encanto de las excepciones.
Las excepciones son eventos excepcionales en PHP, a diferencia de los errores, no solo indican un problema, sino que requieren acciones adicionales por parte del programador para manejar cada caso específico.

Por ejemplo, una secuencia de comandos debe guardar algunos datos en un archivo de caché si algo salió mal (sin acceso de escritura, sin espacio en disco), se genera una excepción del tipo correspondiente y la decisión se toma en el controlador de excepciones: guardar en otra ubicación o informar al usuario sobre el problema.

Una excepción es un objeto de la clase Exception o de uno de sus muchos descendientes, contiene el texto del error, el estado y también puede contener un enlace a otra excepción que se convirtió en la causa raíz de esto. El modelo de excepción en PHP es similar al utilizado en otros lenguajes de programación. Se puede lanzar una excepción (como se dice, "lanzar") utilizando el operador de throw , y puede atrapar ("atrapar") la instrucción catch . El código que arroja la excepción debe estar rodeado por un bloque try para capturar la excepción. Cada bloque de try debe tener al menos una catch coincidente o finally bloque:

 try { //      if (random_int(0, 1)) { throw new Exception("One"); } echo "Zero" } catch (Exception $e) { //      echo $e->getMessage(); } 

En cuyo caso vale la pena usar excepciones:

  • si dentro del marco de un m√©todo / funci√≥n hay varias operaciones que pueden fallar
  • si su marco o biblioteca declara su uso

Para ilustrar el primer escenario, tomamos un ejemplo ya expresado de una función para escribir datos en un archivo; muchos factores pueden evitarnos, pero para decirle al código anterior cuál fue exactamente el problema, debe crear y lanzar una excepción:

 $directory = __DIR__ . DIRECTORY_SEPARATOR . 'logs'; //     if (!is_dir($directory)) { throw new Exception('Directory `logs` is not exists'); } //         if (!is_writable($directory)) { throw new Exception('Directory `logs` is not writable'); } //  -   ,      if (!$file = @fopen($directory . DIRECTORY_SEPARATOR . date('Ym-d') . '.log', 'a+')) { throw new Exception('System can\'t create log file'); } fputs($file, date("[H:i:s]") . " done\n"); fclose($file); 

En consecuencia, atraparemos estas excepciones de esta manera:

 try { //      // ... } catch (Exception $e) { //    echo " : ". $e->getMessage(); } 

Este ejemplo muestra un escenario muy simple para manejar excepciones, cuando tenemos cualquier excepción manejada de una manera. Pero a menudo, varias excepciones requieren un enfoque diferente para el procesamiento, y luego debe usar códigos de excepción y establecer la jerarquía de excepciones en la aplicación:

 //    class FileSystemException extends Exception {} //     class DirectoryException extends FileSystemException { //   const DIRECTORY_NOT_EXISTS = 1; const DIRECTORY_NOT_WRITABLE = 2; } //     class FileException extends FileSystemException {} 

Ahora, si usa estas excepciones, puede obtener el siguiente código:

 try { //      if (!is_dir($directory)) { throw new DirectoryException('Directory `logs` is not exists', DirectoryException::DIRECTORY_NOT_EXISTS); } if (!is_writable($directory)) { throw new DirectoryException('Directory `logs` is not writable', DirectoryException::DIRECTORY_NOT_WRITABLE); } if (!$file = @fopen($directory . DIRECTORY_SEPARATOR . date('Ym-d') . '.log', 'a+')) { throw new FileException('System can\'t open log file'); } fputs($file, date("[H:i:s]") . " done\n"); fclose($file); } catch (DirectoryException $e) { echo "   : ". $e->getMessage(); } catch (FileException $e) { echo "   : ". $e->getMessage(); } catch (FileSystemException $e) { echo "  : ". $e->getMessage(); } catch (Exception $e) { echo " : ". $e->getMessage(); } 

Es importante recordar que la excepción es principalmente un evento excepcional, en otras palabras, una excepción a la regla. No necesita usarlos para manejar errores obvios, por ejemplo, para validar la entrada del usuario (aunque esto no es tan simple). En este caso, el controlador de excepciones debe escribirse en el lugar donde podrá manejarlo. Por ejemplo, el controlador para las excepciones causadas por la inaccesibilidad de un archivo para escritura debe estar en el método responsable de seleccionar el archivo o el método que lo llama, para que pueda seleccionar otro archivo o un directorio diferente.

Entonces, ¬Ņqu√© pasar√° si no atrapas la excepci√≥n? Obtendr√° "Error grave: excepci√≥n no detectada ...". Desagradable

Para evitar esta situación, debe usar la función set_exception_handler () y establecer el controlador para las excepciones que se lanzan fuera del bloque try-catch y que no se han procesado. Después de llamar a dicho controlador, la ejecución del script se detendrá:

 //     //     set_exception_handler(function($exception) { /** @var Exception $exception */ echo $exception->getMessage(), "<br/>\n"; echo $exception->getFile(), ':', $exception->getLine(), "<br/>\n"; echo $exception->getTraceAsString(), "<br/>\n"; }); 

También le contaré acerca de la construcción utilizando el bloque finally : este bloque se ejecutará independientemente de si se produjo una excepción o no:

 try { //      } catch (Exception $e) { //      //     } finally { // ,       } 

Para entender lo que esto nos da, daré el siguiente ejemplo de uso del bloque finally :

 try { // -    //     $handler = mysqli_connect('localhost', 'root', '', 'test'); try { //        // ... throw new Exception('DB error'); } catch (Exception $e) { //  ,     //     ,    throw new Exception('Catch exception', 0, $e); } finally { // ,      //      finally mysqli_close($handler); } //     ,       echo "Ok"; } catch (Exception $e) { //  ,    echo $e->getMessage(); echo "<br/>"; //      echo $e->getPrevious()->getMessage(); } 

Es decir recuerde: el finally bloque se ejecutará incluso si lanza una excepción arriba en el bloque catch (de hecho, esto es lo que pretendía).

Para un artículo introductorio de información correcto, quién anhela más detalles, los encontrará en el artículo Código excepcional ;)

Tarea
Escriba su controlador de excepciones, con la salida del texto del archivo donde ocurrió el error, y todo esto con el resaltado de sintaxis, también no olvide mostrar la traza en una forma legible. Como referencia, mira lo genial que se ve en whoops .

PHP7: todo no es como era antes


Entonces, ahora ha aprendido toda la informaci√≥n anterior y ahora lo cargar√© con innovaciones en PHP7, es decirHablar√© sobre lo que encontrar√° cuando trabaje en un proyecto PHP moderno. Anteriormente, te lo dije y mostr√© con ejemplos qu√© muleta necesitas construir para detectar errores cr√≠ticos, y as√≠, en PHP7 decidieron arreglarlo, ¬Ņpero? como siempre? atado a la compatibilidad con versiones anteriores del c√≥digo y recibido, aunque es una soluci√≥n universal, pero est√° lejos de ser ideal. Y ahora sobre los puntos sobre los cambios:

  1. cuando E_ERRORocurren errores fatales del tipo o errores fatales con la posibilidad de procesar E_RECOVERABLE_ERRORPHP arroja una excepción
  2. estas excepciones no heredan la clase Exception (recuerde, hablé sobre compatibilidad con versiones anteriores, todo es por su bien)
  3. estas excepciones heredan la clase Error
  4. Ambas clases de excepción y error implementan la interfaz Throwable
  5. no puedes implementar la interfaz de lanzamiento en tu código

La interfaz Throwablenos repite casi por completo Exception:

 interface Throwable { public function getMessage(): string; public function getCode(): int; public function getFile(): string; public function getLine(): int; public function getTrace(): array; public function getTraceAsString(): string; public function getPrevious(): Throwable; public function __toString(): string; } 

¬ŅEs dificil? Ahora, por ejemplo, tome los que eran m√°s altos y se modernizaron un poco:

 try { // ,     include 'e_parse_include.php'; } catch (Error $e) { var_dump($e); } 

Como resultado, detectamos el error e imprimimos:

 object(ParseError)#1 (7) { ["message":protected] => string(48) "syntax error, unexpected '' (T_STRING)" ["string":"Error":private] => string(0) "" ["code":protected] => int(0) ["file":protected] => string(49) "/www/education/error/e_parse_include.php" ["line":protected] => int(4) ["trace":"Error":private] => array(0) { } ["previous":"Error":private] => NULL } 

Como puede ver, detectaron la excepción ParseError , que es la sucesora de la excepción Errorque implementa la interfaz Throwableen la casa que Jack construyó. Hay muchas otras excepciones, pero no atormentaré; para mayor claridad, daré una jerarquía de excepciones:

 interface Throwable |- Exception implements Throwable | |- ErrorException extends Exception | |- ... extends Exception | `- ... extends Exception `- Error implements Throwable |- TypeError extends Error |- ParseError extends Error |- ArithmeticError extends Error | `- DivisionByZeroError extends ArithmeticError `- AssertionError extends Error 

Y un poco m√°s de detalle:

TypeError - para errores cuando el tipo de argumentos de función no coincide con el tipo pasado:

 try { (function(int $one, int $two) { return; })('one', 'two'); } catch (TypeError $e) { echo $e->getMessage(); } 

ArithmeticError : puede ocurrir durante operaciones matemáticas, por ejemplo, cuando el resultado del cálculo excede el límite asignado para un entero:

 try { 1 << -1; } catch (ArithmeticError $e) { echo $e->getMessage(); } 

DivisionByZeroError - error de división por cero:

 try { 1 / 0; } catch (ArithmeticError $e) { echo $e->getMessage(); } 

AssertionError : una bestia rara que aparece cuando no se cumple la condición especificada en afirmar () :

 ini_set('zend.assertions', 1); ini_set('assert.exception', 1); try { assert(1 === 0); } catch (AssertionError $e) { echo $e->getMessage(); } 

En la configuración de Directiva de producción-servidor zend.assertionsy assert.exceptioncortado, y con razón
Encontrará una lista completa de excepciones predefinidas en el manual oficial , en la misma jerarquía de excepciones SPL .

Tarea
Escriba un controlador de errores universal para PHP7 que detecte todas las excepciones posibles.

Al escribir esta sección, se utilizaron materiales del artículo Excepciones y errores de lanzamiento en PHP 7 .

Uniformidad


- Hay errores, excepciones, pero ¬Ņpuede todo esto ser de alguna manera acumulado?

Sí, es fácil, tenemos set_error_handler()uno y nadie nos prohibirá lanzar una excepción dentro de este controlador:

 //     function errorHandler($severity, $message, $file = null, $line = null) { //  ,       @ if (error_reporting() === 0) { return false; } throw new \ErrorException($message, 0, $severity, $file, $line); } //   -  set_error_handler('errorHandler', E_ALL); 

Pero este enfoque con PHP7 es redundante, ahora puede manejar todo Throwable:

 try { /** ... **/ } catch (\Throwable $e) { //      echo $e->getMessage(); } 

Depuración


A veces, para depurar código, debe realizar un seguimiento de lo que le sucedió a una variable u objeto en una etapa determinada, para estos fines hay una función debug_backtrace () y debug_print_backtrace () que devolverá el historial de llamadas a funciones / métodos en el orden inverso:

 <?php function example() { echo '<pre>'; debug_print_backtrace(); echo '</pre>'; } class ExampleClass { public static function method () { example(); } } ExampleClass::method(); 

Como resultado de la ejecución de la función debug_print_backtrace(), se mostrará una lista de llamadas que nos llevaron a este punto:

 #0 example() called at [/www/education/error/backtrace.php:10] #1 ExampleClass::method() called at [/www/education/error/backtrace.php:14] 

Puede verificar el código en busca de errores de sintaxis utilizando la función php_check_syntax () o el comando php -l [ ], pero no he visto su uso.

Afirmar


También me gustaría hablar sobre una bestia tan exótica como afirmar () en PHP. En realidad, esta pieza se puede considerar como una imitación de la metodología de programación por contrato, y luego te diré cómo nunca la usé :)
La funci√≥n assert()cambi√≥ su comportamiento durante la transici√≥n de la versi√≥n 5.6 a la 7.0, y todo cambi√≥ a√ļn m√°s fuerte en la versi√≥n 7.2, as√≠ que lea cuidadosamente los registros de cambios y PHP;)
El primer caso es cuando necesita escribir TODO directamente en el código, para no olvidarse de implementar la funcionalidad dada:

 //  asserts  php.ini // zend.assertions=1 assert(false, "Remove it!"); 

Como resultado de ejecutar este código, obtenemos E_WARNING:

 Warning: assert(): Remove it! failed 

PHP7 se puede cambiar al modo de excepción, y en lugar de un error, siempre aparecerá una excepción AssertionError:

 //    ¬ę¬Ľ ini_set('assert.exception', 1); assert(false, "Remove it!"); 

Como resultado, esperamos una excepción AssertionError.

Si es necesario, puede lanzar una excepción arbitraria:

 assert(false, new Exception("Remove it!")); 

Recomendaría usar etiquetas @TODO, los IDE modernos funcionan bien con ellos, y no necesitará poner esfuerzo y recursos adicionales para trabajar con ellos, aunque la tentación de "anotar" con ellos es excelente
El segundo caso de uso es crear alg√ļn tipo de TDD, pero recuerde, esto es solo una similitud. Aunque, si te esfuerzas, puedes obtener un resultado divertido que te ayudar√° a probar tu c√≥digo:

 // callback-      function backlog($script, $line, $code, $message) { echo $message; } //  callback- assert_options(ASSERT_CALLBACK, 'backlog'); //    assert_options(ASSERT_WARNING, false); //      assert(sqr(4) === 16, 'When I send integer, function should return square of it'); // ,   function sqr($a) { return; //    } 

La tercera opción es un tipo de programación por contrato, cuando describió las reglas para usar su biblioteca, pero desea asegurarse de que se le entiende correctamente y, en ese caso, informar inmediatamente al desarrollador de un error (ni siquiera estoy seguro de haberlo entendido correctamente, pero un ejemplo El código funciona bastante):

 /** *        * * [ * 'host' => 'localhost', * 'port' => 3306, * 'name' => 'dbname', * 'user' => 'root', * 'pass' => '' * ] * * @param $settings */ function setupDb ($settings) { //   assert(isset($settings['host']), 'Db `host` is required'); assert(isset($settings['port']) && is_int($settings['port']), 'Db `port` is required, should be integer'); assert(isset($settings['name']), 'Db `name` is required, should be integer'); //    // ... } setupDb(['host' => 'localhost']); 


Si está interesado en contratos, específicamente para usted tengo un enlace al marco PhpDeal .


Nunca use assert()para verificar los par√°metros de entrada, porque de hecho assert()interpreta el primer par√°metro (se comporta como eval()), y esto est√° plagado de inyecci√≥n PHP. Y s√≠, este es el comportamiento correcto, porque si deshabilita la aserci√≥n, todos los argumentos pasados ‚Äč‚Äčser√°n ignorados, y si hace como en el ejemplo anterior, el c√≥digo se ejecutar√° y el resultado booleano de la ejecuci√≥n se pasar√° dentro de la aserci√≥n deshabilitada. Ah, y cambi√≥ en PHP 7.2 :)


Si tienes una experiencia de uso en vivo assert(), comparte conmigo, te lo agradeceré. Y sí, aquí hay otra lectura interesante para usted sobre este tema: Afirmaciones PHP , con la misma pregunta al final :)

En conclusión


Escribiré las conclusiones de este artículo para usted:

  • Errores de lucha: no deben estar en su c√≥digo
  • Use excepciones: trabajar con ellos debe organizarse adecuadamente y habr√° felicidad
  • Afirmar: aprend√≠ sobre ellos, y bueno

PS


Este es un reenvío de una serie de artículos "PHP para principiantes":


Si tiene comentarios sobre el material del art√≠culo, o posiblemente en forma, entonces describa la esencia en los comentarios, y haremos que este material sea a√ļn mejor.

Gracias a Maxim Slesarenko por su ayuda en la redacción del artículo.

Source: https://habr.com/ru/post/440744/


All Articles