Blog (65)
Komentarze (803)
Recenzje (0)

Złap mnie, jeśli potrafisz...

@tflZłap mnie, jeśli potrafisz...10.08.2012 17:36

Od mojego ostatniego wpisu minęły okrągłe 4 miesiące, od zlotu miesiąc, czas więc najwyższy przystąpić do czynnego pisania. W końcu nikt nie będzie mnie już podejrzewał, że piszę "pod wyjazd" lub w euforii po wyjazdowej. Zdaje się, że to pierwsze symptomy paranoi... Ale przejdźmy do rzeczy...

Niesiony na fali sławy po moich bardziej technicznych wpisach, zachęcony setkami głosów poparcia przystępuje do kolejnego wpisu stricte technicznego. Poniżej krótko o programowaniu z wyjątkami, ekhm, na przykładzie PHP. Wiem, wiem. Programowanie w PHP to oksymoron. Jednak przy okazji rozmowy z kolegą z pracy musiałem przyznać sam sobie - w PHP można naprawdę wiele zdziałać. Mimo, że obiektowość dopychana jest w tym języku kolanem (cytat z któregoś z redaktorów DP), jak buty do walizki przed wyjazdem na wczasy. Niemniej jednak PHP pozostaje bardzo popularnym językiem i dobrze się sprawdza nawet w dużych projektach.

Czym są wyjątki? Wyjątek, jak sama nazwa wskazuje nie powinien się nigdy zdarzyć, ale prawdopodobieństwo zdarzenia się nieprawdopodobnego jest odwrotnie proporcjonalne do pewności, że nigdy się nie zdarzy. A że dobry(programowo) programista zawsze zakłada, że coś takiego może mieć miejsce to jest na to gotowy. W PHP (jak i w wielu innych językach) wprowadzono więc try, catch i throw. Czy spróbuj, złap i rzuć. Zaczniemy od końca.

Każda metoda i funkcja (własna, o tym za moment) może rzucić wyjątek. Po co? A no po to, żeby zapobiec niemożliwemu i spektakularnemu wywaleniu się aplikacji. Bardzo często przywoływany jest tu przykład dzielenia. Teoretycznie kod:

<?php $a = 1/0; ?>

zakończy się warningiem od PHP. I tyle. Tutaj warto wrócić od poprzedniego akapitu. Funkcje PHP (nazwijmy je - natywne) nie rzucają wyjątków. Ubierzmy więc nasze dzielenie w funkcje. Kod wyglądać będzie tak:

<?php 

function dzielenie($b)
{
	return 1/$b;
}

echo dzielenie(0);

?>

Kolejny raz dostaniemy warning od PHP. Spróbujemy więc postąpić nieco inaczej. Funkcja dzielenie dodatkowo sprawdzi parametr $b i jeśli jest równy 0 rzuci wyjątkiem. Następnie spróbujemy ten wyjątek przechwycić. Kod jest następujący:


function dzielenie($b)
{
	if($b==0) throw new Exception('Nie wolno dzielić przez zero!');
	return 1/$b;
}

try {
	echo dzielenie(0);
	echo dzielenie(1);
	} catch (Exception $e) {
	 echo $e->getMessage();
	}

W odpowiedzi na ten kod otrzymamy informacje, że nie wolno dzielić przez 0. W tym fragmencie już coś się dzieje i choć jest prosty i na pewno każdy go rozumie, to pokuszę się jednak o wytłumaczenie.

Funkcja dzielenie(), jak obiecałem, sprawdza w pierwszej kolejności czy parametr $b nie jest czasem 0. Jeśli jest - rzuca wyjątek. Składania jak widać jest bardzo prosta. Operator throw poprzedza new, czyli wywołanie nowej instancji klasy Exception. Klasa ta jest wbudowana w PHP od nie wiem kiedy, ale chyba od wersji 5. Konstruktor tej klasy przyjmuje kilka parametrów i są to: $message, $code i $previous (w tej kolejności naturalnie). Pierwszy jest oczywisty, drugi zawiera kod błędu (nasz kod, możemy je sobie dowolnie numerować), trzeci oznaczać może poprzedni wyjątek (obiekt). Ten ostatni oczywiście dlatego, że przechwycone wyjątki mogą rzucać wyjątki... Dojdę do tego.

