Próba przedstawienia przeglądu różnych dyskusji i odpowiedzi:
Nie ma jednej odpowiedzi na pytanie, która zastąpiłaby wszystkie issetmoż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 issetreakcja na nie nulljest logicznym zachowaniem.
Rzeczywiste przypadki użycia (z rozwiązaniami)
1. Klucze tablic
Tablice mogą być traktowane jak zbiory zmiennych, ze unseti issettraktują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_existsfunkcja „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 nullwartoś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_globalsi inne zanieczyszczenia globalnej przestrzeni nazw
register_globalsFunkcja 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ą globalsłowa kluczowego lub $GLOBALStablicy.
Po pierwsze, register_globalsjest mało prawdopodobne , że sama nieoczekiwanie wytworzy nullzmienną, ponieważ wartości GET, POST i cookie zawsze będą ciągami znaków (z ''ciągle wracającymi truez isset), a zmienne w sesji powinny być całkowicie pod kontrolą programisty.
Po drugie, zanieczyszczenie zmiennej o wartości nullstanowi problem tylko wtedy, gdy spowoduje to nadpisanie niektórych wcześniejszych inicjalizacji. „Nadpisywanie” niezainicjowanej zmiennej nullbył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_varsicompact
Kilka rzadko używanych funkcji w PHP, takich jak get_defined_varsi 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. issetjest 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 htmlspecialcharstak, 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 $ai $bw 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_functionmoże wrócić null, istnieje możliwość, że echonie zostanie osiągnięty, nawet jeśli zostanie some_testzwrócony true. Intencją programisty było wykrycie, kiedy $resultnigdy 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ż $resultnigdy 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_functionnigdy 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_functionma wyjątkowo nieprzewidywalny typ zwracany (który prawdopodobnie sam w sobie jest złym znakiem), $foundmożna zamiast tego użyć np. Dodatkowej wartości logicznej .
Pierwszy eksperyment myślowy: very_nullstał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 ifpyta 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ż issetmoż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_counti 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_nullstał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.