En PHP, ¿por qué funciona “or die()”, pero “o return” no funciona?
error-handling internals (3)
En PHP, puedes manejar los errores llamando or die
para salir cuando encuentres ciertos errores, como este:
$handle = fopen($location, "r") or die("Couldn''t get handle");
Usar die()
no es una gran manera de manejar errores. Prefiero devolver un código de error para que la función principal pueda decidir qué hacer, en lugar de simplemente terminar el script sin gracia y mostrar el error al usuario.
Sin embargo, PHP muestra un error cuando trato de reemplazar or die
con or return
, como esto:
$handle = fopen($location, "r") or return 0;
¿Por qué or die()
funciona, pero no or return 0
?
El retorno es bastante especial: no puede ser nada como una función, ya que es una herramienta para salir de las funciones. Imagina esto:
if(1==1) return(); // say what??
Si fuera así, el retorno tendría que ser una función que haga una "doble salida", dejando no solo su propio alcance sino también el llamador. Por lo tanto, el retorno no es nada como una expresión, simplemente no puede funcionar de esa manera.
Ahora, en teoría, el retorno podría ser una expresión que evalúe (diga) falsa y luego abandone la función; Tal vez una versión posterior de php implementará esto.
Lo mismo se aplica a goto, que sería un encanto para trabajar como una alternativa; y sí, las alternativas son necesarias y con frecuencia hacen que el código sea legible, por lo que si alguien se queja sobre el "código inteligente" (lo que ciertamente es un buen punto), tal vez php debería tener alguna forma "oficial" de hacer tal cosa:
connectMyDB() fallback return false;
Algo como intentar ... atrapar, solo más al punto. Y personalmente, estaría mucho más contento con "o" haciendo este trabajo, ya que está funcionando bien con la gramática inglesa: "conectar o reportar fallas".
TLDR: tienes toda la razón: vuelve, ve, rompe, ninguno de ellos funciona. Fácil de entender por qué, pero sigue siendo molesto.
Quiero agradecerle por hacer esta pregunta, ya que no tenía idea de que no podía realizar or return
en PHP. Estaba tan sorprendido como tú cuando lo probé. Esta pregunta me dio una buena excusa para investigar un poco en los aspectos internos de PHP, que en realidad fue bastante divertido. Sin embargo, no soy un experto en los aspectos internos de PHP, por lo que la siguiente es una visión de los internos de PHP, aunque creo que es bastante precisa.
or return
no funciona porque el analizador de lenguaje no considera una "expresión", por simple que sea.
La palabra clave or
se define en el lenguaje PHP como un token llamado T_LOGICAL_OR
, y la única expresión donde parece estar definida se ve así:
expr T_LOGICAL_OR { zend_do_boolean_or_begin(&$1, &$2 TSRMLS_CC); } expr { zend_do_boolean_or_end(&$$, &$1, &$4, &$2 TSRMLS_CC); }
No se preocupe por los bits entre llaves, eso solo define cómo se maneja la lógica "o" real. Lo que te queda es expr T_LOGICAL_OR expr
, que solo dice que es una expresión válida para tener una expresión, seguida del token T_LOGICAL_OR
, seguida de otra expresión.
Un analizador también define un expr
, como es de esperar. Puede ser una r_variable
, lo que simplemente significa que es una variable que se le permite leer, o una expr_without_variable
, que es una forma elegante de decir que una expresión se puede hacer con otras expresiones.
Puede hacerlo or die()
porque el constructo de lenguaje die
(¡no una función!) Y su exit
alias están representados por el token T_EXIT
, y T_EXIT
se considera una expr_without_variable
válida, mientras que la declaración de return
- token T_RETURN
- no lo es.
Ahora, ¿por qué T_EXIT
considera una expresión pero T_RETURN
no lo es? Sinceramente, no tengo ni idea. Tal vez fue solo una elección de diseño hecha solo para permitir la construcción or die()
que estás preguntando. El hecho de que solía ser tan ampliamente utilizado, al menos en cosas como tutoriales, ya que no puedo hablar de un gran volumen de código de producción, parece dar a entender que esto puede haber sido una elección intencional. Tendrías que preguntar a los desarrolladores de idiomas para estar seguros.
Con todo lo dicho, esto no debería importar. Si bien el constructo or die()
parecía ubicuo en los tutoriales (ver arriba) hace unos años, no es realmente recomendable, ya que es un ejemplo de "código inteligente". or die()
no es un constructo propio, sino que es un truco que utiliza, algunos podrían decir abusos, dos efectos secundarios del operador or
:
- es muy bajo en la lista de precedencia de operadores, lo que significa que prácticamente todas las demás expresiones se evaluarán antes de ser
- es un operador de cortocircuito, lo que significa que el segundo operando (el bit después de
or
) no se ejecuta si el primer operando devuelveTRUE
, ya que si un operando esTRUE
en una expresiónor
, entonces ambos lo son.
Algunas personas consideran que este tipo de truco es desfavorable, ya que para un programador es más difícil de leer, pero solo guarda unos pocos caracteres de espacio en el código fuente. Como el tiempo del programador es costoso y el espacio en disco es barato, puede ver por qué a la gente no le gusta esto.
En su lugar, debe ser explícito con su intención al expandir su código a una declaración if
completa:
$handle = fopen($location, "r");
if ($handle) {
// process the file
} else {
return 0;
}
Incluso puedes hacer la asignación de variables directamente en la sentencia if
. Algunas personas todavía encuentran esto ilegible, pero la mayoría de las personas (yo incluido) no están de acuerdo:
if ($handle = fopen($location, "r")) {
// process the file
} else {
return 0;
}
Una última cosa: es una convención que devolver 0
como un código de estado indica éxito, por lo que probablemente querrá devolver un valor diferente para indicar que no pudo abrir el archivo.
También me he topado con eso una vez. Todo lo que pude encontrar fue esto:
https://bugs.php.net/bug.php?id=40712
Mira el comentario abajo:
Esto no es un error
He buscado en la documentación y creo que se debe al hecho de que return 0
es una declaración, mientras que die()
es esencialmente una expresión. No puedes ejecutar $handle = return 0;
pero $handle = fun();
es codigo valido
Con respecto al manejo de errores, recomendaría códigos personalizados o el uso de controladores y activadores personalizados. Los últimos se describen aquí por ejemplo.