Il modo migliore per verificare l'esistenza di una variabile in PHP; isset () è chiaramente rotto


187

Dai isset()documenti :

isset() will return FALSE if testing a variable that has been set to NULL.

Fondamentalmente, isset()non controlla se la variabile è impostata affatto, ma se è impostata su qualsiasi cosaNULL .

Detto questo, qual è il modo migliore per verificare effettivamente l'esistenza di una variabile? Ho provato qualcosa del tipo:

if(isset($v) || @is_null($v))

( @è necessario per evitare l'avviso quando $vnon è impostato) ma is_null()presenta un problema simile a isset(): ritorna TRUEsu variabili non impostate! Sembra anche che:

@($v === NULL)

funziona esattamente come @is_null($v) , quindi è anche fuori.

Come dovremmo verificare in modo affidabile l'esistenza di una variabile in PHP?


Modifica: c'è chiaramente una differenza nel PHP tra le variabili che non sono impostate e le variabili che sono impostate su NULL:

<?php
$a = array('b' => NULL);
var_dump($a);

PHP mostra che $a['b']esiste e ha un NULLvalore. Se aggiungi:

var_dump(isset($a['b']));
var_dump(isset($a['c']));

puoi vedere l'ambiguità di cui sto parlando con la isset()funzione. Ecco l'output di tutti e tre questi var_dump()s:

array(1) {
  ["b"]=>
  NULL
}
bool(false)
bool(false)

Ulteriore modifica: due cose.

Uno, un caso d'uso. Una matrice che viene trasformata nei dati di un'istruzione SQL UPDATE, in cui le chiavi della matrice sono le colonne della tabella e i valori della matrice sono i valori da applicare a ciascuna colonna. Qualsiasi colonna della tabella può contenere un NULLvalore, indicato passando un NULLvalore nella matrice. È necessario un modo per distinguere tra una chiave di array non esistente e il valore di un array impostato su NULL; questa è la differenza tra non aggiornare il valore della colonna e aggiornare il valore della colonna inNULL .

In secondo luogo, la risposta di Zoredache , array_key_exists()funziona correttamente, per il mio caso d'uso di cui sopra e per le variabili globali:

<?php
$a = NULL;
var_dump(array_key_exists('a', $GLOBALS));
var_dump(array_key_exists('b', $GLOBALS));

uscite:

bool(true)
bool(false)

Dal momento che gestisce correttamente praticamente ovunque posso vedere che c'è qualche ambiguità tra variabili che non esistono e variabili che sono impostate su NULL, sto chiamando array_key_exists()il modo più semplice ufficiale in PHP per verificare veramente l'esistenza di una variabile .

(Solo un altro caso che mi viene in mente è per le proprietà di classe, per le quali esiste property_exists(), che, secondo i suoi documenti , funziona in modo simile a array_key_exists()quello in cui distingue correttamente tra non essere impostato e essere impostato su NULL.)


Non puoi controllare - ma perché è necessario?
troppo php,

12
NULL ha un significato molto specifico in PHP, ed è un concetto completamente separato dal fatto che una variabile sia impostata o meno.
Chazomaticus,

33
Ci sono ragioni per distinguere tra null e inesistente. Ad esempio, stai costruendo un oggetto per rappresentare una riga in una tabella del database. Per ogni colonna della riga, si crea una variabile privata, accessibile solo tramite il metodo getter dell'oggetto. Supponiamo che il valore di una colonna sia nullo. Ora, come fa quel metodo getter a sapere se non esiste una colonna del genere nella tabella o se questo oggetto ha solo un valore null lì? Fortunatamente, nel mio caso, la variabile privata è in realtà una voce in un array privato, quindi posso usare array_key_exists, ma questo è un vero problema.
Nathan Long,

1
È stato rimosso dalle nuove versioni di PHP, sì. Sfortunatamente, non è passato da ogni distribuzione di PHP. Inoltre, sembra un inutile dettaglio semantico discutere se stiamo parlando di elementi o variabili di array. Indipendentemente da quali standard pensi che il codice debba rispettare, è utile sapere come ovviare a un'incoerenza nel linguaggio PHP.
Chazomaticus,

2
@chazomaticus Ma le variabili e gli elementi dell'array sono cose fondamentalmente diverse ; solo perché puoi fare alcune delle stesse cose con loro non significa che siano o dovrebbero essere intercambiabili al 100%. Non c'è "incoerenza nel linguaggio PHP" qui, solo qualcosa che non ti piace / capisci. Per quanto riguarda register_globals, sto ancora lottando per pensare a una situazione in cui anche questo richiederebbe una tale distinzione, dal momento che qualsiasi cosa registrata dalla richiesta HTTP sarebbe sempre una stringa, non null.
IMSoP,

Risposte:


97

Se la variabile che stai controllando fosse nell'ambito globale, potresti fare:

array_key_exists('v', $GLOBALS) 

3
Ah ah! ORA stai parlando! Come lo faresti, per esempio, per le proprietà della classe?
Chazomaticus,

22
Come variante, se il controllo deve funzionare anche per variabili di ambito locali, on può fare un $defined_vars = get_defined_vars();e quindi testare via array_key_exists('v', $defined_vars);.
Henrik Opel,

1
Questo mi sembra un po 'brutto, ma nel caso in cui stai effettivamente controllando un elemento array, ha molto più senso: isset($foo[$bar])diventaarray_key_exists($bar, $foo)
Arild

property_existssembra promettente, tranne per questo:> La funzione property_exists () non è in grado di rilevare proprietà che sono magicamente accessibili usando il metodo magico __get.
alexw,

@alexw Le variabili "create" tramite __get non esistono. __get è un codice arbitrario utilizzato come fallback per variabili inesistenti, che può restituire tutto ciò che desidera indipendentemente dal fatto che siano mai stati memorizzati dati rilevanti.
Brilliand,

46

Tentativo di fornire una panoramica delle varie discussioni e risposte:

Non esiste una risposta unica alla domanda che può sostituire tutti i modi in cui è issetpossibile utilizzare. Alcuni casi d'uso sono affrontati da altre funzioni, mentre altri non resistono al controllo o hanno un valore dubbio oltre il codice golf. Lungi dall'essere "spezzati" o "incoerenti", altri casi d'uso dimostrano perché issetla reazione nullè il comportamento logico.

Casi d'uso reali (con soluzioni)

1. Tasti array

Le matrici possono essere trattate come raccolte di variabili, trattandole unsete issettrattandole come se lo fossero. Tuttavia, poiché possono essere ripetuti, conteggiati, ecc., Un valore mancante non è uguale a uno il cui valore è null.

La risposta in questo caso è usare al posto diarray_key_exists()isset() .

Poiché questo richiede l'array per verificare come argomento di funzione, PHP genererà comunque "avvisi" se l'array stesso non esiste. In alcuni casi, si può validamente sostenere che ogni dimensione avrebbe dovuto essere inizializzata per prima, quindi l'avviso sta facendo il suo lavoro. Per altri casi, una funzione "ricorsiva" array_key_exists, che controllava a turno ciascuna dimensione dell'array, avrebbe evitato ciò, ma sarebbe sostanzialmente la stessa @array_key_exists. È anche un po 'tangenziale per la gestione dinull valori.

2. Proprietà dell'oggetto

Nella teoria tradizionale della "programmazione orientata agli oggetti", l'incapsulamento e il polimorfismo sono proprietà chiave degli oggetti; in un'implementazione OOP basato su classi come PHP di, le proprietà incapsulati sono dichiarate come parte della definizione di classe, e dato livelli di accesso ( public, protected, o private).

Tuttavia, PHP ti consente anche di aggiungere dinamicamente proprietà a un oggetto, come faresti con le chiavi di un array, e alcune persone usano oggetti senza classe (tecnicamente, istanze del built-in stdClass, che non ha metodi o funzionalità privata) in un simile modo di matrici associative. Questo porta a situazioni in cui una funzione può voler sapere se una particolare proprietà è stata aggiunta all'oggetto che le è stato assegnato.

Come per le chiavi dell'array, una soluzione per il controllo delle proprietà degli oggetti è inclusa nella lingua, chiamata, abbastanza ragionevolmente,property_exists .

Casi d'uso non giustificabili, con discussione

3. register_globalse altro inquinamento dello spazio dei nomi globale

La register_globalsfunzione ha aggiunto variabili all'ambito globale i cui nomi sono stati determinati da aspetti della richiesta HTTP (parametri GET e POST e cookie). Questo può portare a buggy e codice insicuro, motivo per cui è stato disabilitato di default dal PHP 4.2, rilasciato nell'agosto 2000 e rimosso completamente in PHP 5.4, rilasciato nel marzo 2012 . Tuttavia, è possibile che alcuni sistemi siano ancora in esecuzione con questa funzione abilitata o emulata. È anche possibile "inquinare" lo spazio dei nomi globale in altri modi, utilizzando la globalparola chiave o l' $GLOBALSarray.

In primo luogo, register_globalsè improbabile che essa stessa produca inaspettatamente una nullvariabile, poiché i valori GET, POST e cookie saranno sempre stringhe (con ''ancora ritorno trueda isset) e le variabili nella sessione dovrebbero essere interamente sotto il controllo del programmatore.

In secondo luogo, l'inquinamento di una variabile con il valore nullè solo un problema se questo sovrascrive un'inizializzazione precedente. "Sovrascrivere" una variabile non inizializzata con nullsarebbe problematico solo se il codice da qualche altra parte distinguesse tra i due stati, quindi da solo questa possibilità è un argomento contro fare una tale distinzione.

4. get_defined_varsecompact

Alcune funzioni usate raramente in PHP, come get_defined_varse compact, consentono di trattare i nomi delle variabili come se fossero chiavi in ​​un array. Per le variabili globali, l'array super-globale$GLOBALS consente un accesso simile ed è più comune. Questi metodi di accesso si comporteranno diversamente se una variabile non è definita nell'ambito pertinente.

Dopo aver deciso di trattare un insieme di variabili come un array utilizzando uno di questi meccanismi, è possibile eseguire tutte le stesse operazioni su di esso come su qualsiasi array normale. Di conseguenza, vedere 1.

La funzionalità che esisteva solo per prevedere il modo in cui queste funzioni stanno per comportarsi (ad es. "Ci sarà una chiave 'pippo' nell'array restituito da get_defined_vars?") È superflua, dal momento che puoi semplicemente eseguire la funzione e scoprire senza effetti negativi.

4a. Variabili variabili ( $$foo)

Sebbene non siano esattamente le stesse funzioni che trasformano un insieme di variabili in un array associativo, la maggior parte dei casi che utilizzano "variabili variabili" ("assegnare a una variabile denominata in base a quest'altra variabile") può e deve essere modificata per utilizzare invece un array associativo .

Un nome di variabile, fondamentalmente, è l'etichetta data a un valore dal programmatore; se lo stai determinando in fase di esecuzione, in realtà non è un'etichetta ma una chiave in un archivio di valori-chiave. Più praticamente, non usando un array, stai perdendo la capacità di contare, iterare, ecc .; può anche diventare impossibile avere una variabile "esterna" all'archivio valori-chiave, poiché potrebbe essere sovrascritta da $$foo.

Una volta modificato per utilizzare un array associativo, il codice sarà suscettibile alla soluzione 1. L'accesso indiretto alla proprietà dell'oggetto (ad es. $foo->$property_name) Può essere indirizzato con la soluzione 2.

5. issetè molto più facile da digitare diarray_key_exists

Non sono sicuro che questo sia davvero rilevante, ma sì, i nomi delle funzioni di PHP possono essere piuttosto prolissi e talvolta incoerenti. Apparentemente, le versioni preistoriche di PHP usavano la lunghezza di un nome di funzione come chiave hash, quindi i Rasmus avevano deliberatamente inventato nomi di funzioni come htmlspecialcharsse avessero un numero insolito di caratteri ...

Comunque, almeno non stiamo scrivendo Java, eh? ;)

