<table role="table">
<thead>
<tr>
<th>Q</th>
<th>A</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bug report?</td>
<td>no</td>
</tr>
<tr>
<td>Feature request?</td>
<td>yes</td>
</tr>
<tr>
<td>BC Break report?</td>
<td>no</td>
</tr>
<tr>
<td>RFC?</td>
<td>yes</td>
</tr>
<tr>
<td>Symfony version</td>
<td>4.1</td>
</tr>
</tbody>
</table>
<p dir="auto">Still being on 3.3, my first preparation in my application is to change everything to autowiring. Now that we have autoconfigure and resource loading, it's a lot easier to get things started with ~1900 services in my app.</p>
<p dir="auto">However, there are some obstacles I'm hitting that still require me to define a lot of services in my config. While we can currently already use <a href="https://symfony.com/blog/new-in-symfony-3-4-simpler-injection-of-tagged-services" rel="nofollow"><code class="notranslate">!tagged</code></a> to avoid using compiler passes in 3.4, I'm still required to configure a priority when this is needed. In some cases, I have ~20 service definitions that I need to sort and inject into another.</p>
<p dir="auto">Now my autowire.yml looks something like this:</p>
<div class="highlight highlight-source-yaml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="services:
    #...
    App\Option\One:
        tags:
            - { name: app.option, priority: 10 }
            - { name: features.tag, tag: some_feature_name }
    App\Option\Two: { tags: [{name: app.option, priority: 15}] }
    App\Option\Three: { tags: [{name: app.option, priority: 100}] }
    App\Option\Four: { tags: [{name: app.option, priority: -10}] }

    App\Feature\EnvironmentResolver:
        tags: [{ name: features.resolver, config-key: environment }]
    #... "><pre class="notranslate"><span class="pl-ent">services</span>:
    <span class="pl-c"><span class="pl-c">#</span>...</span>
    <span class="pl-ent">App\Option\One</span>:
        <span class="pl-ent">tags</span>:
            - <span class="pl-s">{ name: app.option, priority: 10 }</span>
            - <span class="pl-s">{ name: features.tag, tag: some_feature_name }</span>
    <span class="pl-ent">App\Option\Two</span>: <span class="pl-s">{ tags: [{name: app.option, priority: 15}] }</span>
    <span class="pl-ent">App\Option\Three</span>: <span class="pl-s">{ tags: [{name: app.option, priority: 100}] }</span>
    <span class="pl-ent">App\Option\Four</span>: <span class="pl-s">{ tags: [{name: app.option, priority: -10}] }</span>

    <span class="pl-ent">App\Feature\EnvironmentResolver</span>:
        <span class="pl-ent">tags</span>: <span class="pl-s">[{ name: features.resolver, config-key: environment }]</span>
    <span class="pl-c"><span class="pl-c">#</span>... </span></pre></div>
<p dir="auto">Ideally I'd <em>only</em> define the service definition that collects these options, so I've been wondering if a tag subscriber would be the solution. Similar to event listeners vs event subscribers, this would allow a class to subscribe to specific tags, which can be picked up by autoconfigure:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="interface TagSubscriberInterface
{
    public static function getTags(): array;
}

final class One implements OptionInterface, TagSubscriberInterface
{
    // ...
    public static function getTags()
    {
        // multiple tags
        return [
            ['name' =&gt; 'app.option', 'priority' =&gt; 10],
            ['name' =&gt; 'features.tag', 'tag' =&gt; 'some_feature_name'],
        ];
        // different style in case of single tag
        return ['name' =&gt; 'app.option', 'priority' =&gt; 10];
    }
}

final class EnvironmentResolver implements FeatureResolverInterface, TagSubscriberInterface
{
    // ...
    public static function getTags()
    {
        return ['name' =&gt; 'features.resolver', 'config-key' =&gt; 'environment'];
    }
}"><pre class="notranslate"><span class="pl-k">interface</span> <span class="pl-v">TagSubscriberInterface</span>
{
    <span class="pl-k">public</span> <span class="pl-k">static</span> <span class="pl-k">function</span> <span class="pl-en">getTags</span>(): <span class="pl-smi">array</span>;
}

<span class="pl-k">final</span> <span class="pl-k">class</span> <span class="pl-v">One</span> <span class="pl-k">implements</span> <span class="pl-v">OptionInterface</span>, <span class="pl-v">TagSubscriberInterface</span>
{
    <span class="pl-c">// ...</span>
    <span class="pl-k">public</span> <span class="pl-k">static</span> <span class="pl-k">function</span> <span class="pl-en">getTags</span>()
    {
        <span class="pl-c">// multiple tags</span>
        <span class="pl-k">return</span> [
            [<span class="pl-s">'name'</span> =&gt; <span class="pl-s">'app.option'</span>, <span class="pl-s">'priority'</span> =&gt; <span class="pl-c1">10</span>],
            [<span class="pl-s">'name'</span> =&gt; <span class="pl-s">'features.tag'</span>, <span class="pl-s">'tag'</span> =&gt; <span class="pl-s">'some_feature_name'</span>],
        ];
        <span class="pl-c">// different style in case of single tag</span>
        <span class="pl-k">return</span> [<span class="pl-s">'name'</span> =&gt; <span class="pl-s">'app.option'</span>, <span class="pl-s">'priority'</span> =&gt; <span class="pl-c1">10</span>];
    }
}

