Szybkie komponenty w PHP

30
sie/09
8

Szybkie prototypowanie

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.

Komentarze (8) Odniesienia (0)
  1. antyqjon
    18:37 on Sierpień 30th, 2009

    W pierwszym kodzie metoda klasy nazywa się mail(), a wywołujesz send() ;)

  2. Damian Tylczyński
    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?

  3. MySZ
    19:36 on Sierpień 30th, 2009

    Wolę moją wersję z większą ilością automatyki: http://urzenia.net/339/settery-gettery-i-inna-magia/ :)

  4. Damian Tylczyński
    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 :)

  5. Artur Świerc
    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 :)

  6. Damian Tylczyński
    09:55 on Wrzesień 4th, 2009

    @Artur, true :)

  7. melkorm
    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

  8. Damian Tylczyński
    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.

Niestety, skomentowanie tego wpisu jest niemożliwe.

No trackbacks yet.

Optimization WordPress Plugins & Solutions by W3 EDGE