Facebook
Twitter
Google+
Kommentare
5

Inversion of Control – Teil 3

Nachdem ich nun an einem (zugegebenermaßen unschönen) Beispiel gezeigt habe, wie so ein Container funktioniert möchte ich den Container aus dem Symfony Framework vorstellen. Zend hatte mal einen Container im Programm (zumindest im Labratory) jedoch wurde dieser wieder verworfen. Die genauen Hintergründe sind mir leider nicht bekannt. Ansonsten gibt es noch den einen oder anderen Container für PHP, jedoch macht Symfony mir in diesem Bereich den besten Eindruck. Von der Stabilität als auch von der Geschwindigkeit her. Er ist mit einer Code-Coverage von 100% getestet (das sagt noch nicht alles aus, aber schonmal, dass man da wirklich dahinter ist!). Und ansonsten lassen sich alle Konfigurationen und Zusammenstellungen zu 100% als PHP darstellen und kompilieren. Man kann also bei Bedarf die Unterstützung eines PHP-Opcode-Caches voll ausnutzen.

Auch hier wird ein Container verwendet. Das tolle ist aber, dass sich jeder aussuchen kann wie er seinen Container konfigurieren möchte. Man kann ihn direkt als PHP programatisch beschreiben. Oder mit INI-Dateien, mit XML-Dateien oder als YAML-Dateien. Und wenn der Container geladen ist, kann man ihn auch in jedes dieser Formate abspeichern egal wie man ihn geladen hat. Man kann so also wenn man maximale Performance will in der Entwicklung mit XML arbeiten und auf dem Live-System eine gespeicherte PHP-Konfiguration deployen.

Zuerst einmal wie eine Konfiguration von Hand ohne DI aussehen würde:

$transport = new Zend_Mail_Transport_Smtp('smtp.gmail.com', array(
  'auth'     => 'login',
  'username' => 'foo',
  'password' => 'bar',
  'ssl'      => 'ssl',
  'port'     => 465,
));

$mailer = new Zend_Mail();
$mailer->setDefaultTransport($transport);

Und hier als Beispiel wir er im Symfony als XML konfiguriert wird:

foo
bar
Zend_Mail

      smtp.gmail.com

        login
        %mailer.username%
        %mailer.password%
        ssl
        465

Man beachte, dass hier auch Variablenersetzung verwendet wird. So kann man den Benutzernamen auch mehrfach verwendet und dann schnell für alle Klassen ändern.

So wie wird der Spaß nun aber verwendet? Ganz einfach. Zuerst wird der Container erzeugt und mit der XML-Datei befüllt.

require_once '/PATH/TO/sfServiceContainerAutoloader.php';
sfServiceContainerAutoloader::register();

$sc = new sfServiceContainerBuilder();

$loader = new sfServiceContainerLoaderFileXml($sc);
$loader->load('/somewhere/container.xml');

Wenn man den Container nun als PHP-Datei speichern will um ihn schneller zu laden geht das wie folgt:

$dumper = new sfServiceContainerDumperPhp($sc);
$code = $dumper->dump(array('class' =>'Container'));

file_put_contents('/somewhere/container.php', $code);

Das ist aber nur optional.

Zugriff auf die Objekte holt man sich nun über:

$transport = $sc->getMailTransportService();
$mailer = $sc->getMailerService();

Ein ebenfalls sehr nettes Feature ist, dass man den Container auch als .dot-Datei ausgeben kann und mit dem dot-Programm in ein PNG-Bild konvertieren kann. Auf diese Weise hat man schnell einen Überblick über seine bestehende Anwendung und kann dies auch für die Dokumentation verwenden.

$dumper = new sfServiceContainerDumperGraphviz($sc);
file_put_contents('/somewhere/container.dot', $dumper->dump());
$ dot -Tpng /somewhere/container.dot > /somewhere/container.png

So damit hoffe ich nun einen guten Einblick in das Feld der DI und IoC gegeben zu haben. Und wie einfach und komfortabel man diese Pattern mit Symfony einsetzen kann.

