Pimple – świetny DIC dla PHP

Maj 3, 2012 |  by  |  PHP  |  No Comments

Przy okazji zapoznawania się z micro frameworkiem Silex, opartym na komponentach Symfony 2, moją uwagę zwrócił niepozorny kontener do wstrzykiwania zależności (DIC). Kontener nazywa się Pimple, jest to osobny projekt i nie ma problemu z wykorzystaniem go w dowolnym projekcie PHP – to tylko jedna, niewielka klasa. Siła tego kontenera leży w świetnym wykorzystaniu funkcji anonimowych do inicjowania zarejestrowanych obiektów. Polecam zapoznać się z jego dokumentacją na stronie projektu, jest ona tak prosta, że zajmie Wam to góra 5 min, a warto.

Potencjał Traits

Kwiecień 28, 2012 |  by  |  PHP  |  No Comments

W jednym z poprzednich artykułów mówiliśmy o nowym mechaniźmie języka php, którym są traits. Wprowadzenie ich wywołało dużo kontrowersji. Ten artykuł ma na celu próbę pokazania, że traits faktycznie mogą okazać się przydatne.

Szukając materiałów na prezentacje o traits, natknąłem się w sieci na ciekawy przykład zastosowania. Chodziło o trait, który umożliwiał zwrócenie obiektu w postaci JSON’a. Przykład widoczny poniżej jest dosyć prosty i można mieć do niego zastrzeżenia, ale nie to przykuło moją uwagę.

trait JSONized 
{
    public function toJson() {
        $properties = get_object_vars($this);
        return json_encode($properties);
    }
}

 
Oryginalny artykuł dostępny jest tutaj

„We need to go deeper”

To co widzieliśmy przy okazji JSONized to mapowanie obiektu na określoną strukturę. W tym przypadku był to JSON, ale przecież może być to cokolwiek innego… co powiecie na tabele w bazie danych ? A może tak implementacja Active Record bez narzuconej hierarchii dziedziczenia !?

trait ActiveRecord
{
    public function save() { /*...*/ }
 
    protected function update() { /*…*/ }
 
    protected function insert() { /*…*/ }
 
    public function delete() { /*...*/ }
}
 
class Article extends Whatever
{
    use ActiveRecord;
    ...
}

 
Moim zdaniem ma to wielki sens i świetnie wpasowuje się w to co chcemy przedstawić, w ten wycinek rzeczywistości, który chcemy zinterpretować za pomocą kodu. Wróćmy do Active Record i powyższego przypadku, ale bez traits. Czym jest Artykuł ? Czy na prawdę jest rekordem ? Albo czym jest inny obiekt, który chcemy zmapować, powiedzmy Użytkownik – czy on też dziedziczy po rekordzie ? Otóż nie ! To jest z góry bez sensu logicznie, ma nijak do rzeczywistości. On jest rekordem, bo inaczej nie zaimplementujemy Active Record, ale to co chcemy na prawdę zrobić to sprawić, żeby Artykuł, Użytkownik i inne mapowane obiekty zachowywały się jak rekord. Takie zastosowanie traits pozwala na uniknięcie głównego grzechu Active Record, czyli narzuconej hierarchii dziedziczenia.

Od zera do traits

Prześledźmy od początku próbę napisania w miarę sprawnego i dobrze zaprojektowanego logowania do jednej z klas aplikacji. Spójrzmy na poniższy kod:

class IAmUsingLogging
{
    public function doSomething() {
        $this->log('I am about to do something');
        // I am doing something
        $this->log(”I've done it!”);
    }
 
    private function log($message) {
        // I am doin logging
    }
}

 
Kod zadziała, ale czy jest to dobry projekt ? Co jeżeli inna klasa też chciała by logować swoje akcje ? Mamy duplikować kod ? Logowanie jest na tyle odrębnym zestawem operacji, że zasługuje na własną klasę, do której należałoby oddelegować wykonywanie logowania. Od razu uspokajam – jeszcze nie wprowadzmy traits, czyli absolutnie nie robimy czegoś takiego jak poniżej.

trait Logging
{
    private function log($message) {
        // I am doin logging
    }
}
 
class IAmUsingLogging
{
    use Logging;
 
    public function doSomething() {
        $this->log('I am about to do something');
        // I am doing something
        $this->log(”I've done it!”);
    }
}
 
class IAmUsingLoggingToo
{
    use Logging;
 
