Pretendo apresentar a você uma discussão muito incomum sobre controle de erros.
Eu criei um gerenciador de erros muito bom em uma linguagem anos atrás e, embora alguns dos nomes tenham mudado, os princípios do processamento de erros são os mesmos hoje. Eu tinha um sistema operacional multitarefa personalizado e precisava ser capaz de me recuperar de erros de dados em todos os níveis, sem vazamentos de memória, crescimento da pilha ou travamentos. Portanto, o que se segue é meu entendimento de como os erros e exceções devem operar e como eles diferem. Direi apenas que não tenho um entendimento de como funciona o funcionamento interno do try catch, então estou supondo até certo ponto.
A primeira coisa que acontece nos bastidores para o processamento de erros é pular de um estado do programa para outro. Como isso é feito? Eu vou chegar lá.
Historicamente, os erros são mais antigos e simples, e as exceções são mais recentes e um pouco mais complexas e capazes. Os erros funcionam bem até que você precise colocá-los em bolha, o que equivale a entregar um problema difícil ao seu supervisor.
Os erros podem ser números, como números de erros e, às vezes, com uma ou mais strings associadas. Por exemplo, se ocorrer um erro de leitura de arquivo, você poderá relatar o que é e possivelmente falhar normalmente. (Hay, é um passo além de apenas bater como nos velhos tempos.)
O que não é freqüentemente dito sobre exceções é que exceções são objetos dispostos em camadas em uma pilha de exceções especial. É como uma pilha de retorno para o fluxo do programa, mas mantém um estado de retorno apenas para erros e capturas. (Eu costumava chamá-los de ePush e ePop, e? Abort era um lançamento condicional que iria ePop e se recuperaria para aquele nível, enquanto Abort era um dado completo ou saída.)
Na parte inferior da pilha estão as informações sobre o chamador inicial, o objeto que sabe sobre o estado quando o try externo foi iniciado, que geralmente é quando seu programa foi iniciado. Além disso, ou a próxima camada na pilha, com up sendo os filhos e down sendo os pais, está o objeto de exceção do próximo bloco try / catch interno.
Se você colocar uma tentativa dentro de uma tentativa, estará empilhando a tentativa interna sobre a tentativa externa. Quando ocorre um erro no try interno e o catch interno não consegue lidar com isso ou o erro é lançado para o try externo, o controle é passado para o bloco catch externo (objeto) para ver se ele pode tratar o erro, ou seja, seu supervisor.
Portanto, o que essa pilha de erros realmente faz é ser capaz de marcar e restaurar o fluxo do programa e o estado do sistema, em outras palavras, permite que um programa não bloqueie a pilha de retorno e bagunce as coisas para os outros (dados) quando as coisas dão errado. Portanto, ele também salva o estado de quaisquer outros recursos, como pools de alocação de memória, e pode limpá-los quando a captura for concluída. Em geral, isso pode ser uma coisa muito complicada e é por isso que o tratamento de exceções costuma ser lento. Em geral, um pouco de estado precisa entrar nesses blocos de exceção.
Assim, um tipo de bloco try / catch define um estado ao qual poderemos retornar se todo o resto for bagunçado. É como um pai. Quando nossas vidas ficam confusas, podemos cair de volta no colo de nossos pais e eles vão consertar tudo de novo.
Espero não ter te desapontado.
Errors are generally unrecoverable
<- na verdade, isso não é verdade.E_ERROR
eE_PARSE
são os dois erros irrecuperáveis mais comuns (há alguns outros), mas a grande maioria dos erros que você verá no dev são recuperáveis (E_NOTICE
,E_WARNING
et al). Infelizmente, o tratamento de erros do PHP é uma bagunça completa - todos os tipos de coisas acionam erros desnecessariamente (a grande maioria das funções do sistema de arquivos, por exemplo). Em geral, as exceções são "o caminho OOP", mas infelizmente algumas das APIs OOP nativas do PHP usam erros em vez de exceções :-(