Da ich DI und IoC auch jahrelang im Javaumfeld eingesetzt habe und es nun auch in PHP nun ausprobiert habe, kann ich sagen, dass es eine feine Sache ist. Grundsätzlich ist es ein wenig Mehraufwand. Die so oft hochgehaltene Flexibilität tritt in der Praxis aber eher nie ein. Weil bei einem Ansatz ohne DI kann man sich Stück für Stück seine Klasse aufbauen. Bei DI muss man sie konfigurieren. Bei jeder Änderung fasst man die Klasse als auch die Konfigurationsdatei an. Ebenso bekommt man damit einen Single-Point-of-Failure. Wenn die Konfigurationsdatei kaputt geht geht im Zweifel gar nix mehr. Ebenso arbeiten so alle Teammitglieder an der Konfigurationsdatei, was zu häufigeren Merges führen kann. Dies kann man jedoch durch aufteilen in mehrere Konfigurationsdateien und entsprechende Includes verringern. Bleibt noch der Aufwand, das man jedesmal zwei Dateien anfasst anstatt nur eine. Soweit zu den negativen Aspekten. Die Vorteile sind aber auch sehr deutlich. Jeder weiß wo er schauen muss wenn ein Logger oder eine Klasse konfiguriert werden soll. Kein Wühlen mehr durch viele Bootstrap und Konfig-Klassen oder gar durch die halbe Anwendung. Man hat einfach Ordnung. Man kann Anwendungsweit einen Logger schnell mal aktivieren oder deaktivieren. Man kann durch ein geschicktes Deployment konfigurativ auf einem Live-System einen Debugger deaktivieren ohne in der Entwicklung irgend etwas verändern zu müssen. In der Entwicklungsumgebung hat man also weiterhin seine Debugausgaben.

Das ganze ist also auf jeden Fall ein guter Schritt in Richtung saubere Architektur, weil es eine Menge kleiner Schweinereien die man so gerne macht unterbindet. Und aus diesem Grund gehört dieses Pattern ab einer gewissen Projektgröße eigentlich zu den Must-Haves.

Über den Autor

Sven Weingartner

Sven Weingartner ist aktuell Software-Entwickler bei einem großen deutschen Portalbetreiber. Nach einem Ausflug in die Enterprise-Entwicklung mit Java und SAP ist er seit drei Jahren wieder in der professionellen PHP-Entwicklung tätig. Dabei hat er vor allem Portale mit sehr hohen Besucherzahlen und weltweiter Internationalisierung entwickelt. Daher liegt sein Schwerpunkt vor allem in Performanceoptimierung und der Systemarchitektur.
Kommentare

5 Comments

  1. Die Schlussfolgerungen kann ich nur unterstreichen 🙂
    Genial an DI ist, wie ich finde, die einfache Austauschbarkeit von Abhängigkeiten. Ich muss nur eine Zeile in der Konfiguration (XML, JSON, PHP, …) ändern und schon läuft meine Anwendung statt gegen den Live-Webservice gegen eine Dummy-Implementierung. Da die Änderungen nicht im Programmcode erfolgt besteht i.d.R. die Gefahr nicht dass die Änderungen im SVN landet und ggf. auf dem Live-System 🙂

    Reply
  2. @Stephan
    „Austauschbarkeit von Abhängigkeiten“ ist dann meines Erachtens etwas schlecht ausgedrückt, da wir streng genommen mittels DI sämtliche Abhängigkeiten zu „eliminieren“ versuchen (Implementation gegen das Interface). Aber Recht hast du schon ;).
    Hat jemand Erfahrung mit Stubbles? Soll ja nahe an google’s guice sein (punkto DI).

    Reply
  3. @Michael Guter Einwand, Abhängigkeiten tauscht man nicht aus, aber die Implementierung. Ich werde es mir merken 🙂
    Die Abhängigkeit als solche bleiben natürlich irgendwo bestehen, allerdings nicht gegen eine konkrete Implementierung sondern wie du richtig schriebst gegen ein Interface.

    Reply

Leave a Comment.

Link erfolgreich vorgeschlagen.

Vielen Dank, dass du einen Link vorgeschlagen hast. Wir werden ihn sobald wie möglich prüfen. Schließen