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