Pimple – a simple Dependency Injection Container (DI), ce-i aia PIMPLE, ce-i aia DI??

Salut, daca va intereseaza limbajul de progrmamare PHP, va recomand sa cititi mai departe, in caz contrar, dati un “previous post”, nu cred ca o sa va fie interesant.
Nu prea intleg de ce i-au pus numele “PIMPLE”, dar… dupa mine, n-ar avea nimic in comun cu semnificatia acestui cuvint, ba din potriva, de o acnee vrei sa scapi, pe DI-ul asta, vrei sa-l folosesti cat mai mult 🙂

Mai inti de toate,  sa ne lamurim, ce este un DI (Dependency Injection)?
Cum spune WIKI,  Dependency Injection este un pattern de programmare, sau “design pattern”, sau model de proiectare software (fiecare cum doreste asa-i spune)  care permite cu usurinta, eliminarea, sau modificarea blocurilor de cod, si mai mult, blocuri de HARD-cod,  in run-time si chiar in timpul compilarii.

Acest pattern poate fi folosit, spre exemplu, pentru încărcarea (upload) diferitor plugin-uri in mod dinamic, in timpul testarii programmului, sau in timpul lucrului acestuia, spre exemplu, fara a inchide site-ul, putem instala/deinstala un plugin. Nu mai avem nevoie de a pune o imagine ca asta

website_under_construction1

Eu, spre exemplu, il folosesc ca un “singleton” general, daca, patternul singleton se foloseste pentru un obiect (class) anumit, Pimple, este ecelas singleton doar pentru initializarea oricaror obiecte doresti.
PIMPLE, este un DI Container genial, doar in mai putin de 50 de rinduri de cod, se ascunde un singur obiect de dimensiuni minime, cu caracteristici DEOSEBITE. Conainer-ul este un class pe care-l conectam in proiectul nostru in felul urmator:

require_once '/path/to/Pimple.php';

initializarea obiectului, la fel este simpla ca si oricare alt obiect:

$container = new Pimple();

Ca multe alte DI patterne, Pimple este capabil să gestioneze două tipuri de date: obiecte și simpli parametri.

Initializarea parametrilor

Definirea, sau initializarea parametrilor, este foarte simpla, folosim containerul ca o simpla matrice:

// Initializarea parametrilor
$container['cookie_name'] = 'SESSION_ID';
$container['session_storage_class'] = 'SessionStorage';

Initializarea Obiectelor

Obiectele, sunt o parte din sistem, o parte din site, care îndeplinește o sarcina specifică.
De exemplu, obiectele pot avea urmatoarele functii: obiectul care ofera conexiunea la baza de date, obiectul responsabil pentru trimiterea e-mail-urilor, standardizarea datelor de ieșire, etc
În Pimple, obiectele le definim cu ajutorul unei funcții anonime, care returnează obiectul respectiv:

// Initializarea Obiectelor
$container['session_storage'] = function ($c) {
  return new $c['session_storage_class']($c['cookie_name']);
};
$container['session'] = function ($c) {
  return new Session($c['session_storage']);
};

Observați că funcția anonim are acces la containerul actual, și vă permite să-l folosiți în alte setări sau servicii (obiecte).
Obiectele sunt create doar atunci când sunt accesate, astfel încât ordinea inițializărilor nu contează.
Folosirea obiectelor initializate, de asemenea este foarte ușoră:

// initializam obiectul sessiei
$Session = $container ["session"];
// Apelul de mai sus este aproximativ echivalent cu urmatorul cod:
// $storage = new SessionStorage('SESSION_ID');
// $Session = new Session($storage);

Initializarea Obiectelor “Singleton”

În mod implicit, de fiecare dată când chemam un obiect, Pimple returnează o nouă instanță al acestui obiect. Dar dacă doriți ca aceeași instanță să fie returnată pentru toate apelurile, cum lucreaza pattern-ul Singleton, tot ce trebuie sa faci, este să initializezi functia anonima in metoda share():

$container['session'] = $container->share(function ($c) {
  return new Session($c['session_storage']);
});

Initializarea functiilor

Asa cum Pimple consideră toate funcțiile anonime ca fiind o declarație a obiectelor, pentru declarare functiilor simple, avem la dispozitie urmatoarea metoda protect():

$container['random'] = $container->protect( function () { 
        return rand(); 
});

Modificarea obiectelor dupa initializarea lor

În unele cazuri, ar putea fi necesară modificarea obiectelor deja inițializate. Pentru acest lucru, putem folosi metoda extend() care va fi executata imediat dupa inițializarea obiectului:

$container['mail'] = function ($c) {
  return new \Zend_Mail();
};
$container['mail'] = $container->extend('mail', function($mail, $c) {
  $mail->setFrom($c['mail.default_from']);
  return $mail;
});

Primul parametru pentru această funcție este  numele obiectului pe care dorim să îl adăugam  în container, iar al doilea – o funcție anonima care ia ca argument acest obiect  și containerul curent. Ca urmare, atunci când apelăm  obiectul cu ajutorul container-ului pimple, acest obiect este returnat de funcția dată.
În cazul în care obiectul a fost “Singleton”, adică initializat cu share(), trebuie sa redifinim (reinitializam) codul cu ajutorul metodei share(), in caz contrar, de fiecare data cand apelam obiectul dat, extinderile deasemenea vor fi chemate de fiecare dată:

$container['twig'] = $container->share(function ($c) {
  return new Twig_Environment($c['twig.loader'], $c['twig.options']);
});
$container['twig'] = $container->share($container->extend('twig', function ($twig, $c) {
  $twig->addExtension(new MyTwigExtension());
  return $twig;
}));

Accesul la funcțiile care returneaza obiectul

De fiecare dată când accesați serviciul (obiectul), Pimple, în mod automat cheama funcția care inițializează acest obiect. Dacă doriți să obțineți acces direct la funcțiile de inițializare, puteți folosi metoda raw():

$container['session'] = $container->share(function ($c) {
  return new Session($c['session_storage']);
});
$sessionFunction = $container->raw('session');

Reinițializarea container-ului deja inițializat

Dacă folosiți aceeași biblioteci in diferite proiecte, puteți crea containere pentru re-utilizare. Tot ce trebuie sa faci – este de a extinde classul Pimple:

class SomeContainer extends Pimple
{
  public function __construct()
  {
    $this['parameter'] = 'foo';
    $this['object'] = function () { return stdClass(); };
  }
}

Cu usurinta puteti folosi acest container in cadrul altuia

$container = new Pimple();
// initializarea obiectelor si parametrilor container-ului principal
// ...
// extinderea altui container
$container['embedded'] = $container->share(function () { return new SomeContainer(); });
// configurarea 
$container['embedded']['parameter'] = 'bar';
// accesul final al container-ului
$container['embedded']['object']->...;

Concluzii

Gestionarea dependenței – una dintre cele mai importante și, în același timp, cele mai grele probleme în dezvoltarea aplicatiilor web. Majoritatea framework-urilor oferă propriile soluții la această problemă, sau nu le ofera deloc, ceea ce nu este optimal pentru proiectarea rapida a aplicatiilor

PS: Link-ul pentru download, de aici am folosit si exemplele de mai sus