6. Le variabili non inizializzate hanno un tipo

La pagina di manuale sulle nozioni di base sulle variabili include questa affermazione:

Le variabili non inizializzate hanno un valore predefinito del loro tipo a seconda del contesto in cui vengono utilizzate

Non sono sicuro se nel motore Zend sia presente qualche nozione di "tipo non inizializzato ma noto" o se ciò stia leggendo troppo nell'affermazione.

Ciò che è chiaro è che non fa alcuna differenza pratica nel loro comportamento, poiché i comportamenti descritti in quella pagina per variabili non inizializzate sono identici al comportamento di una variabile il cui valore è null. Per scegliere un esempio, entrambi $ae $bin questo codice finiranno come intero 42:

unset($a);
$a += 42;

$b = null;
$b += 42;

(Il primo genererà un avviso su una variabile non dichiarata, nel tentativo di farti scrivere codice migliore, ma non farà alcuna differenza sul modo in cui il codice viene effettivamente eseguito.)

99. Rilevare se una funzione è stata eseguita

(Mantenendolo per ultimo, poiché è molto più lungo degli altri. Forse lo modificherò più avanti ...)

Considera il seguente codice:

$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!';
}

Se some_functionpuò tornare null, c'è la possibilità che echonon venga raggiunto anche se some_testrestituito true. L'intenzione del programmatore era di rilevare quando $resultnon era mai stato impostato, ma PHP non consente loro di farlo.

