<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Dependency-Injection on Guillaume Delré</title><link>https://guillaumedelre.github.io/tags/dependency-injection/</link><description>Recent content in Dependency-Injection on Guillaume Delré</description><generator>Hugo</generator><language>en</language><lastBuildDate>Thu, 13 Jul 2017 00:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/tags/dependency-injection/index.xml" rel="self" type="application/rss+xml"/><item><title>Symfony 3.3: when services stopped being a configuration nightmare</title><link>https://guillaumedelre.github.io/2017/07/13/symfony-3.3-when-services-stopped-being-a-configuration-nightmare/</link><pubDate>Thu, 13 Jul 2017 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/2017/07/13/symfony-3.3-when-services-stopped-being-a-configuration-nightmare/</guid><description>Part 1 of 11 in &amp;quot;Symfony Releases&amp;quot;: Symfony 3.3 made autowiring the default and turned service configuration from mountains of YAML into almost nothing.</description><category>symfony-releases</category><content:encoded><![CDATA[<p>Symfony 3.3 shipped May 29th. It&rsquo;s the release that changed how I think about service configuration. In hindsight, it was basically a preview of what 4.0 would make the new default.</p>
<h2 id="the-autowiring-problem">The autowiring problem</h2>
<p>Before 3.3, Symfony&rsquo;s DI was powerful but verbose. Every service had to be declared explicitly in <code>services.yml</code> with its arguments listed. Autowiring existed since 3.1, but it was opt-in per service and had enough edge cases to bite you. Teams either wrote mountains of YAML or leaned on third-party bundles to cut the noise.</p>
<p>3.3 rewrote the defaults. With <code>autoconfigure: true</code> and <code>autowire: true</code> set once in the defaults section, every class in <code>src/</code> becomes a service automatically, and its constructor dependencies are resolved by type. What used to take twenty lines of YAML now takes zero:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">_defaults</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">autowire</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">autoconfigure</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">App\</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">resource</span>: <span style="color:#e6db74">&#39;../src/&#39;</span>
</span></span></code></pre></div><p>That single block is the entire service configuration for most apps. The framework discovers services, injects dependencies, and applies tags (command, event subscriber, voter&hellip;) based on the interfaces each class implements.</p>
<h2 id="instanceof-conditionals">instanceof conditionals</h2>
<p>The <code>instanceof</code> keyword in service configuration handles the tagging that previously required explicit declaration:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">_instanceof</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">Symfony\Component\EventDispatcher\EventSubscriberInterface</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">tags</span>: [<span style="color:#e6db74">&#39;kernel.event_subscriber&#39;</span>]
</span></span></code></pre></div><p>Any service implementing <code>EventSubscriberInterface</code> gets the tag automatically. Same for <code>Command</code>, <code>Voter</code>, <code>MessageHandlerInterface</code>. The boilerplate evaporates.</p>
<h2 id="dotenv-component">Dotenv component</h2>
<p>Before 3.3, Symfony had no built-in way to load <code>.env</code> files. The standard answer was a third-party package. The new <code>Dotenv</code> component reads <code>.env</code> and populates <code>$_ENV</code> and <code>$_SERVER</code>, making environment-based configuration a first-class citizen at last.</p>
<h2 id="service-discovery-from-the-filesystem">Service discovery from the filesystem</h2>
<p>The <code>resource</code> option ties it all together. Instead of registering every class individually, you point the container at a directory and it scans for PSR-4 classes:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">App\</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">resource</span>: <span style="color:#e6db74">&#39;../src/&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">exclude</span>: <span style="color:#e6db74">&#39;../src/{Entity,Migrations}&#39;</span>
</span></span></code></pre></div><p>Every class found becomes a service with its FQCN as the service ID. The <code>exclude</code> option handles things like Doctrine entities that you don&rsquo;t want the container touching. And no, it&rsquo;s not magic: it&rsquo;s a filesystem scan at compile time, so the cost is paid once during cache warmup, not per request.</p>
<h2 id="when-you-need-a-subset-of-the-container">When you need a subset of the container</h2>
<p>Service locators solve a specific tension: some services legitimately need lazy access to a variable set of other services, but injecting the full container is an anti-pattern — it hides dependencies and defeats static analysis. The solution is a locator that explicitly declares what it contains.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">App\Handler\HandlerLocator</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">class</span>: <span style="color:#ae81ff">Symfony\Component\DependencyInjection\ServiceLocator</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">tags</span>: [<span style="color:#e6db74">&#39;container.service_locator&#39;</span>]
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">arguments</span>:
</span></span><span style="display:flex;"><span>            -
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">App\Command\CreateOrder</span>: <span style="color:#e6db74">&#39;@App\Handler\CreateOrderHandler&#39;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">App\Command\CancelOrder</span>: <span style="color:#e6db74">&#39;@App\Handler\CancelOrderHandler&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">App\Bus\CommandBus</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">arguments</span>: [<span style="color:#e6db74">&#39;@App\Handler\HandlerLocator&#39;</span>]
</span></span></code></pre></div><p>The locator implements PSR-11&rsquo;s <code>ContainerInterface</code>, so the receiving class type-hints against <code>Psr\Container\ContainerInterface</code>. Services inside it are lazily instantiated: if a given handler never gets called during a request, it never gets built.</p>
<p>And speaking of PSR-11: Symfony 3.3 made its container implement that standard. Which means any library expecting a PSR-11 container now works directly with Symfony&rsquo;s container, no adapter needed.</p>
<h2 id="routing-got-faster">Routing got faster</h2>
<p>The routing component rewrote how it generates dump files. In an app with 900 routes, URL matching dropped from 7.5ms to 2.5ms per match: a 66% reduction. The optimizations live in the compiled output, not the runtime path, so existing route definitions benefit automatically after a cache clear.</p>
<h2 id="finding-the-project-root-without-counting-directory-separators">Finding the project root without counting directory separators</h2>
<p>Before 3.3, getting the project root meant using the delightfully awkward <code>%kernel.root_dir%/../</code> pattern, because <code>getRootDir()</code> pointed at the <code>app/</code> directory. The new <code>getProjectDir()</code> method walks up from the kernel file until it finds <code>composer.json</code> and returns that directory.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// Before
</span></span></span><span style="display:flex;"><span>$path <span style="color:#f92672">=</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getParameter</span>(<span style="color:#e6db74">&#39;kernel.root_dir&#39;</span>) <span style="color:#f92672">.</span> <span style="color:#e6db74">&#39;/../var/data.db&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// After
</span></span></span><span style="display:flex;"><span>$path <span style="color:#f92672">=</span> $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">getParameter</span>(<span style="color:#e6db74">&#39;kernel.project_dir&#39;</span>) <span style="color:#f92672">.</span> <span style="color:#e6db74">&#39;/var/data.db&#39;</span>;
</span></span></code></pre></div><p>The corresponding parameter is <code>%kernel.project_dir%</code>. If you deploy without <code>composer.json</code>, you can override the method in your kernel class and return whatever path makes sense.</p>
<h2 id="flash-messages-without-touching-the-session-object">Flash messages without touching the session object</h2>
<p>The old way of iterating flash messages in Twig required reaching through <code>app.session.flashbag</code>, which also forced the session to start whether or not there were any messages. The new <code>app.flashes</code> helper avoids both:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-twig" data-lang="twig"><span style="display:flex;"><span><span style="color:#75715e">{%</span> <span style="color:#66d9ef">raw</span> <span style="color:#75715e">%}</span>{% for label, messages in app.flashes %}
</span></span><span style="display:flex;"><span>    {% for message in messages %}
</span></span><span style="display:flex;"><span>        &lt;div class=&#34;flash-{{ label }}&#34;&gt;{{ message }}&lt;/div&gt;
</span></span><span style="display:flex;"><span>    {% endfor %}
</span></span><span style="display:flex;"><span>{% endfor %}<span style="color:#75715e">{%</span> <span style="color:#66d9ef">endraw</span> <span style="color:#75715e">%}</span>
</span></span></code></pre></div><p>If there are no flash messages, the session never starts. You can also filter by type: <code>app.flashes('error')</code> returns only error messages.</p>
<h2 id="the-encode-password-command-grew-a-brain">The encode-password command grew a brain</h2>
<p>The <code>security:encode-password</code> console command got smarter. Instead of requiring you to pass the user class as an argument, it now lists the configured user classes and lets you pick:</p>
<pre tabindex="0"><code>$ bin/console security:encode-password

  For which user class would you like to encode a password?
  [0] App\Entity\User
  [1] App\Entity\AdminUser
</code></pre><p>It also normalizes encoder configuration to handle edge cases with email-format usernames that the previous version would silently corrupt by replacing <code>@</code> with underscores. Nice catch.</p>
<h2 id="http2-push-and-resource-hints">HTTP/2 push and resource hints</h2>
<p>The WebLink component handles the <code>Link</code> HTTP header, which tells browsers (and HTTP/2 proxies) to preload, prefetch, or preconnect to resources before the page even asks for them. It comes as a set of Twig functions:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-twig" data-lang="twig"><span style="display:flex;"><span><span style="color:#75715e">{%</span> <span style="color:#66d9ef">raw</span> <span style="color:#75715e">%}</span>{{ preload(&#39;/fonts/custom.woff2&#39;, { as: &#39;font&#39;, crossorigin: true }) }}
</span></span><span style="display:flex;"><span>{{ prefetch(&#39;/api/next-page-data.json&#39;) }}
</span></span><span style="display:flex;"><span>{{ dns_prefetch(&#39;https://fonts.googleapis.com&#39;) }}<span style="color:#75715e">{%</span> <span style="color:#66d9ef">endraw</span> <span style="color:#75715e">%}</span>
</span></span></code></pre></div><p>Each call adds a corresponding <code>Link</code> header to the response. For apps behind an HTTP/2-capable proxy, this can trigger server push before the browser has even parsed the HTML. You enable it in <code>config.yml</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">framework</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">web_link</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">enabled</span>: <span style="color:#66d9ef">true</span>
</span></span></code></pre></div><h2 id="deprecations-you-can-actually-trust">Deprecations you can actually trust</h2>
<p>Container compilation used to generate deprecation warnings that vanished on the next page load because the cached container was already built. 3.3 persists those messages to disk and surfaces them in the web debug toolbar alongside request-phase deprecations. If a class is being deprecated during service compilation, you&rsquo;ll see it without having to nuke the cache first.</p>
<h2 id="what-this-meant-for-40">What this meant for 4.0</h2>
<p>3.3&rsquo;s autowiring defaults are exactly what Symfony 4.0 shipped as the new standard project structure. The <code>services.yaml</code> in every new Symfony 4 project is essentially the snippet above. If you had already picked up what 3.3 introduced, 4.0&rsquo;s &ldquo;new way&rdquo; felt familiar rather than foreign.</p>
<p>The direction was clear: less configuration, more convention. Let PHP figure out what to wire together.</p>
]]></content:encoded></item></channel></rss>