Pokaż wszystkie błędy i ostrzeżenia, część 1

Photo by Ricardo Rocha on Unsplash

W swojej pracy zauważyłem, że nie jest rzadko sytuacją, gdy developerzy wyłączają wszystkie ostrzeżenia (warnings) i uwagi (notices) interpretera PHP. Może nawet częściej jest to spowodowane tym, że ktoś tak domyślnie ustawia PHP a developerzy nie dbają o to, aby jednak wszystkie te komunikaty pokazać. Osobiście jestem zwolennikiem wskazywania każdego błędu, łącznie z tymi ostrzegającymi, że się używa przestarzałych konstrukcji językowych (E_DEPRECATED). Wszystko to, rzecz jasna, na serwerze development oraz staging. Na serwerze live jest to z kolei niedopuszczalne, tam każdy błąd winien był „tłumiony” w zarodku. Ale powinien być logowany i trafiać do pliku, który co jakiś czas winien być sprawdzany (albo np. zmiana zawartości pliku powinna powodować wysłanie maila z ostrzeżeniem do właściwej osoby).

Tego typu zachowanie nie sprawi, że Twój kod będzie dobry. Ale jest świetnym pierwszym krokiem do tego, aby nie był zły. Posłużę się poniżej przykładami, używając do tego PHP w wersji 7.4 (aktualnie używam wersji 7.4.4).

Weźmy sobie taki kod, który jest pełen błędów! I w ogóle się nie uruchomi.

Po próbie uruchomienia tego kodu, dostaniemy szybką informację:

Nasz program w ogóle się nie uruchomi. Gdybyśmy mieli wyłączone pokazywanie błędów, zobaczylibyśmy po prostu biały ekran, może komunikat serwera o błędzie, ale nic byśmy nie wiedzieli o tym, w którym miejscu jest błąd i jakiego rodzaju. A jeżeli jeszcze nie mielibyśmy włączonego logowania błędów do pliku… To powodzenia z szukaniem. Oczywiście tego rodzaju błąd znalazłoby też dla nas dobre IDE czy analizator kodu, ale różnie z tym bywa. Zarówno z dostępnością takich narzędzi na stanowisku pracy, jak i samą chęcią korzystania z nich.

Dzięki temu, że mamy włączone wyświetlanie wszystkich błędów (odpowiadają za to linijki 4-5), wiemy, że błąd jest w linii 11 i jest to błąd „Parse error” czyli PHP nie jest w stanie właściwie zinterpretować tego, co napisaliśmy. Sprawdzamy i wiemy już dlaczego. Chcieliśmy użyć nowości w PHP 7.4, operatora spread, który pozwala w zasadzie na to samo co array_merge, ale z dwoma ogromnymi plusami. Po pierwsze, array_merge działa tylko na tablice a operator spread również na obiekty, które implementuję klasę Traversable (de facto iteratory). A po drugie, jest to konstrukcja języka – w odróżnieniu od array_merge, który jest funkcją. Powinno więc być szybsze i umożliwiać pewną pre-kompilację, jeśli używamy stałych. Więc co jest nie tak z tym operatorem? Te trzy kropki powinny być przed zmienną a nie po niej. Poprawiamy nasz kod, który w tym miejscu wygląda tak:

Włączamy nasz kod i… i prawie działa!

Wiemy, że nadal coś jest nie tak. Nie zobaczyliśmy słowa 'KONIEC', które powinno pojawić się na ekranie. Naszą uwagę zwracają liczne komunikaty błędów. Notice, Deprecated, Warning, Fatal error. Z tej listy fatalnie, nomen omen, brzmi fatal error 🙂 Śmiertelny błąd, który sprawił, że skrypt przestał się wykonywać. Co tam jest nie tak? Popatrzmy na ten błąd:

Podejrzanym jest linia nr 33, która wygląda tak:

No ale co jest złego w tym, że do właściwości nazwanej 'liczba' przypisujemy łańcuch znaków? PHP przecież nie rozumie słowa 'liczba'. Równie dobrze mogliśmy tam wstawić 'nogaSlonia' i dla PHP nie miałoby to znaczenia. Ano, ten fragment nie ma znaczenia. Dużo większe znaczenie ma deklaracja tej właściwości w jej klasie. Zerknijmy zatem na klasę:

I znaleźliśmy winnego. Zadeklarowaliśmy, że właściwość to 'int' a więc liczba całkowita. Wyrzucamy int i po sprawie. Ale, ale… To tylko jedno rozwiązanie, być może prawidłowe. Być może powinniśmy dopuścić, aby w tej właściwości lądowały łańcuchy, czy choćby wartości zmiennoprzecinkowe (typu float). Ale problem należy rozpatrzeć i z drugiej strony. Być może – i tylko być może – dobrze zakładamy, że ta właściwość może być tylko typem całkowitym. I lepiej tak załóżmy, jeżeli ktoś inny pisał taką klasę, bo z jakiegoś powodu oczekuje w danym miejscu wartości całkowitej. Wówczas musimy zastanowić się, skąd się tam wziął łańcuch. Tutaj go po prostu wpisaliśmy, ale jeżeli jest brany z jakichś wyliczeń, z funkcji, od użytkownika… musimy to lepiej kontrolować. W naszym przypadku zmienimy po prostu łańcuch na jedynkę:

I, dla jasności momentu w którym jesteśmy, przedstawiamy całość naszego kodu:

Teraz uruchamiamy nasz program i widzimy:

nasze upragnione słowo KONIEC! Wreszcie skrypt wykonał się od początku, do końca. Teraz wyłączamy tylko pokazywanie tych durnych Notice, Deprecated i Warning, i wrzucamy stronę na serwer klienta.

Tylko, czy aby na pewno?

Oczywiście, że nie. Nasz kod nadal jest napisany źle, nie działa dobrze i musimy sobie z tym poradzić. Ale że wpis robi się już bardzo długi, zapraszam do części drugiej.