Tuttavia, ci sono altri problemi con questo approccio, che diventano chiari se aggiungi un ciclo esterno:

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!';
    }
}

Poiché $resultnon viene mai inizializzato esplicitamente, assumerà un valore al superamento del primo test, rendendo impossibile stabilire se i test successivi sono stati superati o meno. Questo è in realtà un bug estremamente comune quando le variabili non sono inizializzate correttamente.

Per risolvere questo problema, dobbiamo fare qualcosa sulla linea in cui ho commentato che manca qualcosa. La soluzione più ovvia è impostare $resultun "valore terminale" che some_functionnon può mai tornare; in questo caso null, il resto del codice funzionerà correttamente. Se non esiste un candidato naturale per un valore terminale perché some_functionha un tipo di ritorno estremamente imprevedibile (che probabilmente è un brutto segno in sé), è $foundpossibile utilizzare invece un valore booleano aggiuntivo, ad esempio .

Esperimento del pensiero uno: la very_nullcostante

PHP potrebbe teoricamente fornire una costante speciale - oltre che null- da utilizzare come valore terminale qui; presumibilmente, sarebbe illegale restituirlo da una funzione, o sarebbe costretto a farlo null, e lo stesso si applicherebbe probabilmente al passaggio come argomento di una funzione. Ciò renderebbe questo caso molto specifico leggermente più semplice, ma non appena decidessi di ricodificare il codice - per esempio, per mettere il ciclo interno in una funzione separata - diventerebbe inutile. Se la costante potesse essere passata tra le funzioni, non si poteva garantire che some_functionnon l'avrebbe restituita, quindi non sarebbe più utile come valore terminale universale.

L'argomento per il rilevamento di variabili non inizializzate in questo caso si riduce all'argomento per quella costante speciale: se si sostituisce il commento con unset($result)e lo si tratta in modo diverso da quello $result = null, si sta introducendo un "valore" per $resultcui non può essere passato e può essere solo rilevato da specifiche funzioni integrate.

Esperimento di pensiero due: contatore delle assegnazioni

Un altro modo di pensare a ciò che l'ultimo ifsta chiedendo è "ha qualcosa a cui è stato assegnato un compito $result?" Invece di considerarlo un valore speciale di $result, potresti forse pensare a questo come "metadati" sulla variabile, un po 'come il "contaminazione variabile" di Perl. Quindi, piuttosto che issetsi potrebbe chiamare has_been_assigned_to, e piuttosto che unset, reset_assignment_state.

Ma se è così, perché fermarsi a un booleano? Cosa succede se si desidera sapere quante volte ha superato il test; potresti semplicemente estendere i tuoi metadati a un numero intero e avere get_assignment_counte reset_assignment_count...

Ovviamente, l'aggiunta di tale funzionalità avrebbe un compromesso in termini di complessità e prestazioni della lingua, quindi dovrebbe essere attentamente valutata rispetto alla sua utilità prevista. Come per una very_nullcostante, sarebbe utile solo in circostanze molto ristrette e sarebbe altrettanto resistente al ricopertura.