Teraz co się dzieje dalej? Następuje fragment kodu try{}. Kod w tym fragmencie powinien, ale oczywiście nie musi, móc rzucić wyjątek. Jeśli tak się stanie następuje jego przechwycenie i wykonanie kodu w fragmencie catch (Exception $e) {}. Jeśli wyjątek zostanie rzucony przez którykolwiek fragment kodu następują natychmiastowe przerwanie wykonywania kodu i przejście do catch.

Sama klasa wyjątków jest dość prosta. Posiada kilka metod: getMessage(), getType(), getFile(), getLine(), getTrace(), getPrevious(), getTraceAsString(), __toString(). Wszystkie są zdaje się wystarczająco instynktownie zrozumiałe i nie będę ich tłumaczył. Dla wszystkich, którzy mają wątpliwości, że wyjątki są fajne sugeruję wyobrazić sobie jak wygodne może się stać debugowanie aplikacji.

Oczywiście pozostaje odpowiedzieć sobie dlaczego powinienem używać wyjątków, a nie na przykład zwracać FALSE oraz arraya (albo obiekt) zwierający te wszystkie parametry (bo oczywiście jest to wykonalne). Jest kilka powodów.

Po pierwsze - kod dzięki takiemu zastosowaniu staje się o wiele bardziej czytelny. Nie trzeba robić za każdym wywołaniem metody, która może się nie powieść, weryfikacji jej "statusu". Po drugie, doskonale sprawdzają się wyjątki, gdy chcemy zgrabnie rozdzielić warstwę aplikacji "workową" od logicznej. Po trzecie są takie funkcje i metody, które po prostu zwracają boola i wartość FALSE w return jest wartością oczekiwaną a nie wyjątkową (na przykład gdy sprawdzamy, czy istnieje plik).

Fragment kodu catch() {} może zawierać... try{}. Dzięki temu możemy założyć, że nasza metoda typu disaster również się nie powiedzie i odpowiednio na to zareagujemy (przykład - nie udało się zapytanie do bazy danych. Chcemy zapisać sobie wszystko w logu w pliku... jednak nie plik nie istnieje/nie mamy do niego prawa zapisu. Wysyłamy więc (drugi catch) maila o całej sytuacji do siebie).

Kiedy rzucać wyjątki?

Na koniec krótko napiszę, kiedy ja rzucam wyjątki, albo raczej kiedy robią to moje aplikacje (a co! Nie boję się tego słowa!). Rzucają je zawsze. No... prawie zawsze. Jeśli posiadam w swoim kodzie metodę, która dodaje użytkowników do bazy danych, to rzucać wyjątkiem będzie wszystko po drodze. Od połączenia z bazą danych, aż po samą metodę, która robi query. Jeśli jednak metoda sprawdza, czy użytkownik się zalogował poprawnie (więc zrobi selecta do bazy z jego danymi) to rzucać wyjątek będzie wszystko po drodze, poza ostatnią metodą sprawdzającą ilość krotek (lub przyrównującą tę ilość do 1).

Rzucanie wyjątków w sposób, który opisałem to... podstawa. Wyobraźcie sobie co można osiągnąć, gdy z klasy Exception zaczniemy dziedziczyć i stworzymy sobie potężny portfel klas specjalizujących się w wyjątkach na specjalne okazje (wyjątki dla baz danych, obsługi plików, połączeń do webserwisów...).

PS. Jeśli metoda rzuca wyjątek, a jest wywoływana bez try{} i zdarzy się tenże wyjątek kod zostanie przerwany z fatal errorem o nie przechwyconym wyjątku. Chyba, że PHP ignoruje fatal errory...

PS2. Jeśli było, to przepraszam

Szanowna Użytkowniczko! Szanowny Użytkowniku!
×
Aby dalej móc dostarczać coraz lepsze materiały redakcyjne i udostępniać coraz lepsze usługi, potrzebujemy zgody na dopasowanie treści marketingowych do Twojego zachowania. Twoje dane są u nas bezpieczne, a zgodę możesz wycofać w każdej chwili na podstronie polityka prywatności.