    public function doSomething() {
        $this->log('I am about to do something');
        // I am doing something
        $this->log(”I've done it!);
    }
}

 
To jest przykład na coś co może się stać kiedy ludzie którzy nie mają za dużo pojęcia o wzrocach projektowych, o klasach usługowych, kompozycji, zaczną używać traits.

Traits to potężna broń. Pozwala zepsuć projekt aplikacji, a przy tym spowodować, że nawet linijka kodu się nie powtórzy

To co powinniśmy zrobić to stworzyć osobną klasę loggera i do niej oddelegować metody logujące.

interface LoggerInterface
{
    public function log($message);
}
 
class Logger implements LoggerInterface
{
    public function log($message) {
        // I am doin logging
    }
}
 
class IAmUsingLogging implements LoggerInterface
{
    private $logger;
 
    public function setLogger(LoggerInterface $logger) {
        $this->logger = $logger;
    }
 
    private function log($message) {
        $this->logger->log($message);
    }
 
    public function doSomething() {
        $this->log('I am about do do something');
        // I am doing something
        $this->log(”I've done it!”);
    }	
}

 
Nasz wycinek aplikacji zaczyna mieć ręcę i nogi. Logger robi ciężką robotę, klasa która logowania potrzebuje oddelegowuje do niego zadania. Dla porządku, zarówno Logger jak i nasza klasa implementują ten sam interfejs. I nagle do gry wchodzi kolejna klasa która chce używać logowania.

class IAmUsingLoggingToo implements LoggerInterface
{
    private $logger;	
 
    public function setLogger(LoggerInterface $logger) {
        $this->logger = $logger;
    }
 
    private function log($message) {
        $this->logger->log($message);
    }
 
    public function doSomething() {
        $this->log('I am about do do something');
        // I am doing something
        $this->log(”I've done it!”);
    }	
}

 
Sytuacja nie jest krytyczna. Ciężką robotę nadal wykonuje Logger. Widzimy jednak, że musimy powtórzyć 3 elementy. Ich implementacja jest banalna, jednak zawsze są to 3 elementy na każdą klasę wymagającą logowania. Sądzę, że do takich przypadków – pole przechowujące klasę usługową, setter, metody wynikające z interfejsu klasy usługowej – traits nadają się znakomicie. Spójrzmy:

trait Logging
{
    private $logger;
 
    public function setLogger(LoggerInterface $logger)  {
        $this->logger = $logger;
    }
 
    public function log($message)  {
        $this->logger->log($message);
    }
}
 
class IAmUsingLogging implements LoggerInterface
{
    use Logging;
 
