Somente quem não faz nada não comete erros, e somos um exemplo disso - sentamos e trabalhamos incansavelmente, lemos o Habr :)
Neste artigo, contarei minha história sobre erros no PHP e como controlá-los.
Erros
Variedades na família de erros
Antes de domar erros, eu recomendaria estudar
cada espécie e prestar atenção separadamente aos representantes mais importantes.
Para impedir que um único erro passe despercebido, é necessário ativar o rastreamento de todos os erros usando a função
error_reporting () e a diretiva
display_errors para ativar a exibição:
<?php error_reporting(E_ALL); ini_set('display_errors', 1);
Erros fatais
O tipo de erro mais formidável é fatal, pode ocorrer durante a compilação e durante o trabalho do analisador ou do script PHP, enquanto o script é interrompido.
E_PARSEEste erro aparece quando você comete um erro grave de sintaxe e o intérprete PHP não entende o que você deseja dele, por exemplo, se você não fechou o cacheado ou o parêntese:
<?php {
Ou eles escreveram em uma linguagem incompreensível:
<?php
Suportes extras também ocorrem, e não são tão importantes redondos ou encaracolados:
<?php }
Observo um ponto importante - o código do arquivo no qual você cometeu o erro de análise não será executado; portanto, se você tentar ativar a exibição de erros no mesmo arquivo em que ocorreu o erro do analisador, isso não funcionará:
<?php
E_ERROREste erro aparece quando o PHP entendeu o que você deseja, mas isso não funcionou por vários motivos. Este erro também interrompe a execução do script e o código funcionará antes que o erro apareça:
O plug-in não foi encontrado:
require_once 'not-exists.php';
Uma exceção foi lançada (que tipo de animal, eu vou te contar um pouco mais tarde), mas não foi processada:
throw new Exception();
Ao tentar chamar um método de classe inexistente:
$stdClass = new stdClass(); $stdClass->notExists();
Falta de memória livre (mais do que o prescrito na diretiva
memory_limit ) ou algo semelhante:
$arr = array(); while (true) { $arr[] = str_pad(' ', 1024); }
É muito comum ao ler ou baixar arquivos grandes, portanto, tenha cuidado com a questão do consumo de memória
Chamada de função recursiva. Neste exemplo, ele terminou na 256ª iteração, porque está escrito
nas configurações do xdebug (sim, esse erro pode aparecer neste formulário somente quando a extensão xdebug está ativada):
function deep() { deep(); } deep();
Não fatal
Essa visualização não interrompe a execução do script, mas é o testador que normalmente os encontra. São esses erros que causam mais problemas para desenvolvedores iniciantes.
E_WARNINGGeralmente ocorre quando você conecta um arquivo usando o
include
, mas ele não aparece no servidor ou você cometeu um erro ao indicar o caminho para o arquivo:
include_once 'not-exists.php';
Isso acontece se você usar o tipo errado de argumentos ao chamar funções:
join('string', 'string');
Existem muitos deles, e listar tudo não faz sentido ...
E_NOTICEEsses são os erros mais comuns. Além disso, existem ventiladores para desativar a saída de erros e rebitá-los o dia todo. Há vários erros triviais.
Ao acessar uma variável indefinida:
echo $a;
Ao acessar um elemento de matriz inexistente:
$b = []; $b['a'];
Ao acessar uma constante inexistente:
echo UNKNOWN_CONSTANT;
Quando os tipos de dados não são convertidos:
echo array();
Para evitar esses erros - tenha cuidado e, se o IDE informar algo, não o ignore:
E_STRICTSão erros que ensinam a escrever o código corretamente, para que você não tenha vergonha, principalmente porque o IDE mostra imediatamente esses erros. Por exemplo, se você chamou um método não estático como estático, o código funcionará, mas de alguma forma está errado, e poderão ocorrer erros graves se o método de classe for alterado no futuro, e um apelo a
$this
exibido:
class Strict { public function test() { echo "Test"; } } Strict::test();
Este tipo de erro é relevante para o PHP versão 5.6 e quase todos foram cortados
7 correspondências. Leia mais na RFC relevante . Se alguém souber onde mais esses erros permaneceram, escreva nos comentários
E_DEPRECATEDPortanto, o PHP jurará se você usar funções obsoletas (ou seja, aquelas marcadas como obsoletas e que não estarão no próximo grande lançamento):
No meu editor, funções semelhantes serão riscadas:

