"Silne" typy w locie |
Wpisany przez Patryk yarpo Jar | |||
piątek, 30 lipca 2010 12:09 | |||
PHP jeszcze do niedawna nie miał wcale kontroli typów, teraz to się już trochę zmieniło. Ja jednak uważam, że dla języka skryptowego, który nie jest kompilowany są lepsze sposoby na "wymuszenie" typu niż podawanie go jawnie. Jak? Zapraszam do lektury.
Na początek
Problem z typamiPHP jak to język skryptowy nie posiada silnej typizacji. Do zmiennej można przypisać cokolwiek i nie powoduje to błędu. To, że nie powioduje to błędu składniowego to dobrze. Ale co z logiką. przykładowy kod:
W wyniku działania takiego skryptu pojawi się komunikat:
niby nie błąd, ale przecież ten skrypt działa nieprawidłowo. Skoro oczekiwaliśmy jakiejś wartości w tym miejscu, to ona powinna wystąpić.
Łata goni łatęPHP mimo, że nie posiada silnej typizacji, rozpoznaje typy zmiennych. Pozwala to sprawdzić, czy oby podane dane na pewno są prawidłowe (czyli takie, jakich oczekiwaliśmy). Oto zestaw funkcji, których można użyć, aby sprawdzić, czy dane są poprawne:
Każda z tych funkcji może być użyta, aby sprawdzić, czy podany parametr jest odpowiedniego typu. Gdybym chciał poprawić powyższy kod, mógłbym zrobić:
Jednak taki if wewnątrz funkcji troche niepotrzebnie nam wydłuża kod. Jak wcześniej wspomniałem w najnowszych wersjach PHP mamy możliwość zrobienia tego ładniej. No dobrze, pojawił nie pojawi sie nam komuniakat (który można zresztą wyłączyć). Ale nadal nie było tam wartości, jakiej oczekiwaliśmy. Programowanie wymaga konkretów i ścisłości. Jeśli nie ma jakichś danych, powinno to oznaczać błędne i nieporządane działanie. Działanie szkodliwe i potencjalnie niebezpieczne. A takie działanie powinno wywoływać błąd, o czym za chwilę.
Kontrola typów w PHP - mechanizm wbudowanyW PHP 5 pojawiła się kontrola typu obiektu, a w PHP5.1 pojawiła się możliwość wymuszenia, by przekazany parametr był tablicą. Prosty przykład:
Powyższy kod jest trochę czytelniejszy. Przypomina nawet troche kody języków C-podobnych. W wyniku uruchomienia dostajemy taki komunikat:
No i mamy to, czego chciałem. Błędna dane = błąd = koniec skryptu. Ale teraz powstaje problem, bo być może chcielibyśmy sobie jeszcze wysłać informacje o tym, co sie stało na maila. A może musimy jescze zamknąć połączenie z bazą danych, lub skasowac jakiś plik... Co prawda - błędy można przechwycić za pomocą funckji set_error_handler:
Mi jednak na myśl o takich zabiegach nie zgadzają się drobne w kieszeni. Przecież mamy wyjątki. Dlaczego nie zamienić naszego podejścia do skryptu i zamiast wywoływać błędy - rzucać wyjątki. Które są czytelniejsze, pozwalają uzyskać wiele informacji w bardziej przyjazny sposób. W przypadku języków kompilowanych takie błędy byłyby wykryte na etapie kompilacji - przed uruchomieniem. Niestety w PHP zostaną wykryte dopiero w trakcie działania. To na pewno jest dosyć uporczywe. Co więcej w ten sposób można wykrywać jedynie obiekty lub tablice. Nie ma możliwości, aby rozpoznać typy proste (ciąg znaków, liczbę całkowitą lub zmiennoprzecinkową).
Run-time type hintingCzyli sprawdzanie typu w locie. Jest właściwy typ - super, działamy. Jest niewłaściwy - trudno, kończymy zabawę. Na początek potrzebujemy klasy. Tak sobie myślę, że można uznać, że nie tylko klasy. Pakietu (jeśli nie wiesz jak tworzyć "pakiety" w PHP < 5.3.3 to zapraszam do lektury)! nazwijmy ten pakiet `Validation'. Czyli w folderze `Validation' tworzymy sobie plik klasy o nazwie `Type.php'. Oznacza to, że nasza klasa nazywać sie będzie `Validation_Type':
Pełny kod klasy na svn: http://php-validation.googlecode.com/svn/trunk/Type.php Oraz klasy wyjątków, będące "podpakietem" - przydatne przy odpowiednim użyciu autoloadera. Przykładowa klasa wygląda tak:
Kody pozostałych klasy dostępne na SVN: http://php-validation.googlecode.com/svn/trunk/Exception/
Wykorzystanie
Wynik działania:
Dlaczego? otóż 12.0 to nie jest liczba całkowita.
Zalety?Moim zdaniem takie rozwiązanie ma wiele zalet, np. to że możemy taki błąd obsłuzyć w wielu miejscach. Choćby: Elastyczna obsługa błędów
I dzięki temu możemy już w funkcji przechwycić niektóre błędy. Tak jak na powyższym listingu. Niepoprawne dane zostały zwalidowane i doprowadzone do stanu, w ktorym nie szkodzą. Równie dobrze można zrobić tak:
lub też przechwycić to jeszcze wyżej obejmując cały kod w jedne try catch i wyświetlac jedynie informacje o błędnym działaniu systemu, a do logga systemowego lub na maila wysłać sobie dokładną informację o tym, co było nie tak. Wg mnie jest to rozsądne w sytuacji, kiedy takie błędy ujawniają się w trakcie działania (jak już mówiłem i pewnie wiesz, PHP nie kompiluje się).
Wymuszenie walidacjiTakie podejście wymusza także walidację. Jeśli skrypt nam sie wyłoży, gdy przekażemy nieprawidłowe dane, to będziemy bardzo dbali o to, aby dane te były prawidłowe. I - w co bardzo chcę wierzyć - taki kod nie pojawi się więcej:
Oczywiście to przykład bardzo niewłaściwie napisanego kodu. Choćby samo używanie zmienych superglobalnych jest ubogie*, a do tego dochodzi jeszcze używanie bardzo rozbudowanych struktur, jak choćby `$_SESSION['user_store']['data']['newDate']', bez sprawdzenia czy tamte dane istnieją. Polecam w tych sytuacjach konstrukcje językowe isset / empty. Trochę lepiej ten kod wyglądałby tak:
Funckje intval, strval, floatval itp. służą do jawnego wymuszenia rzutowania do danego typu. Więcej o nich możesz przeczytać w manualu. Oczywiście lepiej byłoby w powyższym kodzie zwalidowac te dane poprawniej. Jednak już tylko taka walidacja (szczególnie zrzutowanie do intów) pozwala nam się zabezpieczyć przed kilkoma błędami. Więcej o walidacji wartości napiszę w przyszłości.
--
|