Video Tutorial Create a tag on Twig


Welcome to this tutorial where I suggest you discover how to create a custom tag on Twig (3.X).

Goal

Our goal is to create a tag that will allow part of our template to be cached. This is to optimize the performance of certain parts of our application (markdown conversion for example)

{% cache post%}
  
  • Post {{post.id}} {{slow ()}}
  • {% endcache%}

    The cache will be able to determine a key from the object received as a parameter {Id} {-Post- updatedAt}. For this to work we will create an interface which will allow us to define what is expected as a type.

    <? Php
    namespace App  Twig  Cache;
    
    CacheableInterface
    {
        public function getId (): int;
        public function getUpdatedAt ():  DateTimeInterface;
    }
    

    Twig extension

    The first step is fairly standard and consists of creating a new Twig extension that will allow you to define the tag to add.

    cache = $ cache;
        }
    
        / **
         * @return array
         * /
        public function getTokenParsers (): array
        {
            return (
                new CacheTokenParser ()
            );
        }
    
        public function getCachedValue (CacheableInterface $ key):? string
        {
            return $ this-> getItem ($ key) -> get ();
        }
    
        public function setCachedValue (CacheableInterface $ key, string $ value): void
        {
            $ item = $ this-> getItem ($ key);
            $ Item-> set ($ value);
            $ This-> cache-> save ($ item);
        }
    
        private function getItem (CacheableInterface $ entity): CacheItem
        {
            $ className = get_class ($ entity);
            $ className = substr ($ className, strrpos ($ className, '\') + 1);
            $ key = $ entity-> getId (). $ className. $ Entity-> getUpdatedAt () -> getTimestamp ();
            return $ this-> cache-> getItem ($ key);
        }
    
    }
    

    So that twig understands our new tag we will define a new parser thanks to the method getTokenParsers (). We will also add within this extension different methods (which will be usable from the generated code) in order to interact with the cache.

    Token Parse

    This class will tell twig how to process our tag and its content. We will have several important methods inside:

    • getTag (), returns the name of your custom tag.
    • parse (), allows you to detail the parsing process and will have to return a node (which will then be processed by Twig).

    For our logic we will keep all the content between our {% hidden %} and {% endcache%} in a knot body. We will then create a new type of node CacheNode.

    getLine ();
            $ stream = $ this-> parser-> getStream ();
    
            $ key = $ this-> parser-> getExpressionParser () -> parseExpression ();
            $ key-> setAttribute ('always_defined', true);
            $ Stream-> expect (Token :: BLOCK_END_TYPE);
            $ body = $ this-> parser-> subparse (($ this, 'decideCacheEnd'), true);
            $ Stream-> expect (Token :: BLOCK_END_TYPE);
    
            return new CacheNode ($ key, $ body, $ lineno, $ this-> getTag ());
        }
    
        public function getTag (): string
        {
            return 'cache';
        }
    
        public function decideCacheEnd (Token $ token): bool
        {
            return $ token-> test ('endcache');
        }
    
    }
    
    

    The knot

    The knot is the last (but also the most important) point in our system. It is him, through his method compile (), who will be responsible for generating the PHP code corresponding to the functionality of our tag.

    It will be possible to recover the instance of our extension through $ This-> env> getExtension ( 'Namepsace In Our Extension').

      $ key, 'body' => $ body), (), $ lineno, $ tag);
        }
    
        public function compile
        {
            $ i = self :: $ cacheCount ++;
            $ extension = TwigCacheExtension :: class;
            $ compile
                -> addDebugInfo ($ this)
                -> write (" $ twigCacheExtension =  $ this-> env-> getExtension ('{$ extension}');  n")
                -> write (" $ twigCacheBody {$ i} =  $ twigCacheExtension-> getCachedValue (")
                -> subcompile ($ this-> GetNode ( 'key'))
                -> raw ( ");  n")
                -> write ("if ( $ twigCacheBody {$ i}! == null) { n")
                -> indent ()
                -> write ("echo  $ twigCacheBody {$ i};  n")
                -> outdent ()
                -> write ("} else { n")
                -> indent ()
                -> write ( "ob_start ()  n")
                -> subcompile ($ this-> GetNode ( 'body'))
                -> write (" $ twigCacheBody {$ i} = ob_get_clean ();  n")
                -> write ("echo  $ twigCacheBody {$ i};  n")
                -> write ( " $ twigCacheExtension-> setCachedValue (")
                -> subcompile ($ this-> GetNode ( 'key'))
                -> raw (",  $ twigCacheBody {$ i});  n")
                -> outdent ()
                -> write ( "}  n");
            ;
    
        }
    
    }
    

    And There you go !

    For further

    If you want to push things further you can set up arrays support in the generation of the key.

    {% cache ('card', post)%}
      
  • Post {{post.id}} {{slow ()}}
  • {% endcache%}