Custom
Esse tipo, que o próprio desenvolvedor de código “cria”, não os vejo há muito tempo e não recomendo que você os abuse:
E_USER_ERROR
- erro críticoE_USER_WARNING
- não é um erro críticoE_USER_NOTICE
- mensagens que não são erros
Separadamente, vale a pena notar
E_USER_DEPRECATED
- esse tipo ainda é usado com muita frequência para lembrar ao programador que o método ou função está desatualizado e é hora de reescrever o código sem usá-lo. A função
trigger_error () é usada para criar erros semelhantes e semelhantes:
function generateToken() { trigger_error('Function `generateToken` is deprecated, use class `Token` instead', E_USER_DEPRECATED);
Agora que você se familiarizou com a maioria dos tipos e tipos de erros, é hora de dar uma breve explicação sobre a operação da diretiva display_errors
:
- se
display_errors = on
, em caso de erro, o navegador receberá html com o texto e o código do erro 200 - se
display_errors = off
, para erros fatais o código de resposta será 500 e o resultado não será retornado ao usuário, para outros erros - o código não funcionará corretamente, mas não contará a ninguém sobre isso
Domar
Existem 3 funções para trabalhar com erros no PHP:
- set_error_handler () - define um manipulador para erros que não interrompem o script (ou seja, para erros não fatais)
- error_get_last () - obtém informações sobre o último erro
- register_shutdown_function () - registra um manipulador que será iniciado quando o script terminar. Esta função não se aplica diretamente a manipuladores de erros, mas é frequentemente usada para esse fim.
Agora, alguns detalhes sobre o tratamento de erros usando
set_error_handler()
, como argumentos, essa função aceita o nome da função à qual será atribuída a missão para manipular erros e os
tipos de erros que serão monitorados. Um manipulador de erros também pode ser um método de classe ou uma função anônima. O principal é que ele recebe a seguinte lista de argumentos:
$errno
- o primeiro argumento contém o tipo de erro como um número inteiro$errstr
- o segundo argumento contém uma mensagem de erro$errfile
- um terceiro argumento opcional contém o nome do arquivo no qual o erro ocorreu$errline
- um quarto argumento opcional contém o número da linha na qual o erro ocorreu$errcontext
- o quinto argumento opcional contém uma matriz de todas as variáveis existentes no escopo em que ocorreu o erro
Se o manipulador retornou
true
, o erro será considerado processado e o script continuará sendo executado; caso contrário, será chamado um manipulador padrão que registrará o erro e, dependendo do seu tipo, continuará executando o script ou completá-lo. Aqui está um manipulador de exemplo:
<?php
Você não poderá atribuir mais de uma função para manipular erros, embora eu realmente queira registrar meu próprio manipulador para cada tipo de erro, mas não - escreva um manipulador e descreva toda a lógica de exibição de cada tipo diretamente nele
Há um problema significativo com o manipulador que está escrito acima - ele não captura erros fatais e, com esses erros, em vez do site, os usuários verão apenas uma página em branco ou, pior ainda, uma mensagem de erro. Para evitar esse cenário, você deve usar a função
register_shutdown_function () e usá-la para registrar uma função que sempre será executada no final do script:
function shutdown() { echo ' '; } register_shutdown_function('shutdown');
Esta função sempre funcionará!
Mas voltando aos erros, para rastrear a aparência do erro no código do erro, usamos a função
error_get_last () , com sua ajuda, você pode obter informações sobre o último erro detectado e, como erros fatais interrompem a execução do código, eles sempre desempenharão o papel do "último":
function shutdown() { $error = error_get_last(); if (
Gostaria de chamar a atenção para o fato de que esse código ocorre até para tratamento de erros, e você pode até encontrá-lo, mas perdeu relevância desde a 7ª versão do PHP. O que veio substituir, vou contar um pouco mais tarde.
TarefaComplemente o manipulador de erros fatais com a saída do código-fonte do arquivo em que o erro foi cometido e também adicione realce de sintaxe do código de saída.
Sobre gula
Vamos fazer um teste simples e descobrir quantos recursos preciosos o erro mais trivial consome:
Como resultado da execução desse script, obtive este resultado:
0.002867 seconds
Agora adicione o erro no loop:
O resultado é provavelmente pior, e uma ordem de magnitude (mesmo duas ordens de magnitude!):
0.263645 seconds
A conclusão é clara - os erros no código levam à excessiva gula de scripts - portanto, ative a exibição de todos os erros durante o desenvolvimento e teste de aplicativos!Os testes foram realizados em várias versões do PHP e, em todo lugar, a diferença é dezenas de vezes, portanto, esse é outro motivo para corrigir todos os erros no código
Onde está o cachorro enterrado
O PHP possui um símbolo especial “@” - um operador de supressão de erro, que é usado para não gravar a manipulação de erros, mas depende do comportamento correto do PHP; nesse caso:
<?php echo @UNKNOWN_CONSTANT;
Nesse caso, o manipulador de erros especificado em
set_error_handler()
ainda será chamado e o fato de a supressão ter sido aplicada ao erro pode ser rastreado chamando a função
error_reporting()
dentro do manipulador, caso em que retornará
0
.
Se você suprimir erros dessa maneira, isso reduzirá a carga no processador em comparação com se você simplesmente os oculta (veja o teste comparativo acima), mas, em qualquer caso, suprimir erros é ruim.
TarefaVerifique como a supressão de erro com @
afeta o exemplo do loop anterior.
Exceções
Na era do PHP4, não havia exceções, tudo era muito mais complicado, e os desenvolvedores lutaram com os erros da melhor maneira possível: foi uma batalha não pela vida, mas pela morte ... Você pode mergulhar nessa fascinante história do confronto no artigo Código Excepcional. Parte 1 Devo ler agora? Não posso dar uma resposta definitiva. Quero apenas observar que isso ajudará você a entender a evolução do idioma e revelará todo o charme das exceções.
Exceções são eventos excepcionais no PHP, diferentemente dos erros, não apenas indicam um problema, mas requerem ações adicionais do programador para lidar com cada caso específico.
Por exemplo, um script deve salvar alguns dados em um arquivo de cache se algo der errado (sem acesso de gravação, sem espaço em disco), uma exceção do tipo correspondente for gerada e a decisão for tomada no manipulador de exceções - salve em outro local ou informe o usuário sobre o problema.
Uma exceção é um objeto da classe
Exception
ou de um de seus muitos descendentes, contém o texto do erro, status e também pode conter um link para outra exceção que se tornou a causa raiz disso. O modelo de exceção no PHP é semelhante ao usado em outras linguagens de programação. Uma exceção pode ser lançada (como se diz, "throw") usando o operador
throw
, e você pode capturar ("catch") a instrução
catch
. O código que gera a exceção deve estar entre um bloco
try
para capturar a exceção. Cada bloco
try
deve ter pelo menos uma
catch
correspondente ou,
finally
bloco:
try {
Em quais casos vale a pena usar exceções:
- se dentro da estrutura de um método / função houver várias operações que podem falhar
- se sua estrutura ou biblioteca declara seu uso
Para ilustrar o primeiro cenário, tomamos um exemplo já verbalizado de uma função para gravar dados em um arquivo - muitos fatores podem nos impedir, mas para informar o código acima qual era exatamente o problema, você precisa criar e lançar uma exceção:
$directory = __DIR__ . DIRECTORY_SEPARATOR . 'logs';
Conseqüentemente, capturaremos essas exceções assim:
try {
Este exemplo mostra um cenário muito simples para lidar com exceções, quando temos alguma exceção tratada de uma maneira. Mas, muitas vezes, várias exceções exigem uma abordagem diferente para o processamento, e você deve usar códigos de exceção e definir a hierarquia de exceções no aplicativo:
Agora, se você usar essas exceções, poderá obter o seguinte código:
try {
É importante lembrar que a exceção é principalmente um evento excepcional, ou seja, uma exceção à regra. Você não precisa usá-los para lidar com erros óbvios, por exemplo, para validar a entrada do usuário (embora isso não seja tão simples). Nesse caso, o manipulador de exceção deve ser gravado no local em que será capaz de lidar com isso. Por exemplo, o manipulador de exceções causadas pela inacessibilidade de um arquivo para gravação deve estar no método responsável pela seleção do arquivo ou no método que o chama, para que ele possa selecionar outro arquivo ou um diretório diferente.
Então, o que acontecerá se você não capturar a exceção? Você receberá "Erro fatal: exceção não capturada ...". Desagradável.
Para evitar essa situação, você deve usar a função
set_exception_handler () e definir o manipulador para exceções que são lançadas fora do bloco try-catch e não foram processadas. Após chamar esse manipulador, a execução do script será interrompida:
Também vou falar sobre a construção usando o bloco
finally
- esse bloco será executado independentemente de uma exceção ter sido lançada ou não:
try {
Para entender o que isso nos dá, darei o seguinte exemplo de uso do bloco
finally
:
try {
I.e. lembre-se - o bloco
finally
será executado mesmo se você lançar uma exceção acima no bloco de
catch
(na verdade, é isso que ele pretendia).
Para um artigo introdutório de informações, que deseja obter mais detalhes, você as encontrará no artigo
Código excepcional ;)
TarefaEscreva seu manipulador de exceção, com a saída do texto do arquivo em que ocorreu o erro e tudo isso com destaque para a sintaxe, também não se esqueça de exibir o rastreio de forma legível. Para referência, veja como é legal nos
gritos .
PHP7 - nem tudo está como antes
Então, agora você aprendeu todas as informações acima e agora carregarei inovações no PHP7, ou seja,
Vou falar sobre o que você encontrará ao trabalhar em um projeto PHP moderno. Anteriormente, eu lhe disse e mostrei com exemplos quais muletas você precisa construir para detectar erros críticos, e assim - no PHP7 eles decidiram consertar, mas? como sempre? vinculado à compatibilidade com versões anteriores do código e recebido, embora seja uma solução universal, mas está longe de ser o ideal. E agora sobre os pontos sobre as mudanças:- quando erros fatais do tipo
E_ERROR
ou erros fatais ocorrem com a possibilidade de processar o E_RECOVERABLE_ERROR
PHP lança uma exceção - essas exceções não herdam a classe Exception (lembre-se, eu falei sobre compatibilidade com versões anteriores, é tudo por causa dela)
- essas exceções herdam a classe Error
- as classes Exception e Error implementam a interface Throwable
- você não pode implementar a interface jogável no seu código
A interface Throwable
nos repete quase completamente 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; }
Isso é difícil? Agora, para exemplos, considere aqueles que foram mais altos e levemente modernizados: try {
Como resultado, captamos o erro e imprimimos: object(ParseError)
Como você pode ver, eles capturaram a exceção ParseError , que é a sucessora da exceção Error
que implementa a interface Throwable
na casa que Jack construiu. Existem muitas outras exceções, mas não vou atormentar - para maior clareza, darei uma hierarquia de exceções: 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
E um pouco mais detalhadamente:TypeError - para erros quando o tipo de argumento da função não corresponde ao tipo passado: try { (function(int $one, int $two) { return; })('one', 'two'); } catch (TypeError $e) { echo $e->getMessage(); }
ArithmeticError - pode ocorrer durante operações matemáticas, por exemplo, quando o resultado do cálculo excede o limite alocado para um número inteiro: try { 1 << -1; } catch (ArithmeticError $e) { echo $e->getMessage(); }
DivisionByZeroError - divisão por erro zero: try { 1 / 0; } catch (ArithmeticError $e) { echo $e->getMessage(); }
AssertionError - um animal raro que aparece quando a condição especificada em assert () não é satisfeita: ini_set('zend.assertions', 1); ini_set('assert.exception', 1); try { assert(1 === 0); } catch (AssertionError $e) { echo $e->getMessage(); }
Em configurações directiva produção em servidor zend.assertions
e assert.exception
cortado, e com razão
Você encontrará uma lista completa de exceções predefinidas no manual oficial , na mesma hierarquia de exceções de SPL .TarefaEscreva um manipulador de erros universal para o PHP7 que capture todas as exceções possíveis.
Ao escrever esta seção, foram utilizados materiais do artigo Exceções e erros lançáveis no PHP 7 .
Uniformidade
- Existem erros, exceções, mas tudo isso pode ser de alguma forma associado à pilha?Sim, é fácil, temos set_error_handler()
um e ninguém nos proibirá de lançar uma exceção dentro deste manipulador:
Mas essa abordagem com o PHP7 é redundante, agora ele pode lidar com tudo Throwable
: try { } catch (\Throwable $e) {
Depuração
Às vezes, para depurar código, é necessário rastrear o que aconteceu com uma variável ou objeto em um determinado estágio; para esses fins, existe uma função debug_backtrace () e debug_print_backtrace () que retornará o histórico de chamadas para funções / métodos na ordem inversa: <?php function example() { echo '<pre>'; debug_print_backtrace(); echo '</pre>'; } class ExampleClass { public static function method () { example(); } } ExampleClass::method();
Como resultado da execução da função debug_print_backtrace()
, uma lista de chamadas que nos levaram a este ponto será exibida:
Você pode verificar o código quanto a erros de sintaxe usando a função php_check_syntax () ou o comando php -l [ ]
, mas eu não vi o uso deles.Afirmar
Eu também gostaria de falar sobre uma fera exótica como assert () em PHP. Na verdade, esta peça pode ser considerada uma imitação para a metodologia de programação de contratos, e depois vou lhe contar como nunca a usei :)A função assert()
mudou seu comportamento durante a transição da versão 5.6 para a 7.0, e tudo mudou ainda mais na versão 7.2, portanto, leia atentamente os registros de alterações e o PHP;)
O primeiro caso é quando você precisa escrever TODO diretamente no código, para não esquecer de implementar a funcionalidade fornecida:
Como resultado da execução desse código, obtemos E_WARNING
: Warning: assert(): Remove it! failed
O PHP7 pode ser alternado para o modo de exceção e, em vez de um erro, sempre aparecerá uma exceção AssertionError
:
Como resultado, esperamos uma exceção AssertionError
.Se necessário, você pode lançar uma exceção arbitrária: assert(false, new Exception("Remove it!"));
Eu recomendaria o uso de tags @TODO
, os IDEs modernos funcionam bem com eles e você não precisará empregar esforços e recursos extras para trabalhar com eles, embora a tentação de "marcar" com eles seja grande
O segundo caso de uso é criar algum tipo de TDD, mas lembre-se, isso é apenas uma semelhança. Embora, se você se esforçar, pode obter um resultado engraçado que ajudará no teste do seu código:
A terceira opção é um tipo de programação de contrato, quando você descreveu as regras para usar sua biblioteca, mas deseja certificar-se de que é entendido corretamente e, nesse caso, informar imediatamente ao desenvolvedor um erro (nem tenho certeza se o entendi corretamente, mas um exemplo O código está funcionando bem): function setupDb ($settings) {
Se você está interessado em contratos, especificamente para você, tenho um link para o framework PhpDeal .
Nunca use assert()
para verificar os parâmetros de entrada, porque na verdade ele assert()
interpreta o primeiro parâmetro (se comporta como eval()
), e isso está repleto de injeção de PHP. E sim, esse é o comportamento correto, porque se você desabilitar a declaração, todos os argumentos passados serão ignorados e, se você fizer como no exemplo acima, o código será executado e o resultado booleano da execução será passado dentro da declaração desabilitada. Ah, e isso mudou no PHP 7.2 :)
Se você tiver uma experiência de uso ao vivo assert()
- compartilhe comigo, serei grato. E sim, aqui está outra leitura interessante para você sobre este tópico - asserções PHP , com a mesma pergunta no final :)Em conclusão
Escreverei as conclusões deste artigo para você:- Luta contra erros - eles não devem estar no seu código
- Use exceções - o trabalho com elas precisa ser organizado adequadamente e haverá felicidade
- Afirmar - aprendeu sobre eles e bem
PS
Este é um repost de uma série de artigos "PHP para iniciantes":Se você tiver comentários sobre o material do artigo, ou possivelmente em forma, descreva a essência dos comentários, e tornaremos esse material ainda melhor.Agradecimentos a Maxim Slesarenko pela ajuda na redação do artigo.