Szybkie komponenty w PHP
sie/098

Ostatnio kolegom zaprezentowałem szybki sposób na stworzenie sprawnego prototypu komponentu w PHP. Ostatecznym wnioskiem, który zaraz wysuniemy jest przypomnienie faktu pracy z dynamicznym językiem programowania, który udostępniając nam hermetyzację i inne paradygmaty języka obiektowego pozwala nam uniknąć licznych “dżawawizmów” (czyt. rozwiązań Javy).
Najbardziej meczącym etapem pisania klas komponentów jest właśnie hermetyzacja i liczny nadmiarowy kod udostępniający funkcjonalności czy atrybuty danej klasy na zewnątrz z określeniem praw dostępu. Z wykorzystaniem klas magicznych możemy stworzyć prototyp pełny metod oraz atrybutów publicznych by dopiero w kolejnym etapie rozwoju aplikacji zabezpieczyć model obiektowy. Dzięki temu zapomnimy o nużącej pracy przy tworzeniu metod “get” i “set.
Napiszmy w charakterze prototypu prostą klasę “Mail”.
class Mail
{
public $receiver;
public $message;
public $title;
public function send()
{
mail($this->receiver,$this->subject,$this->message);
}
}
$mail = new Mail;
$mail->message = 'Pozdrawiam';
$mail->receiver = 'seo@google.com';
$mail->title = 'Krótki test';
$mail->send();
Żadnych zabezpieczeń przed wpisaniem w atrybut “receiver” stringu nie będącego mailem, żadnej walidacji, żadnych metod “get” i “set”. Działa? Jak złoto.
Problem oczywiście pojawia się przy wyjściu danej klasy z etapu prototypowania. Nie możemy tak napisanej klasy oddać w ręce całego zespołu. Pal sześć jeśli byłaby to klasa tak prosta jak wyżej, gdy przy wywołaniu funkcji “mail()” z błędnymi argumentami zostaniemy poczęstowani warningiem. Co byłoby gdyby była to część krytycznego komponentu aplikacji? Z użyciem metod magicznych przejdźmy do wersji produkcyjnej.
class Mail
{
private $receiver; // zmieniliśmy widoczność atrybutu "receiver"
public $message;
public $title;
// Odwołania "set" do niedostępnego już atrybutu "receiver"
// zostaną przekierowane do tej metody magicznej:
public function __set( $attr, $value )
{
// Wywołujemy metodę "set" dla niewidocznego
// już publcznie atrybutu
switch($attr) {
case 'receiver':
$this->setReceiver($value);
return;
}
// Rzucamy wyjątekiem (z SPL) jeśli są odwołania "set"
// do nieistniejących atrybutów
throw new InvalidArgumentException(sprintf('Attribute %s dosent exists',$attr));
}
public function setReceiver( $mail )
{
// jeśli argument nie jest mailem (funkcja wbudowana > PHP 5.2.0)
// rzucamy wyjątkiem (z SPL)
if( filter_var($mail, FILTER_VALIDATE_EMAIL) === false ) {
throw new InvalidArgumentException('Given e-mail is invalid');
}
$this->receiver = $mail;
}
public function send()
{
mail($this->receiver,$this->subject,$this->message);
}
}
Istniejący kod prototypu będzie działał perfekcyjnie, a my już posiadamy walidację ustawianego z zewnątrz maila. Szybko, gładko i przyjemnie możemy wdrażać wersję produkcyjną. Należy tylko pamiętać jeszcze o walidacji innych atrybutów klasy.
Przy okazji polecam zapoznanie się z zestawem funkcji do filtracji, które weszły w PHP wersji 5.2 oraz zestawem wbudowanych wyjątków z niedocenianego wciąż SPL.
Brak powiązanych postów.
Dodaj komentarz
No trackbacks yet.
18:37 on Sierpień 30th, 2009
W pierwszym kodzie metoda klasy nazywa się mail(), a wywołujesz send() ;)
18:51 on Sierpień 30th, 2009
Poplątało mi się z PHPowską funkcją, dzięki za uwagę :) Jakiś komentarz co do zaprezentowanego rozwiązania?
19:36 on Sierpień 30th, 2009
Wolę moją wersję z większą ilością automatyki: http://urzenia.net/339/settery-gettery-i-inna-magia/ :)
19:44 on Sierpień 30th, 2009
@MySZ, prawda, jednak żonglowanie atrybutami prywatnymi i atrybutami “prywatnymi, ale z dostępem publicznym” moim zdaniem wprowadza wiele chaosu, szczególnie przy pracy w zespołach. Wykorzystanie mniejszej ilości automatyzacji, przeznaczenie takiego sposobu pracy tylko do tworzenia prototypów oraz późniejsze jawne tworzenie metod “set”/”get” jest bardziej klarowne. Ale ostatecznie i tak trzeba wybierać odpowiedni sposób do odpowiedniego zastosowania, więc moja metoda nie jest Świętym Graalem :)
09:50 on Wrzesień 4th, 2009
na forum.php.pl prowadzona była niegdyś dyskusja na temat słuszności w używaniu magicznych setterów/getterów. Większość ludzi jednak była zdania, że wprowadza to zbędne zamieszanie. Oddając klasę składającą się w większości z magicznych metod, do tego nieudokumentowaną, można się pogubić. Standardowe getXXX/setXXX samo się dokumentuje :)
09:55 on Wrzesień 4th, 2009
@Artur, true :)
16:27 on Maj 24th, 2011
Wydaje mi się że rzucasz złymi typami wyjątków, `InvalidArgumentException` dziedziczy po `LogicException` @manual : ” This kind of exceptions should directly lead to a fix in your code.”, a zły adres e-mail nie powinien raczej prowadzić do poprawki kodu lecz co najmniej do nie wysłania mail’a.
Ostatnio Zyx napisał dość obszerny artykuł na ten temat, zapraszam do zapoznania się:
http://www.zyxist.com/pokaz.php/wyjatki_w_php
17:06 on Maj 24th, 2011
@melkorm, to zależy. Zależy od tego do czego jest wykorzystywana klasa. Jeśli jest to krytyczny element procesu rejestracji, taki kod musi być przecież poprawiony. Główną cechą wyjątku jest fakt, że może on zostać złapany i zinterpretowany na swój sposób. Sam fakt, że jest on rzucony o niczym nie powinien świadczyć, bez interpretacji w kontekście wybranego procesu aplikacji, sytuacji wystąpienia błędu. Klasa taka jak “Mail” świadczy usługę bez kontekstu dopóty, dopóki programista nie wykorzysta jej jako element swojej aplikacji. Stąd bardzo ciężko mi się zgodzić z Twoim spostrzeżeniem.