Próba przedstawienia przeglądu różnych dyskusji i odpowiedzi:
Nie ma jednej odpowiedzi na pytanie, która zastąpiłaby wszystkie isset
możliwe sposoby . Niektóre przypadki użycia są rozwiązywane przez inne funkcje, podczas gdy inne nie poddają się kontroli lub mają wątpliwą wartość poza kodowaniem. Inne przypadki użycia nie są „zepsute” ani „niespójne”, ale pokazują, dlaczego isset
reakcja na nie null
jest logicznym zachowaniem.
Rzeczywiste przypadki użycia (z rozwiązaniami)
1. Klucze tablic
Tablice mogą być traktowane jak zbiory zmiennych, ze unset
i isset
traktując je tak, jakby były. Ponieważ jednak można je powtarzać, zliczać itp., Brakująca wartość nie jest taka sama jak wartość, której wartość jest null
.
Odpowiedzią w tym przypadku jest użycie array_key_exists()
zamiastisset()
.
Ponieważ wymaga to sprawdzenia tablicy jako argumentu funkcji, PHP nadal będzie generować „powiadomienia”, jeśli sama tablica nie istnieje. W niektórych przypadkach można zasadnie argumentować, że każdy wymiar powinien zostać zainicjowany jako pierwszy, więc zawiadomienie wykonuje swoją pracę. W innych przypadkach array_key_exists
funkcja „rekurencyjna” , która z kolei sprawdza każdy wymiar tablicy, unikałaby tego, ale zasadniczo byłaby taka sama jak @array_key_exists
. Jest to również nieco styczne do obsługi null
wartości.
2. Właściwości obiektu
W tradycyjnej teorii programowania obiektowego enkapsulacja i polimorfizm są kluczowymi właściwościami obiektów; w implementacji klasy oparte OOP jak PHP, kapsułkowane właściwości są zadeklarowane jako część definicji klasy, a biorąc pod uwagę poziomy dostępu ( public
, protected
, lub private
).
Jednak PHP pozwala również dynamicznie dodawać właściwości do obiektu, tak jak klucze do tablicy, a niektórzy ludzie używają obiektów bezklasowych (technicznie instancji wbudowanej stdClass
, która nie ma metod ani funkcji prywatnej) w podobny sposób droga do tablic asocjacyjnych. Prowadzi to do sytuacji, w których funkcja może chcieć wiedzieć, czy dana właściwość została dodana do danego obiektu.
Podobnie jak w przypadku kluczy tablic, w języku znajduje się rozwiązanie do sprawdzania właściwości obiektu, zwane, w miarę rozsądnym,property_exists
.
Przypadki użycia, których nie można uzasadnić, z dyskusją
3. register_globals
i inne zanieczyszczenia globalnej przestrzeni nazw
register_globals
Funkcja dodana do zakresu zmiennych globalnych, których nazwiska zostały określone przez aspektów żądania HTTP (GET i POST parametrów i ciasteczka). Może to prowadzić do błędnego i niepewnego kodu, dlatego został domyślnie wyłączony od wersji PHP 4.2 wydanej w sierpniu 2000 r. I całkowicie usunięty w wersji PHP 5.4 wydanej w marcu 2012 r . Możliwe jest jednak, że niektóre systemy nadal działają z włączoną lub emulowaną tą funkcją. Możliwe jest również „zanieczyszczenie” globalnej przestrzeni nazw na inne sposoby, za pomocą global
słowa kluczowego lub $GLOBALS
tablicy.
Po pierwsze, register_globals
jest mało prawdopodobne , że sama nieoczekiwanie wytworzy null
zmienną, ponieważ wartości GET, POST i cookie zawsze będą ciągami znaków (z ''
ciągle wracającymi true
z isset
), a zmienne w sesji powinny być całkowicie pod kontrolą programisty.
Po drugie, zanieczyszczenie zmiennej o wartości null
stanowi problem tylko wtedy, gdy spowoduje to nadpisanie niektórych wcześniejszych inicjalizacji. „Nadpisywanie” niezainicjowanej zmiennej null
byłoby problematyczne, gdyby kod gdzieś indziej rozróżniał oba te stany, więc sama ta możliwość stanowi argument przeciwko takiemu rozróżnieniu.
4. get_defined_vars
icompact
Kilka rzadko używanych funkcji w PHP, takich jak get_defined_vars
i compact
, pozwala traktować nazwy zmiennych tak, jakby były kluczami w tablicy. W przypadku zmiennych globalnych tablica superglobalna$GLOBALS
umożliwia podobny dostęp i jest bardziej powszechna. Te metody dostępu będą zachowywać się inaczej, jeśli zmienna nie zostanie zdefiniowana w odpowiednim zakresie.
Gdy zdecydujesz się traktować zestaw zmiennych jako tablicę za pomocą jednego z tych mechanizmów, możesz wykonać na nim wszystkie te same operacje, co na dowolnej normalnej tablicy. W związku z tym patrz 1.
Funkcjonalność, która istniała tylko po to, by przewidzieć, jak te funkcje będą się zachowywać (np. „Czy w tablicy zwróci się klucz„ foo ” get_defined_vars
?)) Jest zbędna, ponieważ można po prostu uruchomić tę funkcję i dowiedzieć się bez żadnych niepożądanych efektów.
4a Zmienne zmienne ( $$foo
)
Chociaż nie do końca takie same, jak funkcje, które zamieniają zestaw zmiennych w tablicę asocjacyjną, większość przypadków wykorzystujących „zmienne zmienne” („przypisanie do zmiennej nazwanej na podstawie tej innej zmiennej”) może i powinna zostać zmieniona, aby zamiast tego użyć tablicy asocjacyjnej .
Zasadniczo nazwa zmiennej jest etykietą nadaną wartości przez programistę; jeśli określasz to w czasie wykonywania, tak naprawdę nie jest to etykieta, ale klucz w sklepie z kluczowymi wartościami. Praktycznie, nieużywając tablicy, tracisz zdolność liczenia, iterowania itp .; może również okazać się niemożliwe posiadanie zmiennej „poza” składnicą klucz-wartość, ponieważ może zostać ona nadpisana $$foo
.
Po zmianie na użycie tablicy asocjacyjnej kod będzie podlegał rozwiązaniu 1. Pośredni dostęp do właściwości obiektu (np. $foo->$property_name
) Można rozwiązać za pomocą rozwiązania 2.
5. isset
jest o wiele łatwiejszy do pisania niżarray_key_exists
Nie jestem pewien, czy to jest naprawdę istotne, ale tak, nazwy funkcji PHP mogą być czasem dość skomplikowane i niespójne. Najwyraźniej w prehistorycznych wersjach PHP użyto długości nazwy funkcji jako klucza skrótu, więc Rasmus celowo wymyślił nazwy funkcji htmlspecialchars
tak, aby miały niezwykłą liczbę znaków ...
Ale przynajmniej nie piszemy w Javie, co? ;)
6. Niezainicjowane zmienne mają typ
Strona podręcznika na temat zmiennych podstawowych zawiera następujące stwierdzenie:
Niezainicjowane zmienne mają domyślną wartość swojego typu w zależności od kontekstu, w którym są używane
Nie jestem pewien, czy w Zend Engine istnieje jakieś pojęcie „niezainicjowanego, ale znanego typu”, czy też czyta to zbyt wiele w oświadczeniu.
Oczywiste jest, że nie ma to praktycznego wpływu na ich zachowanie, ponieważ zachowania opisane na tej stronie dla niezainicjowanych zmiennych są identyczne z zachowaniem zmiennej, której wartość jest null
. Aby wybrać jeden z przykładów, jak $a
i $b
w tym kodzie skończy się jako liczbę całkowitą 42
:
unset($a);
$a += 42;
$b = null;
$b += 42;
(Pierwszy podniesie powiadomienie o niezadeklarowanej zmiennej, próbując sprawić, że napiszesz lepszy kod, ale nie będzie miało to żadnego wpływu na to, jak naprawdę działa kod.)
99. Wykrywanie, czy funkcja się uruchomiła
(Zachowaj ten ostatni, ponieważ jest znacznie dłuższy niż inne. Może później go zmodyfikuję ...)
Rozważ następujący kod:
$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!';
}
Jeśli some_function
może wrócić null
, istnieje możliwość, że echo
nie zostanie osiągnięty, nawet jeśli zostanie some_test
zwrócony true
. Intencją programisty było wykrycie, kiedy $result
nigdy nie zostało ustawione, ale PHP nie pozwala na to.
Istnieją jednak inne problemy z tym podejściem, które stają się jasne, jeśli dodasz zewnętrzną pętlę:
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!';
}
}
Ponieważ $result
nigdy nie jest jawnie inicjowany, nabierze wartości po przejściu pierwszego testu, uniemożliwiając stwierdzenie, czy kolejne testy przeszły, czy nie. To jest naprawdę bardzo częsty błąd, gdy zmienne nie są poprawnie inicjowane.
Aby to naprawić, musimy zrobić coś na linii, w której skomentowałem, że czegoś brakuje. Najbardziej oczywistym rozwiązaniem jest ustawienie $result
„wartości końcowej”, która some_function
nigdy nie może powrócić; jeśli tak null
, reszta kodu będzie działać poprawnie. Jeśli nie ma naturalnego kandydata na wartość końcową, ponieważ some_function
ma wyjątkowo nieprzewidywalny typ zwracany (który prawdopodobnie sam w sobie jest złym znakiem), $found
można zamiast tego użyć np. Dodatkowej wartości logicznej .
Pierwszy eksperyment myślowy: very_null
stała
PHP teoretycznie może zapewnić specjalną stałą - a także null
- do wykorzystania jako wartość końcową tutaj; prawdopodobnie zwrócenie tego z funkcji byłoby nielegalne lub byłoby do tego przymuszone null
, i to samo prawdopodobnie dotyczyłoby przekazania go jako argumentu funkcji. To sprawiłoby, że ten bardzo konkretny przypadek byłby nieco prostszy, ale jak tylko zdecydowałbyś ponownie rozłożyć kod - na przykład, aby umieścić wewnętrzną pętlę w osobnej funkcji - stałby się bezużyteczny. Jeśli stała mogłaby być przekazywana między funkcjami, nie można zagwarantować, some_function
że jej nie zwróci, więc nie byłaby użyteczna jako uniwersalna wartość końcowa.
Argument wykrywania niezainicjowanych zmiennych w tym przypadku sprowadza się do argumentu dla tej specjalnej stałej: jeśli zastąpisz komentarz unset($result)
i potraktujesz to inaczej $result = null
, wprowadzisz „wartość” $result
, której nie można przekazać, a jedynie wykryte przez określone funkcje wbudowane.
Myśl eksperyment drugi: licznik przydziału
Innym sposobem myślenia o to, o co if
pyta ostatni, jest „czy coś ma jakieś zadanie $result
?” Zamiast uważać to za specjalną wartość $result
, można by pomyśleć o tym jako o „metadanych” o zmiennej, trochę jak o „zabarwieniu zmiennych” przez Perla. Więc raczej niż isset
można to nazwać has_been_assigned_to
, i zamiast unset
, reset_assignment_state
.
Ale jeśli tak, to po co zatrzymywać się na boolean? Co jeśli chcesz wiedzieć, ile razy test przeszedł pomyślnie; możesz po prostu rozszerzyć swoje metadane na liczbę całkowitą i mieć get_assignment_count
i reset_assignment_count
...
Oczywiście dodanie takiej funkcji miałoby kompromis w zakresie złożoności i wydajności języka, dlatego należałoby dokładnie rozważyć jego oczekiwaną przydatność. Podobnie jak w przypadku very_null
stałej, byłby użyteczny tylko w bardzo wąskich okolicznościach i byłby podobnie odporny na ponowne faktoring.
Mam nadzieję, że oczywiste pytanie brzmi, dlaczego silnik wykonawczy PHP powinien z góry założyć, że chcesz śledzić takie rzeczy, zamiast pozostawić to jawne, używając normalnego kodu.