Kliknij "PRZECHODZĘ DO SERWISU" lub na symbol "X" w górnym rogu tej planszy, jeżeli zgadzasz się na przetwarzanie przez Wirtualną Polskę i naszych Zaufanych Partnerów Twoich danych osobowych, zbieranych w ramach korzystania przez Ciebie z usług, portali i serwisów internetowych Wirtualnej Polski (w tym danych zapisywanych w plikach cookies) w celach marketingowych realizowanych na zlecenie naszych Zaufanych Partnerów. Jeśli nie zgadzasz się na przetwarzanie Twoich danych osobowych skorzystaj z ustawień w polityce prywatności. Zgoda jest dobrowolna i możesz ją w dowolnym momencie wycofać zmieniając ustawienia w polityce prywatności (w której znajdziesz odpowiedzi na wszystkie pytania związane z przetwarzaniem Twoich danych osobowych).

Od 25 maja 2018 roku obowiązuje Rozporządzenie Parlamentu Europejskiego i Rady (UE) 2016/679 (określane jako "RODO"). W związku z tym chcielibyśmy poinformować o przetwarzaniu Twoich danych oraz zasadach, na jakich odbywa się to po dniu 25 maja 2018 roku.

Kto będzie administratorem Twoich danych?

Administratorami Twoich danych będzie Wirtualna Polska Media Spółka Akcyjna z siedzibą w Warszawie, oraz pozostałe spółki z grupy Wirtualna Polska, jak również nasi Zaufani Partnerzy, z którymi stale współpracujemy. Szczegółowe informacje dotyczące administratorów znajdują się w polityce prywatności.

O jakich danych mówimy?

Chodzi o dane osobowe, które są zbierane w ramach korzystania przez Ciebie z naszych usług, portali i serwisów internetowych udostępnianych przez Wirtualną Polskę, w tym zapisywanych w plikach cookies, które są instalowane na naszych stronach przez Wirtualną Polskę oraz naszych Zaufanych Partnerów.

Dlaczego chcemy przetwarzać Twoje dane?

Przetwarzamy je dostarczać coraz lepsze materiały redakcyjne, dopasować ich tematykę do Twoich zainteresowań, tworzyć portale i serwisy internetowe, z których będziesz korzystać z przyjemnością, zapewniać większe bezpieczeństwo usług, udoskonalać nasze usługi i maksymalnie dopasować je do Twoich zainteresowań, pokazywać reklamy dopasowane do Twoich potrzeb. Szczegółowe informacje dotyczące celów przetwarzania Twoich danych znajdują się w polityce prywatności.

Komu możemy przekazać dane?

Twoje dane możemy przekazywać podmiotom przetwarzającym je na nasze zlecenie oraz podmiotom uprawnionym do uzyskania danych na podstawie obowiązującego prawa – oczywiście tylko, gdy wystąpią z żądaniem w oparciu o stosowną podstawę prawną.

Jakie masz prawa w stosunku do Twoich danych?

Masz prawo żądania dostępu, sprostowania, usunięcia lub ograniczenia przetwarzania danych. Możesz wycofać zgodę na przetwarzanie, zgłosić sprzeciw oraz skorzystać z innych praw wymienionych szczegółowo w polityce prywatności.

Jakie są podstawy prawne przetwarzania Twoich danych?

Podstawą prawną przetwarzania Twoich danych w celu świadczenia usług jest niezbędność do wykonania umów o ich świadczenie (tymi umowami są zazwyczaj regulaminy). Podstawą prawną przetwarzania danych w celu pomiarów statystycznych i marketingu własnego administratorów jest tzw. uzasadniony interes administratora. Przetwarzanie Twoich danych w celach marketingowych realizowanych przez Wirtualną Polskę na zlecenie Zaufanych Partnerów i bezpośrednio przez Zaufanych Partnerów będzie odbywać się na podstawie Twojej dobrowolnej zgody.