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 è isset
possibile 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é isset
la reazione null
è il comportamento logico.
Casi d'uso reali (con soluzioni)
1. Tasti array
Le matrici possono essere trattate come raccolte di variabili, trattandole unset
e isset
trattandole 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_globals
e altro inquinamento dello spazio dei nomi globale
La register_globals
funzione 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 global
parola chiave o l' $GLOBALS
array.
In primo luogo, register_globals
è improbabile che essa stessa produca inaspettatamente una null
variabile, poiché i valori GET, POST e cookie saranno sempre stringhe (con ''
ancora ritorno true
da 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 null
sarebbe 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_vars
ecompact
Alcune funzioni usate raramente in PHP, come get_defined_vars
e 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 htmlspecialchars
se 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 $a
e $b
in 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_function
può tornare null
, c'è la possibilità che echo
non venga raggiunto anche se some_test
restituito true
. L'intenzione del programmatore era di rilevare quando $result
non 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é $result
non 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 $result
un "valore terminale" che some_function
non 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_function
ha un tipo di ritorno estremamente imprevedibile (che probabilmente è un brutto segno in sé), è $found
possibile utilizzare invece un valore booleano aggiuntivo, ad esempio .
Esperimento del pensiero uno: la very_null
costante
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_function
non 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 $result
cui 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 if
sta 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 isset
si 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_count
e 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_null
costante, 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.