validar - si existe variable en php
La mejor manera de probar la existencia de una variable en PHP; isset() está claramente roto (17)
Explicando NULL, pensando lógicamente
Supongo que la respuesta obvia a todo esto es ... No inicialices tus variables como NULL, inícializándolas como algo relevante para lo que se pretende que sean.
Tratar NULL apropiadamente
NULL debe tratarse como "valor no existente", que es el significado de NULL. La variable no se puede clasificar como existente en PHP porque no se le ha dicho qué tipo de entidad intenta ser. Es posible que tampoco exista, por lo que PHP simplemente dice "Bien, no funciona porque de todos modos no tiene sentido y NULL es mi manera de decir esto".
Un argumento
Discutamos ahora. "Pero NULL es como decir 0 o FALSO o ''''.
Incorrecto, 0-FALSO- '''' todavía se clasifican como valores vacíos, pero se especifican como algún tipo de valor o respuesta predeterminada a una pregunta. FALSE es la respuesta a sí o no, '''' es la respuesta al título que alguien envió, y 0 es la respuesta a la cantidad o tiempo, etc. Se establecen como algún tipo de respuesta / resultado que los hace válidos como establecidos.
NULL simplemente no responde, nunca nos dice sí o no y no nos dice la hora y tampoco nos dice que se envió una cadena en blanco. Esa es la lógica básica para entender NULL.
Resumen
No se trata de crear funciones absurdas para eludir el problema, solo está cambiando la forma en que tu cerebro mira NULL. Si es NULO, supongamos que no está configurado como algo. Si está predefiniendo variables, prefírelas como 0, FALSE o "" según el tipo de uso que pretenda.
Siéntase libre de citar esto. Está fuera de mi cabeza lógica :)
Desde el isset()
documentos :
isset() will return FALSE if testing a variable that has been set to NULL.
Básicamente, isset()
no verifica si la variable está establecida en absoluto, sino si está configurada en cualquier cosa que no sea NULL
.
Dado que, ¿cuál es la mejor manera de verificar realmente la existencia de una variable? Intenté algo como:
if(isset($v) || @is_null($v))
(el @
es necesario para evitar la advertencia cuando $v
no está establecido) pero is_null()
tiene un problema similar al isset()
: ¡devuelve TRUE
en las variables no establecidas! También parece que:
@($v === NULL)
funciona exactamente como @is_null($v)
, así que eso también está fuera.
¿Cómo se supone que debemos verificar de forma confiable la existencia de una variable en PHP?
Editar: claramente hay una diferencia en PHP entre las variables que no están establecidas, y las variables que se establecen en NULL
:
<?php
$a = array(''b'' => NULL);
var_dump($a);
PHP muestra que $a[''b'']
existe, y tiene un valor NULL
. Si agrega:
var_dump(isset($a[''b'']));
var_dump(isset($a[''c'']));
puedes ver la ambigüedad de la que estoy hablando con la función isset()
. Aquí está el resultado de estos tres var_dump()s
:
array(1) {
["b"]=>
NULL
}
bool(false)
bool(false)
Edición adicional: dos cosas.
Uno, un caso de uso. Una matriz se convierte en los datos de una instrucción SQL UPDATE
, donde las claves de la matriz son columnas de la tabla, y los valores de la matriz son los valores que se aplicarán a cada columna. Cualquiera de las columnas de la tabla puede contener un valor NULL
, significado al pasar un valor NULL
en la matriz. Necesita una forma de diferenciar entre una clave de matriz que no existe y el valor de una matriz que se establece en NULL
; esa es la diferencia entre no actualizar el valor de la columna y actualizar el valor de la columna a NULL
.
En segundo lugar, la respuesta de array_key_exists()
, array_key_exists()
funciona correctamente, para mi caso de uso anterior y para cualquier variable global:
<?php
$a = NULL;
var_dump(array_key_exists(''a'', $GLOBALS));
var_dump(array_key_exists(''b'', $GLOBALS));
productos:
bool(true)
bool(false)
Dado que se maneja correctamente en casi todos lados, puedo ver que existe una ambigüedad entre las variables que no existen y las variables que están establecidas en NULL
, llamo a array_key_exists()
la forma más fácil oficial en PHP para verificar realmente la existencia de un variable .
(Solo se puede pensar en otro caso para las propiedades de clase, para las cuales hay property_exists()
, que, de acuerdo con sus documentos , funciona de forma similar a array_key_exists()
porque distingue correctamente entre no establecerse y establecerse en NULL
).
A veces me pierdo un poco tratando de descubrir qué operación de comparación usar en una situación dada. isset()
solo se aplica a valores no inicializados o explícitamente nulos. Pasar / asignar nulo es una gran manera de garantizar que una comparación lógica funcione como se espera.
Aún así, es un poco difícil pensar así que aquí hay una matriz simple que compara cómo diferentes valores serán evaluados por diferentes operaciones:
| | ===null | is_null | isset | empty | if/else | ternary | count>0 |
| ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- |
| $a; | true | true | | true | | | |
| null | true | true | | true | | | |
| [] | | | true | true | | | |
| 0 | | | true | true | | | true |
| "" | | | true | true | | | true |
| 1 | | | true | | true | true | true |
| -1 | | | true | | true | true | true |
| " " | | | true | | true | true | true |
| "str" | | | true | | true | true | true |
| [0,1] | | | true | | true | true | true |
| new Class | | | true | | true | true | true |
Para caber en la mesa compré un poco las etiquetas:
-
$a;
se refiere a una variable declarada pero no asignada - todo lo demás en la primera columna se refiere a un valor asignado, como:
-
$a = null;
-
$a = [];
-
$a = 0;
- ...
-
- las columnas se refieren a operaciones de comparación, como:
-
$a === null
-
isset($a)
-
empty($a)
-
$a ? true : false
- ...
-
Todos los resultados son booleanos, los true
se imprimen y los false
se omiten.
Puede ejecutar las pruebas usted mismo, verifique esta idea:
https://gist.github.com/mfdj/8165967
Como una adición a la discusión de greatbigmassive de lo que significa NULL , considere lo que realmente significa "la existencia de una variable".
En muchos idiomas, debe declarar explícitamente cada variable antes de usarla ; esto puede determinar su tipo, pero lo más importante es que declara su alcance . Una variable "existe" en todas partes en su alcance, y en ningún lugar fuera de ella, ya sea una función completa o un solo "bloque".
Dentro de su alcance, una variable asigna algún significado a una etiqueta que usted, el programador, ha elegido. Fuera de su alcance, esa etiqueta no tiene sentido (si usas la misma etiqueta en un alcance diferente es básicamente irrelevante).
En PHP, las variables no necesitan ser declaradas , cobran vida tan pronto como las necesita. Cuando escribe en una variable por primera vez, PHP asigna una entrada en la memoria para esa variable. Si lee de una variable que actualmente no tiene una entrada, PHP considera que esa variable tiene el valor NULL
.
Sin embargo, los detectores de calidad de código automático generalmente le advertirán si usa una variable sin "inicializarla" primero. En primer lugar, esto ayuda a detectar errores tipográficos, como la asignación a $thingId
pero leyendo desde $thing_id
; pero en segundo lugar, te obliga a considerar el alcance sobre el cual esa variable tiene significado, tal como lo haría una declaración.
Cualquier código que se preocupe si una variable "existe" es parte del alcance de esa variable , ya sea que se haya inicializado o no, usted como programador le ha dado a esa etiqueta el significado en ese punto del código. Como lo está usando, debe "existir" en cierto sentido, y si existe, debe tener un valor implícito; en PHP, ese valor implícito es null
.
Debido a la forma en que PHP funciona, es posible escribir código que trate el espacio de nombres de las variables existentes no como un alcance de las etiquetas a las que le ha dado significado, sino como un tipo de almacén de valores-clave. Puede, por ejemplo, ejecutar código como este: $var = $_GET[''var_name'']; $$var = $_GET[''var_value''];
$var = $_GET[''var_name'']; $$var = $_GET[''var_value''];
. Solo porque puedas, no significa que sea una buena idea.
Resulta que PHP tiene una forma mucho mejor de representar almacenes de clave-valor, llamados arrays asociativos. Y aunque los valores de una matriz se pueden tratar como variables, también puede realizar operaciones en la matriz como un todo. Si tiene una matriz asociativa, puede probar si contiene una clave usando array_key_exists()
.
También puede usar objetos de forma similar, estableciendo propiedades dinámicamente, en cuyo caso puede usar property_exists()
exactamente de la misma manera. Por supuesto, si defines una clase, puedes declarar qué propiedades tiene , incluso puedes elegir entre alcance public
, private
y protected
.
Aunque existe una diferencia técnica entre una variable (a diferencia de una clave de matriz, o una propiedad de objeto) que no se ha inicializado (o que ha sido explícitamente unset()
) y una cuyo valor es null
, cualquier código que considere eso La diferencia para ser significativo es usar variables de una manera que no están destinadas a ser utilizadas.
Intenta usar
unset($v)
It seems the only time a variable is not set is when it is specifically unset($v). It sounds like your meaning of ''existence'' is different than PHP''s definition. NULL is certainly existing, it is NULL.
Intentando dar una visión general de las diversas discusiones y respuestas:
No hay una respuesta única a la pregunta que pueda reemplazar todas las formas en que se puede usar isset
. Algunos casos de uso son abordados por otras funciones, mientras que otros no resisten el escrutinio o tienen un valor dudoso más allá del código de golf. Lejos de ser "roto" o "inconsistente", otros casos de uso demuestran por isset
la reacción de isset
a null
es el comportamiento lógico.
Casos de uso real (con soluciones)
1. Teclas de matriz
Las matrices se pueden tratar como colecciones de variables, con unset
y isset
tratándolas como si lo fueran. Sin embargo, dado que pueden ser iterados, contados, etc., un valor faltante no es lo mismo que uno cuyo valor es null
.
La respuesta en este caso es usar array_key_exists()
lugar de isset()
.
Como esto toma la matriz para verificar como un argumento de función, PHP aún generará "avisos" si la matriz en sí no existe. En algunos casos, se puede argumentar válidamente que cada dimensión debería haberse inicializado primero, por lo que el aviso está haciendo su trabajo. Para otros casos, una función "recursiva" array_key_exists
, que verificaba cada dimensión de la matriz, evitaría esto, pero básicamente sería lo mismo que @array_key_exists
. También es algo tangencial al manejo de valores null
.
2. Propiedades del objeto
En la teoría tradicional de la "Programación Orientada a Objetos", la encapsulación y el polimorfismo son propiedades clave de los objetos; en una implementación OOP basada en clases como PHP, las propiedades encapsuladas se declaran como parte de la definición de clase y se les dan niveles de acceso ( public
, protected
o private
).
Sin embargo, PHP también le permite agregar propiedades dinámicamente a un objeto, como lo haría con las teclas de una matriz, y algunas personas usan objetos sin clase (técnicamente, instancias de stdClass
incorporado, que no tiene métodos o funcionalidad privada) en una manera similar a las matrices asociativas. Esto lleva a situaciones en las que una función puede querer saber si se ha agregado una propiedad en particular al objeto que se le ha asignado.
Al igual que con las teclas de matriz, se incluye una solución para verificar las propiedades de los objetos en el lenguaje, llamada, bastante razonable, property_exists
.
Casos de uso no justificables, con discusión
3. register_globals
, y otra contaminación del espacio de nombres global
Los register_globals
incluyen variables añadidas al alcance global cuyos nombres fueron determinados por aspectos de la solicitud HTTP (parámetros GET y POST, y cookies). Esto puede conducir a errores y códigos inseguros, por lo que se ha desactivado de forma predeterminada desde PHP 4.2, lanzado en agosto de 2000 y eliminado por completo en PHP 5.4, lanzado en marzo de 2012 . Sin embargo, es posible que algunos sistemas todavía se estén ejecutando con esta característica habilitada o emulada. También es posible "contaminar" el espacio de nombres global de otras formas, utilizando la palabra clave global
o matriz $GLOBALS
.
En primer lugar, es poco probable que register_globals
produzca de forma inesperada una variable null
, ya que los valores GET, POST y de cookie siempre serán cadenas (con ''''
todavía retornando true
desde isset
), y las variables en la sesión deben estar bajo el control del programador.
En segundo lugar, la contaminación de una variable con el valor null
es solo un problema si esto sobre escribe alguna inicialización previa. "Sobreescribir" una variable no inicializada con null
solo sería problemático si el código en algún otro lugar estuviera distinguiendo entre los dos estados, por lo que, en sí misma, esta posibilidad es un argumento en contra de hacer tal distinción.
4. get_defined_vars
y compact
Algunas funciones poco utilizadas en PHP, como get_defined_vars
y compact
, le permiten tratar nombres de variables como si fueran claves en una matriz. Para las variables globales, la matriz $GLOBALS
permite un acceso similar, y es más común. Estos métodos de acceso se comportarán de manera diferente si una variable no está definida en el alcance relevante.
Una vez que haya decidido tratar un conjunto de variables como una matriz utilizando uno de estos mecanismos, puede hacer todas las mismas operaciones que en cualquier matriz normal. En consecuencia, ver 1.
La funcionalidad que existía solo para predecir cómo se comportarían estas funciones (por ejemplo, "¿habrá una clave ''foo'' en la matriz devuelta por get_defined_vars
?") Es superflua, ya que simplemente puede ejecutar la función y descubrir sin efectos negativos .
4a. Variables variables ( $$foo
)
Aunque no es exactamente lo mismo que las funciones que convierten un conjunto de variables en una matriz asociativa, la mayoría de los casos que usan "variables variables" ("asignar a una variable nombrada basada en esta otra variable") pueden y deben cambiarse para usar una matriz asociativa .
Un nombre de variable, fundamentalmente, es la etiqueta dada a un valor por el programador; si lo está determinando en tiempo de ejecución, no es realmente una etiqueta sino una clave en algún almacén de clave-valor. Más prácticamente, al no usar una matriz, está perdiendo la capacidad de contar, iterar, etc. también puede ser imposible tener una variable "fuera" del almacén de clave-valor, ya que podría estar escrito en exceso por $$foo
.
Una vez cambiado para usar una matriz asociativa, el código será susceptible a la solución 1. El acceso indirecto a la propiedad del objeto (por ejemplo, $foo->$property_name
) puede abordarse con la solución 2.
5. isset
es mucho más fácil de escribir que array_key_exists
No estoy seguro de que esto sea realmente relevante, pero sí, los nombres de funciones de PHP pueden ser bastante largos e inconsistentes a veces. Aparentemente, las versiones prehistóricas de PHP usaban la longitud de un nombre de función como una clave hash, por lo que Rasmus inventó nombres de funciones como htmlspecialchars
para que tuvieran un número inusual de caracteres ...
Aún así, al menos no estamos escribiendo Java, ¿eh? ;)
6. Las variables no inicializadas tienen un tipo
La página de manual sobre conceptos básicos de variables incluye esta declaración:
Las variables no inicializadas tienen un valor predeterminado de su tipo según el contexto en el que se utilizan.
No estoy seguro de si hay alguna noción en el Zend Engine de "tipo no inicializado pero conocido" o si esto está leyendo demasiado en la declaración.
Lo que está claro es que no tiene ninguna diferencia práctica en su comportamiento, ya que los comportamientos descritos en esa página para las variables no inicializadas son idénticos al comportamiento de una variable cuyo valor es null
. Para elegir un ejemplo, tanto $a
como $b
en este código terminarán como el número entero 42
:
unset($a);
$a += 42;
$b = null;
$b += 42;
(El primero generará un aviso acerca de una variable no declarada, en un intento de hacer que escriba un mejor código, pero no hará ninguna diferencia en cómo se ejecuta realmente el código).
99. Detectando si una función se ha ejecutado
(Manteniendo este último, ya que es mucho más largo que los demás. Tal vez lo edite más tarde ...)
Considera el siguiente código:
$test_value = ''hello'';
foreach ( $list_of_things as $thing ) {
if ( some_test($thing, $test_value) ) {
$result = some_function($thing);
}
}
if ( isset($result) ) {
echo ''The test passed at least once!'';
}
Si some_function
puede devolver null
, existe la posibilidad de que no se alcance el echo
aunque some_test
devuelto el valor true
. La intención del programador era detectar cuándo $result
nunca se había establecido, pero PHP no les permite hacerlo.
Sin embargo, hay otros problemas con este enfoque, que se vuelven claros si agrega un bucle externo:
foreach ( $list_of_tests as $test_value ) {
// something''s missing here...
foreach ( $list_of_things as $thing ) {
if ( some_test($thing, $test_value) ) {
$result = some_function($thing);
}
}
if ( isset($result) ) {
echo ''The test passed at least once!'';
}
}
Como $result
nunca se inicializa explícitamente, tendrá un valor cuando pase la primera prueba, por lo que es imposible saber si pasaron las pruebas posteriores o no. Esto es realmente un error extremadamente común cuando las variables no se inicializan correctamente.
Para solucionar esto, tenemos que hacer algo en la línea donde he comentado que falta algo. La solución más obvia es establecer $result
a un "valor de terminal" que some_function
nunca puede devolver; si esto es null
, entonces el resto del código funcionará bien. Si no hay un candidato natural para un valor de terminal porque some_function
tiene un tipo de devolución extremadamente impredecible (que probablemente sea un mal signo en sí mismo), entonces podría usarse un valor booleano adicional, por ejemplo, $found
.
Pensé experimento uno: la constante muy very_null
PHP teóricamente podría proporcionar una constante especial, así como null
, para usar aquí como un valor de terminal; presumiblemente, sería ilegal devolver esto desde una función, o se lo coaccionaría a null
, y lo mismo probablemente se aplicaría a pasarlo como un argumento de función. Eso haría que este caso muy específico sea ligeramente más simple, pero tan pronto como decida volver a factorizar el código, por ejemplo, para poner el bucle interno en una función separada, sería inútil. Si la constante se puede pasar entre funciones, no se puede garantizar que some_function
no la devuelva, por lo que ya no sería útil como valor de terminal universal.
El argumento para detectar variables no inicializadas en este caso se reduce al argumento de esa constante especial: si reemplaza el comentario con unset($result)
, y lo trata de manera diferente de $result = null
, está introduciendo un "valor" para $result
que no puede transmitirse y solo puede detectarse mediante funciones integradas específicas.
Pensamiento experimento dos: contador de tareas
Otra forma de pensar qué es lo último if
está preguntando es "¿ha hecho algo algo asignado a $result
?" En lugar de considerar que tiene un valor especial de $result
, quizás podría pensar en esto como "metadatos" sobre la variable, algo así como la "contaminación variable" de Perl. Entonces, en lugar de isset
, puede llamarlo has_been_assigned_to
, y en lugar de unset
, reset_assignment_state
.
Pero si es así, ¿por qué detenerse en un booleano? ¿Qué sucede si quiere saber cuántas veces pasó la prueba? simplemente podría ampliar sus metadatos a un número entero y tener get_assignment_count
y reset_assignment_count
...
Obviamente, agregar dicha característica tendría una compensación en la complejidad y el rendimiento del idioma, por lo que tendría que sopesarse cuidadosamente con respecto a su utilidad esperada. Al igual que con una constante muy very_null
, sería útil solo en circunstancias muy limitadas, y sería igualmente resistente al refactorizado.
La pregunta, afortunadamente obvia, es por qué el motor de tiempo de ejecución de PHP debe suponer de antemano que desea hacer un seguimiento de tales cosas, en lugar de dejar que lo haga explícitamente, utilizando el código normal.
Las propiedades del objeto se pueden verificar para la existencia por property_exists
Ejemplo de una prueba unitaria:
function testPropertiesExist()
{
$sl =& $this->system_log;
$props = array(''log_id'',
''type'',
''message'',
''username'',
''ip_address'',
''date_added'');
foreach($props as $prop) {
$this->assertTrue(property_exists($sl, $prop),
"Property <{$prop}> exists");
}
}
Puede usar la construcción de lenguaje compacto para probar la existencia de una variable nula. Las variables que no existen no aparecerán en el resultado, mientras que los valores nulos se mostrarán.
$x = null;
$y = ''y'';
$r = compact(''x'', ''y'', ''z'');
print_r($r);
// Output:
// Array (
// [x] =>
// [y] => y
// )
En el caso de tu ejemplo:
if (compact(''v'')) {
// True if $v exists, even when null.
// False on var $v; without assignment and when $v does not exist.
}
Por supuesto, para las variables en el alcance global también puede usar array_key_exists ().
Btw personalmente evitaría situaciones como la peste donde hay una diferencia semántica entre una variable que no existe y la variable que tiene un valor nulo. PHP y la mayoría de los demás idiomas simplemente no creen que exista.
Si la variable que está revisando estaría dentro del alcance global que podría hacer:
array_key_exists(''v'', $GLOBALS)
isset
comprueba si la variable está configurada y, de ser así, si su valor no es NULO. La última parte (en mi opinión) no está dentro del alcance de esta función. No hay una solución decente para determinar si una variable es NULL porque no está establecida o porque está establecida explícitamente en NULL .
Aquí hay una posible solución:
$e1 = error_get_last();
$isNULL = is_null(@$x);
$e2 = error_get_last();
$isNOTSET = $e1 != $e2;
echo sprintf("isNOTSET: %d, isNULL: %d", $isNOTSET, $isNULL);
// Sample output:
// when $x is not set: isNOTSET: 1, isNULL: 1
// when $x = NULL: isNOTSET: 0, isNULL: 1
// when $x = false: isNOTSET: 0, isNULL: 0
Otra solución alternativa es sondear el resultado de get_defined_vars()
:
$vars = get_defined_vars();
$isNOTSET = !array_key_exists("x", $vars);
$isNULL = $isNOTSET ? true : is_null($x);
echo sprintf("isNOTSET: %d, isNULL: %d", $isNOTSET, $isNULL);
// Sample output:
// when $x is not set: isNOTSET: 1, isNULL: 1
// when $x = NULL: isNOTSET: 0, isNULL: 1
// when $x = false: isNOTSET: 0, isNULL: 0
No estoy de acuerdo con tu razonamiento sobre NULL , y decir que necesitas cambiar tu mentalidad acerca de NULL es simplemente extraño.
Creo que isset () no fue diseñado correctamente, isset () debería decirle si la variable ha sido configurada y no debería preocuparse por el valor real de la variable.
¿Qué sucede si está comprobando los valores devueltos desde una base de datos y una de las columnas tiene un valor NULL, aún desea saber si existe incluso si el valor es NULL ... no, no confíe en isset () aquí.
igualmente
$a = array (''test'' => 1, ''hello'' => NULL);
var_dump(isset($a[''test''])); // TRUE
var_dump(isset($a[''foo''])); // FALSE
var_dump(isset($a[''hello''])); // FALSE
isset () debería haber sido diseñado para funcionar así:
if(isset($var) && $var===NULL){....
De esta forma, dejamos que el programador compruebe los tipos y no deje que isset () suponga que no está allí porque el valor es NULL, es solo un diseño estúpido.
According to the PHP Manual for the empty() function, "Determine whether a variable is considered to be empty. A variable is considered empty IF IT DOES NOT EXIST or if its value equals FALSE. empty() does not generate a warning if the variable does not exist." (My emphasis.) That means the empty() function should qualify as the "best way to test a variable''s existence in PHP", per the title Question.
However, this is not good enough, because the empty() function can be fooled by a variable that does exist and is set to NULL.
I''m interrupting my earlier answer to present something better, because it is less cumbersome than my original answer (which follows this interruption, for comparing).
function undef($dnc) //do not care what we receive
{ $inf=ob_get_contents(); //get the content of the buffer
ob_end_clean(); //stop buffering outputs, and empty the buffer
if($inf>"") //if test associated with the call to this function had an output
{ if(false!==strpos($inf, "Undef"); //if the word "Undefined" was part of the output
return true; //tested variable is undefined
}
return false; //tested variable is not undefined
}
Two simple lines of code can use the above function to reveal if a variable is undefined:
ob_start(); //pass all output messages (including errors) to a buffer
if(undef($testvar===null)) //in this case the variable being tested is $testvar
You can follow those two lines with anything appropriate, such as this example:
echo("variable is undefined");
else
echo("variable exists, holding some value");
I wanted to put the call to ob_start() and the ($testvar===null) inside the function, and simply pass the variable to the function, but it doesn''t work. Even if you try to use "pass by reference" of the variable to the function, the variable BECOMES defined, and then the function can never detect that it previously had been undefined. What is presented here is a compromise between what I wanted to do, and what actually works.
The preceding implies that there is another way to always avoid running into the "Undefined variable" error message. (The assumption here is, preventing such a message is why you want to test to see if a variable is undefined.)
function inst(&$v) { return; } //receive any variable passed by reference; instantiates the undefined
Just call that function before doing something to your $testvar:
inst($testvar); //The function doesn''t affect any value of any already-existing variable
The newly-instantiated variable''s value is set to null, of course!
(Interruption ends)
So, after some studying and experimenting, here is something guaranteed to work:
function myHndlr($en, $es, $ef, $el)
{ global $er;
$er = (substr($es, 0, 18) == "Undefined variable");
return;
}
$er = false;
if(empty($testvar))
{ set_error_handler("myHndlr");
($testvar === null);
restore_error_handler();
}
if($er) // will be 1 (true) if the tested variable was not defined.
{ ; //do whatever you think is appropriate to the undefined variable
}
The explanation: A variable $er is initialized to a default value of "no error". A "handler function" is defined. If the $testvar (the variable we want to know whether or not is undefined) passes the preliminary empty() function test, then we do the more thorough test. We call the set_error_handler() function to use the previously-defined handler function. Then we do a simple identity-comparison involving $testvar, WHICH IF UNDEFINED WILL TRIGGER AN ERROR. The handler function captures the error and specifically tests to see if the reason for the error is the fact that the variable is undefined. The result is placed in the error-information variable $er, which we can later test to do whatever we want as a result of knowing for sure whether or not $testvar was defined. Because we only need the handler function for this limited purpose, we restore the original error-handling function. The "myHndlr" function only needs to be declared once; the other code can be copied to whatever places are appropriate, for $testvar or any other variable we want to test this way.
I have to say in all my years of PHP programming, I have never encountered a problem with isset()
returning false on a null variable. OTOH, I have encountered problems with isset()
failing on a null array entry - but array_key_exists()
works correctly in that case.
For some comparison, Icon explicitly defines an unused variable as returning &null
so you use the is-null test in Icon to also check for an unset variable. This does make things easier. On the other hand, Visual BASIC has multiple states for a variable that doesn''t have a value (Null, Empty, Nothing, ...), and you often have to check for more than one of them. This is known to be a source of bugs.
I prefer using not empty as the best method to check for the existence of a variable that a) exists, and b) is not null.
if (!empty($variable)) do_something();
I think the only full solution is to report notices with
error_reporting(E_ALL); // Enables E_NOTICE
But you will have to fix all the notices generated by undefined variables, constants, array keys, class properties amongst others. Once you have done that you won''t have to worry about the difference between null and not declared variables, and the ambiguity dissappears.
Enabling notice reporting might not be a good alternative in all situations, but there are good reasons to enable it:
Why should I fix E_NOTICE errors?
In my case was more than a year working in a proyect without it, but was used to be careful about declaring variables, so it was fast to transition.
I''m going to add a quick two cents to this. One reason this issue is confusing is because this scenario seems to return the same result with error reporting not on full:
$a = null;
var_dump($a); // NULL
var_dump($b); // NULL
You could assume from this result that the difference between $a = null
and not defining $b
at all is nothing.
Crank error reporting up:
NULL
Notice: Undefined variable: b in xxx on line n
NULL
Note: it threw an undefined variable error, but the output value of var_dump
is still NULL
.
PHP obviously does have an internal ability to distinguish between a null variable and an undefined variable. It seems to me that there should be a built in function to check for this.
I think the accepted answer is good for the most part, but if I was going to implement it I would write a wrapper for it. As previously mentioned in this answer , I have to agree that I haven''t actually encountered a situation where this has been a problem. I seem to almost always end up in a scenario where my variables are either set and defined, or they aren''t (undefined, unset, null, blank, etc). Not to say that a situation like this won''t occur in future, but as it seems to be quite a unique issue I''m not surprised that the PHP devs haven''t bothered to put this in.
If I run the following:
echo ''<?php echo $foo; ?>'' | php
I get an error:
PHP Notice: Undefined variable: foo in /home/altern8/- on line 1
If I run the following:
echo ''<?php if ( isset($foo) ) { echo $foo; } ?>'' | php
I do not get the error.
If I have a variable that should be set, I usually do something like the following.
$foo = isset($foo) ? $foo : null;
o
if ( ! isset($foo) ) $foo = null;
That way, later in the script, I can safely use $foo and know that it "is set", and that it defaults to null. Later I can if ( is_null($foo) ) { /* ... */ }
if I need to and know for certain that the variable exists, even if it is null.
The full isset documentation reads a little more than just what was initially pasted. Yes, it returns false for a variable that was previously set but is now null, but it also returns false if a variable has not yet been set (ever) and for any variable that has been marked as unset. It also notes that the NULL byte ("/0") is not considered null and will return true.
Determine whether a variable is set.
If a variable has been unset with unset(), it will no longer be set. isset() will return FALSE if testing a variable that has been set to NULL. Also note that a NULL byte ("/0") is not equivalent to the PHP NULL constant.
THE only way to know if a variable is defined in current scope ( $GLOBALS
is not trustworthy) is array_key_exists( ''var_name'', get_defined_vars() )
.