La domanda, ovviamente, ovvia, è il motivo per cui il motore di runtime di PHP dovrebbe assumere in anticipo che si desidera tenere traccia di tali cose, piuttosto che lasciarti fare esplicitamente, usando il normale codice.


Per quanto riguarda le classi e le proprietà, purtroppo property_exists () non funziona quando la proprietà è array, ad esempio: Class {public $ property = array ()}. Genera un errore.
Andrew,

1
@Andrew Sembra funzionare bene per me: 3v4l.org/TnAY5 Vuoi fornire un esempio completo?
IMSoP,

sì, sembra funzionare bene, c'era qualcosa di sbagliato nella mia configurazione. Ci scusiamo per il falso allarme :)
Andrew,

20

A volte mi perdo un po 'cercando di capire quale operazione di confronto usare in una determinata situazione. isset()si applica solo a valori non inizializzati o esplicitamente nulli. Passare / assegnare null è un ottimo modo per garantire che un confronto logico funzioni come previsto.

Tuttavia, è un po 'difficile da pensare, quindi ecco una semplice matrice che confronta il modo in cui diversi valori verranno valutati da diverse operazioni:

|           | ===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    |

Per adattarsi alla tabella ho compresso un po 'le etichette:

  • $a; si riferisce a una variabile dichiarata ma non assegnata
  • tutto il resto nella prima colonna si riferisce a un valore assegnato, come:
    • $a = null;
    • $a = [];
    • $a = 0;
    • ...
  • le colonne si riferiscono a operazioni di confronto, come:
    • $a === null
    • isset($a)
    • empty($a)
    • $a ? true : false
    • ...

Tutti i risultati sono booleani, truevengono stampati e falseomessi.

Puoi eseguire tu stesso i test, controlla questa sintesi:
https://gist.github.com/mfdj/8165967


Forse non rientra nell'ambito di questa domanda, ma potresti voler aggiungere "0"al tavolo, per completezza e chiarezza emptydell'operazione
Rik Schaaf,

17

È possibile utilizzare il costrutto del linguaggio compatto per verificare l'esistenza di una variabile nulla. Le variabili che non esistono non verranno visualizzate nel risultato, mentre verranno visualizzati i valori null.

$x = null;
$y = 'y';

$r = compact('x', 'y', 'z');
print_r($r);

// Output:
// Array ( 
//  [x] => 
//  [y] => y 
// ) 

Nel caso del tuo esempio:

if (compact('v')) {
   // True if $v exists, even when null. 
   // False on var $v; without assignment and when $v does not exist.
}

Naturalmente per le variabili in ambito globale puoi anche usare array_key_exists ().

A proposito personale, eviterei situazioni come la peste in cui esiste una differenza semantica tra una variabile non esistente e la variabile che ha un valore nullo. PHP e la maggior parte delle altre lingue non pensa proprio che ci sia.


3
PHP no, ma non direi che la maggior parte delle altre lingue no. Quasi tutte le lingue che dichiarano variabili generano un errore se una variabile non è stata dichiarata, ma è possibile impostarle su NULL. Semanticamente, NULLdovrebbe significare "nessuna risorsa", ma non definire una variabile è un errore del programmatore.
M Miller,

1
@MMiller Certo, ma scrivere codice che segue un percorso nel caso di "nessuna risorsa" e un percorso diverso nel caso di "errore del programmatore" è abbastanza privo di senso. Se si desidera rilevare variabili non dichiarate durante il debug, utilizzare uno strumento di analisi statica, come se si trovassero potenziali errori in qualsiasi lingua.
IMSoP,

@MMiller, Cool, come ci hai pensato.
Pacerier,

1
@MMiller Ma non funziona come una confutazione, perché l'istruzione nella risposta riguarda esplicitamente "una variabile non esistente" e il tuo contro-esempio riguarda una proprietà dell'oggetto / chiave hash inesistente . La distinzione tra questi casi non è solo casuale.
IMSoP

1
@MMiller - questo è davvero un esempio migliore. Tuttavia, dopo oltre 20 anni di programmazione in linguaggi rigorosi, le situazioni in cui avevo bisogno di una distinzione tra undefinede nullsono così rare che non mi manca. IMHO, l'uso principale undefinedè "errore del programmatore in un linguaggio non rigoroso". In un linguaggio rigoroso, se ho bisogno di uno stato distinto per client did not state a value, allora dichiaro un valore appropriato alla situazione e lo provo. Nel peggiore dei casi, è necessario aggiungere una variabile flag separata. Ma farlo raramente è meglio che dover SEMPRE far fronte a DUE diversi stati senza valore !!
ToolmakerSteve

15

Spiegare NULL, pensando logicamente

Immagino che la risposta ovvia a tutto questo sia ... Non inizializzare le variabili come NULL, inizializzarle come qualcosa di rilevante per quello che dovrebbero diventare.

Trattare NULL correttamente

NULL deve essere trattato come "valore inesistente", che è il significato di NULL. La variabile non può essere classificata come esistente in PHP perché non è stato detto quale tipo di entità sta cercando di essere. Potrebbe anche non esistere, quindi PHP dice semplicemente "Va bene, non perché comunque non ha senso e NULL è il mio modo di dirlo".

Un argomento

Discutiamo ora. "Ma NULL è come dire 0 o FALSO o ''.

