symfonytwig

twig urlencodes string, ignoring the raw-filter


First off, I am well aware that twig is server- and JavaScript is client-side.

Still I would like to use twigs path() function to render my JavaScript code and inject the JavaScript variable as a RAW-String, but it gets escaped all the time, and I am curious why the raw-filter does not apply?

I do have the following JavaScript within my twig file (simplified):

// file.html.twig
<script>
print '<a href="{{ path('article_edit', {id: ("' + full['id'] + '"|raw)}) }}">Linktext</a>';
</script>

As the path for article_edit is /articles/{id}/edit I expect twig to do the following two steps:

// Expected twig-render, step 1
<script>
print '<a href="/articles/{id}/edit">Linktext</a>';
</script>

Step 2: {id} then gets replaced with my given string ' + full['id'] + ', resulting in a valid Javascript:

// Expected twig-render, final step
<script>
print '<a href="/articles/' + full['id'] + '/edit">Linktext</a>';
</script>

Which would be rendered on clientside to <a href="/articles/1/edit">Linktext</a>

But still, even though I used the |raw filter, twig does urlencode the string. Resulting in the following output:

// Actual twig-render
<script>
print '<a href="/articles/%27%20+%20full%5B%27id%27%5D%20+%20%27/edit">Linktext</a>';
</script>

The following twig-codes do have all the very same output:

{{ path('article_edit', {id: ("' + full['id'] + '")}) }} // no filter
{{ path('article_edit', {id: ("' + full['id'] + '"|raw)}) }} // filter on the id
{{ path('article_edit', {id: ("' + full['id'] + '")})|raw }} // filter on the function
{{ path('article_edit', {id: ("' + full['id'] + '"|raw)})|raw }} // filter on both id and function

If you still think it is a problem of client- and server-side rendering, I have created a little PHP fiddle to demonstrate the problem and expected behaviour with plain PHP, without twig: https://ideone.com/amZ4T4

Why is twig ignoring the filter and does urlencode the JavaScript? Isn't that what that filter is for, so I could build up a dynamic JavaScript code within twig?

I now use the hard-coded path as a workaround, but I am curious why the filter is ignored...


Solution

  • twig urlencodes string, ignoring the raw-filter

    Exactly. As it should. As explained in the docs:

    The raw filter marks the value as being "safe", which means that in an environment with automatic escaping enabled this variable will not be escaped if raw is the last filter applied to it:

    This filter simply prevents the escaping of HTML entities, in case you want to output them as they are.

    E.g. if your output contained <b>bold</bold>, it would be rendered verbatim, instead of as &lt;b&gt;bold&lt;/b&gt;.

    Your output is not being escaped at all, so raw does nothing from preventing the escaping of the string. It's a no-op filter in this context.

    The string is being URL encoded, as you can see, because it's the output of path(), which is assumed to be a URL and usually something you'd want to encode as a URL.

    That's not even controlled by Twig. The path function is defined by the RoutingExtension in the Twig Symfony bridge (here), and the only thing it does is use UrlGeneratorInterface::generate to get the URL, which is returned to the template.

    The URL will always be generated URL encoded... because it's a URL. And since no escaping is necessary to print a URL encoded URL, raw does nothing to that output.

    I now use the hard-coded path as a workaround, but I am curious why the filter is ignored...

    At first sight, this seems like using the wrong tool for the job. The filter is not being ignored, it simply does not do what you think it does.