<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Dependency Injection | Nerdpress.org</title>
	<atom:link href="https://nerdpress.org/tag/dependency-injection/feed/" rel="self" type="application/rss+xml" />
	<link>https://nerdpress.org</link>
	<description>...dev, tech problems and solutions.</description>
	<lastBuildDate>Wed, 19 Oct 2011 12:01:30 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>[Symfony 2][Twig] &#8211; Enabling (native) Twig Extensions</title>
		<link>https://nerdpress.org/2011/10/19/symfony-2-twig-enabling-native-twig-extensions/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Wed, 19 Oct 2011 11:39:46 +0000</pubDate>
				<category><![CDATA[Deployment]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Project Setup]]></category>
		<category><![CDATA[Symfony]]></category>
		<category><![CDATA[Tools]]></category>
		<category><![CDATA[Dependency Injection]]></category>
		<category><![CDATA[DIC]]></category>
		<category><![CDATA[Extensions]]></category>
		<category><![CDATA[symfony 2]]></category>
		<category><![CDATA[Twig]]></category>
		<category><![CDATA[Twig Extension]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=1899</guid>

					<description><![CDATA[<p>Twig Extensions is a tiny official repository for extensions to the Twig templating markup language, the default templating engine in each Symfony 2 (Standard Ed.) project. This short article shows how to purposeful enable them per-environment for your projects. The twig-extension repository is usually fetched by calling into your project´s ./vendor directory (because it is &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2011/10/19/symfony-2-twig-enabling-native-twig-extensions/" class="more-link">Continue reading<span class="screen-reader-text"> "[Symfony 2][Twig] &#8211; Enabling (native) Twig Extensions"</span></a></p>
The post <a href="https://nerdpress.org/2011/10/19/symfony-2-twig-enabling-native-twig-extensions/">[Symfony 2][Twig] – Enabling (native) Twig Extensions</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p><a href="https://github.com/fabpot/Twig-extensions">Twig Extensions</a> is a tiny official repository for extensions to the <a href="http://twig.sensiolabs.org/">Twig templating markup language</a>, the default templating engine in each Symfony 2 (Standard Ed.) project. This short article shows how to purposeful enable them per-environment for your projects.</p>
<p><span id="more-1899"></span></p>
<p>The twig-extension repository is usually fetched by calling</p>
<pre class="brush: bash; title: ; notranslate">
$ bin/vendor install
</pre>
<p>into your project´s ./vendor directory (because it is part of the ./deps file of each symfony 2 standard edition).</p>
<p>After installing the required vendor files, register the namespace fallback in your autoload.php (if you use the one delivered with Symfony 2 Standard, this should already have been done for you):</p>
<pre class="brush: php; title: ; notranslate">
use Symfony\Component\ClassLoader\UniversalClassLoader;
use Doctrine\Common\Annotations\AnnotationRegistry;

$loader = new UniversalClassLoader();
$loader-&gt;registerNamespaces(array(
    'Symfony'          =&gt; array(__DIR__.'/../vendor/symfony/src', __DIR__.'/../vendor/bundles'),
    // ...
));

$loader-&gt;registerPrefixes(array(
    'Twig_Extensions_' =&gt; __DIR__.'/../vendor/twig-extensions/lib',
    'Twig_'            =&gt; __DIR__.'/../vendor/twig/lib',
));
</pre>
<p>Remember that twig extensions are always registered as <a href="http://symfony.com/doc/2.0/book/service_container.html#tags-tags">tagged services</a>, but it is not always desirable to register a twig extension like e.g. the Debug-Extension as a regular service globally. We could do so by adding the service definition to the services.xml-file in one of our bundles. But due to the special purpose of the debug extension, we will choose a different, more &#8220;project-wide-configuration&#8221;-way.</p>
<p>Luckily, Symfony provides means to dynamically load services &#8220;on demand&#8221; and depending on the current <a href="http://symfony.com/doc/2.0/cookbook/configuration/environments.html">environment</a>.</p>
<p>Add the following lines to your app/config/config_dev.yml:</p>
<pre class="brush: python; title: ; notranslate">
services:
    debug.twig.extension:
        class: Twig_Extensions_Extension_Debug
        tags: &#x5B;{ name: 'twig.extension' }]
</pre>
<p>The &#8220;services&#8221;-configuration option may be utilized to register any class as a <a href="http://symfony.com/doc/2.0/book/service_container.html">DIC</a> managed <a href="http://symfony.com/doc/2.0/book/service_container.html#what-is-a-service">service</a>, for each project and independent from your bundle configuration (But remember that &#8220;regular&#8221; service definition configuration should usually happen in your bundle´s configuration files, otherwise you will not able to distribute your bundles because of missing or broken dependencies).</p>
<p>For twig extensions that should only be loaded for special purposes (like our DEBUG-function which is only needed in development environments), config_dev.yml is the perfect place to register them.</p>
<p>Note the &#8220;tags&#8221;-configuration which marks the service as a twig extension. Now, let´s create a demo twig-template and try the {% debug %} function:</p>
<pre class="brush: xml; title: ; notranslate">
{% extends &quot;NerdpressDemoBundle::layout.html.twig&quot; %}
{% debug %}
</pre>
<p>That will do. Open the page in your favorite webbrower (after defining a route, a controller and other stuff i won´t mention here) and admire the debug output. Note that the output will be much more nicer if you have had installed a native php debug extension like e.g. <a href="http://xdebug.org/">XDebug</a>.</p>The post <a href="https://nerdpress.org/2011/10/19/symfony-2-twig-enabling-native-twig-extensions/">[Symfony 2][Twig] – Enabling (native) Twig Extensions</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Symfony 2 wird super. Oder &#8230;?</title>
		<link>https://nerdpress.org/2011/05/11/symfony-2-wird-super-oder/</link>
					<comments>https://nerdpress.org/2011/05/11/symfony-2-wird-super-oder/#comments</comments>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Tue, 10 May 2011 22:09:30 +0000</pubDate>
				<category><![CDATA[API]]></category>
		<category><![CDATA[Doctrine ORM]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Software engineering]]></category>
		<category><![CDATA[Symfony]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Dependency Injection]]></category>
		<category><![CDATA[Design Pattern]]></category>
		<category><![CDATA[DIC]]></category>
		<category><![CDATA[IoC]]></category>
		<category><![CDATA[Software Architecture]]></category>
		<category><![CDATA[symfony 2]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=1462</guid>

					<description><![CDATA[<p>Natürlich wird Symfony 2 super. Die Dokumentation ist wie gewohnt zum jetzigen, frühen Zeitpunkt genial, die Architektur durchdacht, die Entwickler-Community steckt sowieso alles in die Tasche, man sieht einfach: Da steckt eine Menge Arbeit, Hirnschmalz und Erfahrung hinter. Aber genug geschleimt ;) Mein Lieblingsthema ist ja zur Zeit der Dependency Injection Container. Und irgendwie stinkt &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2011/05/11/symfony-2-wird-super-oder/" class="more-link">Continue reading<span class="screen-reader-text"> "Symfony 2 wird super. Oder &#8230;?"</span></a></p>
The post <a href="https://nerdpress.org/2011/05/11/symfony-2-wird-super-oder/">Symfony 2 wird super. Oder …?</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>Natürlich wird Symfony 2 super. Die Dokumentation ist wie gewohnt zum jetzigen, frühen Zeitpunkt genial, die Architektur durchdacht, die Entwickler-Community steckt sowieso alles in die Tasche, man sieht einfach: Da steckt eine Menge Arbeit, Hirnschmalz und Erfahrung hinter. Aber genug geschleimt ;) </p>
<p>Mein Lieblingsthema ist ja zur Zeit der Dependency Injection Container. Und irgendwie stinkt mir die ganze Container-Konfiguration noch gewaltig. Meine <a href="https://nerdpress.org/2010/09/26/dependency-injection-mit-php-5-3-runkit-erweiterung-und-doctrine-2-annotationen/">kläglichen Versuche</a>, selbst mal so was ähnliches wie eine brauchbare Autowiring-Implementierung herunterzubrechen, waren natürlich auch bzw. erst recht nicht der große Wurf &#8211; was vor allem daran lag, dass ich den Service Container und damit den ganzen Sinn und Zweck des Ganzen einfach mal wegrationalisiert hatte &#8211; Loose Coupling sieht natürlich anders aus, das sei den Kritikern zugestanden. Ich verteidige mich mal dadurch, dass ich eigentlich nur mal mit Mixins rumspielen wollte &#8211; da hab&#8217; ich wohl die eine oder andere Begrifflichkeit durcheinander geworfen. </p>
<p>Aber um mal zu des Pudels Kern zu kommen: Ist es wirklich so geil, mit kilometerlangen XML-Dateien, &#8216;ner Menge Initialisierungscode und ohne mit der heißgeliebten Code-Completion in der IDE meiner Wahl ein Paradigma zu kaufen, das im speziellen Anwendungskontext &#8211; nicht im Framework-Kontext &#8211; eher selten Anwendung findet? </p>
<p><span id="more-1462"></span></p>
<p>Es ist doch so: PHP bietet zwar mittlerweile eine vernünftige Reflection API, aber wenig (keine) Möglichkeiten, Code zur Compile- oder Runtime effizient zu injizieren (vorbei bspw. an der Sichtbarkeit, was für Property Injection aus meiner Sicht erforderlich wäre). Mit Handständen geht das mittels der Reflection API, noch fieser mit bspw. Runkit. </p>
<p>Somit ist &#8211; (natürlich nur aus meiner Sicht, was im Übrigen für den gesamten &#8220;Complaint&#8221; gilt &#8211; dabei wäre ich wirklich froh, berichtigt zu werden) jeder Versuch, die Konfiguration eines wie auch immer gearteten, auf PHP sitzenden, DIC mit Syntactic Sugar anzureichern, vergeblich. Und Syntactic Sugar ist dringend vonnöten, um das Zeug in der Praxis RAD-tauglich zu machen. Nehme ich PHP den RAD-Faktor, was bleibt denn dann noch übrig?</p>
<p>Symfony 2 behilft sich damit, einen DI-Container zu definieren und ihn als PHP-Code wegzucachen. Dieser hat dann Accessoren für alle definierten Services, Constructor-Injection ist durch Referenzierung in den Konfigurationsdateien möglich. Die Doku spricht zusätzlich von Setter- und Property-Injection, dazu konnte ich aber irgendwie keine dokumentierten Beispiele finden. Es gibt aber immerhin die Möglichkeit, Methoden in der Service-Klasse als Pre- oder Post-Initialization Interceptoren auszuführen.</p>
<p>Letztlich tut der Container, was er soll: Abhängigkeiten in beliebige Instanzen zu injizieren. Bei Bedarf auch gegen ein Interface, aber das ist letztlich aufgrund des Sprachentwurfs nur optional (Was impliziert, das man auf Kosten riesiger Flexibilität &#8211; die man nicht braucht &#8211; schnell mal riskiert, durch einen falsch konfigurierten DI-Container seine Anwendung kaputt zu machen).</p>
<p>Aber der Aufwand steht meiner Meinung nach noch in keinem Verhältnis zum Nutzen. Am Beispiel einer Controller Extension, die als Service definiert wird, möchte ich das mal demonstrieren. Die offizielle Symfony 2-Dokumentation sagt, dass</p>
<blockquote><p>To keep things simple, Symfony2 by defaults does not require that controllers be defined as services. Furthermore Symfony2 injects the entire service container into your controller.
</p></blockquote>
<p>Das ist immerhin schon einmal eine Information. Das Standardverhalten ist also, dass ich automagisch eine Controllerklasse habe, die den Container kennt (Wie? Magisch? Ich dachte, Symfony 2 hat die Magie zugunsten transparentem Code und ständiger Kontrolle über jeden Aspekt des Core-Frameworks aufgegeben?). Dadurch, dass er ein ContainerAware Interface implementiert, kennt er auch einen Shortcut auf </p>
<pre class="brush: php; title: ; notranslate">$this-&gt;container-&gt;get('serviceid')</pre>
<p>, nämlich </p>
<pre class="brush: php; title: ; notranslate">$this-&gt;get('serviceid');</pre>
<p>. Um Bspw. ein Doctrine-Entity zu persistieren, reichen &#8220;ein paar codezeilen&#8221;:</p>
<pre class="brush: php; title: ; notranslate">
     $em = $this-&gt;get('doctrine.orm.entity_manager');
     $em-&gt;???($myEntity);
</pre>
<p>Wie hieß die Methode da am Entity Manager jetzt nochmal? Das ist das erste Problem: Dadurch, dass alle Services anonym im Service Container herumdümpeln bzw. auf ihre Instanziierung warten, habe ich in meiner IDE keinerlei Code-Completion mehr. Um das wieder zu erreichen, muss ich entweder<br />
&#8211; einen Accessor schreiben (getEntityManager()) und diesen mittels PHP-Doc type-hinten (annotieren) oder<br />
&#8211; mindestens einen magischen Docblock davorsetzen, der genau *meiner* IDE die Möglichkeit gibt, den Variablentyp zu erraten (aber ggf. proprietär ist, d.h. in anderen IDEs nicht unbedingt funktionieren muss). Für die Netbeans IDE gibt es dafür bspw. das &#8220;<a href="http://blogs.oracle.com/netbeansphp/entry/defining_a_variable_type_in">vdoc</a>&#8220;-Feature.</p>
<p>Das ganze setzt natürlich voraus, dass ich *weiß*, welches Interface der Service doctrine.orm.entity_manager implementiert bzw wie die konkrete Klasse (samt Namespace) desselben heißt. Und vorher muss ich auch noch wissen, wie überhaupt der *Servicename* des Entity Managers lautet. </p>
<p>Bin ich also komplett Ahnungslos, rufe ich erstmal ein Terminal auf, tippe sowas wie</p>
<pre class="brush: jscript; title: ; notranslate">$ app/console container:debug --show-private</pre>
<p> ein und suche mir anschließend in der kilometerlangen Liste genau die Services zusammen, die ich brauche.</p>
<p>Dafür brauche ich als Framework-Newbie schonmal richtig tiefgehende Kenntnisse darüber, was ein DIC überhaupt ist, tut und zur Konfiguration erfordert.</p>
<p>Um also das, was &#8220;früher&#8221; ein </p>
<pre class="brush: php; title: ; notranslate">new Cart($color, $maxSpeed)-&gt;save()</pre>
<p> war, zu erreichen, muss ich nun erstmal ganz schön viel Sport treiben (dieses Beispiel nur exemplarisch, es steht außer Frage, dass die Doctrine 2-Architektur wirklich klasse ist. Das Manko aus meiner Sicht ist die Integration in Sf2, nicht der ORM-Layer an sich.)  </p>
<p>Möchte ich den Accessor der ContainerAware-Implementierung am Controller vermeiden und die Services &#8220;direkt&#8221; injizieren, muss ich ihn selbst als Service am DIC registrieren. Wie das funktioniert, würde den Rahmen sprengen, nur so viel: Der Aufwand steht aus meiner Sicht wieder in keinem Verhältnis zum Ergebnis. Allerdings habe ich damit den Vorteil, dass ich bspw. keine abstrakte Controllerklasse mehr zu erweitern habe &#8211; der Controller kann ein simples Popo sein. Dass es aber nicht zu simpel sein darf, zeigt bspw., das ich trotz alledem mindestens einen Constructor definieren muss, der eben alle benötigten Services aufnimmt und an Instanzvariablen bindet. Und das können einige sein &#8211; fängt an beim Templating, geht übers Formframework, ggf. den Entity-Manager, einen Mailer und was einem sonst noch so einfällt. Dabei wollten wir doch ab jetzt nur noch leichtgewichtige Controller haben?</p>
<p>Meiner Meinung nach liegt die Lösung der Malaise in zwei Alternativen: Man baut so lange Abstracts, die den ganzen Kram als Shortcuts bereitstellen (dann hätten wir bspw. wieder eine komplexe, abstrakte Kontrollerklasse, die mindestens alles kennt, was in Symfony 1 noch dem ApplicationContext gehörte) &#8211; womit wir unser Loose Coupling Pardigma wieder in den Schrank zu den übrigen Ideen stellen können, die im Nachgang irgendwie doch nicht funktionierten. Oder legt Autowiring drüber. </p>
<p>Flow 3 baut bspw. an einem DIC + Autowiring, der alle verwalteten Services durch eine Kombination der Analyse von Annotationen (also PHP-Doccomments) und Signaturen (Typehints) direkt an die erzeugten Instanzen bindet. Die Konfiguration scheint damit sehr viel leichtgewichtiger. </p>
<p>Grails (was so weit ich weiß auf der Spring IOC-Komponente sitzt) geht noch einen Schritt weiter und mappt Instanzvariablen durch reine Naming-Conventions: Wenn also eine Instanzvariable auf &#8220;Service&#8221; endet und es zur Eigenschaft &#8220;mailerService&#8221; einen passenden Service &#8220;mailer&#8221; gibt, wird dieser automagisch injiziert. Kein zusätzlicher Code noch Annotations vonnöten.</p>
<p>Ich wüsste aber nicht, wie man solche Features wirklich performanceneutral, effizient und stabil auf einer Plattform wie PHP aufsetzen könnte &#8211; dafür ist die Sprache einfach (noch) nicht weit genug. </p>
<p>Abschließend bleibt ein übler Nachgeschmack &#8211; und der Eindruck, dass man sich hier fatal an den XML-&#8220;Konfigurationswahnsinn&#8221; der entsprechenden Java-&#8220;Vorbilder&#8221; annähern möchte. Wo doch gerade in diesem Lager  intensiv daran gearbeitet wird, genau davon abzurücken und Konfigurationen eben durch Konventionen zu ersetzen.  </p>
<p>Was denkt ihr? Kann das ganze doch durch Routine punkten, sodass es durch ein wenig konkrete Projekterfahrung doch ganz leicht von der Hand geht? Wird der DIC im Tagesgeschäft schon intensiv benutzt, oder werden nur die Standardfunktionen des Frameworks verwendet und das Zeug &#8220;unter der Haube&#8221; doch lieber den Framework- und Bundle-Entwicklern überlassen? Ich bin wieder sehr gespannt auf Meinungen und Kritik&#8230;</p>The post <a href="https://nerdpress.org/2011/05/11/symfony-2-wird-super-oder/">Symfony 2 wird super. Oder …?</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
					<wfw:commentRss>https://nerdpress.org/2011/05/11/symfony-2-wird-super-oder/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>Symfony 2 &#8220;from scratch&#8221; bootstrappen</title>
		<link>https://nerdpress.org/2010/12/01/symfony-2-from-scratch-bootstrappen/</link>
					<comments>https://nerdpress.org/2010/12/01/symfony-2-from-scratch-bootstrappen/#comments</comments>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Wed, 01 Dec 2010 11:52:52 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Symfony]]></category>
		<category><![CDATA[Bootstrap]]></category>
		<category><![CDATA[Bundle]]></category>
		<category><![CDATA[Dependency Injection]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[Install]]></category>
		<category><![CDATA[Kernel]]></category>
		<category><![CDATA[PHP 5.3]]></category>
		<category><![CDATA[Project]]></category>
		<category><![CDATA[Setup]]></category>
		<category><![CDATA[symfony 2]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=1247</guid>

					<description><![CDATA[<p>&#8230; machen wir heute mal, weil die Sandbox komisch ist mit den vielen (ärm &#8211; 2) redundanten /vendor und /wasweißich-Verzeichnissen. Also, bauen wir die Sandbox mal nach: Wir brauchen: Ein halbwegs aktuelles PHP 5.3.x, MySQL 5.x, Apache 2 und git. Dann erstmal den aktuellen Symfony2-Master ziehen&#8230; $ git clone https://github.com/fabpot/symfony.git symfony2test &#8230; und die Vendor-Scripte &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2010/12/01/symfony-2-from-scratch-bootstrappen/" class="more-link">Continue reading<span class="screen-reader-text"> "Symfony 2 &#8220;from scratch&#8221; bootstrappen"</span></a></p>
The post <a href="https://nerdpress.org/2010/12/01/symfony-2-from-scratch-bootstrappen/">Symfony 2 “from scratch” bootstrappen</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>&#8230; machen wir heute mal, weil die Sandbox komisch ist mit den vielen (ärm &#8211; 2) redundanten /vendor und /wasweißich-Verzeichnissen. </p>
<p>Also, bauen wir die Sandbox mal nach:</p>
<p><span id="more-1247"></span></p>
<p>Wir brauchen: Ein halbwegs aktuelles PHP 5.3.x, MySQL 5.x, Apache 2 und git.</p>
<p>Dann erstmal den aktuellen Symfony2-Master ziehen&#8230;</p>
<pre class="brush: bash; title: ; notranslate">
$ git clone https://github.com/fabpot/symfony.git symfony2test
</pre>
<p>&#8230; und die Vendor-Scripte installieren:</p>
<pre class="brush: bash; title: ; notranslate">
$ cd symfony2test
$ sh ./install_vendors.sh
</pre>
<p>Nun sollte sich folgendes Verzeichnislayout ergeben haben:</p>
<pre class="brush: bash; title: ; notranslate">
drwxr-xr-x  6 joshi joshi 4,0K 2010-11-30 21:19 .
drwxr-xr-x 22 joshi joshi 4,0K 2010-11-27 16:34 ..
-rw-rw-rw-  1 joshi joshi  998 2010-11-30 21:10 autoload.php.dist
drwxr-xr-x  8 joshi joshi 4,0K 2010-11-30 21:19 .git
-rw-r--r--  1 joshi joshi   34 2010-11-30 15:49 .gitignore
-rwxrwxrwx  1 joshi joshi  840 2010-11-30 21:10 install_vendors.sh
-rw-rw-rw-  1 joshi joshi 1,1K 2010-11-30 21:10 LICENSE
-rw-rw-rw-  1 joshi joshi 1,1K 2010-11-30 21:10 phpunit.xml.dist
-rw-rw-rw-  1 joshi joshi 1,2K 2010-11-30 21:10 README
drwxrwxrwx  3 joshi joshi 4,0K 2010-11-30 21:10 src
drwxrwxrwx  3 joshi joshi 4,0K 2010-11-30 21:10 tests
-rwxrwxrwx  1 joshi joshi  541 2010-11-30 21:10 update_vendors.sh
drwxrwxrwx 11 joshi joshi 4,0K 2010-11-30 21:13 vendor
</pre>
<p>Die Vendor-Scripts liegen zunächst mal im Projekt-Root-Verzeichnis; Dort haben sie aber nichts verloren:</p>
<pre class="brush: bash; title: ; notranslate">
$ mv vendor/ src/
</pre>
<p>Nun haben wir alles, um eine Symfony-App zu erstellen &#8211; das funktioniert momentan aber noch ausschließlich in Handarbeit:</p>
<pre class="brush: bash; title: ; notranslate">
$ mkdir -p web app/cache app/logs app/views app/config  src/Bundle src/Application/HelloBundle/Controller src/Application/HelloBundle/Resources/config src/Application/HelloBundle/Resources/views src/Application/HelloBundle/Resources/views/Hello src/Application/HelloBundle/Entity
</pre>
<p>Anmerkung: Diese wilde Verzeichnisstruktur entspricht 1:1 der Sandbox. Sicherlich steht das endgültige Verzeichnis-Layout eines typischen Symfony2-Projekts noch nicht fest &#8211; auch gibt es ja, wie bereits erwähnt, (noch) keine globale Executable samt symfony:generate-project Task. Ein Ziel von Symfony2 ist, die Modularisierung und Entkopplung der einzelnen Framework-Komponenenten (genannt &#8220;Bundles&#8221;) zu verfeinern. Ähnliche Konzepte kennt man aus der Java-Welt, aber auch bspw. aus CMF wie Silverstripe.</p>
<p>So ist der aktuelle Dev-Branch also eher als lose Sammlung einzelner &#8220;Bundles&#8221; zu verstehen und nicht als Full-Stack-Framework (Die Menge aller Bundles + Task-Bundle wird aber wieder genau das sein). Am ehesten vielleicht vergleichbar mit dem Aufbau des Zend-Frameworks &#8211; wobei das wohl fast als Negativ-Beispiel für &#8220;fake-loose-coupling&#8221; herhalten kann. Symfony2-Bundles sind &#8220;echt&#8221; decoupled und erwarten als einzige Abhängigkeit einen PHP-Autoloader.</p>
<p>&#8220;Bundles&#8221; sind die &#8220;Plugins&#8221; aus Symfony 1.x, mit dem Unterschied, dass das <a href="http://docs.symfony-reloaded.org/guides/internals/overview.html">Core-Framework selbst (&#8220;Kernel&#8221;) wiederum auch ein Bundle ist</a>.</p>
<p>Kurz gesagt: Das Verzeichnislayout, das in Symfony1.x zwar auch mehr oder weniger flexibel war, doch nur unter Schmerzen tiefgreifend veränderlich war, ist nun erstmal völlig variabel. Da ich mich aber an Fabians vorausschauender best-practice orientieren möchte, sieht &#8220;mein&#8221; Verzeichnislayout jetzt eben zufälligerweise aus wie das der Symfony2-Sandbox:</p>
<pre class="brush: bash; title: ; notranslate">
app/
  cache/
  config/
  logs/
  views/
autoload.php.dist
install_vendors.sh
LICENSE
phpunit.xml.dist
README
src/
  Application/
    HelloBundle/
      Controller/
      Entity/
      Resources/
        config/
        views/
  Bundle/
  Symfony/
  vendor/
    ...
tests/
update_vendors.sh
web/
</pre>
<p>Das Verzeichnis /web ist unser Webroot, es sollte nun also ein VirtualHost angelegt werden, der auf dieses zeigt: </p>
<pre class="brush: xml; title: ; notranslate">
&lt;VirtualHost *:80&gt;
  SetEnv APP_ENV dev
  ServerName symfony2.int
  DocumentRoot /var/www/symfony2test/web
&lt;/VirtualHost&gt;
</pre>
<p>Nun können wir damit beginnen, unsere Anwendungsdateien zu erstellen, indem wir einen Frontcontroller anlegen:</p>
<p>web/index.php</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
require_once __DIR__.'/../app/AppKernel.php';
use SymfonyComponentHttpFoundationRequest;
$kernel = new AppKernel('prod', false);
$kernel-&gt;handle(new Request())-&gt;send();
</pre>
<p>Zunächst holen wir uns den für unsere Anwendung spezifischen Kernel, instanziieren und bitten ihn, einen Http-Request zu bearbeiten.</p>
<p>Damit das Routing funktioniert, benötigen wir noch eine .htaccess-Datei (oder eine entsprechende mod_rewrite-Regel in der VirtualHost-Konfiguration):</p>
<p>web/.htaccess</p>
<pre class="brush: xml; title: ; notranslate">
&lt;IfModule mod_rewrite.c&gt;
  RewriteEngine On
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteRule ^(.*)$ index.php &#x5B;QSA,L]
&lt;/IfModule&gt;
</pre>
<p>Dann erzeugen wir unseren Kernel und eine Cache-Definition in app/:</p>
<p>app/AppKernel.php</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
require_once __DIR__.'/../src/autoload.php';

use SymfonyComponentHttpKernelKernel;
use SymfonyComponentDependencyInjectionLoaderLoaderInterface;

class AppKernel extends Kernel
{
    public function registerRootDir()
    {
        return __DIR__;
    }

    public function registerBundles()
    {
        $bundles = array(
            new SymfonyBundleFrameworkBundleFrameworkBundle(),
            new SymfonyBundleTwigBundleTwigBundle(),

            // enable third-party bundles
            new SymfonyBundleZendBundleZendBundle(),
            new SymfonyBundleSwiftmailerBundleSwiftmailerBundle(),
            new SymfonyBundleDoctrineBundleDoctrineBundle(),
            
            // register your bundles
            new ApplicationHelloBundleHelloBundle(),
        );

        if ($this-&gt;isDebug()) {
            $bundles&#x5B;] = new SymfonyBundleWebProfilerBundleWebProfilerBundle();
        }

        return $bundles;
    }

    public function registerBundleDirs()
    {
        return array(
            'Application'     =&gt; __DIR__.'/../src/Application',
            'Bundle'          =&gt; __DIR__.'/../src/Bundle',
            'Symfony\Bundle' =&gt; __DIR__.'/../src/Symfony/Bundle',
        );
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        // use YAML for configuration
        $loader-&gt;load(__DIR__.'/config/config_'.$this-&gt;getEnvironment().'.yml');
    }
}
</pre>
<p>Interessant sind die beiden zu implementierenden Methoden registerBundles() und registerBundleDirs(). RegisterBundles() teilt dem Kernel mit, welche Bundles in der Anwendung aktiv sind und verwendet werden &#8211; vergleichbar mit enablePlugins() der projectConfiguration.class.php eines beliebigen Symfony 1.x Projekts. registerBundleDirs() mappt wiederum die einzelnen Bundle-Namespaces auf ihre Pfade. Dies ist eigentlich ein wenig doppelt-gemoppelt, verwenden wir doch einen SPL-Autoloader, um unsere Namespaces zu registrieren. Allerdings benötigt der Kernel noch einmal explizit ein Namespace-Bundle-Pfad-Mapping, um bspw. Template-Namespaces korrekt aufzulösen. Stimmt bspw. der Pfad zum SymfonyBundle-Namespace nicht, läuft zwar die Anwendung, doch die Template-Engine kann die Pfade zu bspw. den Assets (Bildern und CSS-Stylesheets) des WebProfilerBundles nicht auflösen. Dies hätte den gleichen Effekt, als hätte ich vergessen, das Assets-Verzeichnis web/sf in einer Symfony 1.x-Anwendung zu referenzieren.</p>
<p>Vorsicht: Die Sandbox und Beispielscripts verwenden hier teils falsche Pfade: So zeigt der Namespace-Pfad der Symfony-Bundles zunächst auf src/vendor/symfony/src/Symfony/Bundle statt auf src/Symfony/Bundle. Das funktioniert in dem Falle auch, weil es eben zwei Symfony-Installationen in der Sandbox gibt (einmal die via install_vendors.sh und die, die man sich selbst gezogen hat). Man sollte sich immerhin für eine entscheiden und seine Projekt- und Arbeitsdateien entsprechend aufräumen. Aber dafür machen wir&#8217;s hier ja auch &#8220;from scratch&#8221;.</p>
<p>Jede Symfony2-Anwendung benötigt zudem eine konkrete, anwendungsspezifische Cache-Implementierung:</p>
<p>app/AppCache.php</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
require_once __DIR__.'/AppKernel.php';
use SymfonyBundleFrameworkBundleCacheCache;
class AppCache extends Cache
{
}
</pre>
<p>Nun können wir uns an die Konfiguration begeben. In der Datei app/AppKernel.php wird bestimmt, wie und wo konfiguriert wird:</p>
<pre class="brush: php; title: ; notranslate">
    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        // use YAML for configuration
        $loader-&gt;load(__DIR__.'/config/config_'.$this-&gt;getEnvironment().'.yml');
    }
</pre>
<p>Demnach müssen wir für jedes Environment eine config_%env%.yml-Datei erstellen. Dadurch, dass das Symfony2 Configuration-Framework explizite Vererbung (besser: imports) untestützt, reicht eine &#8220;globale&#8221; config.yml und entsprechend angepasste config_%env%.yml-Dateien:</p>
<p>app/config/config.yml</p>
<pre class="brush: python; title: ; notranslate">
app.config:
    charset:       UTF-8
    error_handler: null
    csrf_secret:   xxxxxxxxxx
    router:        { resource: &quot;%kernel.root_dir%/config/routing.yml&quot; }
    validation:    { enabled: true, annotations: true }
    templating:    {} #assets_version: SomeVersionScheme
    session:
        default_locale: en
        lifetime:       3600
        auto_start:     true

# Twig Configuration
twig.config:
    debug:            %kernel.debug%
    strict_variables: %kernel.debug%
</pre>
<p>Dann erstellen wir die Konfiguration für das development environment:</p>
<p>app/config/config_dev.yml</p>
<pre class="brush: python; title: ; notranslate">
imports:
    - { resource: config.yml }

app.config:
    router:   { resource: &quot;%kernel.root_dir%/config/routing_dev.yml&quot; }
    profiler: { only_exceptions: false }

webprofiler.config:
    toolbar: true
    intercept_redirects: true

zend.config:
    logger:
        priority: debug
        path:     %kernel.logs_dir%/%kernel.environment%.log
</pre>
<p>In Z. 1 wird config.yml als &#8220;gobale&#8221; Konfiguration importiert. Ansonsten alles mehr oder weniger selbsterklärend, die einzige Auffälligkeit ist, dass das Routing hier nun explizit konfiguriert wird (siehe Z. 5) &#8211; im Gegensatz zu Symfony 1.x-Projekten.</p>
<p>Anmerkung: Falls sich jemand wundern sollte, wo und wie diese Fülle an Optionen dokumentiert sind: Hier hilft nur in die jeweilige <a href="http://components.symfony-project.org/dependency-injection/">Service-Definition des DI-Containers</a> zu schauen. Das mag zu Beginn sehr verwirren, doch die einheitliche Bundle-Struktur hilft da wiederum bei der Navigation. Bundle-Konfigurationen findet man ausschließlich im Verzeichnis Resources/ eines Bundles.</p>
<p>Zurück zur Routing-Configuration, die nun angelegt werden muss:</p>
<p>app/config/routing.yml</p>
<pre class="brush: python; title: ; notranslate">
homepage:
    pattern:  /
    defaults: { _controller: FrameworkBundle:Default:index }

hello:
    resource: HelloBundle/Resources/config/routing.yml
</pre>
<p>app/config/routing_dev.yml</p>
<pre class="brush: python; title: ; notranslate">
_main:
    resource: routing.yml

_profiler:
    resource: WebProfilerBundle/Resources/config/routing/profiler.xml
    prefix:   /_profiler
</pre>
<p>Auch jetzt wurden zwei Routing-Konfigurationen (global und dev), beide werden transparent in config.yml bzw. config_dev.yml referenziert. Die routing_dev.yml ist ausschließlich da um die Route für das WebProfilerBundle zu registrieren.</p>
<p>Die Route &#8220;hello&#8221; in Z. 5 der routing.yml referenziert wiederum eine Bundle-spezifische Route, abzulegen im HelloBundle:</p>
<p>src/Application/HelloBundle/Resources/config/routing.yml:</p>
<pre class="brush: python; title: ; notranslate">
hello:
    pattern:  /hello/:name
    defaults: { _controller: HelloBundle:Hello:index }
</pre>
<p>Somit haben wir fast alles zusammen, um eine Anwendung zum Laufen zu bringen &#8211; Front Controller, Kernel-Konfiguration, Cache, Routing &#8211; nur die konkrete Implementierung unserer Anwendung fehlt noch sowie ein winziges Detail, der Autoloader. Dieser wird ebenfalls durch unseren Kernel angeschmissen (siehe Z. 1 app/AppKernel.php). Erwartet wird eine flache autoload.php-Datei in src/ &#8211; ob sie dort korrekt gelagert ist, oder eher in src/Application liegen sollte oder gar im Projekt root &#8211; das kann sich jeder selbst zusammenreimen.</p>
<p>src/autoload.php:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
require_once __DIR__ . '/Symfony/Component/HttpFoundation/UniversalClassLoader.php';

use SymfonyComponentHttpFoundationUniversalClassLoader;

$vendorDir = __DIR__ . '/vendor';

$loader = new UniversalClassLoader();
$loader-&gt;registerNamespaces(array(
    'Symfony'                        =&gt; __DIR__,
    'Application'                    =&gt; __DIR__,
    'Bundle'                         =&gt; __DIR__,
    'Doctrine\Common\DataFixtures' =&gt; $vendorDir.'/doctrine-data-fixtures/lib',
    'Doctrine\Common'               =&gt; $vendorDir.'/doctrine-common/lib',
    'Doctrine\DBAL\Migrations'     =&gt; $vendorDir.'/doctrine-migrations/lib',
    'Doctrine\ODM\MongoDB'         =&gt; $vendorDir.'/doctrine-mongodb/lib',
    'Doctrine\DBAL'                 =&gt; $vendorDir.'/doctrine-dbal/lib',
    'Doctrine'                       =&gt; $vendorDir.'/doctrine/lib',
    'Zend'                           =&gt; $vendorDir.'/zend/library',
));
$loader-&gt;registerPrefixes(array(
    'Swift_' =&gt; $vendorDir.'/swiftmailer/lib/classes',
    'Twig_'  =&gt; $vendorDir.'/twig/lib',
));
$loader-&gt;register();
</pre>
<p>In autoload.php verwenden wir keine PHP-nativen Autoload-Mechanismen noch den SPL-Autoload-Wrapper direkt, sondern eine weitere Symfony2 Framework-Komponente: Den UniversalClassLoader. Dieser Loader kann Namespaces auflösen, lässt sich aber ebenfalls auf die &#8220;alte&#8221;, unter PHP >= 5.2 gängige Zend bzw. Pear-Notation mappen, was das obige Codesnippet aufzeigt. Intern baut der UniversalClassLoader natürlich auf den entsprechenden SPL-Funktionen auf.</p>
<p>Vorsicht: Der Code entspricht wieder nicht dem originalen Sandbox-Code (Der Namespace-Pfad für die Symfony-Komponenten ist wieder src/ statt src/vendor/symfony/src).</p>
<p>Wir haben weiter oben Routen definiert, die nun entsprechende Endpunkte im HelloBundle brauchen. Wir implementieren dazu eine Bundle-Definition und einen HelloController:</p>
<p>src/Application/HelloBundle/HelloBundle.php</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php

namespace ApplicationHelloBundle;

use SymfonyComponentHttpKernelBundleBundle;

class HelloBundle extends Bundle
{
}

</pre>
<p>src/Application/HelloBundle/Controller:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php

namespace ApplicationHelloBundleController;

use SymfonyBundleFrameworkBundleControllerController;

class HelloController extends Controller
{
    public function indexAction($name)
    {
        return $this-&gt;render('HelloBundle:Hello:index.twig', array('name' =&gt; $name));

        // render a PHP template instead
        // return $this-&gt;render('HelloBundle:Hello:index.php', array('name' =&gt; $name));
    }
}
</pre>
<p>und die zugehörige View:</p>
<pre class="brush: xml; title: ; notranslate">
{% extends &quot;HelloBundle::layout.twig&quot; %}

{% block content %}
    Hello {{ name }}!
{% endblock %}
</pre>
<p>Nun müssen wir nur noch die im Template referenzierte Layout-Datei anlegen:</p>
<p>src/Application/HelloBundle/Resources/views/layout.twig:</p>
<pre class="brush: xml; title: ; notranslate">
{% extends &quot;::layout.twig&quot; %}

{% block body %}
    &lt;h1&gt;Hello Application&lt;/h1&gt;

    {% block content %}{% endblock %}
{% endblock %}
</pre>
<p>Und wiederum die in diesem Bundle-spezifischen Layout-Template referenzierte &#8220;globale&#8221; Layout-Datei &#8211; stilsicher mit HTML5-Doctype:</p>
<p>app/views/layout.twig:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot; /&gt;
        &lt;title&gt;{% block title %}Hello Application{% endblock %}&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        {% block body %}{% endblock %}
    &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Das wars. Man sollte nun im Browser zwei Seiten sehen können, einmal die Standard-Projekt-Route &#8220;/&#8221; auf FrameworkBundle:Default:index gemapped und die Route /hello/:name auf HelloBundle:Hello:index gemapped. Zu beachten ist hierbei das Namespace-Mapping auf die Pfade, die im AppKernel unter registerBundleDirs() angegeben wurden &#8211; zum besseren Verständis der Namensraumauflösung der Routen sowie der Templates.</p>
<p>Abschließend kann man sich noch eine Symfony-Konsolenanwendung nach /app legen:</p>
<p>app/console</p>
<pre class="brush: php; title: ; notranslate">
#!/usr/bin/env php
&lt;?php
require_once __DIR__.'/AppKernel.php';
use SymfonyBundleFrameworkBundleConsoleApplication;
$kernel = new AppKernel('dev', true);
$application = new Application($kernel);
$application-&gt;run();
</pre>
<p>nach einem </p>
<pre class="brush: bash; title: ; notranslate">
$ chmod + x app/console
</pre>
<p>kann man nun via ./app/console folgende Ausgabe bewundern und sich mit den ersten Symfony-Bundle-Tasks vertraut machen:</p>
<pre class="brush: bash; title: ; notranslate">
$ app/console
Symfony version 2.0.0-DEV - app

Usage:
  &#x5B;options] command &#x5B;arguments]

Options:
  --help           -h Display this help message.
  --quiet          -q Do not output any message.
  --verbose        -v Increase verbosity of messages.
  --version        -V Display this program version.
  --ansi           -a Force ANSI output.
  --no-interaction -n Do not ask any interactive question.
  --shell          -s Launch the shell.

Available commands:
  help                         Displays help for a command (?)
  list                         Lists commands
assets
  :install                     
doctrine
  :ensure-production-settings  Verify that Doctrine is properly configured for a production environment.
doctrine:cache
  :clear-metadata              Clear all metadata cache for a entity manager.
  :clear-query                 Clear all query cache for a entity manager.
  :clear-result                Clear result cache for a entity manager.
doctrine:data
  :load                        Load data fixtures to your database.
doctrine:database
  :create                      Create the configured databases.
  :drop                        Drop the configured databases.
doctrine:generate
  :entities                    Generate entity classes and method stubs from your mapping information.
  :entity                      Generate a new Doctrine entity inside a bundle.
  :proxies                     Generates proxy classes for entity classes.
  :repositories                Generate repository classes from your mapping information.
doctrine:mapping
  :convert                     Convert mapping information between supported formats.
  :convert-d1-schema           Convert a Doctrine 1 schema to Doctrine 2 mapping files.
  :import                      Import mapping information from an existing database.
doctrine:query
  :dql                         Executes arbitrary DQL directly from the command line.
  :sql                         Executes arbitrary SQL directly from the command line.
doctrine:schema
  :create                      Processes the schema and either create it directly on EntityManager Storage Connection or generate the SQL output.
  :drop                        Drop the complete database schema of EntityManager Storage Connection or generate the corresponding SQL output.
  :update                      Processes the schema and either update the database schema of EntityManager Storage Connection or generate the SQL output.
init
  :bundle                      
router
  :debug                       Displays current routes for an application
  :dump-apache                 Dumps all routes as Apache rewrite rules

</pre>The post <a href="https://nerdpress.org/2010/12/01/symfony-2-from-scratch-bootstrappen/">Symfony 2 “from scratch” bootstrappen</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
					<wfw:commentRss>https://nerdpress.org/2010/12/01/symfony-2-from-scratch-bootstrappen/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
		<item>
		<title>Dependency Injection mit PHP 5.3, Runkit-Erweiterung und Doctrine 2-Annotationen</title>
		<link>https://nerdpress.org/2010/09/26/dependency-injection-mit-php-5-3-runkit-erweiterung-und-doctrine-2-annotationen/</link>
					<comments>https://nerdpress.org/2010/09/26/dependency-injection-mit-php-5-3-runkit-erweiterung-und-doctrine-2-annotationen/#comments</comments>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Sun, 26 Sep 2010 15:33:33 +0000</pubDate>
				<category><![CDATA[Doctrine ORM]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Software engineering]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Annotations]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[Dependency Injection]]></category>
		<category><![CDATA[Doctrine 2]]></category>
		<category><![CDATA[IoC]]></category>
		<category><![CDATA[PHP 5.3]]></category>
		<category><![CDATA[Runkit]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=1085</guid>

					<description><![CDATA[<p>Unter Dependency Injection versteht man heute nicht nur ein einfaches Entwurfsmuster, sondern vor allem Framework-gestützte Mechanismen, die den konkreten Implementierungsaufwand verringern (Entwicklungszeitoptimierung), dem Entwickler bessere Übersicht über Abhängigkeiten zu schaffen (Applicationdesignoptimierung) und die Anzahl der Instanzen gleichen Prototyps zu minimieren (Performanceoptimierung). Heute möchte ich einen alternativen, vielleicht pragmatischeren Ansatz als der andererer populärer Implementierungenn herbeispinnen, &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2010/09/26/dependency-injection-mit-php-5-3-runkit-erweiterung-und-doctrine-2-annotationen/" class="more-link">Continue reading<span class="screen-reader-text"> "Dependency Injection mit PHP 5.3, Runkit-Erweiterung und Doctrine 2-Annotationen"</span></a></p>
The post <a href="https://nerdpress.org/2010/09/26/dependency-injection-mit-php-5-3-runkit-erweiterung-und-doctrine-2-annotationen/">Dependency Injection mit PHP 5.3, Runkit-Erweiterung und Doctrine 2-Annotationen</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>Unter <a href="http://de.wikipedia.org/wiki/Dependency_Injection">Dependency Injection</a> versteht man heute nicht nur ein einfaches Entwurfsmuster, sondern vor allem Framework-gestützte Mechanismen, die den konkreten Implementierungsaufwand verringern (Entwicklungszeitoptimierung), dem Entwickler bessere Übersicht über Abhängigkeiten zu schaffen (Applicationdesignoptimierung) und die Anzahl der Instanzen gleichen Prototyps zu minimieren (Performanceoptimierung).</p>
<p>Heute möchte ich einen alternativen, vielleicht pragmatischeren Ansatz als der andererer populärer Implementierungenn herbeispinnen, um Dependency Injection (DI) in PHP 5.3 zu realisieren.<br />
<span id="more-1085"></span><br />
Für diverse Programmiersprachen gibt es &#8211; auf den jeweiligen Anwendungsbereich mehr oder weniger spezialisierte &#8211; sogenannte Dependency-Injection (DI)-Container. Der DI-Container als solcher dient im Grunde als Manager oder Konfigurator, der das Zusammenspiel unserer Abhängigkeiten definiert &#8211; meist auf Grundlage einer Konfigurationsdatei, die bspw. in XML vorliegt:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
       xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd&quot;&gt;

  &lt;bean id=&quot;...&quot; class=&quot;...&quot;&gt;
    &lt;!-- collaborators and configuration for this bean go here --&gt;
  &lt;/bean&gt;

  &lt;bean id=&quot;...&quot; class=&quot;...&quot;&gt;
    &lt;!-- collaborators and configuration for this bean go here --&gt;
  &lt;/bean&gt;

  &lt;!-- more bean definitions go here... --&gt;

&lt;/beans&gt;
</pre>
<p>(Quelle: <a href="http://static.springsource.org/spring/docs/2.0.x/reference/beans.html">Springsource</a>)</p>
<p>Eine weitere Möglichkeit Abhängigkeiten zu definieren, bietet das <a href="http://en.wikipedia.org/wiki/Convention_over_configuration">Convention Over Configuration-Paradigma</a>: Heißt bspw. eine Member-Variable &#8220;Mailservice&#8221;, so kann der zugrundeliegende DI-Container entsprechend konfiguriert werden, sodass bei Instanziierung automatisch eine Klasse gleichen Namens (Mailservice) instanziiert und an das Objekt gebunden wird (so macht&#8217;s bspw. Grails in seinen MVC-Controller-Instanzen, wobei hier natürlich wieder das <a href="http://static.springsource.org/spring/docs/2.0.x/reference/beans.html">Spring IoC</a> (Das hat nichts mit dem internationalen olympischen Komitee zu tun, sondern ist die Abkürzung für &#8220;Inversion of Control&#8221; und steht gleichbedeutend für &#8220;Dependency Injection&#8221;). Diese Implementierung erfordert natürlich ausgereifte Introspektionsmechanismen der zugrundeliegenden Programmiersprache (Stichwort &#8220;<a href="http://de.wikipedia.org/wiki/Reflexion_(Programmierung)">Reflection</a>&#8220;), außerdem muss man dem DI bzw. IOC-Container vorher beigebracht haben, <em>welche</em> Klassen oder Verzeichnisstrukturen überhaupt durchsucht und unter &#8220;DI-Kontrolle&#8221; gestellt werden müssen &#8211; ein zusätzlicher Programmieraufwand ist also immer noch gegeben. Unvorhersehbarkeiten im Code und eine erhöhte WTF&#8217;s/min-Rate kommen dabei frei Haus.</p>
<p>PHP bieted zu dem ganzen Thema ab Werk erstmal &#8211; nix. Die <a href="http://php.net/manual/en/book.reflection.php">Reflection-API</a> ist aber schonmal ein ausgereiftes Werkzeug, um definierte Abhängigkeiten auszuwerten. Das allerdings ausschließlich mittels lesendem Zugriff auf Metadaten von bspw. Klassen, Methoden oder auch System- bzw. benutzerdefinierten Funktionen. Schreibende Operationen sind im Sprachkern erst einmal nicht vorgesehen. Es ist also prinzipiell möglich, zur Laufzeit Abhängigkeiten durch Konvention zu definieren, die Auflösung selbiger ist aber &#8220;mit Boardmitteln&#8221; nicht zu realisieren. </p>
<p>Ein eigenes Sprachkonstrukt wie <a href="http://de.wikipedia.org/wiki/Mixin">Mixins</a> ist auch (noch) nicht implementiert, und anonyme (&#8220;<a href="http://de.wikipedia.org/wiki/Lambda-Funktion">Lambda</a>&#8220;)-Funktionen lassen sich nicht wie in Javascript oder auch Groovy direkt an den Objekt-Prototypen hängen oder auch an einzelne Instanzen, sondern müssen mühsam &#8220;by reference&#8221; gezogen und dann erst ausgewertet werden.</p>
<p>Javascript:</p>
<pre class="brush: jscript; title: ; notranslate">
  var foo = function() { }
  foo.prototype.bar = function() { return &quot;bar&quot;; }
  foo.baz = foo.prototype.bar;

  var foobar = new foo;
  foo.bar(); // -&gt; &quot;bar&quot;
  foo.baz(); // -&gt; &quot;bar&quot;
</pre>
<p>PHP:</p>
<pre class="brush: php; title: ; notranslate">
  $foo = new stdClass();
  $foo-&gt;bar = function() 
  {
    return &quot;bar&quot;;
  }
  $foo-&gt;bar(); // method not found
   
  $bar = $foo-&gt;bar;
  $bar(); // -&gt; &quot;bar&quot;
</pre>
<p>Zugegeben: Gäbe es bereits Mixins oder Prototypen im PHP-Kontext, wäre die Implementierung eines DI-Containers wohl eher unspannend, mindestens aber überflüssig. Dennoch fehlt eine zuverlässige Möglichkeit, PHP-Instanzen zur Laufzeit mit neuen Methoden anreichern zu können. Daher gehen die meisten PHP-DI-Frameworks immer den Weg über einen recht aufgeblähten DI-Container. Dieser macht aber im Grunde nichts anderes, als Referenzen zu verwalten. Der Container selbst wird dann an die anzureichernde Instanz gebunden, im Idealfall eventuell aber noch via magischer Methode __call() verborgen. Dann aber muss die zu aufnehmende Instanz wiederum mindestens ein Interface &#8220;Injectable&#8221;  o.Ä. implementieren. Letzlich aber passiert am Ende immer folgendes:</p>
<pre class="brush: php; title: ; notranslate">
class myController extends Controller
{
  public function indexAction(HttpRequest $request)
  {
    $db = $this-&gt;getService('Database');
  }
}
</pre>
<p>Der Service-Container übernimmt also die klassische Aufgabe des &#8220;ApplicationContext&#8221;: Er managed Referenzen, rückt diese aber erst &#8220;on demand&#8221; heraus. Meiner Meinung hat man dabei nicht allzu viel gewonnen, vor allem, wenn das obige Codeschnippsel noch eine Menge Konfiguration, Code zum Bootstrappen des DI-Containers und/oder Caching voraussetzt.</p>
<p>Ich persönlich würde mir dann doch eher &#8220;ganz oldschool&#8221; ein paar getter/setter zusätzlich schreiben und mich einfach darauf verlassen, dass der gute, alte ApplicationContext sowieso (fast) alles enthält, was ich brauch&#8217;, mit der Konsequenz, dass dieser eben nicht gerade leichtgewichtig ist.</p>
<p>Wozu also das ganze?</p>
<h2>Ein alternativer DI-Container mit Doctrine 2 Annotations und Runkit</h2>
<p>Die Annotations-Implementierung von Doctrine 2 ist einfach und mächtig genug, um eine beliebigen Klasse mit Metainformationen auszustatten, die die zu injizierenden Abhängigkeiten definieren. Runkit ermöglicht es, auf Basis dieser Metadaten Abhänigkeiten an das ensprechende Objekt zu binden &#8211; zur Laufzeit und ohne zusätzliche Implementierung von Schnittstellen o.Ä. Vom Prinzip her sollte es also möglich sein, eine Klasse zu schreiben, diese zu annotieren und dann bei Instanziierung automatisch alle gewünschten Abhängigkeiten zur Verfügung stehen zu haben. Ein Codebeispiel:</p>
<p>index.php:</p>
<pre class="brush: php; title: ; notranslate">
  require __DIR__ . '/lib/vendor/doctrine-common/lib/Doctrine/Common/ClassLoader.php';

  use DoctrineCommonClassLoader;
  
  $classLoader = new ClassLoader('DoctrineCommon', __DIR__ . '/lib/vendor/doctrine-common/lib');
  $classLoader-&gt;register();

  $classLoader = new ClassLoader('deifschleife', __DIR__ . '/lib');
  $classLoader-&gt;register();

  // INITIALISIERUNG DES DI-CONTAINERS
  $di_container = new deifschleifediContainer;
  
  $di_container-&gt;setClassnames(array(
    'deifschleifeDITest'
  ));
  
  // &quot;AUFNEHMENDE&quot; KLASSE
  $test = new deifschleifeDITest();

  // GEHÖRT deifschleifeADependency
  echo $test-&gt;foo();
  
  // GEHÖRT deifschleifeAnotherDependency
  echo $test-&gt;bar();

  // GEHÖRT deifschleifeYetAnotherDependency
  echo $test-&gt;baz();
  
  // GIBT's NICHT, ÜBER __call() ABGEFANGEN
  echo $test-&gt;methodThatDoesNotExist();
</pre>
<p>Kurz erklärt:</p>
<pre class="brush: php; title: ; notranslate">
$di_container = new deifschleifediContainer;
$di_container-&gt;setClassnames(array(
    'deifschleifeDITest'
  ));
</pre>
<p>Instanziiert den DI-Container und stellt eine Klasse (deifschleifeDITest) unter seine &#8220;Kontrolle&#8221;.</p>
<pre class="brush: php; title: ; notranslate">
$test = new deifschleifeDITest();

  // GEHÖRT deifschleifeADependency
  echo $test-&gt;foo();
  
  // GEHÖRT deifschleifeAnotherDependency
  echo $test-&gt;bar();

  // GEHÖRT deifschleifeYetAnotherDependency
  echo $test-&gt;baz();
  
  // GIBT's NICHT, ÜBER __call() ABGEFANGEN
  echo $test-&gt;methodThatDoesNotExist();
</pre>
<p>Instanziiert die überwachte Klasse deifschleifeDITest und ruft einige Methoden auf, die sämtlich aus anderen Klassen nach DITest injiziert wurden. Die letze Methode ist nirgends existent. deifschleifeDITest implementiert ausschließlich __call(), ansonsten ist die Klasse (fast) leer:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
namespace deifschleife;

/**
 * @Services({
 *   @Service(
 *    class=&quot;deifschleifeADependency&quot;,
 *    constructorArgs={
 *      &quot;pong&quot;
 *    }
 *  ),
 *  @Service(
 *    class=&quot;deifschleifeAnotherDependency&quot;,
 *    constructor={&quot;deifschleifeAnotherDependency&quot;, &quot;getAnotherDependency&quot;}
 *  ),
 *  @Service(
 *    class=&quot;deifschleifeYetAnotherDependency&quot;,
 *    constructor=&quot;makeYetAnotherDependency&quot;
 *  )
 * })
 */
class DITest
{
  public function __call($method, $args)
  {
    return sprintf(&quot;Called __call, Method: %s, Args: %s&quot;, $method . '()', implode(', ', $args));
  }

  public function makeYetAnotherDependency()
  {
    return new YetAnotherDependency();
  }
}
</pre>
<p>Interessant sind die Klassen-Annotationen ganz oben: @Services teilt dem Doctrine 2 Annotation Parser mit, dass hier eine Reihe von PHP-Klassen, die zu injizierende Methoden enthalten, zusammenzusuchen sind. Jeder einzelne @Service in der Liste kann noch grob konfiguriert werden, so kann bspw. eine eigene Factory-Methode für jede Dependency angegeben werden.</p>
<p>Die einzelnen Dependencies sind &#8220;ganz normale&#8221; PHP-Klassen. Intern passiert folgendes: Die Runkit-Erweiterung dient dazu, über runkit_method_add() &#8220;on the fly&#8221; eine magische Methode __call() an die mit zusätzlcihen Methoden anzureichernde Klasse deifschleifeDITest zu binden. Existiert bereits eine entsprechende Methode __call(), wird diese intern via runkit_method_rename() umbenannt und am Ende von __call() (neu) aufgerufen. Das bedingt natürlich eine gewisse Rücksichtname bei zusätzlicher Verwendung von __call(). </p>
<p>Die Idee unterscheidet sch also nur unwesentlich von bereits vorhandenen Konzepten, spart aber einige Schritte ein:</p>
<p>1.) Man benötigt keine Abstrakte Klasse und/oder Interface, die dass die getService()-Logik bereitstellt.<br />
2.) Man benötigt kaum zusätzliche Konfigurationsaufwand &#8211; eine einfache Annotation im PHPDOc-Comment-Format reicht aus (es spricht allerdings nichts dagegen, je nach Gschmäckle einen Config-Adapter dazwischen zu schieben, der das Mapping via XML oder bspw. Yaml erlaubt)<br />
3.) Man kann auf Proxy-Instanzen verzichten, die __call() transparent bereitstellen.</p>
<p>Fazit: Es ist prinzipiell möglich, Dependency Injection &#8211; passender noch &#8220;Mixins&#8221; &#8211; mit PHP zu realisieren, ohne nennenswert viel Codeoverhead zu produzieren. </p>
<p>Allerdings muss man berücksichtigen, dass Runkit eine experimentelle Erweiterung ist, deren Zuverlässigkeit in komplexen Projekten nicht vorauszusehen ist. Außerdem wird es sehr wahrscheinlich in Bälde ein mächtiges, neues Sprachkonstrukt geben, das Mixins ermöglicht (siehe <a href="http://wiki.php.net/rfc/traits">RFC zu Traits</a>). Und natürlich kann ich mir auch totalen Murks zusammen gereimt haben, denn sicherlich sind die existierenden Implementierungen von fähigeren Architekten entworfen worden und haben bestimmt ihre Daseinsberechtigungen in einem Kontext, den ich vielleicht einfach noch nicht erfasst haben. Comments dazu wären wir sehr willkommen!</p>
<h2>Ausblick</h2>
<p>Die Lösung zentral über __call() ist die Schnelle, aber nicht optimale. Vorstellbar ist eine injizierung sämtlicher öffentlicher Methoden wie runkit_method_add, und ein globaler Accessor, der die Objektinstanz der Abhängigkeit liefert. Das ganze könnte man (optional) konfigurierbar gestalten (über Doctrine 2 Annotationen), um eventuellen Namenskonflikten auszuweichen.</p>
<h2>Test-Sourcen</h2>
<p>Die PHP-Quelltexte zu den obigen Beispielen kann man hier herunterladen:<br />
<a href="http://ifschleife.de/dependency_injection.tar.gz">http://ifschleife.de/dependency_injection.tar.gz</a></p>
<p>Ich habe die DoctrineCommon-Package bereits beigelegt, das Beispiel sollte also unter PHP5.3 funktionieren (bitte di.php aufrufen, der Rest ist noch durchsetzt mit Doctrine 2 Testklamotten).</p>
<p>Eine Anleitung, wie man Runkit unter php5.3 zum laufen kriegt, gibt es <a href="http://d.hatena.ne.jp/shimooka/20100729/1280379407">hier</a> (Japanisch, aber der Quelltext ist auf PHP ;))</p>
<p>Die Test-Sourcen liegen, wie in den obigen Codebeispielen beschrieben, unter lib/de/ifschleife. Ich möchte betonen, dass das nur eine Art Fallstudie ist, ich wäre aber über jeden Kommentar erfreut, der mir widerlegt oder bestätigt, ob man den programmatischen Ansatz vielleicht weiterverfolgen sollte. </p>
<p>Vielen Dank für die Aufmerksamkeit! :)</p>The post <a href="https://nerdpress.org/2010/09/26/dependency-injection-mit-php-5-3-runkit-erweiterung-und-doctrine-2-annotationen/">Dependency Injection mit PHP 5.3, Runkit-Erweiterung und Doctrine 2-Annotationen</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
					<wfw:commentRss>https://nerdpress.org/2010/09/26/dependency-injection-mit-php-5-3-runkit-erweiterung-und-doctrine-2-annotationen/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>Dependency Injection mit Symfony 1.x</title>
		<link>https://nerdpress.org/2010/05/27/dependency-injection-mit-symfony-1-x/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Thu, 27 May 2010 16:58:23 +0000</pubDate>
				<category><![CDATA[API]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Software engineering]]></category>
		<category><![CDATA[Symfony]]></category>
		<category><![CDATA[Dependency Injection]]></category>
		<category><![CDATA[Grails]]></category>
		<category><![CDATA[Inversion of Control]]></category>
		<category><![CDATA[IoC]]></category>
		<category><![CDATA[Plugin]]></category>
		<category><![CDATA[Service]]></category>
		<category><![CDATA[sfDependencyInjectionContainerPlugin]]></category>
		<category><![CDATA[Symfony Components]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=987</guid>

					<description><![CDATA[<p>Beschreibung IoC Pattern anhand Grails Services und Kurze Einführung in die Symfony-Komponente "Dependency Injection".</p>
The post <a href="https://nerdpress.org/2010/05/27/dependency-injection-mit-symfony-1-x/">Dependency Injection mit Symfony 1.x</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>Durch meine ersten Gehversuche mit <a href="http://www.grails.org/">Grails</a> sowie einen interessanten Vortrag über JSF2 bin ich kürzlich über das Kürzel &#8220;<a href="http://de.wikipedia.org/wiki/Inversion_of_Control">IoC</a>&#8221; gestoßen. Das ganze ist vom Prinzip her recht einfach und in Grails, das im Grunde ein stinknormales MVC-Pattern implementiert, konkret gelöst, indem man durch eine Namenskonvention am Controller automatisch sogenannte Services instanziiert. </p>
<p><span id="more-987"></span></p>
<p>Also anstatt zu schreiben:</p>
<pre class="brush: groovy; title: ; notranslate">
class DefaultController {
  
  def mySuperService
  
  def index = {
   mySuperService = new MySuperService(params);
   mySuperService.getData()
  }
}
</pre>
<p>schreibe ich einfach</p>
<pre class="brush: groovy; title: ; notranslate">
class DefaultController {
 
  def mySuperService
 
  def index = {
   mySuperService.getData()
  }
}
</pre>
<p>Gut, sieht jetzt nicht besonders spektakulär aus, doch wenn man weiß, dass mySuperService im Hintergrund (Stichwort &#8220;Spring Managed Bean&#8221;) automatisch instanziiert sowie an meinen Controller gebunden wurde und dass da prinzipiell noch viel mehr <a href="http://www.grails.org/Services">geht</a>, ist das bereits ein ganz brauchbares Feature.</p>
<p>Natürlich ist die Injizierung per Naming-convention eine Grails-eigene Lösung (alles, was auf Service endet, ist ein Service und wird injiziert), um Konfigurationsarbeit zu sparen. Für gewöhnlich wird das Verhalten sogenanner &#8220;<a href="http://java.sun.com/javaee/6/docs/api/javax/annotation/ManagedBean.html">managed Beans</a>&#8221; natürlich konfiguriert, entweder Java-typisch via XML-Konfigurationsdatei oder durch <a href="http://de.wikipedia.org/wiki/Annotation#Informatik">Annotationen</a>.</p>
<p>Dabei kann wiederum java-typisch auch der &#8220;Scope&#8221; einer Bean bestimmt werden; hier bringt vor allem der &#8220;Singleton&#8221; bzw. Application Scope einen Vorteil gegenüber einer statuslosen PHP-Anwendung. Doch bereits die Fähigkeit, den Scope überhaupt fein granulieren zu können, setzt das ganze wieder von PHP-Gemurkse ala $_SESSION, $_REQUEST etc. ab. </p>
<p>Also egal wo und in welchem Kontext man Managed Beans, Enterprise Java Beans, Spring Beans (Die Unterschiede interessieren hier mal nicht die Bohne &#8211; ich könnte sie auch gar nicht qualifiziert erläutern) einsetzt &#8211; das IoC-Muster spart Code, vereinfacht Abhängigkeiten und sorgt für schlankere Konstruktoren &#8211; ist insgesamt also eine sehr mächtige Sache.</p>
<p>Was aber nicht heißt, dass so etwas generell nicht auch in meiner PHP-Anwendung funktionieren kann.</p>
<h3><strong>sfDependencyInjectionContainerPlugin</strong></h3>
<p>Dieses Plugin (<a href="http://www.symfony-project.org/plugins/sfDependencyInjectionContainerPlugin">http://www.symfony-project.org/plugins/sfDependencyInjectionContainerPlugin</a>) leistet genau das: Es verwaltet entweder über eine in PHP formulierte <a href="http://de.wikipedia.org/wiki/Dom%C3%A4nenspezifische_Sprache">DSL</a>(hust), über XML oder eine symfony-typische .yml-Datei konfigurierte Abhängigkeiten. Das Plugin ist selbst nur ein Adapter, das die <a href="http://components.symfony-project.org/">Symfony-Komponente</a> <a href="http://components.symfony-project.org/dependency-injection/">DependencyInjection</a> in ein Symfony 1.2 (und höher) Projekt integriert &#8211; in Symfony 2.0 wird DependencyInjection voraussichtlich Bestandteil des Systemkerns sein.</p>
<p>Zur Installation und Konfiguration des Plugins verweise ich auf die <a href="http://www.symfony-project.org/plugins/sfDependencyInjectionContainerPlugin">README</a>-Datei. Trotzdem vielleicht der Hinweis, dass die Bibliotheken mit einem eigenen Autoloader daherkommen, daher empfehle ich, folgende Programm-Zeilen in eure ProjectConfiguration.class.php hinzuzufügen:</p>
<p>config/projectConfiguration.class.php</p>
<pre class="brush: php; title: ; notranslate">
  require_once dirname(__FILE__) . '/../pfad/zur/DependencyInjection/Komponente/sfServiceContainerAutoloader.php';
  sfServiceContainerAutoloader::register();
</pre>
<p>Anders habe ich es nicht hinbekommen, zwar lässt sich durch eine config/autoload.yml die Laufzeit von Class Not Found-Fehlern befreien, aber das Symfony Command Line Interface geht dann trotzdem hops. Für einen Tipp, wie man&#8217;s eleganter hinkriegt, wäre ich dankbar.</p>
<p>Beispielsweise möchte ich den Nerdpress-RSS-Feed in meine Seite einbinden. Dazu baue ich einen Proxy, um bequem via AJAX auf das entfernte RSS-XML zugreifen zu können.<br />
Diesen Proxy definiere ich als Service und injiziere ihn meine Symfony-Module:</p>
<p>config/services.yml:</p>
<pre class="brush: jscript; title: ; notranslate">
services:
  NerdpressRssProxy:
    class: NerdpressRssProxy
</pre>
<p>Diese drei Zeilen reichen bereits aus, um meine Abhängigkeit zu definieren.</p>
<p>Nun implementiere ich die Service-Klasse:</p>
<p>lib/NerdpressRssProxy.php</p>
<pre class="brush: php; title: ; notranslate">
class NerdpressRssProxy
{
  /**
   * @var sfFileCache
   */
  protected $cache;

  /**
   * @return sfFileCache
   */
  public function getCache()
  {
    if (null === $this-&gt;cache)
    {
      $this-&gt;cache = new sfFileCache(array('cache_dir' =&gt; sfConfig::get('sf_cache_dir')));
    }
    return $this-&gt;cache;
  }

  /**
   * @return string
   * @throws Exception
   */
  public function getEntries()
  {
    $cache = $this-&gt;getCache();

    sfContext::getInstance()-&gt;getConfiguration()-&gt;loadHelpers(array('Helper', 'Tag', 'Url', 'Text'));

    if(!($rss = $cache-&gt;get('nerdpress_rss')))
    {
      if(($rss = @file_get_contents('https://nerdpress.org/feed')))
      {
        $rss = auto_link_text($rss, 'all', array('class' =&gt; 'external'));

        $cache-&gt;set('nerdpress_rss', $rss, 3600);
      }
      else
      {
        throw new Exception('Rss feed could not be opened');
      }
    }
    return $rss;
  }
}
</pre>
<p>Die Methode getEntries() rufe ich nun in meinem Nerdpress-RSS-Modul auf, um die WebResponse zu befüllen:</p>
<p>apps/frontend/modules/rssProxy/actions/actions.class.php</p>
<pre class="brush: php; title: ; notranslate">
class rssProxyActions extends sfActions
{
  public function preExecute()
  {
    $this-&gt;getResponse()-&gt;setContentType('application/rss+xml');
  }
  
  public function executeNerdpress(sfWebRequest $request)
  {
    try
    {
      // SERVICE HOLEN UND getEntries() AUFRUFEN
      $rss = $this-&gt;getService('NerdpressRssProxy')-&gt;getEntries();

      $this-&gt;getResponse()-&gt;setHttpHeader('Content-Length', mb_strlen($rss));
      $this-&gt;getResponse()-&gt;setContent($rss);
    }
    catch(Exception $e)
    {
      $this-&gt;forward404($e-&gt;getMessage());
    }
    return sfView::NONE;
  }
}
</pre>
<p>Das wars. Einfach, oder? Ingesamt 3 Zeilen yml-Konfiguration reichen, um Dependency Injection &#8220;the PHP way&#8221; zu realisieren, Symfony sei Dank. Natürlich ist nicht *ganz* so hübsch wie in der J2EE-Welt und sicherlich funktional noch weit davon entfernt. Aber ein Anfang ist gemacht, und wenn man seine Services erst einmal auf diese Art und Weise unter Kontrolle bringt, ist man vom Nutzen der Sache schnell überzeugt: Mit ein bisschen Disziplin gehören &#8220;Controller-Spaghettiwürste&#8221; nämlich der Vergangenheit an.</p>
<p>Bitte entschuldigt eventuell fehlerhafte oder unsaubere Beschreibungen der Begriffe &#8220;Bean&#8221;, &#8220;IoC&#8221;, JSF2 etc. Ich bitte explizit um Korrektur in Form von Kommentaren :)</p>The post <a href="https://nerdpress.org/2010/05/27/dependency-injection-mit-symfony-1-x/">Dependency Injection mit Symfony 1.x</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