Sbagliato, 0-FALSO- '' sono ancora tutti classificati come valori vuoti, ma SONO specificati come un tipo di valore o risposta predeterminata a una domanda. FALSO è la risposta a sì o no, "" è la risposta al titolo inviato da qualcuno e 0 è la risposta a quantità o tempo ecc. SONO impostati come un tipo di risposta / risultato che li rende validi come impostati.

NULL è semplicemente una risposta, quindi non ci dice sì o no e non ci dice l'ora e non ci dice che è stata inviata una stringa vuota. Questa è la logica di base per comprendere NULL.

Sommario

Non si tratta di creare funzioni stravaganti per aggirare il problema, sta solo cambiando il modo in cui il tuo cervello guarda NULL. Se è NULL, supponi che non sia impostato come nulla. Se stai pre-definendo le variabili, pre-definiscile come 0, FALSE o "" a seconda del tipo di utilizzo che intendi per loro.

Sentiti libero di citare questo. È fuori dalla cima della mia testa logica :)


5
Bella risposta. Tante volte vedo persone che si lamentano di come odiano questa o quella caratteristica di una lingua. Ma sembrano supporre che "se non lo fa nel MIO modo, allora è rotto". Sì, ci sono decisioni sbagliate sul design. Ma ci sono anche sviluppatori molto affiatati!
curtisdf,

23
C'è una differenza ENORME tra la variabile non impostata e la variabile === null. Uno non esiste, l'altro ha valore null. Argomenta che null significa che nessun valore semplicemente non è vero. NULL È UN VALORE di tipo null. È un valore perfettamente valido e non c'è motivo per php di trattarlo come un valore inesistente, cosa che purtroppo fa. Sarebbe OK, se le variabili inesistenti fossero nulle e ogni variabile esistente non fosse nulla e l'assegnazione di null in variabile la annullerebbe. Ma ci sono MOLTE situazioni, in cui le funzioni restituiscono null come valore effettivo. Quindi siamo incasinati, perché non esiste un modo sanguinoso per provarlo.
Enrey,

2
So che non dovremmo "controllare" l'esistenza variabile in php, inferno, non c'è nemmeno un modo reale per controllarlo. Non ho intenzione di scrivere codice che dipende da esso, perché non è possibile in php. Questa è una limitazione di php. Esiste chiaramente una differenza tra variabile non impostata e nulla nulla, tuttavia php non fornisce alcun modo per distinguerle. Eppure molte meta funzionalità dipendono da esso internamente: la lettura di var inesistenti produce un avviso, isset($a['x'])ti dirà falso se xè nullo, eppure apparirà in count($a).. compactfunzionerà su tutte le variabili impostate, incluso nullse così via.
enrey,

3
Questa risposta è imperfetta in un modo importante: nella programmazione OO, null è la scelta logica per significare "nessun oggetto". Ad esempio, in circostanze ineccepibili quando una funzione può restituire un oggetto o nessun oggetto, null è la scelta ovvia. Tecnicamente in PHP, potrebbe essere usato falso o qualsiasi altro valore considerato falso in un contesto booleano, ma stai perdendo un po 'di purezza semantica. Pertanto, null è un valore perfettamente ragionevole per inizializzare una variabile che dovrebbe contenere un oggetto alla fine, perché è rilevante per quello che dovrebbe diventare.
Chazomaticus,

3
Fintanto che PHP genera errori per variabili non definite, ma non per null, allora c'è una differenza. Se null e undefined fossero davvero lo stesso concetto, allora PHP dovrebbe assumere le variabili undefined / undeclar predefinite su null e non generare mai un errore, ma nessuno lo vuole perché è un incubo di sviluppo. Null e indefinito potrebbero non essere realmente diversi nel contesto della semantica del valore, ma sono molto diversi quando si tratta di scrivere codice chiaro e debuggable.
Chris Middleton,

9

Le proprietà degli oggetti possono essere verificate per l'esistenza da property_exists

Esempio da un test unitario:

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");
    }
}

4

Come aggiunta alla discussione di greatbigmassive su cosa significa NULL , considera cosa significa "l'esistenza di una variabile".

In molte lingue, devi dichiarare esplicitamente ogni variabile prima di usarla ; questo può determinare il suo tipo, ma soprattutto dichiara il suo scopo . Una variabile "esiste" ovunque nel suo ambito, e in nessun luogo al di fuori di essa - che si tratti di un'intera funzione o di un singolo "blocco".

Nel suo ambito, una variabile assegna un significato a un'etichetta che tu, il programmatore, hai scelto. Al di fuori del suo ambito di applicazione, tale etichetta non ha senso (se si utilizza la stessa etichetta in un ambito diverso è sostanzialmente irrilevante).

In PHP, le variabili non devono essere dichiarate : prendono vita non appena ne hai bisogno. Quando scrivi per la prima volta su una variabile, PHP alloca una voce in memoria per quella variabile. Se leggi da una variabile che al momento non ha una voce, PHP considera tale variabile avere il valore NULL.

Tuttavia, in genere i rilevatori automatici di qualità del codice avvisano se si utilizza una variabile senza "inizializzarla" per prima. In primo luogo, questo aiuta a rilevare errori di battitura, come l'assegnazione $thingIdma la lettura da $thing_id; ma in secondo luogo, ti costringe a considerare l'ambito su cui quella variabile ha significato, proprio come farebbe una dichiarazione.

Qualsiasi codice a cui importa se una variabile "esiste" fa parte dell'ambito di tale variabile - indipendentemente dal fatto che sia stata inizializzata o meno, tu come programmatore hai dato a quell'etichetta il significato in quel punto del codice. Dal momento che lo stai usando, in un certo senso deve "esistere" e, se esiste, deve avere un valore implicito; in PHP, quel valore implicito è null.

