Mam kilka starszych aplikacji, które generują wiele komunikatów „xyz is undefined” i „undefined offset” podczas pracy na poziomie błędu E_NOTICE, ponieważ istnienie zmiennych nie jest jawnie sprawdzane przy użyciu isset()
i consorts.
Rozważam przepracowanie ich, aby uczynić je kompatybilnymi z E_NOTICE, ponieważ powiadomienia o brakujących zmiennych lub przesunięciach mogą uratować życie, mogą być pewne drobne ulepszenia wydajności do uzyskania i ogólnie jest to czystszy sposób.
Jednak nie podoba mi się to, co powoduje setki isset()
empty()
i array_key_exists()
w moim kodzie. Nadąża się, staje się mniej czytelny, nie zyskując niczego pod względem wartości ani znaczenia.
Jak mogę ustrukturyzować swój kod bez nadmiaru kontroli zmiennych, będąc jednocześnie kompatybilnym z E_NOTICE?
źródło
Odpowiedzi:
Dla zainteresowanych rozszerzyłem ten temat do małego artykułu, który zawiera poniższe informacje w nieco lepiej zorganizowanej formie: The Definitive Guide to PHP isset And empty
IMHO, powinieneś pomyśleć nie tylko o uczynieniu aplikacji „kompatybilną z E_NOTICE”, ale także o zmianie całości. Posiadanie setek punktów w kodzie, które regularnie próbują używać nieistniejących zmiennych, brzmi jak raczej źle skonstruowany program. Próba uzyskania dostępu do nieistniejących zmiennych nigdy nie powinna się zdarzyć, inne języki narzekają na to w czasie kompilacji. Fakt, że PHP pozwala ci to zrobić, nie oznacza, że powinieneś.
Te ostrzeżenia mają ci pomóc , a nie drażnić. Jeśli pojawi się ostrzeżenie „Próbujesz pracować z czymś, co nie istnieje!” Twoja reakcja powinna brzmieć: „Ups, moja wina, naprawię to jak najszybciej”. Jak inaczej zamierzasz odróżnić „zmienne, które działają dobrze, nieokreślone”, a szczerze błędny kod, który może prowadzić do poważnych błędów ? Jest to również powód, dla którego zawsze, zawsze , opracowujesz program z raportowaniem błędów ustawionym na 11 i ciągle podłączasz swój kod, dopóki nie ma ani jednej
NOTICE
jest wydane. Wyłączenie raportowania błędów dotyczy tylko środowisk produkcyjnych, aby uniknąć wycieku informacji i zapewnić lepsze wrażenia użytkownika, nawet w przypadku błędnego kodu.Opracować:
Zawsze będziesz potrzebować
isset
lubempty
gdzieś w kodzie, jedynym sposobem na ograniczenie ich występowania jest prawidłowe zainicjowanie zmiennych. W zależności od sytuacji można to zrobić na różne sposoby:Argumenty funkcji:
Nie ma potrzeby sprawdzania, czy są ustawione
$bar
lub$baz
są ustawione w funkcji, ponieważ po prostu je ustawiasz, jedyne, o co musisz się martwić, to to, czy ich wartość wyniesietrue
lubfalse
(lub cokolwiek innego).Zwykłe zmienne w dowolnym miejscu:
Zainicjuj swoje zmienne u góry bloku kodu, w którym zamierzasz ich użyć. To rozwiązuje
!isset
problem, zapewnia, że zmienne zawsze mają znaną wartość domyślną, daje czytelnikowi wyobrażenie o tym, na czym będzie działać poniższy kod, a tym samym służy również jako rodzaj samodokumentacji.Tablice:
Tak samo jak powyżej, inicjalizujesz tablicę wartościami domyślnymi i nadpisujesz je rzeczywistymi wartościami.
W pozostałych przypadkach, powiedzmy, szablon, w którym wyprowadzasz wartości, które mogą, ale nie muszą być ustawione przez kontroler, wystarczy sprawdzić:
Jeśli regularnie używasz
array_key_exists
, powinieneś ocenić, do czego go używasz. To robi różnicę tylko tutaj:Jak wspomniano powyżej, jeśli poprawnie inicjalizujesz zmienne, nie musisz sprawdzać, czy klucz istnieje, czy nie, ponieważ wiesz, że tak. Jeśli otrzymujesz tablicę z zewnętrznego źródła, wartość najprawdopodobniej nie będzie
null
jednak''
,0
,'0'
,false
lub coś podobnego, to znaczy wartość można ocenić zisset
lubempty
, w zależności od intencji. Jeśli regularnie ustawiasz klucz tablicy nanull
i chcesz, aby miał jakiekolwiek znaczenie, alefalse
np. Jeśli w powyższym przykładzie różne wynikiisset
iarray_key_exists
wpływają na logikę programu, powinieneś zadać sobie pytanie, dlaczego. Samo istnienie zmiennej nie powinno być ważne, tylko jej wartość powinna mieć znaczenie. Jeśli klucz totrue
/false
flaga, użyjtrue
albofalse
nienull
. Jedynym wyjątkiem byłyby biblioteki innych firm, które chcąnull
coś znaczyć, ale ponieważnull
jest to tak trudne do wykrycia w PHP, nie znalazłem jeszcze żadnej biblioteki, która to robi.źródło
if ($array["xyz"])
zamiastisset()
lubarray_key_exists()
które uważam za nieco uzasadnione, na pewno nie problemy strukturalne (popraw mnie, jeśli się mylę). Dodawaniearray_key_exists()
po prostu wygląda dla mnie na straszną stratę.array_key_exists
zamiast prostegoisset($array['key'])
lub!empty($array['key'])
. Jasne, oba dodają 7 lub 8 znaków do twojego kodu, ale raczej nie nazwałbym tego problemem. Pomaga również w wyjaśnieniu kodu:if (isset($array['key']))
oznacza, że ta zmienna jest rzeczywiście opcjonalna i może być nieobecna, podczas gdyif ($array['key'])
oznacza po prostu „jeśli prawda”. Jeśli otrzymasz powiadomienie o tym drugim, wiesz, że twoja logika jest gdzieś schrzaniona.Po prostu napisz do tego funkcję. Coś jak:
którego możesz użyć jako
Zrób to samo dla trywialnej rzeczy jak
get_number()
,get_boolean()
,get_array()
i tak dalej.źródło
<input name="something[]" />
. Spowodowałoby to błąd (ponieważ przycinania nie można zastosować do tablic) przy użyciu powyższego kodu, w tym przypadku należy użyćis_string
i ewentualniestrval
. Nie jest to zwykły przypadek, w którym należy użyćget_array
któregoś z nich, ponieważ dane wejściowe użytkownika (złośliwe) mogą być cokolwiek, a parser danych wejściowych użytkownika i tak nigdy nie powinien generować błędów.Uważam, że jednym z najlepszych sposobów radzenia sobie z tym problemem jest dostęp do wartości tablic GET i POST (COOKIE, SESSION itp.) Za pośrednictwem klasy.
Utwórz klasę dla każdej z tych tablic i zadeklaruj
__get
i__set
metody ( przeciążenie ).__get
akceptuje jeden argument, który będzie nazwą wartości. Ta metoda powinna sprawdzić tę wartość w odpowiedniej tablicy globalnej, używającisset()
lubempty()
i zwrócić wartość, jeśli istnieje, lubnull
(lub inną wartość domyślną) w inny sposób.Następnie możesz bezpiecznie uzyskać dostęp do wartości tablic w ten sposób:
$POST->username
i wykonać dowolną walidację, jeśli jest to konieczne, bez użycia żadnejisset()
s lubempty()
s. Jeśliusername
nie istnieje w odpowiedniej tablicy globalnej,null
zostanie zwrócony, więc nie zostaną wygenerowane żadne ostrzeżenia ani powiadomienia.źródło
Nie mam nic przeciwko używaniu
array_key_exists()
funkcji. W rzeczywistości wolę używać tej konkretnej funkcji, zamiast polegać na funkcjachhakerskich,które mogą zmienić swoje zachowanie w przyszłości,jak(przekreślono, aby uniknąć podatności ).empty
iisset
Używam jednak prostej funkcji, która jest przydatna w tym i kilku innych sytuacjach związanych z indeksami tablic :
Załóżmy, że masz następujące tablice:
Jak uzyskać „wartość” z tablic? Prosty:
Mamy już uwzględnione tablice jedno i wielowymiarowe, co jeszcze możemy zrobić?
Weźmy na przykład następujący fragment kodu:
Dość nudne, prawda? Oto inne podejście wykorzystujące
Value()
funkcję:Jako dodatkowy przykład weźmy
RealIP()
funkcję do testu:Schludnie, co? ;)
źródło
isset
iempty
są konstrukcje językowe , a nie funkcje. Po drugie, jeśli jakiekolwiek podstawowe funkcje biblioteczne / konstrukcje językowe zmienią swoje zachowanie, możesz zostać wkręcony lub nie. A jeśliarray_key_exists
zmieni swoje zachowanie? Odpowiedź brzmi: nie będzie, o ile używasz go zgodnie z dokumentacją. Iisset
jest udokumentowany jako dokładnie taki używany. Funkcje w najgorszym przypadku są przestarzałe w stosunku do wersji głównej lub dwóch. Zespół NIH jest zły!array_key_exists()
sprawdzaniu, czy klucz istnieje w tablicy ?!array_key_exists()
został stworzony właśnie w tym celu , raczej polegam na nim w tym celu niżisset()
iw szczególnościempty()
którego oficjalny opis brzmi: „określ, czy zmienna jest pusta”, nie wspomina o tym, czy rzeczywiście istnieje. Twój komentarz i głos przeciwny są jednym z najbardziej absurdalnych, jakich byłem świadkiem przez cały miesiąc .isset
iempty
nie są bardziej ani mniej wiarygodni niżarray_key_exists
i potrafią wykonać dokładnie to samo zadanie. Twój drugi, rozwlekły przykład może być napisany tak, jak$domain = isset($domain['host']) ? $domain['host'] : 'N/A';
tylko z podstawowymi funkcjami języka, bez dodatkowych wywołań funkcji lub deklaracji potrzebnych (zauważ, że niekoniecznie zalecam użycie operatora trójskładnikowego; o)). W przypadku zwykłych zmiennych skalarnych nadal będziesz musiał używaćisset
lubempty
, i możesz ich używać do tablic dokładnie w ten sam sposób. „Niezawodność” to zły powód, aby tego nie robić.Jestem tu z tobą. Ale projektanci PHP popełnili o wiele gorsze błędy. Oprócz zdefiniowania funkcji niestandardowej dla dowolnego odczytu wartości, nie można tego obejść.
źródło
params["width"] = params["width"] || 5
aby ustawić wartości domyślne zamiast tego całego nonsensu zisset()
połączeniami.register_globals
imagic_quotes
. W porównaniu z tymi problemami niezainicjalizowane zmienne wyglądają prawie nieszkodliwie.Używam tych funkcji
Przykłady
źródło
Witamy w operatorze łączenia wartości null (PHP> = 7.0.1):
PHP mówi:
źródło
Utwórz funkcję, która zwraca,
false
jeśli nie jest ustawiona, a jeśli jest określona,false
jeśli jest pusta. Jeśli poprawne, zwraca zmienną. Możesz dodać więcej opcji, jak widać w poniższym kodzie:źródło
Oprogramowanie nie działa magicznie dzięki łasce Boga. Jeśli spodziewasz się czegoś, czego brakuje, musisz odpowiednio sobie z tym poradzić.
Jeśli go zignorujesz, prawdopodobnie tworzysz luki w zabezpieczeniach w swoich aplikacjach. W językach statycznych dostęp do niezdefiniowanej zmiennej jest po prostu niemożliwy. Nie skompiluje ani nie zawiesi twojej aplikacji, jeśli jest zerowa.
Ponadto sprawia, że aplikacja jest nie do utrzymania i oszalejesz, gdy wydarzy się coś nieoczekiwanego. Surowość językowa jest koniecznością, a PHP z założenia jest błędne w wielu aspektach. Jeśli nie będziesz tego świadomy, staniesz się złym programistą.
źródło
Nie jestem pewien, jaka jest twoja definicja czytelności, ale właściwe użycie bloków empty (), isset () i try / throw / catch jest bardzo ważne dla całego procesu.
Jeśli Twoje E_NOTICE pochodzi z $ _GET lub $ _POST, to należy je sprawdzić pod kątem funkcji empty () wraz ze wszystkimi innymi kontrolami bezpieczeństwa, które powinny przejść te dane.
Jeśli pochodzi z zewnętrznych źródeł lub bibliotek, powinien być opakowany w try / catch.
Jeśli pochodzi z bazy danych, należy sprawdzić $ db_num_rows () lub jej odpowiednik.
Jeśli pochodzi ze zmiennych wewnętrznych, należy je odpowiednio zainicjalizować. Często tego typu powiadomienia pochodzą z przypisania nowej zmiennej do wartości zwracanej przez funkcję, która zwraca FALSE w przypadku błędu. Powinny być opakowane w test, który w przypadku niepowodzenia może albo przypisać zmiennej akceptowalną wartość domyślną, którą może obsłużyć kod, albo zgłosić wyjątek, który może obsłużyć kod.
Te rzeczy wydłużają kod, dodają dodatkowe bloki i dodają dodatkowe testy, ale nie zgadzam się z tobą, ponieważ myślę, że zdecydowanie dodają dodatkową wartość.
źródło
A co z używaniem
@
operatora?Na przykład:
Możesz powiedzieć, że jest to złe, ponieważ nie masz kontroli nad tym, co dzieje się "wewnątrz" $ foo (jeśli było to na przykład wywołanie funkcji, które zawiera błąd PHP), ale jeśli używasz tej techniki tylko dla zmiennych, jest to równoważne z:
źródło
if(isset($foo))
właściwie wystarczy. Zwróci,TRUE
jeśli wynikiem wyrażenia jestTRUE
.