    public function doSomething() {
        $this->log('I am about do do something');
        //... I am doing something
        $this->log(”I've done it!”);
    }
}

 

Traits jako Doctrine Behaviors

Traits ma za zadanie określać „zachowanie” klasy. Idąc tym krokiem rozumowania możemy skojarzyć to ze znanymi z Doctrine 1.x behaviorami. W rzeczywistości, sposób „wpinania” behaviorów do klasy poprzez słowo kluczowe actAs bardzo przypomina use znane z traits. W Doctrine 2 znikły znane z Doctrine 1 behaviory i, sądząc po notce na stronie projektu (tutaj), nic nie wskazuje na to że się pojawią. Firma Knp Labs, której chyba głównym obszarem zainteresowań jest Symfony 2 wypuściła jednak ostatnio bardzo ciekawą paczkę z behaviorami pod Doctrine 2, które są oparte właśnie na traits ! Przypominam tylko, że devhelp również sygnalizował podobieństwa pomiędzy tymi mechanizmami w poprzednim artykule :) . Link do artykułu na Knp Labs znajduje się tutaj, polecam również zajrzeć na ich konto na githubie z kodem źródłowym.

Traits należy używać bardzo ostrożnie. Na 90% nie mają sensu ich przy prawidłowo zaprojektowanej aplikacji, ponieważ większość problemów da się rozwiązać przez wzorce projektowe.
Wg mnie traits chwieje podstawami systemu obiektowego. Co jeśli wprowadzenie traits niejako wytyka braki w podejściu do obiektowości, a niektóre wzorce projektowe istnieją w swojej aktualnej postaci tylko dlatego, żeby te braki zakryć (Active Record) ? „Socjalizm bohatersko walczy z problemami, które sam tworzy” – może część wzorców to właśnie oręż w socjalistycznym modelu obiektów :)

 

Zapisywanie serializowanego obiektu do bazy PostgreSQL – Symfony 1.4 + spool + PostgreSQL

Kwiecień 5, 2012 |  by  |  Symfony  |  No Comments

Po skonfigurowaniu kolejkowania maili w Symfony 1.4 z bazą danych PostgreSQL pojawił się problem:

PHP Notice:  unserialize(): Error at offset 30 of 33 bytes in lib/vendor/symfony/lib/plugins/sfDoctrinePlugin/lib/mailer/Swift_DoctrineSpool.class.php on line 126

Do bazy zapisywany powinien być przekształcony obiekt funkcją serialize(), natomiast trafiał do niej tylko fragment danych.

Okazało się, że gdy obiekt ma prywatne lub chronione zmienne, to po zserializowaniu, nie można zapisać go do bazy PostgreSQL. Rozwiązaniem okazało się zamiana zerowego bajtu na inny ciąg znaków:

$serialized_object = serialize($my_object); 
$safe_object = str_replace("\0", "~~NULL_BYTE~~", $serialized_object);

Aby deserializować obiekt należy zamienić użyty ciąg znaków na zerowy bajt:

$serialized_object = str_replace("~~NULL_BYTE~~", "\0", $safe_object); 
$my_object = unserialize($serialized_object);

Dzięki tej operacji serializowany obiekt poprawnie zapisuje się do bazy danych PostgreSQL.

 

Serializacja oraz deserializacja dla strategii Spool odbywa się w pliku:

lib/vendor/symfony/lib/plugins/sfDoctrinePlugin/lib/mailer/Swift_DoctrineSpool.class.php

Po modyfikacji powyższego pliku kolejkowanie maili metodą Spool w Symfony 1.4 na bazie PostgreSQL działa poprawnie.

 

 

Symfony 2 w Drupal 8

Marzec 24, 2012 |  by  |  Symfony  |  No Comments

Symfony 2 zdobywa coraz większe uznanie w świecie PHP. Dzięki przestrzeganiu zasady „Separation of Concerns” poszczególne komponenty, składające się na ten framework, znajdują swoje miejsce w coraz większej ilości platform. Do tego grona dojdzie niebawem najnowsza wersja Drupal’a. Ma ona korzystać z kilku podstawowych komponentów Symfony.

Drupal ma opinie CMS’a o specyficznej konstrukcji. Jest ona na tyle unikalna, że zarówno początkującym jak i doświadczonym deweloperom trudno się w niej odnaleźć. Z tego powodu kiedy ktoś z teamu Drupala odchodzi, bardzo ciężko jest znaleźć kogoś na jego miejsce – zwyczajnie ludzie nie palą się do nauki technologii, której przydatność ogranicza się do jednego CMS’a. Symfony 2 z kolei, jest w dużej mierze oparta o standardy, wzorce projektowe i specyfikację HTTP. Mariaż Drupala i Symfony pozwoli na otwarcie się tej platformy na świat.

więcej: Symfony2 meets Drupal 8

PHP 5.4 w Symfony 2 – web server

Marzec 23, 2012 |  by  |  PHP, Symfony  |  No Comments

Nie trzeba było długo czekać na reakcję teamu Symfony 2 na nowośći w PHP 5.4. W nadchodząca wersja Symfony (2.1)  ma mieć możliwość wykorzystania wbudowanego w PHP 5.4 web servera, jak ogłosił niedawno Fabien Potencier za pośrednictwem Twittera. Ma być on dostępny przez task server:run. Trzeba przyznać – niezły czas reakcji.

Symfony task, a połączenie z drugą bazą danych.

Marzec 10, 2012 |  by  |  Doctrine, Symfony  |  No Comments

Ostatnio potrzebowałem połączyć się z zewnętrzną bazą danych w celu importu danych do bazy lokalnej. Import miał się odbyć z wykorzystaniem tasków symfony. Rozwiązania, które istniały w sieci nie były dla mnie satysfakcjonujące, gdyż w większości przypadków wymagały utworzenia schematu tabel w pliku schema.yml i wygenerowaniu klas. Mi jedynie potrzebne było wykonanie prostego zapytania select w SQL, a następnie zapisanie danych w lokalnej bazie. Poniżej sposób w jaki uzyskuje drugie połączenie. Oczywiście od razu widać, że w tym przypadku korzystałem z postgresqla.

 

protected function execute($arguments = array(), $options = array()) {
 
  $dsn1 = "pgsql://user:password@db_host/db_name";
  $doctrineManager1 = Doctrine_Manager::connection($dsn1, 'import');
 
  $sql = $doctrineManager1->execute('select * from db.table_test');
  $result = $sql->fetchAll();
 
  ...
  //kopiowanie danych
  ...
 
}

Mała rewolucja w PHP 5.4 – traits

Marzec 10, 2012 |  by  |  PHP  |  No Comments

W najnowszej wersji PHP (5.4) pojawił się nowy mechanizm, który na pewno zasługuje na uwagę – są nim tzw „cechy” (traits). Traits określiłbym czymś pośrednim między interefejsem, a klasą. Pozwalają one na wielokrotne wykorzystanie logiki, która jest wspólna dla wielu klas, jednocześnie omijając mechanizm dziedziczenia, który często nie jest najlepszą metodą na uniknięcie powielania kodu.

Traits można zdefiniować jako „zachowanie” klasy, co w swojej koncepcji bardzo przypomina stosowanie behaviour’ów w Doctrine 1.2. Kto korzystał wie jak bardzo jest to przydatne i wie też, że stosowanie dziedziczenia w takich przypadkach jest nieintuicyjne.

Ja z niecierpliwością czekam, aż ten mechanizm się rozwinie i będzie szeroko stosowany. Jest to mała rewolucja w koncepcji klasy, wprowadza – tym razem bezpośrednio wsparte językiem ! – umożliwienie oddzielenia część definicji zachowania klasy i umieszczenie jej w osobnej strukturze. Daje to szerokie pole do manewru, z którego na pewno wyłonią się ciekawe zastosowania.

więcej: http://php.net/manual/en/language.oop5.traits.php

Zmiany w korzystaniu z tablic w PHP 5.4

Marzec 10, 2012 |  by  |  PHP  |  No Comments

Wśród kilku nowych elementów języka jakie wnosi PHP 5.4 znalazło się coś co od dawna aż się prosiło o wprowadzenie. Od tej pory jeżeli funkcja zwraca tablicę jako wartość to nie musimy używać zmiennej pomocniczej żeby dostać się do elementu tablicy. W końcu!

 

function getArray()
{
    return array('one', 'two', three');
}
 
// Błąd ? Nie od PHP >= 5.4
$value = getArray()[1];
 
// wyświetli 'two'
echo $value;

 
Jeszcze jedno ułatwienie, tablicę można będzie definiować w prostszy sposób:
 

$myArray = array('one', 'two', three') // PHP < 5.4
$myArray = ['one', 'two', three']; // PHP >= 5.4

Nowy plugin pod Symfony 1.4

Marzec 2, 2012 |  by  |  Doctrine, Symfony  |  No Comments

dhDoctrineGuardChangeRequestPlugin to świeży plugin pod Symfony 1.4 spod szyldu devhelp :) . Jeżeli chcecie aby sfGuard’owy user musiał potwierdzać zmiany hasła i/lub maila poprzez kliknięcie w link wysłany na jego adres email to jest plugin, którego szukacie. Więcej na githubie, zapraszam do korzystania i zgłaszania uwag

 

github - https://github.com/devhelp/dhDoctrineGuardChangeRequestPlugin

 

  • po zmianie adresu email wysyłany jest mail na nowy adres z unikalnym linkiem do zatwierdzenia zmiany
  • po zmianie hasła wysyłany jest mail na adres użytkownika z unikalnym linkiem do zatwierdzenia zmiany
  • każdy z linków ma datę wygaśnięcia
  • domyślny app.yml pozwala na konfigurację wielu aspektów działania, tak żeby zminimalizować potrzebę nadpisywania akcji
  • (prawie) natychmiastowe wsparcie ajax’owych formularzy (z wykorzystaniem http://jquery.malsup.com/form)

Domyślna tabela bazowa dla wygenerowanych modeli

Marzec 2, 2012 |  by  |  Doctrine, Symfony  |  No Comments

Udało mi się w końcu dokopać do miejsca w kodzie, gdzie można w miarę prosty sposób ustawić domyślną klasę dla generowanych klas tabel modelu. W klasie sfDoctrinePluginConfiguration, linia 87 możemy zauważyć, że wywoływane jest zdarzenie o nazwie ‘doctrine.filter_model_builder_options’, którego listenery mają za zadanie przefiltrować opcje generowania modelu. Wystarczy więc, że napiszemy własny listener do tego zdarzenia i dodamy odpowiednią opcję.

 

class ProjectConfiguration extends sfProjectConfiguration
{
 
    public function setup()
    {        
        $this->dispatcher->connect('doctrine.filter_model_builder_options', array($this, 'filterModelOptions'));
    }
    
    public function filterModelOptions(sfEvent $event, $options)
    {
        $options['baseTableClassName'] = 'myDoctrineTable';
        
        return $options;
    }
}

 

W tym momencie nowo-wygenerowane klasy tabel będą dziedziczyć po klasie myDoctrineTable (więc dobrze by było, żeby dziedziczyła ona po Doctrine_Table :) ). Zwróćcie jednak uwagę na słowo nowo-wygenerowane

Raz wygenerowane klasy tabel nie są już generowane ponownie. Stąd wniosek, że jeżeli użyjecie tego rozwiązania w połowie projektu to i tak skończycie na tym, że będziecie musieli ustawiać ręcznie klasę bazową dla każdej z klas tabeli. Niestety to rozwiązanie nadaje się tylko przy pierwszym generowaniu klas, później raczej się nie przyda