Suggested commit message: Issue #1696786 by Fabianx, stevector, jenlampton, jwilson3, chx, Antoine Lafontaine, steveoliver, amateescu: Integrate Twig into core.

Part of meta-issues

#1788918: [GTD] [META] Prepare for Twig main engine core inclusion until December 1

Changes

  1. twig.engine
  2. the necessary glue code to make the render arrays work with Twig
  3. node.twig and datetime.twig as examples of how things will look
  4. temporary possibility for modules to provide .twig and .tpl.php to be removed once all .tpl.php in core modules are gone (merged from #1697854: Allow modules to provide both .twig and .tpl.php templates temporarily until twig is the default engine)
  5. extensive testing of the above
  6. a bug fix and tests for drupal_find_theme_templates to find suggestions also for files with just one extension (merged from #1678808: Defining a new theme engine with templates without .tpl.[template extension] breaks template suggestions)

Software Benefits

  1. Security. Right now, perhaps aside from a rounding error, all custom themes are ridden with XSS holes. Most template-writing people are not PHP security savvy. Taking away PHP from them is a good thing. We will have autoescape on, check_plain()ing everything printed. This is obviously not complete or correct security in all cases but it's a world of a difference to the situation now where we hand a loaded gun to themers and tell them to hammer a nail with it. Note: the code to have autoescape on already exists in the sandbox.
  2. Performance. Compiled classes instead of repeated includes of template files. . The patch authors benchmarked the patch and no difference is observed in the best case, even with compilation cache. See #1696786-100: Integrate Twig into core: Implementation issue
  3. Slightly cleaner templatesTemplate authors no longer call print function. Calls to hide/render/t are still required, as are if() statements.
  4. Safety. Template authors can't use raw PHP so less trusted parties could contribute template changes ((e.g. Drupal Gardens site owners). Assumes you could teach and support these folks to author in Twig.

Performance regression and benefits follow-ups

#1825952: Turn on twig autoescape by default
#1716048: Do not boot bundle classes on every request
#1696786-160: Integrate Twig into core: Implementation issue
#1815250: Type twig variables in PHP and remove superfluous render_var() and getAttribute() calls from the generated code
#1778610: Remove the check for a link template from l(), have l() always output just a string.

Community Benefits

  1. Wide adoption. It's not just that it's used for Symfony -- it's actually used outside of PHP, even: Liquid for Ruby, Jinja2 and Django in Python. There's a TwigJS too.
  2. The community stands behind it #1499460: [meta] New theme system
  3. New light syntax. Template authors do not have to know PHP now. Preprocess still requires PHP

Downside

  1. Code complexity. This patch's reference workaround is clever but brain splitting. See TwigReference and TwigNodeVisitor.
  2. Code weight. The current patch (without caching or autoescape) is 40K which is not too bad, but the Twig engine, adds 385kb (based on #1591686-9: Add Twig itself).
  3. Security benefit and complexity are undemonstrated. The patch currently disables disables autoescape. It is not clear how we can check_plain all variables indiscriminately. Clearly a lot of data has already been sanitized before template layer.
  4. New syntax. Themers already have to know PHP in order to use preprocess system. They now have to learn Twig syntax.
  5. IDE support needs plugins. PHP already has syntax highlight and syntax check in every IDE and text editor but Twig has support only with plugins. (http://twig.sensiolabs.org/doc/templates.html#ides-integration)

Follow up issues

Documentation for Twig Coding standards: http://drupal.org/node/1823416

CommentFileSizeAuthor
#202 8151141322_fcdc5430d4.jpg110.72 KBschnitzel
#191 core-Integrate-twig-into-core-1696786-191.diff50.09 KBfabianx
#191 core-Integrate-twig-into-core-1696786-188-191-interdiff-do-not-test.diff5.05 KBfabianx
#188 core-Integrate-twig-into-core-1696786-188.diff49.01 KBfabianx
#188 core-Integrate-twig-into-core-1696786-186-188-interdiff-do-not-test.diff5.06 KBfabianx
#186 core-Integrate-twig-into-core-1696786-186.diff51.21 KBfabianx
#186 core-Integrate-twig-into-core-1696786-169-186-interdiff-do-not-test.diff4.86 KBfabianx
#169 core-Integrate-twig-into-core-1696786-169.diff50.84 KBfabianx
#169 core-Integrate-twig-into-core-1696786-167-169-interdiff-do-not-test.diff537 bytesfabianx
#167 core-Integrate-twig-into-core-1696786-167.diff50.87 KBfabianx
#167 core-Integrate-twig-into-core-1696786-155-167-interdiff-do-not-test.diff4.01 KBfabianx
#159 xhprof-diff-core-vs-twig-autoescape-1696786-155.png53.24 KBfabianx
#155 core-Integrate-twig-into-core-1696786-155.diff51.04 KBfabianx
#155 core-Integrate-twig-into-core-1696786-154-155-interdiff-do-not-test.diff5.06 KBfabianx
#154 core-Integrate-twig-into-core-1696786-154.diff48.84 KBfabianx
#154 core-Integrate-twig-into-core-1696786-133-154-interdiff-do-not-test.diff15.73 KBfabianx
#150 1696786_139.patch50.08 KBchx
#136 core-integrate-twig-into-core-1696786-136.patch49.59 KBjenlampton
#133 twig_error_message.png12.33 KBfabianx
#133 core-Integrate-twig-into-core-1696786-133.diff48.09 KBfabianx
#133 core-Integrate-twig-into-core-1696786-128-133-interdiff-do-not-test.diff5.19 KBfabianx
#128 core-Integrate-twig-into-core-1696786-128.diff48.4 KBfabianx
#128 core-Integrate-twig-into-core-1696786-84-128-interdiff-do-not-test.diff18.48 KBfabianx
#84 core-Integrate-twig-into-core-1696786-84.diff40.25 KBfabianx
#84 core-Integrate-twig-into-core-1696786-interdiff-77-84-do-not-test.diff15.83 KBfabianx
#77 core-Integrate-twig-into-core-1696786-77.patch34.12 KBamateescu
#75 core-Integrate-twig-into-core-1696786-74.patch34.41 KBamateescu
#75 interdiff-72-74-do-not-test.patch11.27 KBamateescu
#72 core-Integrate-twig-into-core-1696786-72.patch35.29 KBsteveoliver
#69 core-Integrate-twig-into-core-1696786-66.diff34.37 KBsteveoliver
#67 core-Integrate-twig-into-core-1696786-65.diff34.37 KBsteveoliver
#64 core-Integrate-twig-into-core-1696786-64.diff34.3 KBfabianx
#55 core-Integrate-twig-into-core-1696786-54.patch20.69 KBjwilson3
#53 core-Integrate-twig-into-core-1696786-52.patch20.39 KBjwilson3
#48 core-Integrate-twig-into-core-1696786-48.diff17.67 KBfabianx
#40 drupal-twig-in-core-1696786-40.patch16.4 KBjenlampton
#38 drupal-twig-in-core-1696786-38.patch16.43 KBjenlampton
#27 core-Integrate-twig-into-core-1696786-27.diff18.64 KBfabianx
#27 node_twig_core.png15.88 KBfabianx
#19 twig-example-1696786-19.patch4.14 KBstevector
#17 twig-example-1696786-17.patch4.13 KBjenlampton
#11 twig-example-1696786-11.patch7.39 KBstevector
#9 twig-example-1696786-9.patch4.37 KBstevector
#6 twig-example-1696786-6.patch4.22 KBjenlampton
#4 twig-example-1696786-4.patch4.16 KBstevector
#1 1696786-do-not-test.patch4.16 KBchx

Comments

chx’s picture

StatusFileSize
new4.16 KB

Small moves.

chx’s picture

Status: Active » Needs review
chx’s picture

Issue tags: +Drupal7AndTheArraysOfDoom

Tagging.

stevector’s picture

StatusFileSize
new4.16 KB

I haven't fully tested this yet but I saw that #1290694: Provide consistency for attributes and classes arrays provided by template_preprocess() just got committed.

Reading the patch, some questions came up:

Can twig() have a more descriptive name? It's also setting a 'cache' key to two different values.

    $twig = new Twig_Environment($loader, array(
        'cache' => DRUPAL_ROOT . '/compilation_cache',
        'cache' => FALSE,
        // @TODO: REMOVE ME!!!!!!!!
        'autoescape' => FALSE,
    ));

Also can that @TODO link to a relevant d.o issue? That looks rather important.

Where is twig_extension() going to get used? Should it get called from twig_theme() which is current hard-coding '.twig' ?

Status: Needs review » Needs work

The last submitted patch, twig-example-1696786-4.patch, failed testing.

jenlampton’s picture

Status: Needs work » Needs review
StatusFileSize
new4.22 KB

It doesn't look like we are using twig_extension anywhere yet, so I've removed it from this patch. If we need it later on, we can add it back in.

I updated both the second cache line and the disabling of autoescape to include comments (with links to issues) that tell us when we can change those values back.

I'm not sure we need a more descriptive name than twig() but am open to suggestions if you have any.

I took a peek at the failing tests, but I'm not sure I see how swapping to twig for rendering the stark theme would cause pager/paging issues. Going to let test bot try it again.

Status: Needs review » Needs work

The last submitted patch, twig-example-1696786-6.patch, failed testing.

chx’s picture

You can't remove twig_extension(), it's necessary for the theme engine to pick up .twig files, $extension_function = $theme_engine . '_extension'; in theme(). The fatals are IMO a bot fluke .

stevector’s picture

Status: Needs work » Needs review
StatusFileSize
new4.37 KB

This brings back twig_extension().

Status: Needs review » Needs work

The last submitted patch, twig-example-1696786-9.patch, failed testing.

stevector’s picture

Status: Needs work » Needs review
StatusFileSize
new7.39 KB

There a lot of logic and looping in theme_item_list() that needs to get replicated in template_preprocess_item_list() for this to work. The previous patches were rendering a lot of "Array" into the markup.

I've done a copy-paste refactorof theme_item_list() to get template_preprocess_item_list() working with item_list.twig. However, I suspect this patch will fail testing because template_preprocess_item_list() will run also before theme_item_list(). So should we be taking that out now since I imagine it's coming out eventually anyway? Or should template_preprocess_item_list() just check if global $theme_engine is twig or not? I don't want to introduce a dependency on a global though. chx, do you have something in mind for that problem?

This patch also adds the wrapping div and title element present in theme_item_list to item_list.twig. I don't know if those were intentionally excluded. If so, I think that should be the topic of a separate issue.

chx’s picture

This is not how this should be done, twig has the looping logic itself, we would like to see preprocess die eventually.

Status: Needs review » Needs work

The last submitted patch, twig-example-1696786-11.patch, failed testing.

stevector’s picture

Great, the preprocess feels sub-optimal. Can you be more specific as to the Twig-way to get this working? The patches prior to the #11 print this:

<ul class="Array" Array>
      <li class="" >Array</li>
      <li class="" >Array</li>
      <li class="" >Array</li>
      <li class="" >Array</li>
    </ul>

It should print

<div class="item-list">
  <ul  class="pager">
    <li class="pager-current odd first" >1</li>
    <li class="pager-item even" ><a rel="last" title="Go to page 2" href="/aggregator/sources/1?page=1">2</a></li>
    <li class="pager-next odd" ><a rel="last" title="Go to next page" href="/aggregator/sources/1?page=1">next ›</a></li>
    <li class="pager-last even last" ><a rel="last" title="Go to last page" href="/aggregator/sources/1?page=1">last »</a></li>
  </ul>
</div class="item-list">

BTW, I am testing against Aggregator module which has a pager using item_list. To see this, I'm adding an RSS feed, running cron which imports stories, and going to /aggregator/sources/1

chx’s picture

That's really weird. If you change twig to cache to a directory you can go into the generated php code and add a dump for those things, what's in those arrays?

stevector’s picture

The "Array" inside of <li> is because of the structure that's passed to theme('item_list'). Each item is an array with 'data' and (optionally) 'children.' See http://api.drupal.org/api/drupal/core%21includes%21theme.inc/function/th...

The "Array" inside of <ul> is because drupal_attributes() wasn't getting called anywhere on $variables['attributes'].

jenlampton’s picture

Status: Needs work » Needs review
StatusFileSize
new4.13 KB

After a brief discussion with chx and stevector in IRC, we decided to replace the item_list.twig example with link.twig. That new file is included in this patch.

Status: Needs review » Needs work

The last submitted patch, twig-example-1696786-17.patch, failed testing.

stevector’s picture

Status: Needs work » Needs review
StatusFileSize
new4.14 KB

The link.twig needed a drupal_attributes() call.

Status: Needs review » Needs work

The last submitted patch, twig-example-1696786-19.patch, failed testing.

stevector’s picture

Odd, I'm running tests locally and not getting those fails locally Block, Forum or Update modules. I haven't tried the rest yet. I thought clean urls were the problem but I ran the Block test with the new index.php/some/path structure and it still passed.

c4rl’s picture

I don't think drupal_attributes() is necessary anymore, see http://drupal.org/node/1290694#comment-6273540 for more details.

stevector’s picture

chx or Jen, do you know how that should be implemented? Should all calls to theme_link() be turning $options['attributes'] into an Attributes object? Or can we do that directly in the twig file?

effulgentsia’s picture

I'm concerned about theme_link() being the first example. That's the one that's treated specially by l() for performance reasons, and I don't think we want to make Stark needlessly slow. May I suggest theme_image() instead?

In order to initialize attributes for things that are currently theme functions, I suggest making template_preprocess_image() (if you accept my suggestion of using 'image' as the first example) call template_preprocess($variables, 'image') as its first line. The initializations done there, including initializing $variables['attributes'] is stuff we'll want to make available to all twig templates. We may need to micro-optimize that function a bit more before we can make theme() call it for every theme hook, but in the interim, most theme functions, including theme_image(), are not so frequently called as to make their preprocess function call template_preprocess() a problem.

Also, I unpostponed #1700382: Replace remaining references to drupal_attributes() with new Attributes() in case someone wants to work on that.

effulgentsia’s picture

Or, if we want an example that demonstrates how twig should interact with check_plain(), there's also theme_datetime().

jenlampton’s picture

Issue tags: +Twig

adding Tag

jenlampton’s picture

Issue summary: View changes

Typo :)

fabianx’s picture

StatusFileSize
new15.88 KB
new18.64 KB

This is the first attempt to integrate twig into core.

This includes the twig engine and node.twig and datetime.twig as examples.

Limitations

  • * autoescape is not enabled and doing so would need to touch almost all of core (followup)
  • * to escape use {{ unsafe|e }} for now
  • * caching is not enabled for now
  • * strict variables are disabled for now

Strenghts

  • * Functions available from templates are limited to hide, show, render, unset, url and t as filter
  • * Very nice and easy templates
  • * It just works (tm)

Explanation of functionality

The engine is straightforward besides some specialties that are needed to work around a twig limitation.

  • * twig() is used as singleton to instantiate the TwigEnvironment
  • * twig_render() is a wrapper around render() that can render strings, objects and render arrays
  • * twig_hide() / twig_show() are wrappers around hide / show that do not return the content.
  • * twig_render_template() renders the template via the TwigEnvironment

There are two special cases, which we need for Drupal and for which we are extending twig:

  • * a) Render render arrays automatically on demand
  • * b) Pass variables by reference

Render automatically

To render arrays automatically on demand every Twig_PrintNode (something that would be output) is wrapped in a call to Drupal\Core\Template\TwigReferenceFunctions::twig_render.

Internally twig converts:

{{ content.links }}

to

        if (isset($context["content"])) { $_content_ = $context["content"]; } else { $_content_ = null; }
        echo Drupal\Core\Template\TwigReferenceFunctions::twig_render($this->getAttribute($_content_, "links"));

Twig render detects that what it got from getAttribute is a render array and renders it.

In order to inject the function call, Twig is extended with TwigNodeVisitor.

Passing by reference

getAttribute above returns a value and not a reference. Such first hide / show and partial rendering were not working.

To solve this two things are done:

The variables passed to twig are encapsulated within TwigReference objects (which subclasses ArrayObject for performance).

Once getAttribute accesses the links key in TwigReference, TwigReference wraps a reference to the key in another TwigReference.

That way all twig ever is seeing is objects and such references are hold correctly.

To now get a real reference the Magic Drupal\Core\Template\TwigReferenceFunctions::my_function is used, which calls the getReference method on the TwigReference and calls the real function my_function with all arguments converted to array objects - if applicable.

The usage of a magic __callStatic class allows extending twig ny PHP functions without having to know that one needs to get a reference to the parameter first. Such the system is totally transparent to the called PHP function.

( The class was originally written when twig had much more functions it did support besides hide, show, render, unset. Now the &getReference could be also called directly in the wrapper functions though unset currently does not need one ... )

Fun? Fun! and Motivation

I think twig is lots of fun and seemingly really complicated templates get a piece of cake. To motivate another WIP example:

item-list.twig:

<div class="item-list">
  {% if title %}
  <h3> {{ title }} </h3>
  {% endif %}
  <{{ type }} class="{{ attributes.class }}" {{ attributes }}>
  {% for item in items %}
    <li {{ item.attributes }} >{{ item }}
    {# Append nested child list, if any. #}
    {% if children in item %}
        {% include "item-list" with {'items': item.children, 'attributes': item.children_attributes} %}
    {% endif %}
    </li>
  {% endfor %}
  </{{type}}>
</div>

And now compare this to:

http://api.drupal.org/api/drupal/core!includes!theme.inc/function/theme_...

Thanks goes to chx and jenlampton for mentoring!

Screenshot

Node template powered by Twig

fabianx’s picture

Status: Needs work » Needs review
podarok’s picture

#27 nice write-up as documentation
good to see it inside a summary - very nice documentation BTW

But why do we need integrate only two templates in this issue?
we have already much more converted and successfully working templates in sandbox

* autoescape is not enabled and doing so would need to touch almost all of core (followup)

One click option

* caching is not enabled for now

It just a one click option in twig.engine and it works like a charm when I`d test it - we just need to decide where the cache should be stored and create workaround for regenerating it

* strict variables are disabled for now

One click option

As for me - it looks like we need follow-up issue here for twig admin UI with making this options configurable via UI

* twig() is used as singleton to instantiate the TwigEnvironment

#1777532-9: Extend Twig_Loader_Filesystem instance to get .twig templates paths with hierarchy for theme dependencies looks like we have to decide this or other workaround

chx’s picture

The fabolous reference magic is totally Fabian's work, I really like it. I never heard of exchangeArray before, that's truly handy! I thought ArrayObject was almost useless.

I can't RTBC this because it contains code written by me here and there. The visitor mostly and some small plumbing. I asked fabpot in July to review and fix the node visitor and he helped us by adding new Twig_Node(array($node->getNode('expr'))), $node->getLine()) to the new Twig_Node_Expression_Function part. So the visitor is guaranteed to be good :)

Edit: I mean, we should credit fabpot in here and we should ask FabianX who else.

chx’s picture

#29 we are adding only two templates because we want to move rapidly. Once this is in we can send a rain of conversion issues in parallel .

podarok’s picture

Status: Needs review » Needs work

#31 i got it, thanks!

+++ b/core/themes/stark/templates/node/node.twigundefined
@@ -0,0 +1,112 @@
+ * Available variables:
+ * - $title: the (sanitized) title of the node.

As i remember we should not use $syntax for variables in template help - it has to be just syntax

podarok’s picture

tagging

catch’s picture

Status: Needs work » Needs review
Issue tags: -Needs documentation updates

Here's an initial pass through. Overall I'm really impressed how little code is actually in here to make things work, it looks great, and it's frankly a relief to see this patch since there's not massive amounts of time left and I'm sure we'll find some issues as we get into the conversions.

+++ b/core/themes/engines/twig/twig.engineundefined
@@ -0,0 +1,172 @@
+/**
+ * Convert into a TwigReference object.
+ *
+ * This is needed to workaround a limitation
+ * of twig to only provide variables by value.
+ *
+ * By saving a reference to the original array
+ * the variable can be passed by reference.
+ *
+ * Only complex variables that are no attributes
+ * are converted.
+ *
+ * @see TwigReference
+ * @see TwigReferenceFunctions
+ *
+*/
+function twig_convert_to_reference(&$elements, &$original) {
+  $needs_selfreference = FALSE;
+
+  foreach ($elements as $key => $val) {
+    if (!is_array($val) || $key[0] == '#') {
+      continue;
+    }
+    if (!is_numeric($key)) {
+      $needs_selfreference = TRUE;
+    }
+  }
+  if ($needs_selfreference) {
+    $elements = new TwigReference();
+    $elements->setReference($original);
+  }

Is this worth making an upstream feature request for?

+++ b/core/themes/engines/twig/twig.engineundefined
@@ -0,0 +1,172 @@
+/**
+ * Singleton for the twig environment
+ *
+ * This is used for rendering templates with twig.
+ */
+function twig() {
+  $twig = &drupal_static(__FUNCTION__);

This seems like it could be a class that's put into the DIC. The DIC is more or less the new drupal_static() now.

+++ b/core/themes/engines/twig/twig.engineundefined
@@ -0,0 +1,172 @@
+    $twig = new Twig_Environment($loader, array(
+        'cache' => DRUPAL_ROOT . '/compilation_cache',
+        // @TODO Maybe enable cache, once rebuild has been sorted.
+        'cache' => FALSE,
+        // @TODO: Remove in followup issue.
+        'autoescape' => FALSE,
+        // @TODO: Remove in followup issue to handle exceptions gracefully.
+        'strict_variables' => FALSE,

Would be good to open the follow-up issues (or move them to the core queue) and link to them from the issue summary. I'm assuming we'll absolutely have to cache this to get acceptable performance since Twig is designed to work like that.

+++ b/core/themes/engines/twig/twig.engineundefined
@@ -0,0 +1,172 @@
+function twig_render($arg) {
+  // Keep Twig_Markup objects intact to prepare for later autoescaping support
+  if ($arg instanceOf Twig_Markup) {
+    return $arg;
+  }
+
+  if (is_string($arg) || (is_object($arg) && method_exists($arg, '__toString'))) {
+    return (string) $arg;
+  }
+
+  // This is a normal render array.
+  return render($arg);

If you pass something invalid, like an object that's not an instance of Twig_Markup and doesn't have a __toString method, then this will return NULL but otherwise fail silently. Should we through an exception or just let it fail horribly on the (string) cast instead?

+++ b/core/themes/stark/stark.infoundefined
@@ -3,4 +3,5 @@ description = This theme demonstrates Drupal's default HTML markup and CSS style
diff --git a/core/themes/stark/templates/node/node.twig b/core/themes/stark/templates/node/node.twig

diff --git a/core/themes/stark/templates/node/node.twig b/core/themes/stark/templates/node/node.twig
new file mode 100644
index 0000000..0986a52

index 0000000..0986a52
--- /dev/null

--- /dev/null
+++ b/core/themes/stark/templates/node/node.twigundefined

+++ b/core/themes/stark/templates/node/node.twigundefined
+++ b/core/themes/stark/templates/node/node.twigundefined
@@ -0,0 +1,112 @@

@@ -0,0 +1,112 @@
+{#
+ /**

Why adding these templates here instead of converting the ones provided by modules? Stark is supposed to represent Drupal's default output, which means no templates. So i'd expect to see the default node.tpl.php changed (or duplicated with a twig version until it can be removed), rather than put into Stark like this.

Leaving at needs review since it needs more reviews.

chx’s picture

> Is this worth making an upstream feature request for?

No, it's a render array specific thing. All the problems we have come from the fact that render (and consequently show and hide) get their arguments by reference and change them.

> I'm assuming we'll absolutely have to cache this

In twig-speak yes but in fact we will need to get the php loader functionality we added for dic and twig to work with it.

> If you pass something invalid, like an object that's not an instance of Twig_Markup and doesn't have a __toString method, then this will return NULL but otherwise fail silently. Should we through an exception or just let it fail horribly on the (string) cast instead?

It won't fail silently at all as the default is to pass on to render which expects an array so it'll fatal out pretty soon. We can add an array typehint to render() be more explicit.

catch’s picture

Passing NULL is valid for type hinting, so it's going to fail somewhere in render, can we just remove the __toString() check then so it fails right there?

In twig-speak yes but in fact we will need to get the php loader functionality we added for dic and twig to work with it.

Yep. Is there an issue for this against the sandbox? If not I'm happy to open one.

chx’s picture

As for using our php writer mechanisms, here's fuel for a followup: We instantiate the Twig_Environment class now, instead we need our own class which extends Twig_Environment and overrides writeCacheFile. As this moves into the DIC as catch asked, then we can just pass in a Reference to a a php loader service.

But, followup. Just wanted to show it's doable and not hard.

jenlampton’s picture

StatusFileSize
new16.43 KB

This is the patch from #27 with an updated version of node.twig

Status: Needs review » Needs work

The last submitted patch, drupal-twig-in-core-1696786-38.patch, failed testing.

jenlampton’s picture

Status: Needs work » Needs review
StatusFileSize
new16.4 KB

Removing attributes correction that broke tests

Status: Needs review » Needs work
Issue tags: -Needs documentation updates, -Twig, -Drupal7AndTheArraysOfDoom

The last submitted patch, drupal-twig-in-core-1696786-40.patch, failed testing.

chx’s picture

Status: Needs work » Needs review
Issue tags: +Needs documentation updates, +Twig, +Drupal7AndTheArraysOfDoom
podarok’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs documentation updates

#40 look good for me

chx’s picture

Status: Reviewed & tested by the community » Needs work

Nope it doesn't. We still need to move the twig static into the DIC.

podarok’s picture

+++ b/core/themes/engines/twig/twig.engineundefined
@@ -0,0 +1,172 @@
+function twig() {
+  $twig = &drupal_static(__FUNCTION__);

this one?
Are there any documentation that can be helpful to do that?
I`m not familiar with DIC workaround yet

ok, i`d found one http://symfony.com/doc/current/book/service_container.html

fabianx’s picture

Issue tags: -Twig

I have a working DIC version and will commit shortly.

fabianx’s picture

Re-add tags ...

fabianx’s picture

Status: Needs work » Needs review
StatusFileSize
new17.67 KB

New patch:

  • DIC in
  • node.twig fixed
  • Fixed twig_render to throw Exception on non-printable object
  • Rebased on newest core

Still to be done:

Setting to needs review, because this needs more reviews ... and I want test bot to test this.

Status: Needs review » Needs work
Issue tags: -Needs documentation updates, -Twig, -Drupal7AndTheArraysOfDoom

The last submitted patch, core-Integrate-twig-into-core-1696786-48.diff, failed testing.

fabianx’s picture

Status: Needs work » Needs review
Issue tags: +Needs documentation updates, +Twig, +Drupal7AndTheArraysOfDoom
fabianx’s picture

@TODO here:

jwilson3’s picture

Status: Needs work » Needs review
StatusFileSize
new20.39 KB

This patch combines the patch in comment #18 on #1697854: Allow modules to provide both .twig and .tpl.php templates temporarily until twig is the default engine into the one in #48.

UPDATE: worth noting that we need to later remove parts of this patch: #1805592: Remove static double theme engine code once all module provided .tpl.php files are converted to twig and twig is default This was originally noted in comment #18 on the other issue.

Status: Needs review » Needs work

The last submitted patch, core-Integrate-twig-into-core-1696786-52.patch, failed testing.

jwilson3’s picture

Forgot to pull the latest... there was a merge conflict in core/lib/Drupal/Core/CoreBundle.php

younger870’s picture

Forgot to pull the latest... there was a merge conflict in core/lib/Drupal/Core/CoreBundle.php

chx’s picture

Thanks jwilson3 for moving this forward. Note: most of #52 needs to be done yet.

fabianx’s picture

Just for myself:

+++ b/core/includes/theme.incundefined
@@ -229,6 +229,12 @@ function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callb
+  // @todo Make twig the default engine and remove this

Needs follow-up. Create issue to make twig the default engine with postponed status.

+++ b/core/lib/Drupal/Core/Template/TwigFactory.phpundefined
@@ -0,0 +1,57 @@
+    // @TODO: Maybe we will have our own loader later.

Needs follow-up, move http://drupal.org/node/1777532#comment-6569348 to Core.

+++ b/core/lib/Drupal/Core/Template/TwigFactory.phpundefined
@@ -0,0 +1,57 @@
+        // @TODO Maybe enable cache, once rebuild has been sorted.

Needs follow-up. Make use of DIC and the functionality in http://drupal.org/node/1747970.

+++ b/core/lib/Drupal/Core/Template/TwigFactory.phpundefined
@@ -0,0 +1,57 @@
+        // @TODO: Remove in followup issue.
+        'autoescape' => FALSE,

Create followup meta issue to move Core to auto escape everywhere. Will need code like:

$vars['css'] = new Drupal_Safe_Markup($vars['css'])

or

{{ css|raw }} in template. But this needs to be discussed, etc.

+++ b/core/lib/Drupal/Core/Template/TwigFactory.phpundefined
@@ -0,0 +1,57 @@
+        // @TODO: Remove in followup issue to handle exceptions gracefully.
+        'strict_variables' => FALSE,

Followup: Should be enabled, but the Exception thrown by the Twig Code is very hard, might want to handle Exceptions more gracefully. Needs discussions.

+++ b/core/themes/stark/templates/node/node.twigundefined
@@ -0,0 +1,110 @@
+  @todo: might be a good idea to remove the id attribute, because if that gets
+<article id="node-{{ node.nid }}" class="{{ attributes.class }} clearfix" {{- attributes }}>

Follow-up: Not related to the twig.engine, but should open issue in core for that tag with Twig and theme cleanup or so.

jwilson3’s picture

Was surprised to see that this issue wasn't listed on the issue summary on #1788918: [GTD] [META] Prepare for Twig main engine core inclusion until December 1, so I added it. Catching up on current work on the Twig in core effort is challenging!

jwilson3’s picture

chx’s picture

Notes: Drupal_Safe_Markup , no need, that exists already and is called Twig_Markup.

#60 , I closed the theme engine issue. The other I have no idea.

fabianx’s picture

Issue summary: View changes

Updated issue summary.

fabianx’s picture

Issue summary: View changes

Add follow-up issues

fabianx’s picture

Issue summary: View changes

F'up issues

fabianx’s picture

Issue summary: View changes

add f'ups

fabianx’s picture

Assigned: Unassigned » fabianx
  • Open Followup issues for the @TODOs in the code and link to it in issue queue
  • Move templates to core/modules/node/node.twig and core/templates/theme.inc/datetime.twig
  • Call datetime template in hook_theme of core/includes/theme.inc

Little progress in sandbox d8-core branch. Now on to writing tests.

fabianx’s picture

modules/system/lib/Drupal/system/Tests/Theme/TwigReferenceUnitTest.php has landed in sandbox:

http://drupalcode.org/sandbox/pixelmord/1750250.git/commit/fb2d987

Need more tests for .twig and .tpl.php support.

fabianx’s picture

Here is the new full patch that includes tests and also merges #1678808: Defining a new theme engine with templates without .tpl.[template extension] breaks template suggestions and fixes it. Tests are also added.

Antoine Lafontaine should be also credited for that.

chx’s picture

Assigned: fabianx » dries
Status: Needs review » Reviewed & tested by the community

This IMO is good to go. I wanted to remove the new function_exists call from theme() but phptemplate_render_template doesn't exist so you can't.

chx’s picture

Issue summary: View changes

Order of f'ups

chx’s picture

Assigned: dries » Unassigned
Status: Reviewed & tested by the community » Needs work

Oh :( hate to do this but there's a bunch of code style violations in the tests, like if (isset($context["content"])) { $_content_ = $context["content"]; } else { $_content_ = null; }

steveoliver’s picture

Status: Needs work » Needs review
StatusFileSize
new34.37 KB

Ran through Coder, fixed all but the CamelCase warnings:

b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php: @@ -187,4 +187,19 @@:
 +2: [style_camel_case, normal] do not use mixed case (camelCase), use lower case and _
 +9: [style_camel_case, normal] do not use mixed case (camelCase), use lower case and _
steveoliver’s picture

Issue summary: View changes

rewrote summary

Status: Needs review » Needs work

The last submitted patch, core-Integrate-twig-into-core-1696786-65.diff, failed testing.

steveoliver’s picture

Status: Needs work » Needs review
StatusFileSize
new34.37 KB
+++ b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigReferenceUnitTest.php
@@ -0,0 +1,144 @@
+   *  Helper function to somehow simulate Twigs getAttribute function

Maybe the two preceding spaces before comment text?

Status: Needs review » Needs work

The last submitted patch, core-Integrate-twig-into-core-1696786-66.diff, failed testing.

attiks’s picture

From the test details:

[00:20:05] Command [php -l -f './core/modules/system/lib/Drupal/system/Tests/Theme/TwigReferenceUnitTest.php' 2>&1] failed
  Duration 0 seconds
  Directory [/var/lib/drupaltestbot/sites/default/files/checkout]
  Status [255]
 Output: [PHP Warning:  Unterminated comment starting line 144 in ./core/modules/system/lib/Drupal/system/Tests/Theme/TwigReferenceUnitTest.php on line 144

Warning: Unterminated comment starting line 144 in ./core/modules/system/lib/Drupal/system/Tests/Theme/TwigReferenceUnitTest.php on line 144
PHP Parse error:  syntax error, unexpected $end, expecting T_FUNCTION in ./core/modules/system/lib/Drupal/system/Tests/Theme/TwigReferenceUnitTest.php on line 145

Parse error: syntax error, unexpected $end, expecting T_FUNCTION in ./core/modules/system/lib/Drupal/system/Tests/Theme/TwigReferenceUnitTest.php on line 145
Errors parsing ./core/modules/system/lib/Drupal/system/Tests/Theme/TwigReferenceUnitTest.php].
[00:20:05] Encountered error on [syntax], details:
array (
  '@filename' => 'core/modules/system/lib/Drupal/system/Tests/Theme/TwigReferenceUnitTest.php',
)
[00:20:05] Review complete. test_id=356323 result code=6 details=Array
(
    [@filename] => core/modules/system/lib/Drupal/system/Tests/Theme/TwigReferenceUnitTest.php
)
steveoliver’s picture

Status: Needs work » Needs review
StatusFileSize
new35.29 KB

OK, let's see about this one. As chx pointed out, TwigReferenceObjectTest class also needed to be in its own file as per PSR-0.

kika’s picture

+<!-- node.twig - proudly powered by twig engine -->

I understand the feeling behind this -- agreed, Twig is great! -- but is it neccessary, especially when looking back to it in, say D9. Then it will sound like "proudly powered by PHP template engine".

Perhaps file a followup issue to remove this line before release -- when the feelings have been calmed down?

podarok’s picture

#72 looks good for me with exeption we need follow-ups from #58
agreed with #73

amateescu’s picture

@chx asked me to do a docs review, and who am I to refuse! :)

amateescu’s picture

Issue summary: View changes

added steveoliver

chx’s picture

Issue summary: View changes

added amateescu

fabianx’s picture

Issue summary: View changes

Add more explanation to summary.

fabianx’s picture

+++ b/core/lib/Drupal/Core/CoreBundle.phpundefined
@@ -55,11 +55,13 @@ public function build(ContainerBuilder $container) {
     $container->register('typed_data', 'Drupal\Core\TypedData\TypedDataManager');
-    // Add the user's storage for temporary, non-cache data.
     $container->register('lock', 'Drupal\Core\Lock\DatabaseLockBackend');

I also hate to do that, but the patch in #75 removes a comment that probably should not be removed.

I wrongly merged those lines in #64.

amateescu’s picture

Done, no interdiff needed.

chx’s picture

Assigned: Unassigned » dries
Status: Needs review » Reviewed & tested by the community

Thanks guys for seriously prettifying this!

chx’s picture

Title: Add a simple example of twig to core » Integrate Twig with core

Better title. We are not simply adding examples here.

fabianx’s picture

Title: Integrate Twig with core » Integrate Twig into core
Issue tags: -Needs tests

Small nitpick, we're integrating twig as part of core; now it matches suggested commit message.

Removed: Needs tests as tests are there now.

vlad.dancer’s picture

webchick’s picture

Category: task » feature
Priority: Normal » Major

This seems like it should be at least major, but it is a feature not a task.

steveoliver’s picture

@chx and @stevector: Even though theme_item_list isn't included in this example, I wanted to point you to its issue, as it could use some review: #1800726: Convert theme_item_list to item-list.twig

fabianx’s picture

Per request updated documentation per coding-standards and changed to new \class policy for global non-namespaced objects:

Cosmetical changes in this patch:

* Removed use statements in favor of \class for global objects (per namespacing policy)
* Added missing @param, @return and class descriptions
* Added further extensive documentation

Non-cosmetical changes in this patch:

* Fixed the constructor for TwigReference to never store the array argument as internal reference or pass to the parent. The preferred way is to create the TwigReference object and then explicitly call setReference. TwigReference should be instantiated like this:

$x = new TwigReference();
$x->setReference($variable);

The reason to do this explicitly besides readability is that the PHP docs state:

public ArrayObject::__construct() ([ mixed $input [, int $flags = 0 [, string $iterator_class = "ArrayIterator" ]]] )

Such $input is passed by value according to the API documentation, however in reality it is passed by reference, but as that could be fixed in PHP / change any time I decided to create a new Interface that explicitly passes by reference and document this interface.

New patch and interdiff attached.

As the interdiff is besides the doc and cosmetical changes trivial and just removes a non-used code path (i.e. TwigReference(some-non-empty-val) was never called), I leave this RTBC.

podarok’s picture

#84 looks good
removing tag

moshe weitzman’s picture

Status: Reviewed & tested by the community » Needs work

I'm mixed about Twig engine. The templates get slightly cleaner, but at the cost of significant code complexity. Consider all the wrapping and abstracting we do in TwigReference and TwigNodeVisitor. None of that is needed with PHPTemplate. And how much simpler are templates? We still have if statements and hide() calls. But now we do it in a relatively unpopular format (.twig) which is rarely syntax checked or syntax highlighted in an IDE.

+++ b/core/lib/Drupal/Core/Template/TwigFactory.php
@@ -0,0 +1,86 @@
+    // @todo Maybe we will have our own loader later.
+    $loader = new \Twig_Loader_Filesystem(DRUPAL_ROOT);
+    $twig = new \Twig_Environment($loader, array(
+        'cache' => DRUPAL_ROOT . '/compilation_cache',
+        // @todo Maybe enable cache, once rebuild has been sorted.
+        'cache' => FALSE,
+        // @todo Remove in followup issue.
+        'autoescape' => FALSE,
+        // @todo Remove in followup issue to handle exceptions gracefully.
+        'strict_variables' => FALSE,
+    ));

IMO, it is not OK to disable cache in the initial patch. One of the main complexities of Twig is it is a templating language on top of a templating language. To overcome this speed problem, it caches to raw PHP on disk, which is fine. IMO we need some experience with this, before we commit to it. If compilation adds little new code and complexity, then there is no problem. If it does add complexity, we need to know that before we start converting a boatload of templates. We already committed the Twig engine to core without having Twig having proven its worth. IMO buck stops here. For implementing the compilation cache, we should consider using #1675260: Implement PHP reading/writing secured against "leaky" script.

+++ b/core/lib/Drupal/Core/Template/TwigFactory.php
@@ -0,0 +1,86 @@
+    $functions = array(
+      'hide' => 'twig_hide',
+      'render' => 'twig_render',
+      'show' => 'twig_show',
+      'unset' => 'unset'
+    );
+    $filters = array(
+      't' => 't'
+    );

Do we want contrib to be able to add own functions and filters? Seems worthwhile.

+++ b/core/lib/Drupal/Core/Template/TwigNodeVisitor.php
@@ -0,0 +1,53 @@
+  /**
+   * Implements Twig_NodeVisitorInterface::getPriority().
+   */
+  function getPriority() {
+    return 0;
+  }

Expand Doxygen a bit more please. No idea what priority is or why we hard code it.

+++ b/core/lib/Drupal/Core/Template/TwigFactory.php
@@ -0,0 +1,86 @@
+    // @todo Maybe we will have our own loader later.
+    $loader = new \Twig_Loader_Filesystem(DRUPAL_ROOT);
+    $twig = new \Twig_Environment($loader, array(
+        'cache' => DRUPAL_ROOT . '/compilation_cache',
+        // @todo Maybe enable cache, once rebuild has been sorted.
+        'cache' => FALSE,
+        // @todo Remove in followup issue.
+        'autoescape' => FALSE,
+        // @todo Remove in followup issue to handle exceptions gracefully.
+        'strict_variables' => FALSE,
+    ));

IMO, it is not OK to disable cache in the initial patch. One of the main complexities of Twig is it is a templating language on top of a templating language. As such, it needs to cache to raw PHP. If compilation adds little new code and complexity, then there is no problem. If it does add complexity, we need to know that before we start converting a boatload of templates. We already committed Twig engine to core without having proven its worth. I think the buck stops here. For implementation, we might consider using #1675260: Implement PHP reading/writing secured against "leaky" script

+++ b/core/lib/Drupal/Core/Template/TwigFactory.php
@@ -0,0 +1,86 @@
+    $functions = array(
+      'hide' => 'twig_hide',
+      'render' => 'twig_render',
+      'show' => 'twig_show',
+      'unset' => 'unset'
+    );
+    $filters = array(
+      't' => 't'
+    );

Do we want contrib to be able to add own functions and filters? Seems worthwhile.

+++ b/core/lib/Drupal/Core/Template/TwigNodeVisitor.php
@@ -0,0 +1,53 @@
+  /**
+   * Implements Twig_NodeVisitorInterface::getPriority().
+   */
+  function getPriority() {
+    return 0;
+  }

Expand Doxygen a bit more please. No idea what priority is or why we hard code it.

+++ b/core/modules/system/templates/datetime.twig
@@ -0,0 +1,31 @@
+<time class="{{ attributes.class }}" {{ attributes }}>{{ html?text:(text|e) }}</time>

Wow. Dunno what html?text:(text|e) could possibly mean. I bet I would know if it were written in PHP :)

+++ b/core/modules/system/tests/themes/test_theme_twig/template.php
@@ -0,0 +1,19 @@
+ * The confusing function name here is due to this being an implementation of
+ * the alter hook invoked when the 'theme_test' module calls
+ * drupal_alter('theme_test_alter').
+ */
+function test_theme_theme_test_alter_alter(&$data) {
+  $data = 'test_theme_theme_test_alter_alter was invoked';
+}

Thanks for the Doxygen. Classic function name.

+++ b/core/themes/engines/twig/twig.engine
@@ -0,0 +1,162 @@
+/**
+ * Implements hook_init().
+ */
+function twig_init($template) {
+  $file = dirname($template->filename) . '/template.php';
+  if (file_exists($file)) {
+    include_once DRUPAL_ROOT . '/' . $file;
+  }
+}

It is pretty weird that we include this file during hook_init(). Seems like we could do that when we initiatize the theme system.

+++ b/core/themes/engines/twig/twig.engine
@@ -0,0 +1,162 @@
+ * All complex variables that are no attributes are converted

s/no/not

+++ b/core/themes/engines/twig/twig.engine
@@ -0,0 +1,162 @@
+  // This is a normal render array.
+  return render($arg);

We typically reserve render() for use inside templates. We should use drupal_render() here instead. At some future date we could move to a render() method and deprecate drupal_render() function.

moshe weitzman’s picture

Status: Needs work » Reviewed & tested by the community

I'll add that the current Issue Summary here and in the Meta issue do not bother to justify why we need this. I think such a big change merits a careful analysis of what we gain, and how that exceeds the complexity cost.

moshe weitzman’s picture

Status: Reviewed & tested by the community » Needs work

Sorry, multiple tabs.

c4rl’s picture

I'll add that the current Issue Summary here and in the Meta issue do not bother to justify why we need this. I think such a big change merits a careful analysis of what we gain, and how that exceeds the complexity cost.

Twig has been suggested and generally lauded since March here: #1499460: [meta] New theme system

This present issue deals more with implementation itself, not whether to choose Twig is a good idea or not. It seems to me that philosophical concerns shouldn't be a blocker for the patches here.

rene bakx’s picture

Wow. Dunno what html?text:(text|e) could possibly mean. I bet I would know if it were written in PHP :)

so you understand ternary in php, but the same in twig is a mystery all of a sudden?? :)

echo ($html)?$text:escape($text);
fabianx’s picture

Status: Reviewed & tested by the community » Needs work

Lets discuss this :-).

I'm mixed about Twig engine. The templates get slightly cleaner, but at the cost of significant code complexity. Consider all the wrapping and
abstracting we do in TwigReference and TwigNodeVisitor. None of that is needed with PHPTemplate. And how much simpler are templates? We still have if statements and hide() calls. But now we do it in a relatively unpopular format (.twig) which is rarely syntax checked or syntax highlighted in an IDE.

I do think that:

{{ content }}
{{ content.links }}

is easier than echo render($variables['content'); at least for front-end people. They don't _need_ to know what it is, they can just use it.

We also can get auto escaping and there is a proof-of-concept of this in the twig_engine branch of the sandbox.

Besides this, this should be a decision that the front-end'ers of drupal do, not the backend'ers and there seems to be huge support of front-enders for this transition. Themes can still use .tpl.php if they chose to.

Do we want contrib to be able to add own functions and filters? Seems worthwhile.

Yes, as its a factory class, contrib can integrate with the DIC to provide their own extensions and functions.

Expand Doxygen a bit more please. No idea what priority is or why we hard code it.

This is the default value. According to Doc standards I understood it that functions should not be documented that are only implementing the default and inherited methods. As the parent class is abstract we _need_ to implement getPriority.

Wow. Dunno what html?text:(text|e) could possibly mean. I bet I would know if it were written in PHP :)

e is short for escape, we can use also the full escape syntax. We can also write it in the long form. I should have asked a frontender before.

Thanks for the Doxygen. Classic function name.

This was copied from the default theme_test theme, I probably copied too much of it. I'll fix that and remove the function as this does not need re-testing. Please consider opening up an issue against theme_test doxygen :-).

It is pretty weird that we include this file during hook_init(). Seems like we could do that when we initiatize the theme system.

This is _straight_ copied from phptemplate_init():

/**
 * Implements hook_init().
 */
function phptemplate_init($template) {
  $file = dirname($template->filename) . '/template.php';
  if (file_exists($file)) {
    include_once DRUPAL_ROOT . '/' . $file;
  }
}

Please file a separate issue for that.

s/no/not

Will be fixed.

We typically reserve render() for use inside templates. We should use drupal_render() here instead. At some future date we could move to a render() method and deprecate drupal_render() function.

render() is correct here, because :-) this is only called from templates. That is why the documentation for the function says:

/**
 * Wrapper around render() for twig printed output.
 * [...]
*/

So in _only_ this case this is correct, because this is like a template doing {{ render(content) }}

fabianx’s picture

Issue summary: View changes

Change commit message

chx’s picture

Status: Needs work » Reviewed & tested by the community

I do not think this should held back. The complexity Moshe dislikes is because of integrating the render system with Twig.

I am updating the issue summary for why but the big one is: autoescape.

dozymoe’s picture

#90 might be better read if the ternary operators has spacing. Also to complies with twig coding standards. Disclosure: I'm not well versed with twig yet.

Put one (and only one) space before and after the following operators: comparison operators (==, !=, <, >, >=, <=), math operators (+, -, /, *, %, //, **), logic operators (not, and, or), ~, is, in, and the ternary operator (?:):

dozymoe’s picture

Status: Needs work » Reviewed & tested by the community

Sorry accidentally changed the status to need work.

mortendk’s picture

seriously this makes me smile a lot - this is the way to go forward if we wanna include "frontend" / Themers or whatever we call my kinda people.

can i do a +2 ?

mortendk’s picture

Issue summary: View changes

reasons.

webchick’s picture

Status: Reviewed & tested by the community » Needs review

Folks, what we definitely don't need in this issue is a piling of +1s from front-end developers. Please articulate why Twig is valuable, why the complexity concerns that Moshe is highlighting are not worrisome for you, etc. Links to other places this has been discussed before are great, and can be added to the issue summary.

chx’s picture

Status: Needs review » Reviewed & tested by the community

Also, I find it funny that we have a grudge with the complexity found in a 40kb patch when we are about to use a full blown, AST-using compiler written in a language most unfit to write such a compiler -- PHP, that is. Let me repeat my reasoning which I just put in the issue summary: in my not so humble opinion just having a secure theme system which has the additional bonus of liked by frontend people is enough to justify any complexity in PHP.

If you want I can get all the frontend people who are working on Twig template conversion to this issue to express their support but instead just look http://drupal.org/node/1750250/committers here, just look at the number of people you never heard and are enthusiastic about Twig enough to contribute a template or two.

chx’s picture

Sorry x-post but I think the reasoning is enough.

podarok’s picture

agree with #97
Yes - today #84 TWIG version looks not so valuable like PHPtemplate after a bunch of years after 2004(?) and with hidden background from reviewers that are familiar with tpl.php syntax, but not familiar with much more simple .twig ...
Anyway as described in roadmap http://drupal.org/sandbox/pixelmord/1750250 this is a really initial patch
We have many templates converted already in sandbox http://drupalcode.org/sandbox/pixelmord/1750250.git/tree/0c6519399921a9b...

the syntax in .twig is much more preffered for front-end developers and this is the main point decided before
The good point is that all of the theme functions will be removed and moved into .twig files for making possible to change output at the design layer without php skills needed

Autosanitization, caching, compiling from .twig into .php that can easely be integtated with APC(or any other) caching system.

And of couse - no more @sql select@ code in tpl.php files
...

podarok’s picture

Issue summary: View changes

more reasons

chx’s picture

Issue summary: View changes

more

fabianx’s picture

Issue summary: View changes

Add remove of double engine code for modules once twig is default issue

moshe weitzman’s picture

Issue summary: View changes

Updated issue summary.

fabianx’s picture

I did some little xhprof-ing with cache enabled:

Twig is neither slower nor faster than phptemplate. It is roughly the same currently.

Also: The auto escape filter of the sandbox does not have that big of an impact because __construct and __toString, which is needed to wrap already escaped strings within Twig_Markup objects are really really fast and not double escaping makes most of the filter a trivial pass thru. This is for "normal" pages ...

Regarding the importance of security and auto escaping:

I consider myself well versed of the need to escape strings and check output carefully, but a simple usage of {{ title }} instead of {{ label }} in node.twig was enough to create the XSS. And the same is true for node.tpl.php in current core.

If I copy over a template from Drupal 7 for node.tpl.php I suddenly have a XSS hole. Yep, that is a bug. The label change seems to have been done without thinking over the consequences for security of older templates as $title was safe in node.tpl.php since 2007. The label change probably assumed that $variables['title'] is not set, but it is afterwards by the preprocess. (more about this in bug report issue: #1810710: Remove copying of node properties wholesale into theme variables)

If Drupal 8 had shipped with a node.tpl.php with $title insecure (and no auto escaping), I would probably have been the first victim ;-).

This example just shows how easy it is even for a non-themer to accidentally introduce a XSS, such I would support the statement that 100% (minus rounding errors) of custom templates are insecure and auto escape is a feature that can make drupal more secure.

The twig_engine branch of the sandbox automatically escapes the {{ title }} and the site is safe.

fabianx’s picture

I just finished a first integration of Twig's cache with drupal_php_storage(). It is quite simple to do so and works like a charm.

Here is the diff of the current work in the sandbox:

http://drupalcode.org/sandbox/pixelmord/1750250.git/commitdiff/1541e5810...

Further things for this should be discussed in: #1806546: Twig: Performance Testing / Enable twig cache by using the Drupal PHP Loading/Writing.

fabianx’s picture

Issue summary: View changes

Updated issue summary.

moshe weitzman’s picture

Issue summary: View changes

Updated issue summary.

podarok’s picture

Issue summary: View changes

Updated issue summary.

podarok’s picture

Issue summary: View changes

Updated issue summary.

podarok’s picture

Issue summary: View changes

Updated issue summary.

swentel’s picture

Re: benchmarks, on my machine twig is really slower. The CPU goes up and it's eating more memory. Now, of course, this could be because I just told bartik that its engine is now twig, might be that loading two engines causes overhead, but I just want to pass along the figures. Or am I missing something else ?

single node:
Before: Page execution time was 85.92 ms. Memory used at: devel_boot()=1.19 MB, devel_shutdown()=4.75 MB, PHP peak=5 MB.
After: Page execution time was 101.46 ms. Memory used at: devel_boot()=1.19 MB, devel_shutdown()=5.56 MB, PHP peak=6 MB.

Frontpage with 10 nodes:
Before: Page execution time was 115.5 ms. Memory used at: devel_boot()=1.18 MB, devel_shutdown()=5.05 MB, PHP peak=5.25 MB.
After: Page execution time was 131.58 ms. Memory used at: devel_boot()=1.18 MB, devel_shutdown()=5.89 MB, PHP peak=6.5 MB.

swentel’s picture

Issue summary: View changes

Expanded on downsides

Crell’s picture

swentel: Are those benchmarks with compiled Twig files, or recompiling on each request? Because recompiling Twig on each request is definitely going to be slower since the compiler is non-fast, but also not what anyone should be using day to day.

swentel’s picture

@Crell - by reading the issue summary above, it seems that caching in this patch is disabled, so this is probably recompiling every time.

From the summary

The patch currently disables compilation cache and disables autoescape

It seems ironic to me to commit a patch where auto escaping is disabled by default too, while saying that we need this because of security. Of course, this is the first patch, this call be done in follow ups, but it concerns me a little though.

swentel’s picture

Issue summary: View changes

Added Changes section

podarok’s picture

Issue summary: View changes

Updated issue summary.

moshe weitzman’s picture

Issue summary: View changes

Shortened autoescape downside

podarok’s picture

updated summary with Community Benefits

podarok’s picture

Issue summary: View changes

Updated issue summary.

effulgentsia’s picture

Given how relatively small the diff in #1806546: Twig: Performance Testing / Enable twig cache by using the Drupal PHP Loading/Writing is, why not merge that into this issue?

Auto escaping, I agree can be a follow up, because while that's one benefit of Twig, it's not the only one. Even if we didn't solve auto escaping integration with Drupal (though I'm confident we will), we can escape in preprocess functions or document that escaping is needed in the templates, both of which we already do in all released versions of Drupal.

effulgentsia’s picture

xpost

effulgentsia’s picture

Issue summary: View changes

Updated issue summary. PHP

podarok’s picture

Issue summary: View changes

Updated issue summary.

podarok’s picture

Issue summary: View changes

Updated issue summary.

podarok’s picture

Issue summary: View changes

Updated issue summary.

sun’s picture

html?text:(text|e)

I was equally confused by this when I reviewed this some days ago. The problem is that it looks like "must be some special new Twig command or something", because it does not adhere to our coding standards for ternaries; i.e., it lacks spaces:

html ? text : (text|e)

That way the confusion is reduced to (text|e), and I'm not familiar with Twig's template interpreter yet, but I hope you guys studied it, and so I guess the parenthesis are required since it would otherwise interpret it as (html ? text : text)|e.

Template variable filters generally are a good thing and simplify a themer's interaction with templates, so I don't see anything wrong with this per se.

However, I am slightly confused why we need the escape filter in this case? Wasn't one of the main ideas behind Twig that it is more secure, removes the burden from themers to think about security, and generally tries to keep the internals out of templates? In other words: Shouldn't the preprocessor escape 'text' when needed already, so the template only ever sees 'text' and just simply uses it?

sun’s picture

Issue summary: View changes

Updated issue summary.

fabianx’s picture

However, I am slightly confused why we need the escape filter in this case? Wasn't one of the main ideas behind Twig that it is more secure, removes the burden from themers to think about security, and generally tries to keep the internals out of templates? In other words: Shouldn't the preprocessor escape 'text' when needed already, so the template only ever sees 'text' and just simply uses it?

Yes, I agree that the sample template could be changed to include a preprocess function and I think we have something else in the sandbox, but this example was explicitly requested by effulgentsia as an example for how to escape.

With auto-escape on this can change to either:

html ? (text|raw) : text

Or in case of a proper preprocess function it could do the clean up in preprocess.

And mortendk would probably write (if he needed to change the raw links for some reason:

{% if html %}
<span class="html-link">{{ text|raw }}</span>
{% else %}
{{ text }}
{% endif %}

Note that even if the preprocess already applied a "safe" filter on the text the double raw still works.

fabianx’s picture

Issue summary: View changes

Reverted syntax downside. Last edit was not clear, and there is a corresponding item in Benefits.

fabianx’s picture

Issue summary: View changes

Added corrections to downsides

moshe weitzman’s picture

Issue summary: View changes

Updated issue summary.

dries’s picture

Status: Reviewed & tested by the community » Needs work

Glad to see progress being made on Twig. I still believe Twig could be a really compelling feature for Drupal 8.

My preference would be for this patch to be expanded before it gets committed. If we commit this patch as is, it would create at least one critical follow-up patch (e.g. missing compilation step) and presumably at least one major follow-up patch (eg. missing security step). We're constantly at our major and critical tasks thresholds, and I don't think Twig should be blocking official initiatives (eg. web services) right now. By being a bit more strict here, it helps us prioritize.

Another reason for this is that as the patch stands, it doesn't bring huge benefits to Drupal as the syntax simplifications alone don't seem important enough to warrant the extra code complexity. It would be best if we could demonstrate that Twig can be fast and secure as part of this patch.

Specifically, I think we need to include both the caching and the security components into this first patch. Would that be doable? I'm therefore setting this back to 'needs work'.

As mentioned at the top of my comment, I remain excited about Twig in core and encourage us to keep working on this patch. Great work!

webchick’s picture

Just to give my impression on the Twig syntax as someone who is not a front-end developer, but who taught Drupal Theming 101 off and on for about 5 years with Lullabot...

-<article id="node-<?php print $node->nid; ?>" class="<?php print $attributes['class']; ?>
+<article id="node-{{ node.nid }}" class="{{ attributes.class }}

Themers not having to understand the difference between PHP array and PHP object syntax is absolutely huge. We used to need to give a half hour presentation just on how to navigate through something like $node->field_image[0][LANGUAGE_NONE]->file['alt']['text'] or whatever. "Just use dots everywhere" is SO much easier to explain.

However, it does bring up a really important question, which is "how does someone know what all the 'dot-thingies' are under 'node'"? And also, for that matter, "How do I know what all the possible 'node' like thingies there are?" We can no longer toss a var_dump(get_defined_vars()); at the top of the node.twig file. Alex pointed me to http://twig.sensiolabs.org/doc/functions/dump.html ... do we have that extension? Cos having an equivalent to that is going to be absolutely critical to themers understanding what info they have to work with. The docs are great, as far as core goes, but that completely goes away the second you install even one contributed module (or, for that matter, disable a module from standard profile, like comment).

Also, on the debugging note, what happens when I put:

{{ bananas }} # invalid entry

or

{{ node.nid } # syntax error

...in my twig file? I'm going to assume that in the first case it doesn't output anything, and in the second case it leaves the text as-is since it didn't complete a parse, but not sure, and don't have time to test atm. Will try and do so this weekend. But it's important to understand how Twig performs error handling, cos it's the difference between having a completely frustrating experience and being able to quickly remedy any mistakes.

Back to syntax again...

 clearfix" {{- attributes }}>

That '-' sign was weird, and I thought it was a mistake at first. Alex explained that it's short-hand for "if the attributes variable is empty, don't print a space before it" Kind of a neat short-hand, but will take some explaining to people, I think.

Do we have some general Twig docs in core that we can point themers to to help them understand these kinds of nuances?

-  <?php print render($title_prefix); ?>
+  {{ title_prefix }}
...
-    <h2<?php print $title_attributes; ?>><a href="<?php print $node_url; ?>" rel="bookmark"><?php print $label; ?></a></h2>
+    <h2 {{- title_attributes }}>
+      <a href="{{ node_url }}" rel="bookmark">{{ label }}</a>
+    </h2>

Another huge improvement: Themers no longer have to understand when they print $foo and when they print render($foo). And yet...

+    {# We hide the comments and links now so that we can render them later. #}
+    {{ hide(content.comments) }}
+    {{ hide(content.links) }}
+    {{ content }}

...we still retain the benefits of the render API, in that we don't need to make a template file change if we add a new field to a heavily-themed content type template. Plus. Freaking. One.

However, one thing that I do find kind of confusing...

{{ foo }}

seems to always mean "print 'foo' out the screen.

{% foo %}

seems to always mean "do this PHP logic."

So why then is it:

 {{ hide(content.links) }}

and not

 {% hide(content.links) %}

?

webchick’s picture

Oh, one other minor nit:

-  <?php if (!$page): ?>
+  {% if page != true %}

is {% if !page %} not possible? That'd definitely be more intuitive, IMO.

webchick’s picture

Btw, here is the diff I uploaded for reviewing 8.x's node.tpl.php vs. this patch's node.twig, since the patch doesn't have the removal of node.tpl.php in it for some reason: https://gist.github.com/3880359

fabianx’s picture

is {% if !page %} not possible? That'd definitely be more intuitive, IMO.

Even better:

{% if not page %}
webchick’s picture

Oh, cool, that works too. :)

fabianx’s picture

However, one thing that I do find kind of confusing...

{{ foo }}
seems to always mean "print 'foo' out the screen.

{% foo %}

seems to always mean "do this PHP logic."

So why then is it:

 {{ hide(content.links) }}

and not

 {% hide(content.links) %}

?

We could add back easily the {% hide(xyz) syntax ( I removed it for sake of simplicity of the first patch. )

Twig itself is not totally clear on that.

The basic rule is:

{{ }} means PRINT THIS.
{% %} means statement.

So, from that understanding we should add hide / show tags to twig instead of hide / show functions as those do in twig not return something nor should print something.

Thoughts on that from anyone else? For the policy around our syntax additions?

effulgentsia’s picture

Alex explained that it's short-hand for "if the attributes variable is empty, don't print a space before it" Kind of a neat short-hand, but will take some explaining to people, I think.

Just to clarify, it's not conditional on the content of the variable. A "-" on the left side of the twig tag means remove whitespace preceeding that tag, so "foo" {{- bar }} is identical to "foo"{{ bar }}. The "-" allows a space to exist in the template for human readability but get removed from the output markup. This is explained in more detail in http://twig.sensiolabs.org/doc/templates.html#whitespace-control.

Do we have some general Twig docs in core that we can point themers to to help them understand these kinds of nuances?

Let's not replicate the Twig project's documentation. But finding a place in Drupal docs where we can link to that, and add any Drupal-specific isms (like that the value of "attributes" always contains its own leading space when not empty, so templates shouldn't output an additional leading space) seems like a good thing to do.

fabianx’s picture

@all that are interested in how twig is integrated with caches, please follow:

#1806546: Twig: Performance Testing / Enable twig cache by using the Drupal PHP Loading/Writing

I am using a git two-patches in one approach now using the basic patch here as base and rebasing on this base as needed.

I'll update this issue here once we have something that is RTBC over there.

It would be great if we could have the heavy discussion in the other issue: #1806546: Twig: Performance Testing / Enable twig cache by using the Drupal PHP Loading/Writing

Crell’s picture

Adding Drupal-specific Twig extensions should be fine, in general, but note that it increases the work to support Twig.js in contrib (since I know core won't), so let's not do it willy nilly.

As far as documentation goes, we should primarily point people back to the official docs: http://twig.sensiolabs.org/documentation

Our own extensions we should definitely document but for things that are not Drupal specific, single-source and push upstream. If we find those docs lacking, pull requests are welcome. :-)

webchick’s picture

Sorry, to be clear I wasn't advocating us re-writing http://twig.sensiolabs.org/documentation in Drupal. More making sure that links to that were prominently placed in places folks would be looking for it. The first one I can think of is themes/README.txt maybe.

chx’s picture

About missing variables, when in strict mode which we want you get an exception for missing variables too so that typos are caught.

podarok’s picture

jenlampton’s picture

However, it does bring up a really important question, which is "how does someone know what all the 'dot-thingies' are under 'node'"? And also, for that matter, "How do I know what all the possible 'node' like thingies there are?" We can no longer toss a var_dump(get_defined_vars()); at the top of the node.twig file. Alex pointed me to http://twig.sensiolabs.org/doc/functions/dump.html ... do we have that extension? Cos having an equivalent to that is going to be absolutely critical to themers understanding what info they have to work with. The docs are great, as far as core goes, but that completely goes away the second you install even one contributed module (or, for that matter, disable a module from standard profile, like comment).

This is exactly the same problem we have today in core. There are docs at the top of each template file that explain all the variables that are used in the template file (as well as handful of variables that essentially represent Drupal's wasted efforts, since they are rarely used) but the docs to not include information about all the printable variables at every level of a renderable, they do not remain accurate after contrib adds or removes variables, nor does get_defined_vars() explain all the thingies that are actually printable below node. yes, you can get at *everything* in a node (or a renderable) but the results always contain an overwhelming amount of unnecessary information for a front-end developer, most of which shouldn't even be printed in a template - and I think that's certainly more confusing than what we will be able to get with a Twig inspector.

This patch does not include an inspector, but it is part of our plan. (see #1783134: [META] Make it possible to inspect twig variables and get information about objects and render arrays)

@Fabianx I think we should put back the {% hide(xyz) %} syntax, I agree with @webchick.

When re-rolling this patch I actually tried to change the hide statements to use the {% %] syntax, since I thought it was confusing that they were using the {{ }} syntax. But when it didn't work I abandoned the effort and put things back :)

fabianx’s picture

@webchick @chx:

We cannot use twig strict mode directly as it removes some really nice performance optimizations, but we can add a semi-strict mode that gives a warning when a var is not defined (and drupal error messages are on). I personally find throwing of an Exception too much ...

I however would like to do that as a follow up.

Okay, let me summarize the TODOs for _this_.

Current TODOs

New Followups:

  • Enable semi strict mode that warns on usage of undefined var to catch typos, etc.

Anything I am missing here?

moshe weitzman’s picture

Anything I am missing here?

Demonstrate benefits and risks with autoescape enabled.

fabianx’s picture

Demonstrate benefits and risks with autoescape enabled.

That was what I meant with "Discuss safe mode and show performance characteristics (up next after the above is done)", but yes, thanks! :-)

fabianx’s picture

* On popular request merged #1806546: Twig: Performance Testing / Enable twig cache by using the Drupal PHP Loading/Writing into main patch.

Note that auto reload of templates only works if either

a) debug is enabled (from next patch on the case for enabling "dump")
b) options['auto_reload'] is set in twig_environment (default "null", defaults to "debug" setting).

In case that auto_reload is not enabled a user will need to clear the cache to have TWIG compile new templates.

* Added debug functions so that:
** {{ dump(label) }} works or any other variable
** {{ dump(_context) }} works - useful for printing all available variables

A general production / development mode switch would be really helpful here as well ...

New patch including re-adding of {% hide / show coming shortly.

fabianx’s picture

As of now Twig is neither faster nor slower than Core, see #1806546: Twig: Performance Testing / Enable twig cache by using the Drupal PHP Loading/Writing for details.

And here is the new patch, which is optimized for very fast execution of Twig Templates.

In fact, once "Declare render variables in PHP via hook_...() and remove superfluous twig_render() calls" is in the templates look _almost_ the same like the normal node.tpl.php in compiled form :-D. (Just accessing of array elements is done via getAttribute, but that was the idea of the whole exercise ... )

But that can be left as followup as already now Twig is as fast as core for rendering 100 nodes on /node.

Still @todo:

  • Remove superfluous test_theme functions found by Moshe
  • Remove typo found by Moshe
  • Fix docs, docs, docs
  • Open up follow-up for: Declare render variables in PHP and remove superfluous twig_render() calls
  • Investigate webchick's questions in regards to syntax errors / compile time errors / runtime errors / undefined vars.
  • Show autoescape mode benefits and risks.
fabianx’s picture

Status: Needs work » Needs review

For testbot

chx’s picture

Fabianx told me on IRC that ab -n 100 was 583+-12.8 for Twig and 591+-10.1 for core. That's indeed about the same.

cosmicdreams’s picture

+++ b/core/lib/Drupal/Core/Template/TwigReference.phpundefined
@@ -0,0 +1,131 @@
+ * @see twig_convert_to_reference

+++ b/core/lib/Drupal/Core/Template/TwigReferenceFunctions.phpundefined
@@ -0,0 +1,90 @@
+ * @see twig_convert_to_reference
+ * @see TwigFactory
...
+   * @see twig_convert_to_reference()

+++ b/core/themes/engines/twig/twig.engineundefined
@@ -0,0 +1,133 @@
+ * @see twig_convert_to_reference

twig_convert_to_reference no longer exists

fabianx’s picture

Have most todos cleaned up, new patch coming soon :).

fabianx’s picture

Issue summary: View changes

Updated issue summary.

fabianx’s picture

Done @todo:

  • Remove superfluous test_theme functions found by Moshe
  • Remove typo found by Moshe
  • Open up follow-up for: Declare render variables in PHP and remove superfluous twig_render() calls
  • Investigate webchick's questions in regards to syntax errors / compile time errors / runtime errors / undefined vars.

Follow-up here: #1815250: Type twig variables in PHP and remove superfluous render_var() and getAttribute() calls from the generated code

To answer webchicks questions:

Syntax errors like {% hide content.links) %}: Give a hard exception with lineno and filename in regards to the .twig file
Undefined vars like: {{ non_existing_var }} give a warning right now if being printed (further warnings can be done as part of follow-up of #1806538: Remove @todo about enabling strict_variables in Twig)

Again I was able to get the lineno and filename and it's also very easy:

  $msg = new \Twig_Error(t('@item could not be found in _context.', array('@item' => $item)));
  drupal_set_message($msg->getMessage(), 'warning');

Twig_Error takes care of finding the template in the call stack and finding the right line number in the original .twig file.

I attached an example screenshot of how this looks.

twig_error_message.png

Remaining @todo:

  • Fix docs, docs, docs
  • Show autoescape mode benefits and risks.

We are continuing in #1712444: Twig: Activate autoescape mode for now to work on the integration of auto escape on top of current patch like we did with Performance / Cache.

New patch attached.

fabianx’s picture

Issue summary: View changes

Added new followup

podarok’s picture

Issue tags: +Needs documentation

#133 nice error handling here
all other looks good for me
adding tag

jenlampton’s picture

NM this comment. Found the twig templates moved to core modules :)

jenlampton’s picture

Eh, here's a first attempt at adding some docs. I'm not entirely sure how to reference files within twig, can I do it like this?

/**
 * A class that defines the Twig token parser for Drupal.
 *
 * The token parser converts a token stream created from template source
 * code into an Abstract Syntax Tree (AST).  The AST will later be compiled
 * into PHP code usable for runtime execution of the template.
 *
 * @see core\vendor\twig\twig\lib\Twig\TokenParser.php
 */
class TwigFunctionTokenParser extends \Twig_TokenParser {

There's also a few bits I didn't really understand, so there are a few @todos left. Hopefully this is a step in the right direction.

Anonymous’s picture

-1 Twig

Instead of adding a new template engine, should concentrate on making the existing theming easier.
Not add a new language to learn.

It doesn't the solve the problem in my opinion. Namely making design easier in Drupal.

Being able to add some simple functions such as this:
http://drupal.org/node/1813084

Would make my life so much easier than this Twig stuff.
Should I even spent time writing such a function if we are switching to Twig?
Will I have to redesign stuff again? Please can we have a theme layer which is
for the most part independent of core changes. Designing and testing stuff takes a lot of time.
It is one big negative of a lot of CMS's that keep changing the code to do the layout. Every time you do we have to sand, spackle, and repaint the whole house, figuratively speaking. Stop it, please. I'm really not waiting for this solution.

Most designers I know, speak some kind of PHP, but Drupal is just really complicated that is the problem.
Either that or give us some simple functions such as WordPress please.

If this is going to be implemented please allow for a way to keep using PHP as well or alternate ways of creating themes. For example a module in which to easily make a query from data, add the necessary markup (using templates), and assign this to locations. It would be so much easier than this stuff.
Can we please explore that direction as well? It would fit in with the new admin backend.

For example something like an extended version of these two modules, but then also be able to add elements like div, ul, a, etc:

https://drupal.org/project/block_aria_landmark_roles
http://drupal.org/project/block_class

I believe Concrete5 also works somewhat similar to this.

Twig code way of working looks complicated and convoluted.

Is this solution really providing the answer to the problem? No, it doesn't.

What are the performance figures for adding another layer?

We really don't need another theme layer at this point.

Will we still be able to design without Twig? Who uses Twig? Most of us work with different products, none of which I know use Twig, won't this lead to vendor lock in, and/ or raising the threshold for designers to build designs for Drupal.

There is a risk that Drupal is painting itself out of the market here. Please stay with generally accepted languages and formats, and make those easier if you want more designers. Don't change the label, but not solve the underlying problem.

Thank you for your consideration.

Crell’s picture

DesignDolphin: Not to put too fine a point on it, but that ship has sailed. Twig has been in discussion since March and has broader support from the front-end community than any other front-end change in the last 7 years. All of your questions have already been answered elsewhere. Please do not drag this issue off topic.

Edit: check #1499460: [meta] New theme system

Anonymous’s picture

Crell, not to put too fine a point on this either.

As far as I see I'm following feature request guidelines.
I'm very much on topic.
Only recently heard about Twig, the cue is set to review.
I'm reviewing.

So, please if you want to respond, please follow guidelines.

I know this Twig thing is dear to some. That is fine. I respect other people's opinion. I have stated my case.

I will leave the floor to others unless I have further arguments for the issue. My request for other (additional) options stand. I would very much like this to be taken into consideration.

You cannot expect everybody in the community to react right away, sometimes it takes time to trickle down and hear about stuff. Drupal has an huge ecosystem.

Trying to save everybody some time.

podarok’s picture

#136 for review such longterm patches good to see interdiff
please...

podarok’s picture

@DesignDolpin
Please
this topic is for patch and integration - please - make Your own follow up and discuss there.

chx’s picture

I have reopened the meta issue for further discussion.

webchick’s picture

Right, I agree the "review" in this issue should be focused on the patch at hand.

For review of the meta concept of making the theme system better and where Twig fits into that, probably follow-up at #1499460: [meta] New theme system

EDIT: Link copy/paste fail. :P

Anonymous’s picture

Title: Twig integration into core » Integrate Twig into core

As per cue request moved my comment here, as I do not want to sidetrack an issue:
http://drupal.org/node/1441320

@Crell,
Do want to make a point of order that while doing additional background research I read quite a lot of criticism and alternatives in that issue about Twig, and that it may not be as widely accepted as how your text could be interpreted. Am I to understand that this is a misunderstanding or misinterpretation on my part? I hope designers will have continued input as part of this community.

Another point of order,
This issue is labeled a feature request, something which needs broad community support. If this is a patch review then this should be labeled as a task or something else. It is confusing this way. I do not feel it proper with getting corrected for doing my utmost best to follow guidelines.

Edit:

@Webchick:
Yeah, Noticed that. Thanx for the edit, got me pointed in the right way. :-)

Anonymous’s picture

Title: Integrate Twig into core » Patch review Twig integration into core

To avoid confusion in the future on this issue changed the title to something which (hopefully) defines the issue a bit better.

Please mark from feature request to task if there has been a definite decision made to integrate this into core?

cweagans’s picture

Title: Patch review Twig integration into core » Integrate Twig into core

Pretty sure that's the direction we're going. As Crell pointed out, "Twig has been in discussion since March and has broader support from the front-end community than any other front-end change in the last 7 years."

webchick’s picture

Title: Integrate Twig into core » Twig integration into core

DesignDolphin: There's nothing inherent about feature request vs. task that means review the concept vs. review the patch. I'm not sure where you're getting that from. Normally, "concept" type issues are prefixed with "meta" (like #1499460: [meta] New theme system) but everything else is about reviewing code.

Now, let's please move any further off-topic questions/points to off-topic issues. :) We're already at 145+ comments here, and we're not nearly done with reviewing the code yet.

Anonymous’s picture

Title: Integrate Twig into core » Twig integration into core

@Webchick, last post from me on this issue, but you don't have a contact form, and I would like to clear things up, please see:
http://drupal.org/node/317 -> http://drupal.org/node/945492
Also I don't see that meta thing explained anywhere, but am willing to sit down with a few people to hash out the documentation.

Edit:

Missed a link as well.
See also: http://drupal.org/node/10262

Back on topic.

podarok’s picture

#136 looks good for me
reviewed document changes

chx’s picture

Title: Twig integration into core » Integrate Twig into core
StatusFileSize
new50.08 KB

Documented 'em.

podarok’s picture

Issue tags: -Needs documentation

#150 good to see all @todo for documentation filled with a proper info
looks good
removing tag

catch’s picture

For anyone not following, #1818266: [meta] A secure theme system (with twig) is discussing the auto-escape behaviour (that Dries wanted included here before commit as well as performance).

webchick’s picture

Assigned: dries » Unassigned

Doing some housekeeping. AFAIK this doesn't need to be assigned to Dries right now. Feel free to do so again once it hits RTBC!

fabianx’s picture

Re-rolled #150 for testing against core with DIC / removed one conflict.

fabianx’s picture

Issue summary: View changes

Update issue summary

fabianx’s picture

Issue summary: View changes

Added link to twig coding standards

fabianx’s picture

Status: Reviewed & tested by the community » Needs review
StatusFileSize
new5.06 KB
new51.04 KB

It's show time!

This patch merges all feedback and lessons learned from both #1712444: Twig: Activate autoescape mode and v3 from #1818266: [meta] A secure theme system (with twig).

The auto-escape for twig in this patch is fully enabled. To prevent double-escaping all strings in $variables are marked as secure by twig engine, which is a valid assumption given that we continue to support non autoescaped phptemplate files for now.

Very detailed benchmark results of the best approach when #1815250: Type twig variables in PHP and remove superfluous render_var() and getAttribute() calls from the generated code is in, can be found here: http://drupal.org/node/1818266#comment-6648238.

Compiled template performance

Before we get to the benchmarks, another concern was how compiled twig files look like. Here are the links:

Compiled Template: https://gist.github.com/3950182
Auto-escaped Template: https://gist.github.com/3950401

After #1815250: Type twig variables in PHP and remove superfluous render_var() and getAttribute() calls from the generated code is in:

Optimized compiled TwigTemplate: https://gist.github.com/3950254
Auto Escaped Optimized Twig Compiled Template: https://gist.github.com/3950411

The ternary (?:) operator is not used by Twig, because of a PHP 5.3 bug that makes it slower than if / else when encountering big arrays.

Note: No one ever will see this compiled code and for production mode it could be optimized even more (temporary variables would need to be checked just once).

Benchmarks

The following benchmarks are for the current status quo for 100 nodes on /node. Benchmarks are with APC on and APC ClassLoader enabled as per msonnabaums suggestion:

How to read these benchmarks;

These benchmarks show the Wall-Time (wt), used CPU time (cpu) and the Memory Usage (mu) plus the Peak Memory Usage (pmu). They are run 1000 times and the minimum is taken. For each line there are four numbers: Run 1 time, Run 2 time, Diff and Diff %. So if it shows core..twig, run 1 = core, run 2 = twig and the difference is twig - core.

The report is created via XHProf tools. They have been re-run several times to make sure the found minimum is accurate. The server is completely dedicated and has no other load during the benchmarks. URLs to XHProf raw data can be provided on a case by case basis. Just contact me for that, please.

100 nodes - /node

=== core..twig compared (508ae1d8981fd..508ae4b4be51c):

wt  : 773,189|805,205|32,016|4.1%
cpu : 772,048|800,050|28,002|3.6%
mu  : 15,065,904|15,765,824|699,920|4.6%
pmu : 16,869,344|17,588,080|718,736|4.3%


=== twig..twig-autoescape compared (508ae2673e94f..508ae5153b2e7):

wt  : 804,246|842,969|38,723|4.8%
cpu : 800,050|840,052|40,002|5.0%
mu  : 15,766,688|15,814,568|47,880|0.3%
pmu : 17,589,336|17,651,920|62,584|0.4%


=== core..twig-autoescape compared (508ae1d8981fd..508ae5153b2e7):

wt  : 773,189|842,969|69,780|9.0%
cpu : 772,048|840,052|68,004|8.8%
mu  : 15,065,904|15,814,568|748,664|5.0%
pmu : 16,869,344|17,651,920|782,576|4.6%

EDIT: Above with 1000 runs now - was not different.

Note that #1815250: Type twig variables in PHP and remove superfluous render_var() and getAttribute() calls from the generated code will make this faster as shown in http://drupal.org/node/1818266#comment-6648238.

Patch attached and needs review!

Status: Needs review » Needs work
Issue tags: -Increases learning curve, -Twig, -Drupal7AndTheArraysOfDoom

The last submitted patch, core-Integrate-twig-into-core-1696786-155.diff, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
Issue tags: +Increases learning curve, +Twig, +Drupal7AndTheArraysOfDoom
jenlampton’s picture

Status: Needs review » Reviewed & tested by the community

Marking as RTBC to see if I can prompt another comment from Moshe :)

fabianx’s picture

Status: Needs review » Reviewed & tested by the community
StatusFileSize
new53.24 KB

To elaborate on #155:

The patch as attached from #155 currently makes Drupal around 9% slower when 100 nodes are rendered on /node.

xhprof-diff-core-vs-twig-autoescape-1696786-155.png

This can be improved to 6% with #1815250: Type twig variables in PHP and remove superfluous render_var() and getAttribute() calls from the generated code and possibly further with a production / development switch.

catch’s picture

Assigned: Unassigned » dries

Discussed this a bit with Fabian. This is an obvious performance degradation as it stands at the moment, however I'm reasonably confident we can claw it back. Also I really, really appreciate the amount of work that's gone into both performance analysis and actually optimizing based on that for this patch, this makes me confident that this work will continue to be done after commit.

For comparison, #1599108: Allow modules to register services and subscriber services (events) introduced around a 5% performance degradation, went in before the degradation or potential fixes had been properly discussed, and the issue that was supposed to fix it (#1706064: Compile the Injection Container to disk) actually didn't, instead it's being tracked in #1716048: Do not boot bundle classes on every request. Registering bundles ought to be close to free once we're doing it in a sane way, so by comparison 8% for a new rendering systems doesn't feel so bad. It's also a lot less than the 30-40% overhead we introduced in Drupal 7 when converting the node body to a field - for 30 nodes, not 100.

Since Twig was originally put forward, I've been assuming we can remove a lot of the work we currently do in preprocess preparing various versions of different variables, and rely on the Twig template compilation to handle that stuff instead. Currently all kinds of variables get prepared/sanitized in preprocess that might never even be printed in a template. I've opened #1825952: Turn on twig autoescape by default to discuss this some more. Most Drupal 7 sites spend a lot of time on preprocess, how much of that we can save is going to be hard to quantify until it's actually done though.

I've not reviewed the latest patch yet, however I'll try to do it over the next day or so (so no comment on it's RTBC-ness in general). Assigning to Dries since I think he should also have a look a this sooner rather than later.

podarok’s picture

Assigned: dries » Unassigned
Issue tags: +VDC

Good to see here plan for benefits that can speed-up twig after roadmap done

As for me these points are described at frontend of our sandbox

  1. Update core to use two different theme engines, & fallback to PHPTemplate: #1697854: Allow modules to provide both .twig and .tpl.php templates temporarily until twig is the default engine
  2. Add simple example of Twig to core (patch: #1696786: Integrate Twig into core)
  3. Convert the Bartik theme to Twig (patch: [])
  4. Create a patch against each module in core to switch it from PHPTemplate to Twig
    • aggregator (patch: )
    • block (patch: )
    • ...
    • xmlrpc

  5. Switch core to use Twig as the default theme engine - regression

  6. Cleanup of previous preprocess functions (removal of extra vars) - speedup
  7. Replace all renderables in core with Twig data objects - need testing
  8. Remove render() from all twig templates - need testing - possible speedup
  9. (Maybe) Remove render() from core - speedup

anyway we should commit RTBC patch with a small regression into core for Get This Done #1788918: [GTD] [META] Prepare for Twig main engine core inclusion until December 1 and for possibility to create all follow-ups

PS. looks like it is the point to connect to VDC initiative due to dynamic templating engine in views theme layer and possibility make it more deeper integration with Twig, so taging

podarok’s picture

Issue summary: View changes

Added merged issue

podarok’s picture

Issue summary: View changes

Updated issue summary. Add performance related issues

podarok’s picture

Issue summary: View changes

Updated issue summary. Typo fixed

amateescu’s picture

Assigned: Unassigned » dries

I think the assignee change was unintentional.

David_Rothstein’s picture

Title: Integrate Twig into core » Integrate Twig into core: Implementation issue
Status: Reviewed & tested by the community » Needs review

I think this really needs more reviews. I'm going to try to do one later today myself (assuming the power doesn't go out on the U.S. east coast...)

Also, I agree with @DesignDolphin: 99% of core issues are about reviewing the concept and code together, not just the code. For bigger issues (like this one) that can be different, but there's no way most people will ever know that. Changing the issue title a bit should help clarify things.

David_Rothstein’s picture

Issue summary: View changes

Updated issue summary.

David_Rothstein’s picture

OK, here's a review. And for starters (before I forget), I'm totally impressed with all the work that went into this patch.

node.twig

I started by jumping into node.twig directly and playing around (figuring that's how 99% of people will encounter this). A couple comments:

  1. +<article id="node-{{ node.nid }}" class="{{ attributes.class }} clearfix" {{- attributes }}>
    

    The node.nid and attributes.class syntax was really, really nice to see :)

    In the second part, I had no idea what the "-" was in front of the attributes. I had a few guesses which turned out to be (laughably) incorrect, then eventually read the Twig documentation and also saw comments earlier in this issue which pointed out that it strips whitespace on the left.

    This syntax choice by the Twig project is... uh, interesting :)

    I think it adds significantly to the complexity of the templates because it shows up in random places and therefore gives the impression that it's a lot more important than it actually is. Do we really need to use it in Drupal? (Where is the whitespace being added anyway that it needs to be stripped here - is Twig adding it itself?)

  2. ** {{ dump(label) }} works or any other variable
    ** {{ dump(_context) }} works - useful for printing all available variables

    I tried playing with these, and the first worked nicely, but the second dumped garbage to the screen. I think what might be going on here is that the output of dump() is itself being auto-escaped?... oops :)

  3. +  {% if page != true %}
    

    Somewhere above it was suggested this could be {% if not page %} instead? I agree it would be more readable. (Minor point, though.)

  4. Overall, node.tpl.php felt like too simple of an example to get a real sense of how the Twig conversion would work.

    I'd be curious to see something like page.tpl.php also (which has t() calls, theme() calls, and more complex logic embedded inside of it, that kind of thing)... it's closer to a realistic example of what themers actually deal with on moderately complex websites. I suspect there's already code for page.twig out there somewhere, but it's not in this patch :)

  5. Along the same lines I was very curious how Twig would deal with something like #953034-2: [meta] Themes improperly check renderable arrays when determining visibility (because as far as I know that's the only way to fix that issue and others like it without introducing unwanted side effects).

    After some playing around, I got the impression that this would work:

    {% set sidebar_first = render(page.sidebar_first) %}
    {% if sidebar_first %}
    

    I guess that's not too bad, but it doesn't look like an improvement over standard PHP either. I was a bit thrown off that I had to call render() directly and that using {{page.sidebar_first}} in that statement apparently didn't work.

Security

I'm concerned that there hasn't been a full discussion of the consequences of auto-escaping. I'm personally not yet convinced that the way this patch is using it actually improves security.

Sure, if you do something like {{ node.body.und.0.value }} it will save you from security issues, but if you do {{ node.body.und.0.safe_value }} (which is a better variable to use anyway), it will incorrectly escape your HTML. Your site will have bugs until you figure out that you need to do {{ node.body.und.0.safe_value|raw }} instead. Which is fine until you apply this "lesson learned" again and start doing {{ node.body.und.0.value|raw }} too... Oops, no more security.

In short, this doesn't save template authors from having to think about text filtering. They're still responsible for making security decisions, only we're now making things very confusing because everywhere else in Drupal you do it one way (explicitly escape text), and here in the template files you do it the exact opposite way.

Second, it's worth pointing out that {{ title }} is still an XSS hole even with this patch (#100 mentioned that as something which auto-escaping might fix, but I think the implementation changed since then). As mentioned in that comment, this is an independent bug (#1810710: Remove copying of node properties wholesale into theme variables), but the point is that stupid security mistakes in the preprocess layer still leak into templates even with this patch. And keep in mind that by removing PHP from templates, we're going to force theme developers to work more in the preprocess layer whenever they need to get complex things done, so there will be plenty of opportunities for them to introduce security mistakes there...

Options #1 from #1818266: [meta] A secure theme system (with twig) avoids these problems and could provide actual security in template files (notably, it also has nothing to do with Twig directly, although it does work much more nicely with something like Twig than it would otherwise). It is also harder and might not be possible to do correctly. But I'm worried that the current patch implements solution #3 from that issue as a means of meeting the stated "security" goal and getting this patch committed, but it might actually be better simply to do nothing for now. (Especially because this also seems to be responsible for a lot of the performance hit, plus perhaps the debug() issue above, etc.)

We don't necessarily need "better security" in order to use Twig anyway; there are other benefits too (but see more on that below).

Big picture

Overall, I see a lot of good things about Twig and this patch but I share some of the concerns @moshe weitzman raised above. I think one way to phrase that (in the context of this issue) is that the issue summary provides a list of proposed benefits but the current implementation doesn't really deliver on all of them (in particular, security and performance), nor is it obvious that it ever will. So we're potentially adding a lot of code complexity for a smaller number of benefits than originally assumed. (And notably, a smaller number than assumed by many people in #1499460: [meta] New theme system as well; although the validity of those assumptions was questioned early on in that issue, my impression is that most people seemed to conclude "Twig = security" when they evaluated things and made their decision to support it, even though it was never actually that simple.)

Again, Twig may be worth it regardless (and I'm 100% in favor myself provided that the more complete solution from #1818266: [meta] A secure theme system (with twig) can get in) but I think the tradeoffs right now are really a lot more subtle than they've been given credit for.

Other issues

I didn't really review the whole patch, but a few things stood out. In no particular order (and some are relatively minor):

  1. +  if (!$element && $element !== 0) {
    +    return null;
    +  }
    

    NULL, not null. (Actually lots of these all around, including FALSE vs. false.)

  2. -class Attribute implements \ArrayAccess, \IteratorAggregate {
    +class Attribute extends \Twig_Markup implements \ArrayAccess, \IteratorAggregate {
    

    Hm, this seems to embed Twig very deeply into Drupal... what about other theme engines? Maybe in practice it doesn't matter much.

  3. +{#
    +  @todo: might be a good idea to remove the id attribute, because if that gets
    +  rendered twice on a page this is invalid CSS
    +  for example: two lists in different view modes.
    +#}
    

    I don't see why we need this comment here; that's a bug, but it's been around a long, long time (see #405270: Strange duplicate IDs during node preview and when a node is displayed multiple times on a page) so it has nothing to do with this patch. Having it there is kind of a distraction given how many people we expect to use this file.

  4. +{# @todo Revisit once autoescape is enabled: http://drupal.org/node/1712444 #}
    +<time class="{{ attributes.class }}" {{ attributes }}>{{ html ? text : (text|escape) }}</time>
    

    So presumably it should have been revisited? :) That code looks like it's going to be incorrect otherwise.

catch’s picture

it might actually be better simply to do nothing for now

I still haven't reviewed the patch, but based on discussion with FabianX about this yesterday I'd mostly come to this conclusion as well. This was part of the intention of opening #1825952: Turn on twig autoescape by default (we might not want to do what that issue suggests in the end, but we'll not want to go with the approach currently in the patch whatever happens, and I definitely want to cut down on preprocess bloat - for me that's the main benefit of using Twig at all).

fabianx’s picture

@David Rothstein:

Thanks for the very extensive review.

Let me answer the questions / remarks

node.twig

1. {{ attributes }} vs. {{- attributes }}

This was mainly decided by themers, etc. as part of the twig initiative itself. The Attribute class adds the whitespace to allow to do:

<h2<? print $attributes 

>

?>

in PHP or

<h2{{ attributes }}>

in Twig, but Twig themers did not like it, so chose to use:

<h2 {{- attributes }}>

instead. I think this could be changed easily in the Attribute class and I don't know the background / reasoning for this choice.

I do agree though that twig work arounds a syntax problem here that could be solved directly. But I don't think you want to do this as part of this patch ...

2. Dumping variables

I tried playing with these, and the first worked nicely, but the second dumped garbage to the screen. I think what might be going on here is that the output of dump() is itself being auto-escaped?... oops :)

Uhm, the second worked for me. Adding a <pre>{{ dump(_context) }}</pre> makes this more readable.

And yes, there are Twig_Markup objects for strings in there, but it is not worse than reading a render array. I can also use the $GLOBALS variant here, but I chose not to do so, because I then need to justify the usage of $GLOBALS as part of this original patch, which gets into other discussions and need to create my own twig_escape_filter function to not decrease performance even more. (Each function call / creation of Twig_Markup object costs ...)

That said if you only want all variable names, you can use:

<pre>{{ dump(_context|keys) }}</pre>

3. Syntax

Done: I fixed it with easier syntax.

4. page.tpl.php

@todo: I will provide a cleaned up and QA'ed page.twig as GIST. What is currently in the sandbox is still WIP.

Themes checking visibility

To solve this I need the following issues in:

* #1815250: Type twig variables in PHP and remove superfluous render_var() and getAttribute() calls from the generated code
* Create a render in-memory cache for render arrays, so that they are not rendered twice

Then I can compile time optimize that render arrays are always run through twig_render() - even if they are not printed, which can then reduce the code to:

{% if page.sidebar_first %}
{{ page.sidebar_first }}
{% endif %}

This would be compiled roughly to:

if (twig_render($this->getAttribute($this->getContextReference($context, 'page'), 'sidebar_first'))) {
  print twig_autoescape(twig_render($this->getAttribute($this->getContextReference($context, 'page'), 'sidebar_first')));
}

so having render store the rendered output somewhere is a must for performance reasons.

I might be able to optimize this also to:

$_page_sidebar_first = render($this->getAttribute($this->getContextReference($context, 'page'), 'sidebar_first'));
if ($_page_sidebar_first) {
  print $_page_sidebar_first;
}

But I am still pondering the consequences and while for the former, I have the code ready, I have not for the latter though as we compile it and have all information, it is _possible_.

I however would have loved to keep this patch rather simple ... (uh, yes ...) and do some things as followups.

Security

Well, I did not want to open the can of worms for the $GLOBALS discussion, so #1 from "A secure theme system" was not an option for something _simple_.

Ideally we want template authors to only write:

node.body or node.field_my_nice_field and not having to deal with the other things at all. I can think both of a decorator (performance problem) or a getter called token, which works similar to how tokens work.

node.token.body (would call node->token('body')), but that is a separate discussion.

I can for sure leave auto escape out. That is one git revert away, but Dries wanted to see it in so ...

Big Picture

So we move "Performance" and "Security" out of Benefits to "Potential Benefits"?

However I agree: Yes, benefits could be stated more realistically based on the current situation.

Other issues

* NULL and FALSE fixed
*

Hm, this seems to embed Twig very deeply into Drupal... what about other theme engines? Maybe in practice it doesn't matter much.

Either Drupal commits to Twig or not. But if there is hesitation, I am happy to do:

  if ($arg instanceof Attribute || $arg instanceof AttributeBase) }
    return new Twig_Markup((string)$arg);
  }

Thats three lines of code added to twig_render, but I did not do it for performance reasons ...

* @todo removed from node.twig
* datetime.twig:

That code is a little strange now (and yes I agree that here the variables mark safe leads to a strange situation, but I think this is an edge case as escaping within templates should not be that often:

<time class="{{ attributes.class }}" {{ attributes }}>{{ html ? text|raw : text|escape }}</time>

is a universal version that works always with all autoescape and non-autoescape enabled.

I see that this is a little problem right now.

Actionable Items?

Besides the cleanup, which I did now (and is attached, also re-rolled) and the @todo to provide a cleaned up page.twig, is there anything actionable I can currently do to get this in?

Or is it just sit and wait?

fabianx’s picture

It helps to actually add the patch ;-)

Status: Needs review » Needs work

The last submitted patch, core-Integrate-twig-into-core-1696786-167.diff, failed testing.

fabianx’s picture

This one test really does not like the addition of more classes, but this cleanup would apply both to node.tpl.php and node.twig, so leaving as straight conversion from node.tpl.php for now.

KrisBulman’s picture

Status: Needs work » Needs review

testbot go!

podarok’s picture

Status: Needs review » Reviewed & tested by the community

#16 looks good
and bot is happy

webchick’s picture

Status: Reviewed & tested by the community » Needs review

Uh, let's at least let David respond to fabianx's review. :)

moshe weitzman’s picture

I need to review the autoescape stuff more closely. I will say that without security improvements, this patch does not justify its code complexity and code weight in my mind. Slightly cleaner templates and better support for an edge case (i.e. Template author must be forcibly prevented from using PHP, or at least forced until he moves to preprocess layer) are nice, but not enough to change add a complex (code-wise) theme engine IMO. I'm not sure this improves 'Increases learning curve' tag either. Again, security improvements might tip the scales for me.

I also want to take a pass at the Issue Summary in order to accurately reflect where we at. And thanks to everyone who continues to plug away at this.

chx’s picture

Moshe, people hate current theming and love Twig. Code complexity, meh.

chx’s picture

also: will say that without security improvements, this patch does not justify its code complexity and code weight in my mind.

what? autoescape is on. strings in variables from preprocess are deemed safe but we already made that presumption but it's codified now.

catch’s picture

Status: Needs review » Needs work

Reading through the patch, I don't have major complaints. Supporting show()/hide() etc. has obviously been tricky, but a lot of work has gone into supporting it and the eventual template syntax looks great.

A few nitpicks, we're also discussing the patch in irc at the moment.

+++ b/core/includes/theme.incundefined
@@ -468,6 +474,28 @@ function _theme_process_registry(&$cache, $name, $type, $theme, $path) {
+            if (file_exists($template_file)) {
+              $result[$hook]['template_file'] = $template_file;
+              $result[$hook]['engine'] = $engine;
+              break;

This file_exists() could also be adding some overhead given most of those calls will miss - did that come up in performance testing at all? Either way it'll be gone by the time we get to release.

+++ b/core/lib/Drupal/Core/Template/Attribute.phpundefined
@@ -30,7 +30,7 @@
-class Attribute implements \ArrayAccess, \IteratorAggregate {
+class Attribute extends \Twig_Markup implements \ArrayAccess, \IteratorAggregate {

I can live with the coupling here given how performance-critical this is. We added the Attribute class for Twig integration in the first place.

+++ b/core/lib/Drupal/Core/Template/TwigEnvironment.phpundefined
@@ -0,0 +1,71 @@
+class TwigEnvironment extends \Twig_Environment {
+  protected $cacheO = NULL;

Why $cache0 and not $cache?

+++ b/core/lib/Drupal/Core/Template/TwigEnvironment.phpundefined
@@ -0,0 +1,71 @@
+  /**
+   * Implements Twig_Environment::loadTemplate().
+   *
+   * We need to overwrite this function to integrate with drupal_php_storage().
+   *
+   * This is a straight copy from loadTemplate() changed to use
+   * drupal_php_storage().
+   */
+  public function loadTemplate($name, $index = NULL) {
+    $cls = $this->getTemplateClass($name, $index);
+
+    if (isset($this->loadedTemplates[$cls])) {
+      return $this->loadedTemplates[$cls];
+    }
+
+    if (!class_exists($cls, FALSE)) {
+      if (FALSE === $cache = $this->getCacheFilename($name)) {
+        eval('?' . '>' . $this->compileSource($this->loader->getSource($name), $name));
+      } else {

Even though it's a straight copy, I'd be happy if we could Drupalize the code style a bit more.

+++ b/core/lib/Drupal/Core/Template/TwigReferenceFunctions.phpundefined
@@ -0,0 +1,94 @@
+ * A class used as a helper to unwrap TwigReference objects and pass a
+ * reference of the original variable to the called function.

This isn't one line. Also we know it's a class ;)

+++ b/core/lib/Drupal/Core/Template/TwigTemplate.phpundefined
@@ -0,0 +1,81 @@
+      // We don't want to throw an exception, but issue a warning instead.
+      // This is the easiest way to do so.
+      // @todo Decide based on prod vs. dev setting
+      $msg = new \Twig_Error(t('@item could not be found in _context.', array('@item' => $item)));
+      drupal_set_message($msg->getMessage(), 'warning');

trigger_error() should handle that no?

+++ b/core/modules/node/templates/node.twigundefined
@@ -0,0 +1,106 @@
+#}
+<article id="node-{{ node.nid }}" class="{{ attributes.class }} clearfix" {{- attributes }}>
+

This is weird syntax indeed. I agree it'd be better to do it (Edit: whitespace removal) in Attributes() if that's doable.

chx’s picture

Please disregard #173. It's crystal clear now that Moshe have not even looked at the patch for quite some iterations now.

effulgentsia’s picture

<article id="node-{{ node.nid }}" class="{{ attributes.class }} clearfix" {{- attributes }}>
To clarify, the above is identical to:
<article id="node-{{ node.nid }}" class="{{ attributes.class }} clearfix"{{ attributes }}>

The second form matches what all of our templates currently do in HEAD (with <?php bumping right up to the closing quote), so maybe we should revert to that form in this issue, and have a follow up for using {{-

The reason we need one of the two forms is that if there are no attributes other than ones already individually printed, then {{attributes}} might be an empty string, and themers don't want to see markup with an extra space before the close of tag (<article id="node-1" class="node clearfix" >). However, we can discuss that trade off in a follow up as well.

Crell’s picture

The sense I get on the security front is that Twig == Security iff we let Twig be responsible for security. Right now we're still doing a mix of old-style and twig-style, because conversion is not complete. To really get the full benefit of Twig security-wise we need to finish converting templates to Twig, then let Twig handle escaping rather than trying to second-guess it the way we needed to second-guess PHPTemplate. catch already pointed to #1825952: Turn on twig autoescape by default on that front.

Even then, as chx has highlighted we still block people from putting db_query() directly into a template (win), sane and secure handling of arrays (win), etc. So maybe it's not a massive security win (yet), but it's still an improvement on status-quo.

Plus, front-enders are very loud in preferring Twig to PHPTemplate. So even if performance is a wash, and security is a wash, making life easier for front-enders is worth the effort. We can continue to iterate to make it more "Twig-ish" and let Twig handle more things its good at, which should help with simplification.

The people to decide if this is "enough" to commit and move forward are the leading front-end developers in core, not the leading back-end developers in core. (Meaning, I'm not going to RTBC this; JohnAlbin, Morten, Jacine, etc. should have that authority, not me.)

effulgentsia’s picture

I finally got around to reviewing the code internals in detail here, and like what I saw. I'll save a nit review for later, but none of the little things I came across couldn't be dealt with in a follow up, so I'd be +1 for committing this once catch's feedback is addressed and there's consensus on whether to revert the auto-escaping stuff from this issue and deal with it more fully in a follow up. If we do defer auto-escaping to a follow up, per #164, then I still appreciate the work that went in to implementing what's here so far, because seeing how we're able to hook in to Twig compilation, gives me confidence that once we figure out the complete picture of what we want, that we will be able to implement it. If we don't want to revert the auto escaping that's here so far, I'd be fine with this being committed as the initial approach, and then iterating on it in follow ups.

My biggest disappointment currently is seeing the benchmarks. I really expected to see a performance gain relative to PHPTemplate in situations where the same template is rendered many times per request (e.g., 100 node teasers), and I'm confused as to why that's not turning up. I agree with #160 that the performance regressions can be clawed back, but without a performance gain, I'm concerned we'll be left with at least a dozen theme functions that for performance reasons we won't be able to convert to templates. I hope that's not the case, and that we'll find ways of further micro optimizing the compiled templates such that even extremely frequently called theme functions can be templatized.

I will say that without security improvements, this patch does not justify its code complexity and code weight in my mind. Slightly cleaner templates and better support for an edge case (i.e. Template author must be forcibly prevented from using PHP, or at least forced until he moves to preprocess layer) are nice, but not enough to change add a complex (code-wise) theme engine IMO. I'm not sure this improves 'Increases learning curve' tag either.

I disagree with this. Even if all we did was convert templates from PHPTemplate to Twig, failed to make any improvement to security, and failed to optimize compilation to the point of converting theme functions, I still think it's a net positive (though not nearly as positive as if we do manage to get the security right and convert all our theme functions). What you call "Slightly cleaner templates", webchick in #111 calls "absolutely huge". What you call an "edge case", I call enabling full control of markup in a multi-tenant SaaS environment, which I consider huge to the adoption of Drupal (disclosure: I work for Acquia, the company behind Drupal Gardens). As to the complexity in this patch, it's internal to a half dozen classes in Drupal\Core\Template, and IMO, not any more cumbersome than any of our other low-level systems: IMO, well worth it if it makes things even a little bit friendlier to front-end devs and Drupal SaaS providers.

Overall, node.tpl.php felt like too simple of an example to get a real sense of how the Twig conversion would work. I'd be curious to see something like page.tpl.php also (which has t() calls, theme() calls, and more complex logic embedded inside of it, that kind of thing)... it's closer to a realistic example of what themers actually deal with on moderately complex websites.

page.tpl.php calling theme() is a bug: a hold over from D7's incomplete conversion to render arrays. It's the only core template that does so, and it should be calling render() instead. There might be an issue somewhere for fixing that, but it might be made moot by #1696302: [meta] Blocks/Layouts everywhere. Similarly, the level of logic complexity in it will go away once everything's converted to blocks. forum-list.tpl.php is probably the next best example of a logically complex template. Almost everything else in core is not much more complex than node.tpl.php.

I was very curious how Twig would deal with something like #953034-2: Themes improperly check renderable arrays when determining visibility

The answer to this in #166 works for me, and demonstrates a key benefit of Twig: decoupling object/array/string/boolean internals from template syntax.

but if you do {{ node.body.und.0.safe_value }} (which is a better variable to use anyway)

Is that a better variable or is it a hold over of PHP string mentality? With Twig, we can make node.body.und.0.value into an object with methods for rendering raw or safe that hook into Twig's filter syntax.

David_Rothstein’s picture

  1. I like the plan for clearfix"{{ attributes }} (no in-between space) to deal with the whitespace, since it avoids another new syntax and is consistent with what's already done. (Seeing the technique change in this patch made me think additional whitespace was being introduced somewhere in the pipeline and that the new technique was required to get rid of it).
  2. When I tried <pre>{{ dump(_context) }}</pre> I got this displayed on the screen:
    array
      'nid' => 
        object(Twig_Markup)[159]
          protected 'content' => string '1' (length=1)
          protected 'charset' => string 'UTF-8' (length=5)
      'vid' => 
        object(Twig_Markup)[161]
          protected 'content' => string '1' (length=1)
          protected 'charset' => string 'UTF-8' (length=5)
    ...
    (etc)
    

    (edit: fixed formatting)
    So that's why I assumed auto-escape was coming into play here. Do other people see something else more readable there?

  3. Themes checking visibility ... interesting, yes, and definitely agree it's not part of this patch. (I was mainly curious what's possible and what happens when we start bending Twig beyond the simple use cases. That's good to see.)
  4. One random minor thing I noticed:
    +        // All files can be refreshed via drush cc all.
    

    I'd say "by clearing caches" rather than "via drush cc all".

  5. Regarding page.tpl.php, I suggested that mainly because it calls functions (including t() and theme()), not because it happens to call theme() specifically (which I agree is a bug). It looks like forum-list.tpl.php would be a good example too... These kinds of random function calls are more common in custom themes, which is why I thought it would be good to look at.
  6. Is that a better variable or is it a hold over of PHP string mentality? With Twig, we can make node.body.und.0.value into an object with methods for rendering raw or safe that hook into Twig's filter syntax.

    Hm, I think the best variable is probably {{ content.body.0 }} (and same goes for the equivalent in PHP template). But for people who are choosing the best variables, Drupal's theme system is already secure :)

    If we got rid of strings and made it a "rule" (whether enforced or not) that you should never call the raw method in a template file this could work. But Drupal has so many deep data structures; it is hard to imagine this happening everywhere.

Overall, I have no strong opinion on whether the auto-escaping/improved security should be a followup (as opposed to a core requirement of the decision to move to Twig).... but I'm more concerned about doing it wrong and then Drupal 8 gets released with something that might not quite make sense. Leaving auto-escape off for now, until we know we get it right, is certainly the more conservative choice.

cosmicdreams’s picture

Hello all,

Even though the summary underlines this point, it's worth repeating: Using Twig instead of PHPTemplate allows Large-scale Drupal projects to bring on non-Drupal specialist Front end developers to handle a large portion of theming tasks. I work at a place were we have devs that work with a number of other frameworks and a growing number of them are using Twig.

My hope is that with using Twig I'll have an easier time in the future ramping up themers onto projects. For many that I've spoken to about Drupal 8 (outside of the Drupal community) being able to use Twig's syntax is improvement they are most excited about. These folks are are Frontend developers, Expression Engine devs, Zend Framework PHP devs, and Ruby on Rails devs.

As a side effect of using Twig as a theming engine, we'd have an opportunity to tackle redundancy in our theming functions and take a stab at improving the overal developer experience.

David_Rothstein’s picture

I would say (especially for the purposes of this issue) let's worry more about building the highest quality theme system we can, whatever it is. If it's good, people will learn it. Also, I'm pretty sure I can find you a whole bunch of Wordpress and Joomla themers who are already very happy theming directly in PHP :)

I sort of had a eureka moment earlier, though, when I realized we're not even deciding between PHP and Twig anymore. In Drupal 8, what we're using in tpl.php files isn't even normal PHP. For example, this snippet in node.tpl.php:

<?php print $attributes['class']; ?> clearfix"<?php print $attributes; ?>>

To a person with normal PHP experience that looks like complete nonsense which is likely to result in the word "Array" being printed to the page at the end of the string. (The only reason it doesn't is that we have some magic behind the scenes to deal with it. And to support that magic we already have lots of complex machinery, even without Twig.)

In short, Drupal 8 may have broken PHPTemplate enough already that there's no way to save it :(

Whether that helps answer the question of "do we need to demonstrate security benefits in order to go with Twig" I'm not sure, but I thought it was relevant.

David_Rothstein’s picture

Hm, it looks like someone who was trying to be helpful edited point 2 from my comment in #181 to "fix the formatting". Thank you, but unfortunately the "fix" completely changed the meaning of what I was trying to say :)

So, let me repeat that with a little more clarity.

When I tried <pre>{{ dump(_context) }}</pre> I expected to see something like this displayed on the screen:

array
  'nid' => 
    object(Twig_Markup)[159]
      protected 'content' => string '1' (length=1)
      protected 'charset' => string 'UTF-8' (length=5)
  'vid' => 
    object(Twig_Markup)[161]
      protected 'content' => string '1' (length=1)
      protected 'charset' => string 'UTF-8' (length=5)
...
(etc)

But what I actually saw was this:

<pre class='xdebug-var-dump' dir='ltr'>
<b>array</b>
  'nid' <font color='#888a85'>=&gt;</font> 
    <b>object</b>(<i>Twig_Markup</i>)[<i>159</i>]
      <i>protected</i> 'content' <font color='#888a85'>=&gt;</font> <small>string</small> <font color='#cc0000'>'1'</font> <i>(length=1)</i>
      <i>protected</i> 'charset' <font color='#888a85'>=&gt;</font> <small>string</small> <font color='#cc0000'>'UTF-8'</font> <i>(length=5)</i>
  'vid' <font color='#888a85'>=&gt;</font> 
    <b>object</b>(<i>Twig_Markup</i>)[<i>161</i>]
      <i>protected</i> 'content' <font color='#888a85'>=&gt;</font> <small>string</small> <font color='#cc0000'>'1'</font> <i>(length=1)</i>
      <i>protected</i> 'charset' <font color='#888a85'>=&gt;</font> <small>string</small> <font color='#cc0000'>'UTF-8'</font> <i>(length=5)</i>
...
(etc)

Given that, I assumed (but did not verify) that this is some kind of auto-escaping issue.

Did anyone else reproduce this, or am I doing something wrong? Obviously, for debugging to be meaningful, we need it to work like the first, not the second...

fabianx’s picture

There is a lot to answer for me here, thanks!

Lets start:

#184:

That is an edge case as xdebug is outputting HTML, while normal PHP returns plain text. That issue is clearer now.

Please use:

{{ dump(_context)|raw }}

when using XDebug. This is here also not Drupal specific, but a Twig upstream problem / bug / limitation.

#181:

1. Will be fixed in next patch.
4. Good catch :) - Will be fixed.

5. I'll leave Dries to decide if he wants auto escape on or off as part of the first patch and provide a patch to revert auto-escape just if someone wants to work with that.

I definitely want auto escape and want it properly supported, but can't do it all in one patch ...

We'll surely find a nice way to deal with safe_value vs. value, which seems to be the strongest DX problem right now.

Thanks again for the great feedback.

fabianx’s picture

#183: Yes, but almost anyone agreed (at least from what I could see) that using the new Attribute was way simpler than the classes_array[] mix.

#180:

Performance

Please be aware that node.tpl.php is a rather complicated template, because it needs to call render, show, hide, etc. and such is more of an upper bound for the overhead. The overhead for something like theme_field should be much lower, because there the compiled result should match the theme_field much closer. And if not out of the box, it is just one compilation switch away :-). That is the beauty of a compiled language: You can optimize things on a case per case basis. So I am pretty sure we can get all theme functions converted and to good performance.

And thanks for the encouraging feedback!

#178: After internal discussion and an ACK by @jenlampton and @JohnAlbin we decided to remove the whitespace control for now and do any change within Attribute() and whitespace as followup:

Its now:

<article id="node-{{ node.nid }}" class="{{ attributes.class }} clearfix"{{ attributes }}>

#176: Thanks for a great review!

Here are the points:

This file_exists() could also be adding some overhead given most of those calls will miss - did that come up in performance testing at all? Either way it'll be gone by the time we get to release.

This file_exists is called during theme registry rebuild only, so it is obviously not part of any performance testing and as it will be gone, it should be fine for now.

I can live with the coupling here given how performance-critical this is. We added the Attribute class for Twig integration in the first place.

Great!

Why $cache0 and not $cache?

$cache is already taken by the parent class. I changed it to $cache_object as a compromise and druplified. I also changed $cache to $cache_filename to make it clearer.

I also changed the whole copied code to be adjusted to our coding standards. (as good as I can; if someone wants to check the interdiff, feel free ...)

Even though it's a straight copy, I'd be happy if we could Drupalize the code style a bit more.

DONE, see interdiff.

This isn't one line. Also we know it's a class ;)

FIXED

trigger_error() should handle that no?

Uhm, I tested this out and got:

User warning: content2 could not be found in _context in "core/modules/node/templates/node.twig" at line 101. in Drupal\Core\Template\TwigTemplate->getContextReference() (line 49 of core/lib/Drupal/Core/Template/TwigTemplate.php).

which I found much more confusing than:

content2 could not be found in _context in "core/modules/node/templates/node.twig" at line 101.

Also I don't think its necessary to expose the internals to the Themer ...

So unless using drupal_set_message directly is a huge problem, I'd like to leave the more user friendly version in.

This is weird syntax indeed. I agree it'd be better to do it (Edit: whitespace removal) in Attributes() if that's doable.

Changed in this patch for now and changing white space in Attributes() is potential follow-up.

===

Thats all.

New patch attached! This is exciting, looks good and I hope we can get this back to RTBC and then committed soon :-).

@Dries: Please let me know if you would like the first patch with auto escape disabled. I can easily provide a patch without it and we could also do auto escape as follow up. Thanks!

fabianx’s picture

Status: Needs work » Needs review

Back to Needs Review :-)

fabianx’s picture

Okay, according to feedback from several people including David_Rothstein, I'll defer the autoescape for now and have re-opened and re-titled #1712444: Twig: Activate autoescape mode.

We can definitely do this and auto escape will work great, but we can then talk about it more independently from this patch :-).

This also saves performance for now, which is good.

David_Rothstein’s picture

Looking at the interdiff that seems pretty reasonable to me. Haven't really reviewed beyond that, but assuming other people have I think this is fine.

Regarding the dump() calls not working, good call on xdebug being the culprit (haven't tested but I'm almost certain that's the case). I actually seem to remember {{ dump(_context)|raw }} didn't work for me either... but in any case, I agree this sounds like an upstream problem for Twig to fix (though kind of an unfortunate one since a lot of developers will have xdebug turned on).

catch’s picture

Status: Needs review » Needs work

I just went through this with FabianX in irc


+++ b/core/lib/Drupal/Core/Template/TwigEnvironment.phpundefined
@@ -0,0 +1,80 @@
+      } else {
+        $cid = 'twig:' . $cache_filename;
+        $obj = $this->cache_object->get($cid);
+        $mtime = isset($obj->data) ? $obj->data : FALSE;
+
+        // Check that the compiled template is cached and the storage exists.
+        // If autoreload is on, also check that the template has not been
+        // modified since the last compilation.
+        if (!$mtime || !$this->storage->exists($cache_filename) || ($this->isAutoReload() && !$this->isTemplateFresh($name, $mtime))) {
+          $source = $this->loader->getSource($name);
+          $compiled_source = $this->compileSource($source, $name);
+          $this->storage->save($cache_filename, $compiled_source);
+          // Save the last modification time
+          $this->cache_object->set($cid, REQUEST_TIME);

I didn't understand why we need to store the mtime in the cache system then fetch it again for each template, which at least with a lot of templates would add a lot of overhead.

Summary:

- this is only supposed to happen when autoReload mode is on (which it is by default in core at the moment, but I think we should default to off and let people rm -f the templates directory tbh). the cache()->get() was happening whether it was enabled or not though (i.e. before the check), but Fabian's fixing this now

- php storage doesn't provide an mtime to compare against, hence using the cache system. I don't care about this bit so much if we're not doing any check at all in runtime.

- php_storage doesn't have any kind of gc mechanism, but we need that for the compiled container - that should be a critical task (or even bug) somewhere but it'll cover both Twig compilation and DIC compilation (and the latter is already in core) so this patch is the victim of that, not the perpetrator.

fabianx’s picture

#190:

- this is only supposed to happen when autoReload mode is on (which it is by default in core at the moment, but I think we should default to off and let people rm -f the templates directory tbh). the cache()->get() was happening whether it was enabled or not though (i.e. before the check), but Fabian's fixing this now

auto_reload is FALSE by default now. To update the compiled templates, for now you need to push the clear all caches button. The code has been refactored for performance. I added drupal_php_storage('twig')->deleteAll(); to drupal_flush_all_caches() like with DIC.

- php storage doesn't provide an mtime to compare against, hence using the cache system. I don't care about this bit so much if we're not doing any check at all in runtime.

DONE, will only be enabled in development mode.

- php_storage doesn't have any kind of gc mechanism, but we need that for the compiled container - that should be a critical task (or even bug) somewhere but it'll cover both Twig compilation and DIC compilation (and the latter is already in core) so this patch is the victim of that, not the perpetrator.

Agree.

---

This patch also adresses the trigger_error vs. drupal_set_message issue and favors trigger_error until the prod vs. dev switch is in, so production sites are not inhibited.

Patch and interdiff attached!

I am getting excited here ... :-)

catch’s picture

That looks much better and should reduce some of the performance overhead further as well.

I definitely think we should handle auto-escape in a follow-up - it needs to be based around the template conversions and thinning out variables from preprocess etc., also removing it here means there's no longer any bleeding of Twig_Markup into Attributes().

catch’s picture

Status: Needs review » Reviewed & tested by the community

I can't find anything else to complain about.

I'm sure a tonne of issues are going to come up during conversions etc. but this is pretty self-contained and allows the conversions to proceed.

The overhead added here is significantly less than a lot of other patches and there's a reasonably good plan for clawing it back (although it seems very unlikely it'll end up much better than PHPTemplate and that's with the compilation of templates, which is a shame). Where we'll need to be very careful is in converting theme functions (like theme_field()) which are currently theme functions because .tpl.php files are too expensive, would be great if .twig aren't by the time we get there.

Moving back to RTBC.

chx’s picture

I wonder whether Dries would commit this during his speech at BADcamp...

Status: Reviewed & tested by the community » Needs work
Issue tags: -Increases learning curve, -Twig, -VDC, -Drupal7AndTheArraysOfDoom

The last submitted patch, core-Integrate-twig-into-core-1696786-191.diff, failed testing.

fabianx’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, core-Integrate-twig-into-core-1696786-191.diff, failed testing.

webflo’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, core-Integrate-twig-into-core-1696786-191.diff, failed testing.

webchick’s picture

Status: Needs work » Needs review
Issue tags: +Increases learning curve, +Twig, +VDC, +Drupal7AndTheArraysOfDoom
chx’s picture

Status: Needs review » Reviewed & tested by the community

Back to RTBC after bot problems.

schnitzel’s picture

StatusFileSize
new110.72 KB

commit photo! :)

8151141322_fcdc5430d4.jpg

webchick’s picture

Status: Reviewed & tested by the community » Fixed

YEEAAAHHHH!!! :D :D :D

chx’s picture

Title: Integrate Twig into core: Implementation issue » Change notice: Integrate Twig into core: Implementation issue
Assigned: dries » Unassigned
Category: feature » task
Priority: Major » Critical
Status: Fixed » Active
Issue tags: +Needs change record

Well, my friends, how do we write a change notice for this?

jhodgdon’s picture

I am taking on the twig change notice, will report back shortly

jhodgdon’s picture

I've created at least the start of a change notice:
http://drupal.org/node/1831138

podarok’s picture

Status: Active » Needs review

#206 the code from example not yet in core repo
it is commited only in sandbox repo
possibly we should postpone this change notification untill code merge with sandbox or write another examples

jhodgdon’s picture

RE #207 - I do not think that is all that important whether this particular example was in the patch or not. What I wanted was a quick example of the process -- the main point of the change record is to say "there is now a new process for theming that is different for what was in 7.x".

If you think it's a bad illustration of the process, or there is an error in the example, please edit the change notice and put in a better example. I was not involved in this patch/issue, was just asked to write the change notice, and if a better example would make the change notice more accurate or clearer, by all means put one in. Thanks!

chx’s picture

Title: Change notice: Integrate Twig into core: Implementation issue » Integrate Twig into core: Implementation issue
Category: task » feature
Priority: Critical » Major
Status: Needs review » Fixed

Thanks a ton, I copied datetime over and linked node.tpl.php and node.twig.

jwilson3’s picture

The change notice refers to files as *.html.twig but all the twig files in core right now end in just "*.twig". UPDATE: oops Chx already fixed this!

I'll admit I don't know what the final solution is going to be -- the front-end twig work is using *.html.twig, but the backend engine development has been using *.twig. Which one are we going with?

johnalbin’s picture

I'll admit I don't know what the final solution is going to be -- the front-end twig work is using *.html.twig, but the backend engine development has been using *.twig. Which one are we going with?

That's already been decided. We're going to use *.html.twig. See #1804698: Consider using *.html.twig filename convention

fabianx’s picture

That's already been decided. We're going to use *.html.twig. See #1804698: Consider using *.html.twig filename convention

Yes, the reason this patch does not include this is that this needs another bugfix to a core function to find such files. We have this fix in front-end, but I wanted to avoid to re-start the whole review process on an already complex patch, because of one added line.

We can file an issue now. :-).

Also: WOHOO!

jenlampton’s picture

/me highfives FabianX

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

xjm’s picture

Issue tags: -Needs change record
xjm’s picture

Issue summary: View changes

Updated issue summary. Add performance links

bhagath’s picture

Status: Closed (fixed) » Needs work

Call to undefined method Drupal\Core\Template\TwigExtension::setLinkGenerator() This is the error i was found in my site..

geerlingguy’s picture

Status: Needs work » Closed (fixed)

@bhagath - I think this is related to some other problem; you might want to search the issue queue or file a new issue if you can't find any other issues matching your problem.

quietone’s picture

Published the CR