Dependency Injection Container med anonyma funktioner/closures i PHP 5.3

Tänkte visa hur man kan använda anonyma funktioner/closures (en funktion som infördes i PHP 5.3) för att skapa en enkel Dependency Injection Container.

En DI-container är ett objekt som håller reda på dina objekts beroenden av andra objekt eller variabler. Du konfigurerar den en gång (i en config-fil eller likande) så att du slipper att fundera över vad som krävs för att skapa objekt senare. Du använder helt enkelt containern för att skapa objekt istället för att använda new.

Den lagrar även den skapade instansen, så att du alltid hämtar samma instans varje gång du anropar den. Du kombinerar lämpligen med en autoloader. På det viset får du både lazy initialization och lazy loading på en gång.

**
* Dependency Injection Container / Registry
* För PHP >= 5.3
*
* @author Ola Waljefors
*/
class container
{
    /**
    * $var array Lagrad data
    * @access private
    */
    private $data;

    /**
    * Registrera en egenskap
    *
    * @param string $key
    * @param mixed $value
    */
    public function __set ($key, $value)
    {
        $this->data[$key] = $value;
    }

    /**
    * Returnera en egenskap
    *
    * Om den är en closure anropas den och lagrar
    * ny data innan den returneras
    *
    * @param string $key
    * @return mixed
    */
    public function __get ($key)
    {
        if (is_a ($this->data[$key], 'Closure')) {
            $this->data[$key] = $this->data[$key] ();
        }
        return $this->data[$key];
    }
}

__set() lagrar den variabeln/objektet/whatever i containern.

Om __get() stöter på en closure så kör den funktionen och returnerar svaret, annars returnerar den egenskapen som den är.

Så hur använda den?

För att lagra och hämta tex strängar gör du så här:

$c = new container ();

$c->db_engine = 'mysql';
$c->db_host = 'localhost';
$c->db_name = 'wordpress';
$c->db_user = 'root';
$c->db_password = '';

// Skriv ut en sträng lagrad i containern
echo $c->db_engine;

Lite exempel på hur du konfigurerar objekt:

// Ett enkelt objekt utan argument
$c->mittObjekt = function () use ($c) {
    return new mittObjekt ();
};

// Ett andra objekt som använder det
// första som argument
$c->mittAndraObjekt = function () use ($c) {
    return new mittAndraObjekt ($c->mittObjekt);
};

// Ett PDO-objekt som använder containerns
// egenskaper som argument
$c->pdo = function () use ($c) {
    $dsn = sprintf ('
        %s:dbname=%s;host=%s',
        $c->db_engine,
        $c->db_name,
        $c->db_host
    );
    return new PDO(
        $dsn,
        $c->db_user,
        $c->db_password
    );
};

När du sedan ska hämta objekten så kommer den anonyma funktionen att köras, det skapade objektet lagras i containern och returneras så att du kan börja använda det.

Alla beroenden hämtas rekursivt så att alla objekt skapas upp automatiskt (i detta fallet mittAndraObjekt och mittObjekt). Om något av de ingående objekten redan har skapats upp så används dem.

// Hämta ett objekt från containern
$c->mittObjekt;

// Om du samtidigt vill köra en av objektets
// metoder (utan att behöva köra raden ovan)
$c->mittObjekt->metod();

Fabien Potencier (skaparen av ramverket Symfony) har skrivit ett blogginlägg som förklarar enkelt varför en DI-container kan vara en bra idé.

Lämna ett svar

E-postadressen publiceras inte. Obligatoriska fält är märkta *