A causa del modo in cui funziona PHP, è possibile scrivere codice che tratta lo spazio dei nomi delle variabili esistenti non come un ambito di etichette a cui hai dato significato, ma come una sorta di archivio di valori-chiave. È possibile, ad esempio, eseguire il codice come questo: $var = $_GET['var_name']; $$var = $_GET['var_value'];. Solo perché puoi, non significa che sia una buona idea.

Si scopre che PHP ha un modo molto migliore di rappresentare i negozi di valori-chiave, chiamati array associativi. E sebbene i valori di un array possano essere trattati come variabili, è anche possibile eseguire operazioni sull'array nel suo insieme. Se si dispone di un array associativo, è possibile verificare se contiene una chiave utilizzando array_key_exists().

Puoi anche usare gli oggetti in modo simile, impostando dinamicamente le proprietà, nel qual caso puoi usare property_exists()esattamente allo stesso modo. Naturalmente, se si definisce una classe, è possibile dichiarare quali proprietà ha - si può anche scegliere tra public, privatee protectedla portata.

Sebbene vi sia una differenza tecnica tra una variabile (al contrario di una chiave di matrice o una proprietà dell'oggetto) che non è stata inizializzata (o che è stata esplicitamente unset()) e una il cui valore è null, qualsiasi codice che considera tale differenza significativa sta usando le variabili in un modo in cui non sono pensate per essere utilizzate.


1
Ottimi punti, sebbene non esattamente una risposta alla domanda.
Chazomaticus,

1
Alla domanda esplicita "Come possiamo verificare in modo affidabile l'esistenza di una variabile in PHP?" la mia risposta è "non lo sei, ed ecco perché". Sia questa risposta che i grandi personaggi rispondono anche alla domanda implicita "perché si isset()comporta in questo modo?".
IMSoP

"Se leggi da una variabile che al momento non ha una voce, PHP considera quella variabile con il valore NULL." Questo è falso Una variabile non definita è semplicemente indefinita. Potrebbe restituire null quando si tenta di accedervi, ma ciò è irrilevante.
Hugo Zink,

@HugoZink Non è rilevante per cosa? Qualsiasi test che fai del valore di una variabile non definita ti dirà che il valore è null. Se quel valore esiste prima di guardarlo è una domanda per i filosofi, ma per quanto riguarda qualsiasi comportamento osservabile il valore è coerentemente null.
IMSoP

3

issetcontrolla se la variabile è impostata e, in tal caso, se il suo valore non è NULL. L'ultima parte (a mio avviso) non rientra nell'ambito di questa funzione. Non esiste una soluzione decente per determinare se una variabile è NULL perché non è impostata o perché è impostata esplicitamente su NULL .

Ecco una possibile soluzione:

$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

Un'altra soluzione alternativa consiste nel sondare l'output di 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

2

Non sono d'accordo con il tuo ragionamento su NULL e dire che devi cambiare la tua mentalità su NULL è semplicemente strano.

Penso che isset () non sia stato progettato correttamente, isset () dovrebbe dirti se la variabile è stata impostata e non dovrebbe riguardare il valore effettivo della variabile.

Che cosa succede se si stanno verificando i valori restituiti da un database e una delle colonne ha un valore NULL, si desidera comunque sapere se esiste anche se il valore è NULL ... no, non fidarsi di isset () qui.

allo stesso modo

$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 () avrebbe dovuto essere progettato per funzionare in questo modo:

if(isset($var) && $var===NULL){....

in questo modo lasciamo al programmatore il controllo dei tipi e non lo lasciamo a isset () supporre che non ci sia perché il valore è NULL - è solo uno stupido design


Il tuo esempio non sta verificando l'esistenza di una variabile, ma di una chiave di matrice. Esiste una soluzione a ciò, sotto forma di array_key_exists. Non dovresti mai trovarti in una situazione in cui non sai al momento dell'esecuzione se esiste una variabile effettiva.
IMSoP

@chazomaticus Beh, non dovresti mai trovarti in una situazione in cui register_globals è attivato, quindi resto fedele a questa affermazione.
IMSoP

Oh, sono d'accordo. Tuttavia, non tutti possono controllare dove viene distribuito il loro codice. È utile avere informazioni per ogni situazione, indipendentemente dal fatto che le cose "debbano" essere o meno.
Chazomaticus,

@chazomaticus Se il tuo problema è register_globals, allora la tua risposta non è una modifica isset(). Il manuale di PHP menziona "è davvero una buona pratica di programmazione inizializzare prima le variabili", che risolve register_globalsin fase di progettazione piuttosto che in fase di esecuzione. C'è anche una voce FAQ che fornisce una unregister_globals()funzione per gestirla in fase di esecuzione.
IMSoP

2

Aggiungerò due rapidi centesimi a questo. Uno dei motivi per cui questo problema è confuso è perché questo scenario sembra restituire lo stesso risultato con la segnalazione degli errori non completa:

$a = null;
var_dump($a); // NULL
var_dump($b); // NULL

Si potrebbe supporre da questo risultato che la differenza tra $a = nulle non definire $baffatto è nulla.

Segnalazione errori di avviamento:

NULL

Notice: Undefined variable: b in xxx on line n
NULL

Nota: ha generato un errore variabile non definito, ma il valore di output di var_dumpè ancora NULL.

PHP ha ovviamente una capacità interna di distinguere tra una variabile nulla e una variabile non definita. Mi sembra che ci dovrebbe essere una funzione integrata per verificare questo.

Penso che la risposta accettata sia buona per la maggior parte, ma se avessi intenzione di implementarla scriverei un wrapper per questo. Come accennato in precedenza in questa risposta , devo essere d'accordo sul fatto che non ho effettivamente riscontrato una situazione in cui questo è stato un problema. Mi sembra quasi sempre di finire in uno scenario in cui le mie variabili sono o impostate e definite, oppure non lo sono (non definito, non impostato, null, vuoto, ecc.). Per non dire che una situazione del genere non si verificherà in futuro, ma dato che sembra essere un problema piuttosto singolare, non mi sorprende che gli sviluppatori PHP non si siano preoccupati di inserirla.


L'avvertimento su variabili non definite è un suggerimento per il programmatore di aver fatto qualcosa di sbagliato nel codice. Al di fuori del debug (per il quale esistono strumenti al di fuori della lingua), non dovrebbe mai essere necessario che un programma rilevi un tale stato, perché il programmatore dovrebbe sempre sapere quali variabili stanno dichiarando.
IMSoP,

1

Se eseguo quanto segue:

echo '<?php echo $foo; ?>' | php

Ottengo un errore:

PHP Notice:  Undefined variable: foo in /home/altern8/- on line 1

Se eseguo quanto segue:

echo '<?php if ( isset($foo) ) { echo $foo; } ?>' | php

Non ricevo l'errore.

Se ho una variabile da impostare, di solito faccio qualcosa come il seguente.

$foo = isset($foo) ? $foo : null;

o

if ( ! isset($foo) ) $foo = null;

In questo modo, più avanti nello script, posso tranquillamente usare $ foo e sapere che "è impostato" e che il valore predefinito è null. Più tardi posso if ( is_null($foo) ) { /* ... */ }se devo e so per certo che la variabile esiste, anche se è nulla.

La documentazione completa di isset è un po 'più di ciò che è stato inizialmente incollato. Sì, restituisce false per una variabile precedentemente impostata ma ora è nulla, ma restituisce anche false se una variabile non è stata ancora impostata (mai) e per qualsiasi variabile che è stata contrassegnata come non impostata. Nota inoltre che il byte NULL ("\ 0") non è considerato nullo e restituirà true.

Determina se è impostata una variabile.

Se una variabile non è stata impostata con unset (), non verrà più impostata. isset () restituirà FALSE se si verifica una variabile che è stata impostata su NULL. Si noti inoltre che un byte NULL ("\ 0") non equivale alla costante NULL di PHP.


Ha ottenuto i documenti da quel link. Leggi la prima frase, il secondo paragrafo della sezione descrittiva sul link che hai fornito È esattamente quello che ha citato sopra.
Zoredache,

Questa non è una cattiva pratica per script semplici, ma in progetti complessi (ad es. OO di grandi dimensioni) diventa impossibile. Inoltre, come ho detto sopra, is_null () restituisce VERO per le variabili che non sono impostate, quindi non c'è davvero motivo di fare ciò che stai dicendo se non per evitare un avviso PHP.
Chazomaticus,

1
Per progetti "OO" ben progettati, perché questo dovrebbe essere un problema? Perché dovresti mai avere $ foo in un metodo che potrebbe non essere stato impostato prima del suo primo utilizzo?
Beau Simensen,

1

Prova a usare

unset($v)

Sembra che l'unica volta in cui una variabile non sia impostata sia quando è specificatamente non impostata ($ v). Sembra che il tuo significato di "esistenza" sia diverso dalla definizione di PHP. NULL esiste sicuramente, è NULL.


Non sono sicuro di cosa intendi. Se hai un array, con un elemento 'a', non devi unset () elemento 'b' perché l'elemento 'b' non esista in PHP, semplicemente non esiste. Stessa cosa per esempio con variabili globali, che puoi considerare come elementi dell'array $ GLOBALS.
Chazomaticus,

1
Ma sono d'accordo sul fatto che esiste una variabile con un valore NULL.
Chazomaticus,

0

Devo dire che in tutti i miei anni di programmazione PHP, non ho mai avuto problemi a isset()restituire false su una variabile null. OTOH, ho riscontrato problemi con il isset()fallimento di una voce di array null - ma array_key_exists()funziona correttamente in quel caso.

Per alcuni confronti, Icon definisce esplicitamente una variabile non utilizzata come restituita, &nullquindi si utilizza il test is-null in Icon per verificare anche la presenza di una variabile non impostata. Questo rende le cose più facili. D'altra parte, Visual BASIC ha più stati per una variabile che non ha un valore (Null, Empty, Nothing, ...) e spesso è necessario verificarne più di uno. Questo è noto per essere una fonte di bug.


0

Secondo il Manuale PHP per la funzione empty (), "Determina se una variabile è considerata vuota. Una variabile è considerata vuota SE NON ESISTE o se il suo valore è FALSO. Empty () non genera un avviso se il la variabile non esiste. " (La mia enfasi.) Ciò significa che la funzione empty () dovrebbe qualificarsi come "il modo migliore per testare l'esistenza di una variabile in PHP", secondo il titolo Domanda.

Tuttavia, questo non è abbastanza buono, perché la funzione empty () può essere ingannata da una variabile che esiste ed è impostata su NULL.

Sto interrompendo la mia risposta precedente per presentare qualcosa di meglio, perché è meno ingombrante della mia risposta originale (che segue questa interruzione, per il confronto).

  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
  }

Due semplici righe di codice possono utilizzare la funzione sopra per rivelare se una variabile non è definita:

  ob_start();                           //pass all output messages (including errors) to a buffer
  if(undef($testvar===null))            //in this case the variable being tested is $testvar

Puoi seguire queste due righe con qualcosa di appropriato, come questo esempio:

    echo("variable is undefined");
  else
    echo("variable exists, holding some value");

Volevo mettere la chiamata a ob_start () e ($ testvar === null) all'interno della funzione e semplicemente passare la variabile alla funzione, ma non funziona. Anche se si tenta di utilizzare "passa per riferimento" della variabile alla funzione, la variabile DIVENTA definita e quindi la funzione non può mai rilevare che in precedenza non era stata definita. Ciò che viene presentato qui è un compromesso tra ciò che volevo fare e ciò che effettivamente funziona.

Il precedente implica che esiste un altro modo per evitare di imbattersi sempre nel messaggio di errore "Variabile non definita". (Il presupposto qui è, prevenire un messaggio del genere è il motivo per cui si desidera verificare se una variabile non è definita.)

   function inst(&$v) { return; }  //receive any variable passed by reference; instantiates the undefined

Basta chiamare quella funzione prima di fare qualcosa sul tuo $ testvar:

   inst($testvar);                //The function doesn't affect any value of any already-existing variable

Il valore della variabile appena istanziata è impostato su null, ovviamente!

(L'interruzione termina)

Quindi, dopo aver studiato e sperimentato, ecco qualcosa che funziona:

 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
 }

La spiegazione: una variabile $ er viene inizializzata su un valore predefinito di "nessun errore". Viene definita una "funzione gestore". Se $ testvar (la variabile che vogliamo sapere se non è definita) supera il test preliminare di funzione empty (), allora facciamo il test più approfondito. Chiamiamo la funzione set_error_handler () per utilizzare la funzione del gestore definita in precedenza. Quindi eseguiamo un semplice confronto di identità che coinvolge $ testvar, CHE SE SE NON DEFINITO SCENDE UN ERRORE. La funzione del gestore cattura l'errore e verifica specificamente se la ragione dell'errore è il fatto che la variabile non è definita. Il risultato viene inserito nella variabile di informazioni sull'errore $ er, che in seguito potremo testare per fare tutto ciò che vogliamo come risultato del sapere con certezza se è stato definito o meno $ testvar. Poiché è necessaria solo la funzione del gestore per questo scopo limitato, ripristiniamo la funzione di gestione degli errori originale. La funzione "myHndlr" deve essere dichiarata una sola volta; l'altro codice può essere copiato in qualsiasi posto sia appropriato, per $ testvar o qualsiasi altra variabile che vogliamo testare in questo modo.


1
Se l'intenzione è quella di evitare un avviso che le variabili non sono state dichiarate, la soluzione è correggere il codice per dichiararle correttamente. La tua instfunzione è sostanzialmente come l' @operatore di soppressione degli errori: "So che sto facendo qualcosa di sbagliato qui, ma voglio solo che quel messaggio scompaia, senza cambiare in alcun modo il funzionamento del mio codice".
IMSoP,

I metodi di rilevamento, d'altra parte, sono ingegnosi, ma sono ancora fermamente convinto che non dovresti mai averne altro se non quello di fare eco ai messaggi di avvertimento che stanno catturando. (Probabilmente dovresti chiarire che la versione del tuo buffer di output richiede che error_reporting sia impostato su high e display_errors per essere acceso.)
IMSoP

0

Credo che l'unica soluzione completa è quella di segnalare le comunicazioni con

error_reporting(E_ALL); // Enables E_NOTICE

Ma dovrai correggere tutti gli avvisi generati da variabili non definite, costanti, chiavi dell'array, proprietà della classe. Una volta fatto ciò, non dovrai preoccuparti della differenza tra variabili nulle e non dichiarate e l'ambiguità scompare.

L'abilitazione della notifica degli avvisi potrebbe non essere una buona alternativa in tutte le situazioni, ma ci sono buoni motivi per abilitarla:

Perché dovrei correggere gli errori E_NOTICE?

Nel mio caso è stato più di un anno a lavorare in un progetto senza di esso, ma è stato usato per fare attenzione a dichiarare le variabili, quindi è stato veloce per la transizione.


0

L'unico modo per sapere se una variabile è definita nell'ambito corrente ( $GLOBALSnon è affidabile) è array_key_exists( 'var_name', get_defined_vars() ).


1
Penso che sia quello che molte altre persone hanno detto prima, o sbaglio?
Stephan Vierkant,

-1

Preferisco usare non vuoto come metodo migliore per verificare l'esistenza di una variabile che a) esiste e b) non è nulla.

if (!empty($variable)) do_something();

2
empty()non verifica se la variabile è nulla, esso controlla se è falso-y, ad esempio, non uno dei ""(una stringa vuota), 0(0 come numero intero), 0.0(0 come un galleggiante), "0"(0 come una stringa), NULL, FALSE, array()(un array vuoto) e $var;(una variabile dichiarata, ma senza un valore). Supponiamo che tu abbia un campo radio richiesto in una forma con due ingressi con i valori 0e 1. Se si utilizza empty()per la convalida e l'utente seleziona 0quello, si inavvertitamente errore "campo obbligatorio non può essere vuoto". Vedi il manuale php.net/manual/en/function.empty.php
Halil Özgür
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.