<span class="pl-k">final</span> <span class="pl-k">class</span> <span class="pl-v">EnvironmentResolver</span> <span class="pl-k">implements</span> <span class="pl-v">FeatureResolverInterface</span>, <span class="pl-v">TagSubscriberInterface</span>
{
    <span class="pl-c">// ...</span>
    <span class="pl-k">public</span> <span class="pl-k">static</span> <span class="pl-k">function</span> <span class="pl-en">getTags</span>()
    {
        <span class="pl-k">return</span> [<span class="pl-s">'name'</span> =&gt; <span class="pl-s">'features.resolver'</span>, <span class="pl-s">'config-key'</span> =&gt; <span class="pl-s">'environment'</span>];
    }
}</pre></div>
<p dir="auto">WDYT?</p>
<p dir="auto"><a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/Simperfit/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/Simperfit">@Simperfit</a> <g-emoji class="g-emoji" alias="smile" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f604.png">😄</g-emoji><br>
<a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/1754678/33596935-fb843a80-d99d-11e7-84d4-564609be1054.jpg"><img src="https://user-images.githubusercontent.com/1754678/33596935-fb843a80-d99d-11e7-84d4-564609be1054.jpg" alt="img_20171205_091405" style="max-width: 100%;"></a></p>
<table role="table">
<thead>
<tr>
<th>Q</th>
<th>A</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bug report?</td>
<td>yes</td>
</tr>
<tr>
<td>Feature request?</td>
<td>no</td>
</tr>
<tr>
<td>BC Break report?</td>
<td>yes</td>
</tr>
<tr>
<td>RFC?</td>
<td>no</td>
</tr>
<tr>
<td>Symfony version</td>
<td>3.1.8</td>
</tr>
</tbody>
</table>
<p dir="auto">On our application we're using in some places the twig namespace syntax (<code class="notranslate">@namespace/folder1/template.html.twig</code>) and in templates that uses <code class="notranslate">embed</code> twig uses the SF <a href="https://github.com/symfony/symfony/blob/ae37418e617a6e063482d931d22b1f67b23785f9/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateReference.php#L55"><code class="notranslate">TemplateReference</code></a> to get the logical name which leads to the SF syntax (<code class="notranslate">Bundle:folder1:template.html.twig</code>).</p>
<p dir="auto">What happens is that twig loader loads <code class="notranslate">@namespace/folder1/template.html.twig</code> but fails on <code class="notranslate">Bundle:folder1:template.html.twig</code> then SF filesystem loader is used as fallback. The problem is that twig loader uses <code class="notranslate">realpath()</code> since <code class="notranslate">v1.27</code> and SF loader doesn't.</p>
<p dir="auto">I've solved that on our application by overriding SF loader and using <code class="notranslate">realpath()</code> as well but although it works fine I'm not 100% sure if that's the way to go (that's why I didn't send a PR):</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="use Symfony\Bundle\TwigBundle\Loader\FilesystemLoader;

final class TemplateLoader extends FilesystemLoader
{
    protected function findTemplate($template, $throw = true)
    {
        $file = parent::findTemplate($template, $throw);

        if ($absolutePath = realpath($file)) {
            $file = $absolutePath;
        }

        return $this-&gt;cache[(string) $template] = $file;
    }
}"><pre class="notranslate"><span class="pl-k">use</span> <span class="pl-v">Symfony</span>\<span class="pl-v">Bundle</span>\<span class="pl-v">TwigBundle</span>\<span class="pl-v">Loader</span>\<span class="pl-v">FilesystemLoader</span>;

<span class="pl-k">final</span> <span class="pl-k">class</span> <span class="pl-v">TemplateLoader</span> <span class="pl-k">extends</span> <span class="pl-v">FilesystemLoader</span>
{
    <span class="pl-k">protected</span> <span class="pl-k">function</span> <span class="pl-en">findTemplate</span>(<span class="pl-s1"><span class="pl-c1">$</span>template</span>, <span class="pl-s1"><span class="pl-c1">$</span>throw</span> = <span class="pl-c1">true</span>)
    {
        <span class="pl-s1"><span class="pl-c1">$</span>file</span> = <span class="pl-smi">parent</span>::<span class="pl-en">findTemplate</span>(<span class="pl-s1"><span class="pl-c1">$</span>template</span>, <span class="pl-s1"><span class="pl-c1">$</span>throw</span>);

        <span class="pl-k">if</span> (<span class="pl-s1"><span class="pl-c1">$</span>absolutePath</span> = realpath(<span class="pl-s1"><span class="pl-c1">$</span>file</span>)) {
            <span class="pl-s1"><span class="pl-c1">$</span>file</span> = <span class="pl-s1"><span class="pl-c1">$</span>absolutePath</span>;
        }

        <span class="pl-k">return</span> <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">cache</span>[(<span class="pl-smi">string</span>) <span class="pl-s1"><span class="pl-c1">$</span>template</span>] = <span class="pl-s1"><span class="pl-c1">$</span>file</span>;
    }
}</pre></div>