Twig: accélérer la génération de ses templates (1/2)

Dans cette première partie, je vais vous faire part d’une solution permettant de stabiliser le temps de génération de vos templates Twig.

Récemment je me suis posé pour réfléchir autour des solutions que propose Twig pour accéder aux propriétés d’un objet ou d’un tableau.

Accéder à une propriété d’un objet

Twig a été conçu pour simplifier nos templates, aussi bien en terme de code qu’en terme de propreté. Il a également été conçu pour permettre aux intégrateurs, personnes n’ayant pas tous de connaissances en développement, un accès facile aux propriétés d’un objet ou autres. Cela grâce à une syntaxe simplifiée.

Seulement voilà, étant développeur avant-tout, je me suis posé la question de comment twig faisait pour déterminer quelle méthode d’un objet doit être appelé.

Syntaxe Twig

Dans mon code suivant, je partirai du principe que j’utilise une classe comme ci-dessous.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Object
{
private $name;
public $username;
public function getName() {
return $this->name;
}
public function getUsername() {
return $this->username;
}
}

Voici comment j’appellerai mon template depuis un contrôleur Symfony.

1
2
3
public function indexAction() {
return array("object" => $object);
}

Jusque là, tout est clair. Mon objet ressemble parfaitement aux objets que je peux utiliser dans mes projets.

Analyse des déclarations dans Twig

1
{{ object.name }}

À partir de maintenant nous allons voir comment Twig travaille avec nos appels. Ici, on demande à Twig de prendre la valeur name de mon objet, sauf que pour Twig qui n’est qu’un simple appel PHP au final, cette variable est inaccessible. Twig a donc ajouter un proxy permettant de faire abstraction et de regarder quelles sont les propriétés et méthodes disponibles.

Twig va donc demander dans l’ordre ci-dessous de faire des vérifications sur l’objet.

  1. Regarde si object est un tableau et si name est une clé ;
  2. Regarde si object est un objet et si name est un attribut accessible ;
  3. Regarde si object est un objet et si name est une méthode ;
  4. Regarde si object est un objet et si getName est une méthode ;
  5. Regarde si object est un objet et si isName est une méthode.
    Ici, avec la notation que nous avons utilisé, il nous a fallu attendre la 4ème condition pour parvenir à notre élément. Car name n’est pas un attribut accessible, ni une méthode.
    1
    {{ object.username }}

Dans ce cas, nous essayons d’accéder à username. Cet attribut est un attribut publique (dans Symfony, j’ai rarement rencontré ce cas). Il nous faut attendre la 2ème condition.

1
{{ object.getName() }}

Dans notre troisième cas, je tente de faire appel à ma méthode de manière direct. Je récupère mon résultat dès la troisième condition, soit une condition plus rapide.

1
{{ object.getUsername() }}

Dans notre quatrième et dernier cas, je tente de faire appel à ma méthode de manière direct. Je récupère mon résultat dès la troisième condition. Dans ce cas, je mets une condition supplémentaire pour accéder à ma valeur.

Interprétation des templates Twig

Lorsque je me suis penché sur ce détail, j’ai réfléchis à comment les développeurs de Twig avait pu réaliser la mise en cache et la compilation des templates. Il ne me fallut pas beaucoup de temps pour en venir à l’évidence, qu’avec le peu d’information et la liberté qu’offre le gestionnaire de template, nos fichiers sont simplement transcrit en PHP d’une manière similaire à ce que l’on pourrait tout à fait faire. Vous pouvez d’ailleurs constater par vous même en cherchant dans votre dossier de cache.

1
2
{# Template twig #}
{{ object.name }}

Template interprété en PHP
1
echo twig_escape_filter($this->env, $this->getAttribute((isset($context["object"]) ? $context["object"] : $this->getContext($context, "object")), "name"), "html", null, true);

Le premier bloque correspond à ce que j’ai noté dans mon template twig, le deuxième bloque à la version traduite que j’ai trouvé dans le dossier de cache.

On remarque bien que le twig a été traduit en PHP mais qu’aucun élément ne lui a permis de prédire quelle méthode devrait exactement être appelée. La méthode twig_escape_filter pourrait être comparé à un proxy. C’est dans cette méthode qu’on va déterminer comment on accède à l’attribut.

Conclusion

Malgré que les templates twig soit mis en cache automatiquement par Symfony, seul la version PHP est gardé mais sans interprétation de ce que l’on souhaite récupérer. En théorie, il y a donc ici, le moyen d’optimiser des appels et le temps de génération de nos templates car la vérification est effectuée à chaque appel.

Benchmark

J’ai quand même voulu avoir une idée concernant les gains qui peuvent être fait en appelant les méthodes plutôt que des « alias ».

Dans un premier cas, j’appelle un template qui va appeler 10 fois un même objet qui appelle à son tour 25 alias. Cela représente 250 appels. Les résultats sont grossis par la boucle de 10 pour permettre de faire des calculs juste quant au gain de performance.

Dans un second temps j’appelle un template qui va appeler 10 fois le même objet et qui appelle à son tour 25 méthodes (toujours via le même proxy que pour les alias). Cela représente 250 encore une fois.

J’ai réalisé l’appel de ces templates 5 fois chacun.

Benchmark d'appels de templates Twig

On remarque que l’intégralité des appels au template faisant appels à des méthodes est plus rapide. En faisant les moyennes, on remarque que le template faisant appels aux alias est plus long de 54,6 millisecondes (197.4 - 142.8).

En faisant un rapide calcul, on remarque que si on ramenait ça à un cas général, le template utilisant des appels à des méthodes est plus rapide en moyenne sur des mêmes données de approximativement 26.7%. Cela peut- être intéressant lorsqu’on réalise de nombreux appels à des objets.

Conclusion

Même si le pourcentage de gain peut paraitre important, il faut savoir distinguer lorsque cela peut être réellement nécessaire. Les temps d’accès étant globalement assez faible, cela peut-être intéressant d’optimiser ces détails lorsqu’on souhaite limiter le temps d’exécution d’une requête comme pour un service en production à forte charge. Dans le cadre professionnel, je met un point d’honneur à respecter ce « coding style ».