Analysing a template

When SPIP’s parser analyses a template, it translates the syntax into a vocabulary known and understood by the compiler. We might then say that the parser is translating a particular language (the SPIP syntax), that we refer to as a "concrete syntax", into a precise language that we refer to as an "abstract syntax". This is defined by PHP objects in the ecrire/puclic/interfaces.php file.

With this page analysis, the parser creates a table describing it, sequentially and recursively, by using the vocabulary included by the compiler (the objects containing Text, Fields, Loops, Criteria, Placeholders, Includes, Polyglots).

To make things a little clearer, let’s look at what table is generated by a few template examples.

A text

Template :

Simple text

Generated table: (output by a print_r)

array (
  0 => 
  Texte::__set_state(array(
     'type' => 'texte',
     'texte' => 'Simple text
',
     'avant' => NULL,
     'apres' => '',
     'ligne' => 1,
  )), 
)

The table specifies that the first element read on the page (key 0) is a "Texte" element, starting on line 1, and holding the text string "Simple text".

A tag

Template:

[before(#VAL)after]

We can read from the generated table below, that the first element read on the page is a Field ("champ" in French) (a tag), that it’s name is "VAL", that it is not within a loop (otherwise the id_loop would be defined), and that what is in the optional section before the tag is a "Texte" element with the text string being "before".

Generated table:

array (
  0 => 
  Champ::__set_state(array(
     'type' => 'champ',
     'nom_champ' => 'VAL',
     'nom_boucle' => '',
     'avant' => 
    array (
      0 => 
      Texte::__set_state(array(
         'type' => 'texte',
         'texte' => 'before',
         'avant' => NULL,
         'apres' => '',
         'ligne' => 1,
      )),
    ),
     'after' => 
    array (
      0 => 
      Texte::__set_state(array(
         'type' => 'texte',
         'texte' => 'after',
         'avant' => NULL,
         'apres' => '',
         'ligne' => 1,
      )),
    ),
     'etoile' => '',
     'param' => 
    array (
    ),
     'fonctions' => 
    array (
    ),
     'id_boucle' => NULL,
     'boucles' => NULL,
     'type_requete' => NULL,
     'code' => NULL,
     'interdire_scripts' => true,
     'descr' => 
    array (
    ),
     'ligne' => 1,
  )),
  1 => 
  Texte::__set_state(array(
     'type' => 'texte',
     'texte' => '
',
     'avant' => NULL,
     'apres' => '',
     'ligne' => 1,
  )),
)

A loop

Let’s look at one more example of a loop using a tag, which is a little more complicated since it implies a circular reference in the generated table. Look at this simple template segment:

Template:

<BOUCLE_a(ARTICLES){id_article=3}>
#TITRE
</BOUCLE_a>

This loop selects article 3 and should display the title of the article. The page table, if we were to try to display it, would end up generating a recursion error. The illustration shows that the second element read in the loop is a Field ("champ" in French) or tag named "TITRE". This field contains a reference to the loop which it is defined within ('boucles'=>array(...)). This loop contains the tag which belongs to the loop containing the tag which belongs to the loop ...

Excerpt of the generated table

array (
  0 => 
  Boucle::__set_state(array(
     'type' => 'boucle',
     'id_boucle' => '_a',
     'id_parent' => '',
     'avant' => 
    array (
    ),
     'milieu' => 
    array (
      0 => 
      Texte::__set_state(array(
         'type' => 'texte',
         'texte' => '
',
         'avant' => NULL,
         'apres' => '',
         'ligne' => 1,
      )),
      1 => 
      Champ::__set_state(array(
         'type' => 'champ',
         'nom_champ' => 'TITRE',
         'nom_boucle' => '',
         'avant' => NULL,
         'apres' => NULL,
         'etoile' => '',
         'param' => 
        array (
        ),
         'fonctions' => 
        array (
        ),
         'id_boucle' => '_a',
         'boucles' => 
        array (
          '_a' => 
          Boucle::__set_state(array(
             'type' => 'boucle',
             'id_boucle' => '_a',
             'id_parent' => '',
             'avant' => 
            array (
            ),
             'milieu' => 
            array (
              0 => 
              Texte::__set_state(array(
                 'type' => 'texte',
                 'texte' => '
',
                 'avant' => NULL,
                 'apres' => '',
                 'ligne' => 1,
              )),
              1 => 
              Champ::__set_state(array(
                 'type' => 'champ',
                 'nom_champ' => 'TITRE',
                 'nom_boucle' => '',
                 'avant' => NULL,
                 'apres' => NULL,
                 'etoile' => '',
                 'param' => 
                array (
                ),
                 'fonctions' => 
                array (
                ),
                 'id_boucle' => '_a',
                 'boucles' => 
                array (
                  '_a' => 
                  Boucle::__set_state(array(
...

Why use such references?

Quite simply because they are then used for calculating the tags. When a tag is calculated, a part of this table is passed as a parameter (the famous $p that we will meet often). This part simply relates to the tag’s properties. To retrieve properties from the enclosing loop, all that is required (thanks to these references) is to call the parameter $p->boucles[$p->id_boucle].

Author Mark Baber Published : Updated : 12/03/23

Translations : English, français