Phug

Phug (moteur de rendu de Pug-php et Tale-pug)

Dernière version stable Dernière version stable    |   Tests unitaires Tests unitaires

Vue d'ensemble

Qu'est-ce que Phug ?

Phug est le moteur de templates de pug pour PHP.

Phug offre un moyen simple d'écrire des templates (telles que des pages HTML).

Exemple :

body
  h1 Phug

Au lieu d'écrire de verbeuses balises, Phug est structuré par indentation (comme le Python, CoffeeScript ou Stylus). Ici <h1> est à l'intérieur de <body> car h1 a un niveau d'indentation de plus. Essayez de supprimer les espaces, vous verrez que <h1> et <body> sont alors au même niveau. Vous pouvez indenter avec autant de tabulations ou espaces que vous voulez, Phug va toujours essayer de deviner la structure en détectant les niveaux d'indentation. Toutefois, il est recommandé d'avoir une indentation constante dans vos fichiers.

En tant que moteur de templates, Phug fournit aussi un moyen optimisé de gérer les valeurs dynamiques.

Exemple :

- $var = true
if $var
  p S'affiche si $var vaut true
else
  p S'affiche sinon

Essayez de passer $var à false pour voir comment réagit le template.

Les variables peuvent être définies à l'extérieur des templates (par exemple dans les contrôleurs):

label Nom
input(value=$nom)
[
  'nom' => 'Bob',
]

Phug est écrit en PHP, donc par défaut, les expressions sont en PHP, mais vous pouvez activer des modules tels que : js-phpize ou utiliser un wrapper comme pug-php qui active par défaut js-phpize. Voyez la comparaison ci-dessous :

Phug:

p=$arr['obj']->a . $arr['obj']->b
[
  'arr' => [
    'obj' => (object) [
      'a' => 'A',
      'b' => 'B',
    ],
  ],
]

Pug-php:

p=arr.obj.a + arr.obj.b
[
  'arr' => [
    'obj' => (object) [
      'a' => 'A',
      'b' => 'B',
    ],
  ],
]

Maintenant que vous savez ce qu'est Phug, vous pouvez :

Si vous n'êtes pas sûr que Phug est adapté à vos besoins, consultez la section Pourquoi ci-dessous :

Pourquoi Phug ?

Le HTML est né en 1989 au CERN, et il remplissait très bien son office : écrire des pages de texte avec des titres et des liens. De nos jours, nous construisons des interfaces utilisateurs pour des supports très variés et le HTML final que nous envoyons au navigateur peut vite être verbeux. Et même si nous devons envoyer du HTML, nous pouvons utiliser un langage plus propre et le compiler.

Beaucoup de moteur de template se contentent de permettre l'insertion d'éléments dynamiques dans du HTML, avec un moteur comme Phug, vous n'écrivez plus du tout de HTML et bénéficiez d'une floppée d'outils dédiés aux templates: layouts, mixins, conditions, itérations, etc.

Notez que Phug supporte plusieurs versions et doctypes différents de HTML mais également XML, et que vous pouvez aisément créer n'importe quel autre format qui puisse vous être utile, tout comme vous pouvez personnaliser le traitement des expressions, etc. Phug a beaucoup d'options et de possibilités d'extension, vraiment beaucoup.

Pourquoi un moteur de templates ?

La majorité des frameworks PHP ont un système de templates. C'est un moyen efficace de séparer la couche de présentation. Déléguer la responsabilité de la vue à un moteur de template est une bonne pratique car cela évite de mélanger de la logique (calcul, récupération et traitement de données) avec de la présentation (affichage, formattage) et vous aidera à respecter le PSR (principe de responsabilité unique qui vise en programmation à ne pas donner à une entité de multiples responsabilités), vous pourrez alors organiser vos vues en pages et composants visuels sans aucune contrainte vis-à-vis de votre code PHP.

Enfin, si vous respectez ce principe (en évitant entre autre d'insérer des traitements dans vos templates), alors vos templates ne contiendront pas de code complexe, mais simplement des insertions de variable, ce qui rend ce code aisément modifiable même pour quelqu'un qui ne connaît pas le back-end de votre application, le développeur qui a la responsabilité de modifier les fichiers .pug n'a apriori même pas besoin de connaître le PHP.

Pourquoi pas pugjs ?

N'est-il pas possible d'utiliser le package JavaScript de pug dans une application PHP ? Si. Il y a même de nombreuses façons d'y arriver. Voyez la section alternatives pour plus de détails. Mais sachez que cette approche a des limites. La plus importante est l'aplatissement des données. N'importe quelle instance de classe deviendra un objet plat pour être passé à pugjs, c'est-à-dire qu'il perdra ces méthodes.

Voici un exemple de ce que Phug permet, mais que pugjs ne pourra pas faire s'il est appelé à travers un proxy ou une commande :

p=aujourdhui.format('d/m/Y H:i')
[
  'aujourdhui' => new DateTime('now'),
]

Pourquoi migrer vers Phug ?

Vous utilisez peut-être déjà une autre librairie PHP proposant la syntaxe Pug.

Tout d'abord si vous n'utilisez pas composer, je ne peux que vous encourager à adopter ce système de gestion des dépendences. Il est sans équivalent dans l'écosystème PHP en termes de paquets disponibles et il vous permettra de garder vos dépendences à jour très facilement. Je vous invite donc à choisir votre librairie parmi celle disponible via composer (voir https://packagist.org/)

Ensuite, sachez que si vous utilisez un projet dont le nom contient "jade", il y a de grandes chances qu'il soit obsolète car jade est l'ancien nom de pug, par exemple les packages kylekatarnls/jade-php, ronan-gloo/jadephp, opendena/jade.php, jumplink/jade.php, dz0ny/jade.php et everzet/jade sont tous basés sur le projet de base et aucun n'est plus maintenu, ils ne sont pas compatibles avec pugjs 2 et beaucoup de fonctionnalités de pug leur font défaut. Le projet le plus à jour qui les remplace tous est pug-php/pug. Dans sa version 2, il utilise toujours le même moteur. Sa version 3 quant à elle utilise désormais Phug. De même talesoft/tale-jade a été remplacé par talesoft/tale-pug et la version 2 de talesoft/tale-pug utilisera, elle aussi, Phug.

talesoft/tale-pug et pug-php/pug sont les ports PHP de Pug les plus utilisés et sont activement maintenus. En utilisant la dernière version de ces projets, vous allez donc automatiquement migrer sur le moteur Phug et étant donné que les contributeurs de ces deux projets sont maintenant tous regroupés dans le projet Phug et déveloperont en priorité Phug, vous bénéficierez du meilleur support possible.

Pour mettre à jour pug-php/pug et bénéficier de toutes les fonctionnalités décrites dans cette documentation, exécutez la commande suivante dans votre projet :

composer require pug-php/pug:"^3.0"

Pour être prévenu de la sortie de la version 2 de talesoft/tale-pug vous pouvez utiliser https://www.versioneye.com/ et ajouter talesoft/tale-pug à votre liste des paquets à surveiller.

Enfin, nous pouvons vous assurer que Phug surpasse les autres implémentations existantes sur de nombreux sujets :

  • Extensibilité, personalisation, formats, options
  • Intégration et installation très simple dans les différents frameworks
  • Documentation
  • Outil de test en live
  • Gestion des expressions (js-phpize ou n'importe tel language personnalisé)
  • Gestion des assets et de la minification (pug-assets)
  • Traçace des erreurs
  • Profiling
  • Réactivité de la communauté (issues et pull-request sur GitHub, et les tags [pug] [php] sur https://stackoverflow.com/search?q=pug php)

Installation

Dans votre framework préféré

Si vous utilisez l'un des frameworks suivant, cliquez sur le lien correspondant pour installer phug directement dans votre application.

Les adapteurs ci-dessus utilisent pug-php 3, ce qui signifie que les expressions doivent être écrite en JS par défaut, mais vous pouvez utiliser le style natif PHP en règlant l'option expressionLanguage sur php.

Si vous souhaitez que nous supportions d'autres frameworks, veuillez ouvrir une issue (un ticket) sur GitHub : https://github.com/phug-php/phug/issues/new et si votre issue reçoit des vôtes, nous y travaillerons.

Dans votre CMS préféré

Installation initiale

Premièrement, vous aurez besoin de composer si vous ne l'avez pas déjà : https://getcomposer.org/download/

Puis installez phug en exécutant la commande suivante dans le dossier de votre application :

composer require phug/phug

Remplacez composer par php composer.phar si vous avez installé composer localement. IL en va de même pour toutes les autres commandes composer mentionnées dans cette documentation.

Créez un fichier PHP avec le contenu suivant :

<?php

include_once __DIR__ . '/vendor/autoload.php';

Phug::display('p=$message', [
  'message' => 'Bonjour',
]);

Vous pouvez éditer les premiers et seconds arguments de Phug::display dans les éditeurs de code ci-dessous et voir le résultat dans le panneau de droite.

p=$message
[
  'message' => 'Bonjour',
]

Phug::display prend le contenu du template en premier argument, les valeurs des variables en deuxième argument optionnel et un troisième argument optionnel permet de spécifier des options (voir Options chapter).

Vous pouvez utiliser Phug::displayFile pour afficher un fichier de template :

Phug::displayFile('dossier-des-vues/mon-template.pug');

Les mêmes arguments optionnels, variables et options, sont disponibles.

Vous pouvez aussi retourner le résultat au lieu de l'afficher avec Phug::render et Phug::renderFile.

La classe Phug agit aussi comment une façade de la classe Renderer, ce qui veut dire que vous pouvez appeler statiquement sur Phug\Phug n'importe quel méthode de Phug\Rebderer. Par exemple, cela rend compile et compileFile disponibles :

file_put_contents('cache/ma-page-compilée.php', Phug::compileFile('vue/mon-template.pug'));

Ce code va compiler le fichier de template vue/mon-template.pug et l'enregistrer dans cache/ma-page-compilée.php, c'est basiquement ce que fait notre option de cache.

Vous pouvez remarquer que le fichier PHP contient du code de débogage, ce code permet de fournir des traces d'erreur précise (line et colonne dans le fichier source pug) et des outils de profilage pour vérifier la performance des différents composants du template.

En production, vous pouvez facilement désactiver ces outils avec setOption :

Phug::setOption('debug', false);

echo Phug::compile('p=$utilisateur');

Ceci va afficher le fichier compilé sans code de débogage.

Consultez toutes les méthodes disponibles dans la référence de l'API :

Utiliser des expressions JavaScript

Pour supporter des expressions de style JS, installez l'extension js-phpize pour phug :

composer require js-phpize/js-phpize-phug

Remplacez composer par php composer.phar si vous avez installé composer localement.

Puis activez l'extension avant d'appeler la méthode render ou display :

<?php

use JsPhpize\JsPhpizePhug;

include_once __DIR__ . '/vendor/autoload.php';

Phug::addExtension(JsPhpizePhug::class);

Phug::display('p=utilisateur', [
  'utilisateur' => 'Bob',
]);
label Nom d'utilisateur
input(value=utilisateur)
[
  'utilisateur' => 'Bob',
]

Pour utiliser les expressions PHP dans Pug-php, utilisez l'option expressionLanguage :

<?php

use Pug\Pug;

include_once __DIR__ . '/vendor/autoload.php';

$pug = new Pug([
    'expressionLanguage' => 'php',
]);

$pug->display('p=$utilisateur->nom', [
  'utilisateur' => (object) [
    'nom' => 'Bob',        
  ],
]);
label Nom d'utilisateur
input(value=$utilisateur->nom)
[
  'utilisateur' => (object) [
    'nom' => 'Bob',        
  ],
]

Changer de language à l'intérieur des templates

Depuis la version 2.1.0 de js-phpize-phug, il est maintenant possible de changer le style des expressions à l'inétérieur des templates.

body
  //- Quelque soit l'option, on passe en mode js
  language js
  - counter = 0
  
  node-language php
  div
    //- Ce noeud (la balise div) et tous ses enfants
    //- utiliseront par défaut le mode php
    - $counter++
    span= $counter++
    //- On passe en mode js jusqu'à nouvel ordre
    language js
    - counter++
    - counter++
    //- Et à nouveau en php
    language php
    p= $counter

  section
    //- En ressortant du noeud (balise div), on repasse au mode
    //- précédent
    p= counter
    //- language et node-language sont aussi disponible en
    //- utilisant des commentaires
    //- @language php
    p= $counter

Utilisation

Créez un ou plusieurs dossiers de templates, et créez vos fichiers pug dedans.

Par exemple, imaginons que maVue.pug soit dans le dossier dossiers/des/vues et contienne :

h1=$titre
[
  'titre' => 'Entête',
]

Vous pouvez alors afficher le rendu de ce fichier de la manière suivante :

<?php

include_once __DIR__ . '/vendor/autoload.php';

$variables = [
   'titre' => 'Entête',
];

$options = [
  'paths' => [
    'dossiers/des/vues',
  ],
];

Phug::displayFile('maVue', $variables, $options);

Il est recommandé d'utiliser displayFile autant que possible pour des raisons de performances (displayFile est plus performant que echo renderFile) et les fichiers pour être mis en cache plus rapidement qu'un contenu brut, donc en production displayFile('maVue.pug') est plus rapide que display('le contenu du fichier').

En production, il est également recommandé d'utiliser l'Optimizer et le cache :

<?php

include_once __DIR__ . '/vendor/autoload.php';

// À remplacer par votre propre calcul d'environnement.
$environnement = getenv('ENVIRONNEMENT') ?: 'production';

$variables = [
   'titre' => 'Entête',
];

$options = [
  'debug'     => false,
  'cache_dir' => 'chemin/du/dossier-de-cache', 
  'paths'     => [
    'dossiers/des/vues',
  ],
];

if ($environnement === 'production') {
    \Phug\Optimizer::call('displayFile', ['maVue', $variables], $options);

    exit;
}

$options['debug'] = true;
$options['cache_dir'] = null;

Phug::displayFile('maVue', $variables, $options);

L'Optimizer est un outil qui permet de ne pas charger le moteur de Phug si le fichier est en cache. En contre-partie, il ne permet pas de changer l'adapter ou d'utiliser les événements post-rendu.

Si vous utilisez Pug-php, remplacez juste dans le code ci-dessus \Phug\Optimizer par \Pug\Optimizer et Phug::displayFile par \Pug\Facade::displayFile.

Le cache peut être utilisé en développement également pour gagner du temps.

Enfin en production, vous devriez utiliser l'option --optimize-autoloader de composer pour optimier l'autoloader lors de l'installation des dépendences. Puis vous devriez cacher l'intégralité de vos templates pour bénéficier de l'option up_to_date_check

composer update --optimize-autoloader
./vendor/bin/phug compile-directory dossiers/des/vues chemin/du/dossier-de-cache '{"debug":"false"}'

En faisant ça à chaque déploiement, vous pouvez alors régler l'option up_to_date_check à true pour charger directement le cache sans vérification des fichiers.

En environnement de développement, vous pouvez mettre à jour automatiquement le cache et recharger automatiquement la page en utilisant le watcher.

Voyez aussi la section CLI pour plus d'informations sur la ligne de commande.

Référence de l'API

Ceci est un résumé des méthodes principales disponibles. Vous pouvez également consulter la documentation auto-générée en cliquant sur le lien ci-dessous :

Documentation complète de l'API

displayFile

$template = '/mon/fichier/template.pug';
$variablesLocales = [
  'nomDeVariable' => 'valeur',
];
$options = [
  'cache_dir' => '/chemin/vers/un/dossier/de/cache',
];


// syntaxe Facade (avec Phug)
Phug::displayFile($template, $variablesLocales, $options);


// syntaxe Instance (avec Phug)
$renderer = new Phug\Renderer($options);
$renderer->displayFile($template, $variablesLocales);


// syntaxe Facade (avec Pug-php)
Pug\Facade::displayFile($template, $variablesLocales, $options);


// syntaxe Instance (avec Pug-php)
$renderer = new Pug($options);
$renderer->displayFile($template, $variablesLocales);

Ceci va afficher le fichier de template rendu dans la sortie standard. C'est la manière recommandée d'utiliser Phug.

Consultez la section utilisation pour voir comment optimiser le rendu en production.

Consultez la section des options pour voir la liste complète des options disponibles.

display

Se comporte comme displayFile mais prend du code source pug comme premier argument :

$template = '
div: p.examples
  em Foo
  strong(title="Bar") Bar
';


// syntaxe Facade (avec Phug)
Phug::display($template, $variablesLocales, $options);


// syntaxe Instance (avec Phug)
$renderer = new Phug\Renderer($options);
$renderer->display($template, $variablesLocales);


// syntaxe Facade (avec Pug-php)
Pug\Facade::displayString($template, $variablesLocales, $options);


// syntaxe Instance (avec Pug-php)
$renderer = new Pug($options);
$renderer->displayString($template, $variablesLocales);

Note: Si vous utilisez Pug-php, il va essayer de détecter si la chaîne passée est un chemin de fichier et utiliser displayFile si c'est le cas. Ce comportement existe pour raison de rétro-compatibilité mais vous êtes encouragé à le désactiver en réglant l'option "strict" à true ou utilisez displayString.

renderFile

renderFile est comme displayFile mais retourne la sortie au lieu de l'afficher.

render

render est comme display mais retourne la sortie au lieu de l'afficher.

Note: Si vous utilisez Pug-php, il va essayer de détecter si la chaîne passée est un chemin de fichier et utiliser renderFile si c'est le cas. Ce comportement existe pour raison de rétro-compatibilité mais vous êtes encouragé à le désactiver en réglant l'option "strict" à true ou utilisez renderString.

renderAndWriteFile

Effectue le rendu d'un fichier pug puis écrit le résultat final (souvent du HTML) dans un fichier.

Retourne true en cas de succès, false sinon.

$input = '/mon/fichier/template.pug';
$output = '/ma/page.html';
$variablesLocales = [
  'nomDeVariable' => 'valeur',
];
$options = [/* ... */];


// syntaxe Facade (avec Phug)
if (Phug::renderAndWriteFile($input, $output, $variablesLocales, $options)) {
  echo 'Fichier écrit avec succès.';
}


// syntaxe Instance (avec Phug)
$renderer = new Phug\Renderer($options);
if ($renderer->displayFile($template, $variablesLocales)) {
  echo 'Fichier écrit avec succès.';
}


// syntaxe Facade (avec Pug-php)
if (Pug\Facade::displayFile($template, $variablesLocales, $options)) {
 echo 'Fichier écrit avec succès.';
}


// syntaxe Instance (avec Pug-php)
$renderer = new Pug($options);
if ($renderer->displayFile($template, $variablesLocales)) {
  echo 'Fichier écrit avec succès.';
}

renderDirectory

Effectue le rendu de tous les fichiers pug contenus (récursivement) dans un dossier d'entrée et créer les fichiers obtenus dans un dossier de sortie. Si aucun dossier de sortie n'est spécifié, le même dossier sera utilisé pour l'entrée et la sortie.

Retourne un array avec le nombre de succès et le nombre d'erreurs.

$renderer = new Phug\Renderer($options); // ou $renderer = new Pug($options);

$renderer->renderDirectory('./mes-vues'); // effectue le rendu de tous les fichiers pug dans ./mes-vues
                                          // et écrit les fichiers .html côte-à-côté
$renderer->renderDirectory('./mes-vues', ['foo' => 'bar']); // la même chose avec des variables locales
$renderer->renderDirectory('./mes-vues', './mes-pages'); // effectue le rendu de tous les fichiers pug dans ./mes-vues
                                                       // et écrit les fichiers .html dans ./mes-pages
$renderer->renderDirectory('./mes-vues', './mes-pages', ['machin' => 'chose']); // la même chose avec des variables locales
$renderer->renderDirectory('./mes-vues', './mes-pages', '.xml'); // utilise l'extension .xml au lieu de celle par défaut .html
$renderer->renderDirectory('./mes-vues', './mes-pages', '.xml', ['foo' => 'bar']); // la même chose avec des variables locales

compileFile

Compile a file and return the rendering code. It means when renderFile typically returns HTML, compileFile mostly returns PHP, by executing this PHP code, you would get the final HTML.

This may allow you for example to delegate the view rendering and caching to an other engine/framework.

$template = '/mon/fichier/template.pug';
$options = [
  // options de compilation seulement puisque les
  // options de rendu ne seront pas utilisées.
];

// syntaxe Facade (avec Phug)
Phug::compileFile($template, $options);


// syntaxe Instance (avec Phug)
$renderer = new Phug\Renderer($options);
$renderer->compileFile($template);


// syntaxe Facade (avec Pug-php)
Pug\Facade::compileFile($template, $options);


// syntaxe Instance (avec Pug-php)
Phug::display($template, $variablesLocales, $options);

compile

Se comporte comme compileFile mais prend du code source pug comme premier argument.

cacheFile

Compile un fichier pug et l'enregistre dans le dossier cache spécifié via les options.

cacheFileIfChanged

Compile un fichier pug et l'enregistre dans le dossier cache spécifié via les options à moins qu'un fichier à jour soit déjà présent dans le cache.

cacheDirectory

Met en cache tous les fichiers pug contenu (récursivement) dans le dossier passé en argument.

Méthodes avancées

Voir la documentation complète de l'API.

Référence du language

Comme Phug est aligné sur pugjs, ce chapitre est quasiment le même que celui que vous pouvez trouver sur la documentation de pugjs à lexception des spécificités de Phug et des éditeurs de code qui vous permettent de tester le rendu du moteur Phug.

Attributs

Les attributs des balises s'écrivent comme en HTML (avec des virgules optionnelles), mais leurs valeurs sont de simples expressions.

a(href='google.com') Google
="\n"
a(class='button' href='google.com') Google
="\n"
a(class='button', href='google.com') Google

(="\n" sert uniquement à ajouter des sauts de ligne entre les liens pour une meilleure lecture du rfinu HTML).

Les expressions PHP fonctionnent par défaut dans Phug et Tale-pug ; et dans Pug-php avec l'option expressionLanguage réglée sur php:

- $authentifié = true
body(class=$authentifié ? 'auth' : 'anon')

Les expressions JavaScript fonctionnent également avec js-phpize-phug installé (voir comment l'installer) ou la dernière version de Pug-php avec les options par défaut (soit expressionLanguage réglée sur js) :

- var authentifié = true
body(class=authentifié ? 'auth' : 'anon')

Attributs multi-lignes

Si vous avez beaucoup d'attributs, vous pouvez les séparer en plusieurs lignes :

input(
  type='checkbox'
  name='souscription'
  checked
)

Les textes multi-lignes ne sont pas un problème que vous utilisez les expressions JS ou PHP :

input(data-json='
  {
    "très-long": "du texte",
    "données": true
  }
')

Note : si vous migrez des templates depuis un projet JavaScript, vous pourriez avoir à remplacer ` par des apostrophes ' ou des guillemets ".

Noms d'attributs échappés

Si les noms de vos attributs contiennent des carachtères spéciaux qui pourrait interférer avec la syntaxe des expressions, échappez-le avec "" ou '', ou utilisez des virgules pour séparer les différents attributs. Les caractères concernés sont par exemple [] et () (fréquemment utilisés avec Angular 2).

//- Ici, `[click]` est compris comme une position
//- d'array, il en résulte donc une erreur
div(class='div-class' [click]='play()')
div(class='div-class', [click]='play()')
div(class='div-class' '[click]'='play()')

Interpolation d'attribut

Une simple note à propos d'une syntaxe que vous avez pu connaître avec pugjs 1: a(href="/#{url}") Lien, cette syntaxe n'est plus valide depuis pugjs 2 et nous avons décidé de ne pas la supporter non plus dans Phug. Vous pouvez utiliser l'interpolation native en fonction du style d'expression:

Interpolation PHP (options par défault de Phug sans le module js-phpize):

- $btnType = 'info'
- $btnTaille = 'lg'
button(type="button" class="btn btn-$btnType btn-$btnTaille")
- $btn = (object) ['taille' => 'lg']
button(type="button" class="btn btn-{$btn->taille}")

Pour les expressions JS (options par défault de Pug ou avec le module js-phpize):

- btnType = 'info'
- btnTaille = 'lg'
button(type="button" class=`btn btn-${btnType} btn-${btnTaille}`)
- btn = {taille: 'lg'}
button(type="button" class=`btn btn-${btn.taille}`)

On encore utilisez la concaténation " + btnType + ".

Attributs bruts

Par défaut, tous les attributs sont échappés pour éviter les attaques (telles que les attaques XSS). Si vous avez besoin d'utiliser les caractères spéciaux, utilisez != au lieu =.

div(escaped="&lt;code>")
div(unescaped!="&lt;code>")

Attention, le code non échappé peut être dangereux. Vous devez être sûr de toujours sécuriser les entrées du client pour éviter le cross-site scripting.

Attributs non vérifiés

Ceci est un concept propre à Phug que vous ne trouverez pas dans pugjs, il s'agit de la vérification des variables.

En PHP, quand les erreurs sont affichées et que le niveau d'erreur inclus les notices, l'appel d'une variable non définie déclenche une erreur.

Par défaut, nous cachons ces erreurs, mais cela peut parfois cacher un bug, donc vous pouvez utiliser l'opérateur ?= pour éviter ce comportement :

- $mauvais = ''
img(src?=$mauvai)

Dans cet exemple, l'opérateur ?= va révélé l'erreur orthographique de "mauvais". Cliquez sur le bouton [Preview] pour voir l'erreur.

Les attributs peuvent être à la fois bruts et non vérifiés :

- $html = '&lt;strong>OK&lt;/strong>'
img(alt?!=$html)

Pour désactiver globalement la vérification (toujours afficher une erreur si la variable appelée n'est pas définie) utilisez l'option php_token_handlers :

Phug::setOption(['php_token_handlers', T_VARIABLE], null);

Attributs booléens

Les attributs booléens sont reflétés par Phug. Les valeurs booléennes (true et false) sont acceptées. Quand aucune valeur n'est spécifiée, true est assigné par défaut.

input(type='checkbox' checked)
="\n"
input(type='checkbox' checked=true)
="\n"
input(type='checkbox' checked=false)
="\n"
input(type='checkbox' checked='true')

Si le doctype est html, Phug sait qu'il ne doit pas refléter les attributs, et il utilise le style concis (compris par tous les navigateurs).

doctype html
="\n"
input(type='checkbox' checked)
="\n"
input(type='checkbox' checked=true)
="\n"
input(type='checkbox' checked=false)
="\n"
input(type='checkbox' checked='checked')

Attributs de style

L'attribut style peut être une chaîne de caractères, comme n'importe quel attribut ; mais également un objet ou un array.

Style PHP :

a(style=['color' => 'red', 'background' => 'green'])
="\n"
a(style=(object)['color' => 'red', 'background' => 'green'])

Style JS :

a(style={color: 'red', background: 'green'})

Attributs de classe

L'attribut class peut être une chaîne de caractères, comme n'importe quel attribut ; mais également un array.

- $classes = ['foo', 'bar', 'baz']
a(class=$classes)
="\n"
//- S'il y a plusieurs attributs de classe,
//- ils seront fusionnés
a.bang(class=$classes class=['bing'])

Vous pouvez aussi passer un array dont les clés sont les noms des classes et les valeurs sont des conditions pour l'activer.

Style PHP :

- $urlCourrante = '/apropos'
a(ref='/' class=['actif' => $urlCourrante === '/']) Accueil
="\n"
a(href='/apropos' class=['actif' => $urlCourrante === '/apropos']) À propos

Style JS :

- var urlCourrante = '/apropos'
a(href='/' class={actif: urlCourrante === '/'}) Accueil
="\n"
a(href='/apropos' class={actif: urlCourrante === '/apropos'}) À propos

Raccourci de classe

Les classes peuvent être définies avec la syntaxe .classe:

a.boutton

À défaut de balise spécifiée, la balise div sera utilisée :

.contenu

Raccourci d'id

Les IDs peuvent être définies avec la syntaxe #identifiant :

a#lien-principal

À défaut de balise spécifiée, la balise div sera utilisée :

#contenu

&attributes

Prononcé “and attributes”, la syntaxe &attributes peut être utilisée pour fragmenter un array en attribus d'un élément.

Style PHP :

div#foo(data-bar="foo")&attributes(['data-foo' => 'bar'])

Style JS :

div#foo(data-bar="foo")&attributes({'data-foo': 'bar'})

Les exemples ci-dessus utilisent des array litéraux. Mais vous pouvez aussi utiliser des variables dans la valeur est un array. (Voir aussi : Mixin Attributes).

- $attributs = []
- $attributs['class'] = 'baz'
div#foo(data-bar="foo")&attributes($attributs)

Attention, les attributs extraits de &attributes ne sont pas automatiquement échappés. Vous devez vous assurer que toute entrée utilisateur qu'ils pourrait contenir soit sécurisée pour éviter le cross-site scripting (XSS). En passant attributes via un appel de mixin, l'échappement est fait automatiquement.

Case

Le mot-clé case est un raccourci de switch en PHP. Il a la forme suivante :

- $amis = 10
case $amis
  when 0
    p vous n'avez aucun ami
  when 1
    p vous avez un ami
  default
    p vous avez #{$amis} amis

Cas groupés

Vous pouvez utiliser les cas groupés juste comme vous le feriez avec switch en PHP.

- $amis = 0
case $amis
  when 0
  when 1
    p vous avez very few amis
  default
    p vous avez #{$amis} amis

Cepfinent si, en PHP le groupage est automatique si le mot-clé break n'est pas explicitement inclus ; avec Phug, il a lieu seulement si le block est complètement vide.

Si vous souhaitez ne rien afficher du tout pour un cas spécifique, appelez explicitement break :

- $amis = 0
case $amis
  when 0
    - break
  when 1
    p vous avez very few amis
  default
    p vous avez #{$amis} amis

Expension de bloc

L'expension de block peut aussi être utilisée :

- $amis = 1
case $amis
  when 0: p vous n'avez aucun ami
  when 1: p vous avez un ami
  default: p vous avez #{$amis} amis

Code

Phug permet d'écrire du code PHP ou JavaScript dans les templates. Le résultat du code peut être affiché ou non. Quand il est affiché, il peut être échappé ou non, vérifié ou non de la même manière que les attributs.

Code non affiché

Les codes non affichés commencent par -. De base, rien ne sera affiché.

- for ($x = 0; $x &lt; 3; $x++)
  li article

Phug supporte aussi les blocs de code non affichés :

-
  $chiffres = ["Uno", "Dos", "Tres",
               "Cuatro", "Cinco", "Seis"]
each $chiffre in $chiffres
  li= $chiffre

Code affiché

Les codes affichés comment par =. Ils évaluent l'expression PHP ou JavaScript et affichent le résultat. Par mesure de sécurité, les entités HTML sont échappées par défaut.

p
  = 'Ce code est &lt;échapé> !'

Le code peut aussi être écrit sur la même ligne et supporte toutes sortes d'expressions.

p= 'Ce code est ' . '&lt;échapé> !'

Note: si vous utilisez les expressions JavaScript, la concaténations doivent utiliser l'opérateur + :

p= 'Ce code est ' + ' &lt;échapé> !'

Code brut/échappé

Préfixez l'opérateur = avec ! pour ne pas échapper les entitiés HTML, avec ? pour ne pas vérifier les variables et ?! pour faire les deux :

- $début = '&lt;strong>'
- $fin = '&lt;/strong>'
//- Ceci est échappé
div= $début . 'Word' . $fin
//- Ceci n'est pas échappé
div!= $début . 'Word' . $fin
//- Les deux sont vérifiés
div= 'début' . $milieu . 'fin'
div!= 'début' . $milieu . 'fin'

Attention: le code non échappé peut être dangereux. Vous devez vous assurer que les entrées du client sont sécurisées pour éviter le cross-site scripting (XSS).

Code vérifié/non vérifié

Les codes vérifiés ne déclenchent pas d'erreur lorsque des variables sont indéfinies.

Les codes non vérifiés déclenchent une erreur lorsque des variables sont indéfinies. Ci-dessous les codes vérifiés avec une variable dans le premier cas existante, dans le second manquante :

- $milieu = ' milieu '
div?= 'début' . $milieu . 'fin'
div?!= 'début' . $milieu . 'fin'
div?= 'début' . $milieu . 'fin'
div?!= 'début' . $milieu . 'fin'

Commentaires

Les commentaires affichés sur une seule ligne ressemblent à ceux de nombreux langages (C, PHP, JavaScript) et produisent des commentaires HTML dans la page rendue.

Comme les balises, ils doivent apparaître sur leur propre ligne.

// juste quelques paragraphes
p foo
p bar
// chaque ligne
// va produire
// un commentaire HTML
footer

Phug supporte aussi les commentaires masqués (ils ne seront pas compilés, vous pouvez donc en mettre beaucoup sans craindre d'allourdir vos fichiers de cache). Ajoutez simplement un tiret (-) au début du commentaire.

//- rien ne s'affichera dans le HTML
p foo
p bar

Blocs de Commentaire

Les blocs de commentaire fonctionne aussi :

body
  //-
    Commentaires pour les développeurs de templates.
    Utilisez autant de texte que vous voulez.
  //
    Commentaires pour les lecteurs de HTML.
    Utilisez autant de texte que vous voulez.

Commentaires conditionnels

Phug n'a pas de syntaxe spécifique pour les commentaires conditionnels. (Les commentaires conditionnels sont un méthode spécifique à Internet Explorer pour ajouter des balises dédiées à versions anciennes.)

Cependant, comme toutes les lignes commençant par < sont traitées comme du texte brut, la syntaxe HTML normale des commentaires conditionnels fonctionnera juste telle quelle.

doctype html

&lt;!--[if IE 8]>
&lt;html lang="fr" class="lt-ie9">
&lt;![endif]-->
&lt;!--[if gt IE 8]>&lt;!-->
&lt;html lang="fr">
&lt;!--&lt;![endif]-->

body
  p Supporter les anciennes versions des navigateurs, c'est pénible.

&lt;/html>

Conditions

Les conditions ont la forme suivante si vous utilisez le style PHP :

- $utilisateur = [ 'description' => 'foo bar baz' ]
- $autorisé = false
#utilisateur
  if $utilisateur['description']
    h2.vert Description
    p.description= $utilisateur['description']
  else if $autorisé
    h2.bleu Description
    p.description.
      L'utilisateur n'a pas de description.
      Pourquoi ne pas en ajouter une ?
  else
    h2.rouge Description
    p.description L'utilisateur n'a pas de description.

Si vous utilisez le style JS :

- var utilisateur = { description: 'foo bar baz' }
- var authorised = false
#utilisateur
  if utilisateur.description
    h2.vert Description
    p.description= utilisateur.description
  else if authorised
    h2.bleu Description
    p.description.
      L'utilisateur n'a pas de description.
      Pourquoi ne pas en ajouter une ?
  else
    h2.rouge Description
    p.description L'utilisateur n'a pas de description.

Phug fournit aussi le mot-clé unless, qui fonctionne comme un if négatif.

Exemple en style PHP :

unless $utilisateur['estAnonyme']
  p Vous êtes connecté en tant que #{$utilisateur['nom']}.
//- est équivalent à
if !$utilisateur['estAnonyme']
  p Vous êtes connecté en tant que #{$utilisateur['nom']}.
[
  'utilisateur' => [
    'estAnonyme' => false,
    'nom' => 'Jefferson Airplane',
  ],
]

Exemple en style JS :

unless utilisateur.estAnonyme
  p Vous êtes connecté en tant que #{utilisateur.nom}.
//- est équivalent à
if !utilisateur.estAnonyme
  p Vous êtes connecté en tant que #{utilisateur.nom}.
[
  'utilisateur' => [
    'estAnonyme' => false,
    'nom' => 'Jefferson Airplane',
  ],
]

Doctype

doctype html

Doctype raccourics

Raccourics vers les doctypes courants :

doctype html
doctype xml
doctype transitional
doctype strict
doctype frameset
doctype 1.1
doctype basic
doctype mobile
doctype plist

Doctypes personnalisés

Vous pouvez aussi utiliser votre propre doctype :

doctype html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN"

Option doctype

En plus d'ajouter le doctype HTML, le doctype influe sur la manière dont Phug va formatter les balises auto-fermante /> en XML, > en HTML 5. Il change aussi l'affichage des attributs booléens.

Si, pour quelque raison que ce soit, il n'est pas possible d'utiliser le mot-clé doctype (par exemple, afficher une portion de HTML seule), mais que vous voulez appliquer les spécificités du doctype, vous pouvez utiliser l'option doctype.

$source = 'img(src="foo.png")';

Phug::render($source);
// => '<img src="foo.png"/>'

Phug::render($source, [], [
  'doctype' => 'xml',
]);
// => '<img src="foo.png"></img>'

Phug::render($source, [], [
  'doctype' => 'html',
]);
// => '<img src="foo.png">'

Filtres

Les filtres vous permettent d'utiliser d'autres languages dans vos templates Pug. Ils prennent un bloc de texte comme entrée.

Pour passer des options au filtre, ajouter les entre parenthèses après le nom du filtre comme vous le fériez pour les attributs de balises): :less(compress=false).

Tous les modules JSTransformer peuvent être utilisés comme des filtres. Par exemple :babel, :uglify-js, :scss, ou :markdown-it. Consultez la documentation du JSTransformer pour connaître les options supportées par ce filtre spécifique.

Pour utiliser JSTransformer avec Phug, installez l'extension suivante :

composer require phug/js-transformer-filter

Puis activez-la :

use Phug\JsTransformerExtension;

Phug::addExtension(JsTransformerExtension::class);

Avec Pug-php, cette extension est déjà installée et activée par défaut depuis la version 3.1.0.

Vous pouvez aussi utiliser les projets PHP suivants en tant que filtres dans n'importe quel projet Phug ou Pug-php : http://pug-filters.selfbuild.fr

Si vous ne trouvez pas de filtre approprié pour votre cas d'utilisation, vous pouvez écrire vos propres filtres. Ce peut être n'importe quel callable (closure, fonction ou objet possédant une méthode __invoke).

Phug::setFilter('maj-debut', function ($texte, $options) {
  return strtoupper(mb_substr($texte, 0, $options['longueur'])).
    mb_substr($texte, $options['longueur']);
});

Phug::display('
div
  :maj-debut(longueur=3)
    gggggg
');

Ce qui va afficher :

<div>GGGggg</div>

La même option est disponible dans Pug-php :

$pug = new Pug();
$pug->setFilter('maj-debut', function ($texte, $options) {
  return strtoupper(mb_substr($texte, 0, $options['longueur'])).
    mb_substr($texte, $options['longueur']);
});

$pug->display('
div
  :maj-debut(longueur=3)
    gggggg
');

Le compiler courant est passé en troisième argument, vous pouvez donc en tirer n'importe quelle information :

Phug::setFilter('chemin', function ($texte, $options, CompilerInterface $compiler) {
  return $compiler->getPath();
});

Phug::displayFile('un-template.pug');

Dans cet exemple, si un-template.pug contient des appels au filtre :chemin, le chemin complet du fichier en cours de compilation va s'afficher (exemple : /dossier/un-template.pug ou quelque autre fichier si appelé à l'intérieur d'un fichier étendu ou inclut).

Phug inclut le filtre cdata :

data
  :cdata
    Puisque c'est une section CDATA
    Je peux utiliser toute sorte de caractères
    comme > &lt; " et &
    ou écrire des choses comme
    &lt;machin>&lt;/chose>
    mais mon document reste bien formé

Pug-php pré-installe JsTransformerExtension et embarque cdata, css, escaped, javascript, php, pre, script et si vous n'avez pas réglé l'option filterAutoLoad à false, toutes les classes invokable du namespace Pug\Filter seront chargées comme filtres automatiquement :

doctype html
html
  head
    :css
      a {
        color: red;
      }
  body
    :php
      $calcul = 9 + 8
    p=calcul
    :escaped
      &lt;truc>
    :pre
      div
        h1 Exemple de code Pug
    :javascript
      console.log('Salut')
    :script
      console.log('Alias pour javascript')

Include

Le mot-clé include vous permet d'insérer le contenu d'un fichier Pug dans un autre.

//- index.pug
doctype html
html
  include includes/head.pug
  body
    h1 Mon site web
    p Bienvenue sur mon super site
    include includes/foot.pug
//- includes/head.pug
head
  title Mon site web
  script(src='/javascripts/jquery.js')
  script(src='/javascripts/app.js')
//- includes/foot.pug
footer#footer
  p Tous droits réservés
    =date(' Y')

Si le chemin est absolu (c.f. include /racine.pug), it sera résolus depuis les chemins choisis via l'option paths. Cette option fonctionne comme basedir dans pugjs mais permet de spécifier plusieurs dossiers. L'option basdir existe aussi dans Pug-php pour fournir une pleine compatibilité avec les options de pugjs mais nous vous recommandons d'utiliser paths de préférence.

Sinon, les chemins sont résolus relativement au fichier courrant en court de compilation.

Si le fichier n'a pas d'extension, .pug sera ajouté automatiquement.

Inclusion de texte brut

Tout autre fichier que les fichiers pug seront simplement inclus comme du texte brut.

//- index.pug
doctype html
html
  head
    style
      include style.css
  body
    h1 Mon site web
    p Bienvenue sur mon super site
    script
      include script.js
/* style.css */
h1 {
  color: red;
}
// script.js
console.log('Vous êtes fantastique');

Inclusion de texte filtré

Vous pouvez appliquer des filtres lors de l'inclusion, ce qui vous permet de gérer d'autres formats de fichiers.

//- index.pug
doctype html
html
  head
    title Un article
  body
    include:markdown article.md
# article.md

Ceci est un article écrit en markdown.

Héritage de template

Phug supporte l'héritage de template via les mots-clés block et extends.

Dans un template, un block est simplement un “bloc” de code Pug qu'un template enfant peut remplacer. The processus est récursif.

Les blocs Phug peuvent fournir un contenu par défaut, si approprié. Ce qui reste purement optionnel. L'exemple ci-dessous définit block scripts, block contenu, et block pied.

//- disposition.pug
html
  head
    title Mon site web - #{$titre}
    block scripts
      script(src='/jquery.js')
  body
    block contenu
    block pied
      #pied-de-page
        p Contenu du pied de page
[
  'titre' => 'Journal',
]

*Pas besoin de $ si vous utilisez des expressions JS

Pour étendre cette disposition, créez un nouveau fichier et utilisez la directive extends avec un chemin vers le template parent. (Si aucune extension de fichier n'est fournie, .pug sera ajouté automatiquement au nom de fichier.) Puis définissez un ou plusieurs blocs à remplacer.

Ci-dessous notez que le bloc pied n'est pas redéfini, donc il va utiliser le contenu par défaut reçu du parent et afficher “Contenu du pied de page”.

//- page-a.pug
extends disposition.pug

block scripts
  script(src='/jquery.js')
  script(src='/pets.js')

block contenu
  h1= $titre
  each $nom in $animaux
    include animal.pug
[
  'titre' => 'Blog',
  'animaux'  => ['chat', 'chien']
]
//- animal.pug
p= $nom

*Pas besoin de $ si vous utilisez des expressions JS

Il est aussi possible de remplacer un bloc en fournissant des blocs supplémentaires, comme montré dans l'exemple ci-dessous. Ici, contenu expose désormais deux nouveaux blocs menu et principal qui pourront à leur tour être remplacés. (Alternativement, le template enfant pourrait aussi remplacer le bloc contenu en entier.)

//- sous-disposition.pug
extends disposition.pug

block content
  .menu
    block menu
  .principal
    block principal
//- page-b.pug
extends sous-disposition.pug

block menu
  p Quelque chose

block principal
  p Autre chose

Directives append / prepend

Phug permet de replacer un bloc avec replace (également par défaut), insérer au début du bloc avec prepend, ou d'ajouter à la fin du bloc avec append.

Par exemple si vous avez des scripts par défaut dans un bloc head et que vous souhaitez les utiliser sur toutes les pages, vous pouvez faire ceci :

//- page-disposition.pug
html
  head
    block head
      script(src='/vendor/jquery.js')
      script(src='/vendor/caustic.js')
  body
    block contenu

Maintenant, imaginons une page de votre jeu JavaScript. Vous voulez certains scripts relatif au jeu en plus des scripts par défaut. Vous pouvez simplement utiliser append :

//- page.pug
extends page-disposition.pug

block append head
  script(src='/vendor/three.js')
  script(src='/game.js')

Quand vous utilisez block append ou block prepend, le mot “block” est optionnel :

//- page.pug
extends page-disposition.pug

append head
  script(src='/vendor/three.js')
  script(src='/game.js')

Erreurs fréquentes

L'héritage dans Phug est une fonctionnalité puissante qui permet de séparer des structures de pages complexes en fichiers plus petits et plus simples. Cependant, si vous chaînez beaucoup, beaucoup de templates ensemble, vous pouvez rendre les choses plus compliquées pour vous-même.

Notez que seuls les blocs nommés et les définitions de mixin peuvent apparaître au premier niveau (non indenté) d'un template enfant. C'est important ! Les templates parents définissent une structure globale de page et les templates enfants peuvent seulement append, prepend, ou remplacer des blocs spécifiques de code ou de logique. Si un template enfant essaye d'ajouter du contenu en dehors d'un bloc, Phug n'aura aucun moyen de savoir où le mettre dans la page finale.

Ceci inclut les codes non affichés, dont le placement peut également avoir un impact et les commentaires affichés puisqu'ils produisent du code HTML.

Interpolation

Phug fournit différents opérateurs pour couvrir vos différents besoin en interpolations.

Interpolation de chaîne, échappée

Considérons le code suivant :

- $titre = "Les Misérables";
- $auteur = "Victor Hugo";
- $super = "&lt;span>echappé !&lt;/span>";

h1= $titre
p Écrit par #{$auteur}
p Ceci va être fiable : #{$super}

titre suit le schéma classique d'insertion de variable, mais les codes entre #{ et } est évalué, échappé, et le résultat est inséré dans le texte.

Vous pouvez insérer n'importe quelle expression valide :

- $msg = "pas ma voix intérieure";
p Ce n'est #{strtoupper($msg)}

Phug est assez intelligent pour savoir où l'interpolation finit, même si par exemple ell contient } :

p Pas besoin d'échappement pour #{'}'} !

Si vous avez besoin d'insérer #{ en tant que texte brut, vous pouvez également l'interpoler, ou bien le préfixer d'un anti-slash.

p Échappement par anti-slash \#{interpolation}
p L'interpolation fonctionne aussi avec l'#{'#{interpolation}'} !

Interpolation de chaîne, non échappée

- $affaireRisquée = "&lt;em>Il faut quand même faire attention.&lt;/em>";
.citation
  p Joël: !{$affaireRisquée}

Attention ! Gardez à l'esprit que le code non échappé s'il vient directement d'une entrée utilisateur, présente un risque. Ne faites jamais confiance aux entrées de l'utilisateur !

Interpolation de balise

L'interpolation fonctionne aussi avec du code Phug en utilisant la syntaxe suivante :

p.
  C'est un long et très ennuyeux paragraphe sur plusieurs lignes.
  Soudainement il y a #[strong quelques mots en gras] qui ne
  peuvent être #[em ignorés].
p.
  Et ici un exemple d'interpolation de balise avec un attribut:
  #[q(lang="es") ¡Hola Mundo!]

Encapsulez une déclaration de balise entre #[ et ], et il sera évalué et inséré dans le texte.

Contrôle des espaces blancs

La syntaxe des interpolations de balises est particulièrement utile pour les balises inline, lorsque les espaces blancs avant et après sont important.

Par défaut, cependant, Phug supprime les espaces blancs entre les balises. Voyez dans cet exemple :

p
  | Si vous écrivez ce paragraphe sans interpolations, les balises comme
  strong strong
  | et
  em em
  | pourrait produire des résultats inattendus.
p.
  Avec l'interpolation, les espaces blancs sont #[strong respectés] et #[em tout le monde] est content.

Consultez la section Text brut pour plus d'information et exemples à ce sujet.

Itération

Phug supporte principalement 2 methodes d'itération : each et while.

each

each est le moyen le plus simple de parcourir des arrays et des objets dans un template :

Style PHP :

ul
  each $val in [1, 2, 3, 4, 5]
    li= $val

Style JS :

ul
  each val in [1, 2, 3, 4, 5]
    li= val

Vous pouvez aussi récupérer l'index lorsque vous itérez :

Style PHP :

ul
  each $val, $index in ['zéro', 'un', 'deux']
    li= $index . ' : ' . $val

Style JS :

ul
  each val, index in ['zéro', 'un', 'deux']
    li= index + ' : ' + val

Phug permet aussi d'iétérer les clés d'un objet :

Style PHP :

ul
  each $val, $index in (object) ['un' => 'UN', 'deux' => 'DEUX', 'trois' => 'TROIS']
    li= $index . ' : ' . $val

Style JS :

ul
  each val, index in {un: 'UN', deux: 'DEUX', trois: 'TROIS'}
    li= index + ' : ' + val

L'objet ou l'array que vous itérez peut être une variable, le résultat d'une fonction, ou à peu près n'importe quoi d'autre.

Style PHP :

- $valeurs = []
ul
  each $val in count($valeurs) ? $valeurs : ["Il n'y a aucune valeur"]
    li= $val

Style JS :

- var valeurs = [];
ul
  each val in valeurs.length ? valeurs : ["Il n'y a aucune valeur"]
    li= val

Vous pouvez aussi ajouter un bloc else qui sera exécuté si l'array ou l'objet ne contien aucun élément à itérer. Le code ci-dessous est donc équivalent aux exemples ci-dessus :

Style PHP :

- $valeurs = []
ul
  each $val in $valeurs
    li= $val
  else
    li Il n'y a aucune valeur

Style JS :

- var valeurs = [];
ul
  each val in valeurs
    li= val
  else
    li Il n'y a aucune valeur

Vous pouvez aussi utiliser for comme un alias de each.

Une fonctionnalité spéciales de Phug (non disponible dans pugjs) permet aussi d'utiliser for comme une boucle PHP :

ul
  for $n = 0; $n &lt; 4; $n++
    li= $n

Dernière not au sujet du scope des variables d'itération : soyez conscient qu'elles écrasent les précédentes variables de mêmes noms :

- $index = 'machin'
- $value = 'truc'
each $value, $index in ['clé' => 'valeur']
| $index = #{$index}
| $value = #{$value}

while

Vous pouvez aussi créer des boucles avec while :

- $n = 0
ul
  while $n &lt; 4
    li= $n++

Mixins

Les mixins permettent de créer des blocs de code Phug réutilisables.

//- Déclaration
mixin liste
  ul
    li truc
    li machin
    li chose
//- Utilisation
+liste
+liste

Les mixins sont compilés en fonctions et peuvent prendre des arguments :

mixin animal($nom)
  li.animal= $nom
ul
  +animal('chat')
  +animal('chien')
  +animal('cochon')

Pas besoin de préfixer les paramètres/variables avec $ si vous utiliser le Style JS

Blocs de mixin

Les mixins peuvent aussi prendre un bloc de code Phug qui lui tiendra lieu de contenu :

mixin article($titre)
  .article
    .article-zone
      h1= $titre
      if $block
        block
      else
        p Pas de contenu

+article('Salut tout le monde')

+article('Salut tout le monde')
  p Ceci est mon
  p Super article

Pas besoin de préfixer les paramètres/variables avec $ si vous utiliser le Style JS

Important: Dans pugjs, la variable block est une fonction représentant le bloc. Dans Phug, nous passons seulement un booléen comme indicateur vous permettant de savoir si l'appel au mixins contenant des éléments enfants. Donc vous pouvez utiliser $block n'importe où à l'intérieur de la déclaration du mixin (ou juste block si vous utiliser le Style JS) et vous obtiendrez true si le bloc est rempli, false s'il est vide.

Attributs de mixin

Les mixins peuvent aussi recevoir un argument implicite attributes, qui contient les attributs passé à l'appel du mixin :

Style PHP :

mixin lien($href, $nom)
  //- attributes == {class: "btn"}
  a(class!=$attributes['class'], href=$href)= $nom

+lien('/foo', 'foo')(class="btn")

Style JS :

- var values = [];
mixin lien(href, nom)
  //- attributes == {class: "btn"}
  a(class!=attributes.class href=href)= nom

+lien('/foo', 'foo')(class="btn")

Note: Les valeurs dans attributes sont déjà échappées par défaut. Vous devez donc utiliser != pour éviter un double échappement. (Voir aussi attributs bruts.)

Vous pouvez aussi appeler les mixins avec &attributes:

Style PHP :

mixin lien($href, $nom)
  a(href=$href)&attributes($attributes)= $nom

+lien('/foo', 'foo')(class="btn")

Style JS :

mixin lien(href, nom)
  a(href=href)&attributes(attributes)= nom

+lien('/foo', 'foo')(class="btn")

Note: La syntaxe +lien(class="btn") est aussi valide et équivalente à +lien()(class="btn"), car Phug essaye de détecter si les contenus des parenthèses sont des attributs ou des arguments. Néanmoins nous vous encourageons à utiliser la seconde syntax, car vous passez explicitement aucun argument et vous vous assurrez que la première parenthèse est bien comprise comme la liste des arguments.

Arguments restants

Vous pouvez écrire des mixins qui prennent un nombre inconnu d'arguments en utilisant la syntax des arguments restants (“rest arguments”).

Style PHP :

mixin liste($id, ...$elements)
  ul(id=$id)
    each $element in $elements
      li= $element

+liste('ma-liste', 1, 2, 3, 4)

Style JS :

mixin liste(id, ...elements)
  ul(id=id)
    each element in items
      li= element

+liste('ma-liste', 1, 2, 3, 4)

Component

Comme cette fonctionnalité n'est pas disponible dans Pug.js nous la fournissons en tant que plugin à installer via composer :

composer require phug/component

Les components (composants) sont des mixin multi-slots (plusieurs emplacements remplaçables) similaires aux components de technologies comme Vue.js, Angular, React ou Blade.

Documentation complète : https://github.com/phug-php/component

Texte brut

Phug fournit quatre manières d'obtenir du texte brut — c'est-à-dire n'importe quel code ou contenu textuel qui va, généralement sans traitement, directement dans le rendu HTML. Ils sont utiles dans différentes situations.

Le texte brut permet toujours l'interpolation de texte et de balises, mais comme le texte brut n'est pas échappé, vous pouvez aussi écrire du HTML litéral.

La gestion des espaces blancs dans le rendu HTML peut présenter des écueils, nous vous présenterons comment les éviter à la fin de ce chapitre.

En ligne dans une balise

La méthode la plus simple est d'ajouter le texte à la suite de la balise. Le premier mot de la ligne est la balise elle-même. Tout ce qui arrive après cette balise suivie d'un espace sera du contenu textuel de cette balise. C'est surtout pratique lorsque le texte est court (ou si ça ne vous gêne pas d'avoir des lignes trop longues).

p Ceci est un bon vieux contenu de &lt;em>texte&lt;/em> brut.

HTML litéral

Toutes les lignes qui commencent par un chevron ouvrant (<) sont également traitées comme du texte brut, ce qui peut être utile occasionellement pour écrire des balises HTML telles quelles. Par exemple, un cas d'utilisation est le commentaire conditionnel. Puisque les balises HTML litérales ne sont pas traitées, elle ne sont pas auto-fermantes, à l'inverse des balises Phug.

&lt;html>

body
  p L'ouverture et fermeture de la balise html
  p sont chacun considérés comme une ligne de HTML litéral.

&lt;/html>

Attention: Avec pugjs, indenter le contenu (body dans cet exemple) n'a pas d'incidence. Avec pugjs, seules les lignes commençant par &lt; sont du HTML litéral peut importe l'indentation.

Mais avec Phug (et donc pug-php aussi), nous considérons le contenu indenté après une ligne commençant par &lt; comme étant aussi du HTML litéral et non traité, donc si vous indentez body dans cet exemple, il deviendra du contenu textuel (non traité) de la balise <html>.

Cette fonctionnalité permet de copier-coller du code HTML indenté tel quel depuis n'importe où dans vos templates :

.machin
  #chose
    &lt;p>
      Texte non traité #{'sauf pour les interpolations'}
    &lt;/p>
    &lt;p>
      Titre
      &lt;a href="/lien">
        Boutton
      &lt;/a>
    &lt;/p>
    p Ceci est du code #[strong Phug] à nouveau

Trait vertical

Une autre façon d'ajouter du texte brut dans les templates est de préfixer une ligne avec un trait vertical (|) aussi appelé pipe. Cette méthode est utile pour mélanger du texte brut avec des balise inline, nous en reparlerons juste après dans la section Whitespace Control.

p
  | Le trait vertical va toujours au début de la ligne,
  | l'indentation ne compte pas.

Bloc dans une balise

Souvent vous pouvez avoir besoin de grand blocs de texte à l'intérieur d'une balise. Un bon exemple est d'écrire du code JavaScript et CSS dans les balises script et style. Pour faire ça, ajouter simplement un point . juste après le nom de la balise, ou après la parenthèse fermante si la balise a des attributs.

Il ne doit pas y avoir d'espace entre la balise et le point. Le contenu de la balise doit être indenté d'un niveau :

script.
  if (utilisePhug)
    console.log('vous êtes génial')
  else
    console.log('utilisez Phug')

Vous pouvez aussi créer un bloc de texte brut en utilisant le point après d'autres balises à l'intérieur d'une balise parente.

div
  p Ce texte appartien à la balise p.
  br
  .
    Ce texte appartien à la balise div.

Contrôle des espaces blancs

Gérer les espaces blancs (espaces, sauts de ligne, tabulations) du rendu HTML est une des parties les plus délicates de l'apprentissage de Phug. Mais ne vous inquiétez pas, vous aurez bientôt tous les outils en main pour les gérer facilement.

Vous avez juste besoin de vous souvenir de deux principaux points à propos du fonctionnement des espaces blancs.

Lors de la compilation en HTML :

  1. Phug supprime l'indentation, et tous les espaces blancs entre les élements.
    • Donc, la fermeture d'une balise HTML touchera l'ouverture de la suivante. Ce n'est généralement pas un problème pour les éléments de type block comme les paragraphes, car ils s'afficheront de la même manière dans le navigateur web (à moins que vous n'ayez changé leur propriété display en CSS). Voyez les méthodes ci-dessous pour gérer les cas où vous avez besoin d'insérer un espace entre des éléments.
  2. Phug préserve les espaces blancs à l'intérieur des éléments, ce qui inclut :
    • tous les espaces à l'intérieur d'une ligne de texte,
    • les espaces en début de ligne au-delà de l'indentation du bloc,
    • les espaces en fin de ligne (attention toutefois, selon l'éditeur de code que vous utilisez et les réglages de votre projet, ces espaces peuvent être nettoyés automatiquement),
    • les sauts de lignes dans un bloc de code.

Donc… Phug supprimes les espaces blancs entre les balises, mais les garde à l'intérieur des balises. L'avantage, c'est que vous gardez le plein contrôle sur les balises et/ou textes qui doivent se toucher.

Cela vous permet met de placer des balises au milieu des mots.

| Vous avez mis l'
em ac
| cent sur la mauvaise syl
em la
| ble.

La contrepartie, c'est que vous devez y penser et prendre le contrôle sur la manière dont les balises et le texte se touchent ou non.

Si vous avez besoin que des balises et/ou du texte se touchent — peut-être avez-vous besoin qu'un point apparaisse en dehors d'un lien hypertext à la fin d'une phrase — c'est facile, car c'est basiquement ce que fait Phug sauf contre-indication.

a ...phrase qui se termine par un lien
| .

Si vous avez besoin d'ajouter un espace, vous avez plusieurs options :

Solutions recommandées

Vous pouvez ajouter un ou plusieurs traits verticaux seuls sur leur ligne — un symbole pipe soit suivi d'espaces soit seul. Cela va insérer un espace dans le rendu HTML.

| Ne me
|
button#auto-destruction touche
|
| pas !

Si vos balises inline ne requièrent pas une grande quantité d'attribus, vous pouvez trouver plus simple d'utiliser l'interpolation de balise ou du HTML litéral à l'intérieur d'un bloc de texte brut.

p.
  Utiliser de simples balises peut vous aider à garder
  vos lignes courtes, mais les balises interpolées
  sont beaucoup plus pratiques pour #[em visualiser]
  si les balises sont séparées ou non par des espaces.

Solutions non recommandées

Selon l'endroit où vous avez besoin d'espaces blancs, vous pouvez ajouter un espace supplémentaire en début de texte (après l'indentation de bloc, le symbole pipe, et/ou la balise). Ou vous pouvez ajouter un espace en fin de texte.

NOTEZ les espaces en début et fin de lignes ici :

| Hé, regardez 
a(href="http://exemple.biz/chaton.png") cette photo
|  de mon chat !

La solution ci-dessus fonctionne parfaitement bien, mais est généralement considérée comme dangereuse : beaucoup d'éditeurs de code suppriment par défaut les espaces blancs résiduels à la sauvegarde. Vous et vos contributeurs pourriez avoir à configurer vos éditeurs pour éviter la suppression automatique des espaces.

Tags (balises)

Par défaut, le texte au début d'une ligne (en ignorant les espaces/tabulation d'indentation) représente une balise HTML. Les balises indentées sont imbriquées, créant l'arbre de votre structure HTML.

ul
  li Puce A
  li Puce B
  li Puce C

Phug sait aussi quels éléments sont auto-fermants :

img

Expansion de bloc

Pour économiser de la place, Phug fournit une syntaxe en ligne pour les balises imbriquées.

a: img

Balises auto-fermantes

Les balises telles que img, meta, et link sont automatiquement auto-fermantes (à moins que vous n'utilisiez le doctype XML).

Vous pouvez aussi auto-fermer explicitement une balise en lui ajoutant le symbole /. Ne faites ça que si vous êtes sûr de vous.

machin/
machin(chose='truc')/

Rendu des espaces blancs

Les espaces blancs sont supprimés en début et fin de balises, donc vous avez le contrôle sur le fait que les élémenents HTML doivent se toucher ou non. Le contrôle est généralement géré via le texte brut.

Expressions en style JS

En utilisant le style JS vous n'avez plus besoin des $ devant vos noms de variables mais ce n'est que le début des stupéfiantes transformations fournies par js-phpize. Voici une liste de fonctionnalités plus avancées :

Chaînage

- machin = {truc: [{chose: [42]}]}

p=machin.truc[0].chose[0]

Accès aux arrays/objets

:php
  $obj = (object) ['machin' => 'truc'];
  $arr = ['machin' => 'truc'];

p=obj.machin
p=arr.machin
p=obj['machin']
p=arr['machin']

Appel de getter

:php
  class Machin
  {
    public function __isset($nom)
    {
      return $nom === 'abc';
    }

    public function __get($nom)
    {
      return "getter magique pour $nom";
    }

    public function getTruc()
    {
      return 42;
    }
  }

  $machin = new Machin;

p=machin.truc
p=machin['truc']
p=machin.abc
p=machin['abc']
p=machin.nongere
p=machin['nongere']
:php
  class Machin implements ArrayAccess
  {
    public function offsetExists($nom)
    {
      return $nom === 'abc';
    }

    public function offsetGet($nom)
    {
      return "getter magique pour $nom";
    }

    public function offsetSet($nom, $valeur) {}

    public function offsetUnset($nom) {}

    public function getTruc()
    {
      return 42;
    }
  }

  $machin = new Machin;

p=machin.truc
p=machin['truc']
p=machin.abc
p=machin['abc']
p=machin.nongere
p=machin['nongere']

Closure

p=implode(', ', array_filter([1, 2, 3], function (nombre) {
  return nombre % 2 === 1;
}))

Émulation de Array.prototype

- arr = [1, 2, 3]
p=arr.length
p=arr.filter(nombre => nombre & 1).join(', ')
p=arr.indexOf(2)
p=arr.slice(1).join('/')
p=arr.reverse().join(', ')
p=arr.splice(1, 2).join(', ')
p=arr.reduce((i, n) => i + n)
p=arr.map(n => n * 2).join(', ')
- arr.forEach(function (num) {
  div=num
- })
//- Oui tout ça est convertit en PHP

Émulation de String.prototype

- text = "abcdef"
p=text.length
p=text[1]
p=text[-1]
p=text.substr(2, 3)
p=text.charAt(3)
p=text.indexOf('c')
p=text.toUpperCase()
p=text.toUpperCase().toLowerCase()
p=text.match('d')
p=text.split(/[ce]/).join(', ')
p=text.replace(/(a|e)/, '.')

Options

Équivalents pour les options pugjs

filename string

Le nom du fichier en cours de compilation. Cette option est utilisée pour la résolution des chemins et le fichier mentionné en détail des erreurs :

Phug::compile("\n  indentation\ncassée");

Par défaut, quand vous faites compile, render ou display (en passant donc du code en argument), Phug ne donne aucune information sur le nom du fichier dans les exceptions et ne peut pas résoudre les chemins relatifs pour include/extend.

Failed to parse: Failed to outdent: No parent to outdent to. Seems the parser moved out too many levels.
Near: indent

Line: 3
Offset: 1

Avec l'option filename, un chemin est fournit :

Phug::setOption('filename', 'machin-chose.pug');
Phug::compile("\n  indentation\ncassée");
...
Line: 3
Offset: 1
Path: machin-chose.pug

Mais, cette option est ignorée si vous spécifiez localement le filename :

Phug::setOption('filename', 'machin-chose.pug');
Phug::compile("\n  indentation\ncassée", 'quelque-chose.pug');
...
Line: 3
Offset: 1
Path: quelque-chose.pug

Il en va de même pour compileFile, renderFile et displayFile:

Phug::displayFile('quelque-chose.pug');
...
Line: 3
Offset: 1
Path: quelque-chose.pug

basedir string

Le dossier racine pour les imports par chemin absolu. Cette option est supportée pour raison de compatibilité avec pugjs, mais nous vous recommandons d'utiliser l'option paths à la place. Elle a le même objectif mais vous permet de passer un array de dossiers qui vont tous être essayés (dans l'ordre dans lesquels vous les passez) pour résoudre les chemins absolus lors des include/extend et le premier trouvé est utilisé.

doctype string

Si le doctype n'est pas spécifié dans le template, vous pouvez le spécifier via cette option. C'est parfois utile pour avoir les balises auto-fermantes et supprimer les valeurs en miroir des attributs booléens. Voir la documentation de doctype pour plus d'informations.

Note: le doctype XML peut causer des bugs quand les open short tags sont activés, c'est pourquoi par défaut, nous remplaçons <?xml par <<?= "?" ?>xml quand short_open_tag ou hhvm.enable_short_tags est On (c'est le comportement lorsque l'option short_open_tag_fix est réglée sur sa valeur par défaut : "auto") mais vous pouvez régler cette option à true (pour toujours activer lke correctif) ou false (pour toujours le désactiver).

pretty boolean | string

[Déprécié.] Ajoute des espaces blancs dans le HTML pour le rendre plus facile à lire pour un humain en utilisant ' ' comme indentation. Si une chapine de caractères est passée à cette option, elle sera utilisée à la place comme indentation (par exemple '\t'). Nous vous mettons en garde contre cette option. Trop souvent, elle crée de subtiles problèmes dans les rendus à cause de la manière dont elle altère le rendu, et donc cette fonctionnalité sera prochainement supprimée. Elle est réglée par défaut sur false.

filters array

Array associatif de filtres personnalisé. [] par défaut.

Phug::setOption('filters', [
  'mon-filtre' => function ($texte) {
    return strtoupper($texte);
  },
]);
Phug::render("div\n  :mon-filtre\n    Mon texte");

Retourne :

<div>MON TEXTE</div>

Vous pouvez aussi utiliser la méthode setFilter et votre callback peut prendre des options :

Phug::setFilter('ajoute', function ($texte, $options) {
  return $texte + $options['valeur'];
});
Phug::render("div\n  :ajoute(valeur=4) 5");

Retourne :

<div>9</div>

Et notez que vous pouvez utiliser des classes invocables (classes qui ont une méthode __invoke) au lieu d'une simple fonction de callback pour vos filtres personnalisés.

self boolean | string

Utilise un espace de noms self pour contenir les variables. Au lieu d'écrire variable you devrez donc écrire self.variable pour accéder à une variable. false par défaut.

Phug::setOption('self', true);
Phug::display('p=self.message', [
    'message' => 'Salut',
]);

Affichera :

<p>Salut</p>

Et vous pouvez passer n'importe quelle chaine de caractères tant qu'elle reste un nom de variable valid, donc le code suivant est équivalent :

Phug::setOption('self', 'banane');
Phug::render('p=banane.message', [
    'message' => 'Salut',
]);

debug boolean

Si cette option vaut true, quand une erreur arrive durant le rendu, vous aurez sa trace complète, ce qui inclut la ligne et colonne dans le fichier source pug.

En production, vous devriez la régler sur false pour accélérer le rendu et cacher les informations de débogage. C'est fait automatiquement si vous utiliser un adapteur pour framework comme pug-symfony ou laravel-pug.

shared_variables / globals array

Liste de variables stockée globalement pour être disponible pour tout appel ultérieur à render, renderFile, display ou displayFile.

globals et shared_variables sont deux options différentes fusionnées ensemble, globals n'existe que pour fournir un équivalent à l'option pugjs du même nom. Et des méthodes comme ->share et ->resetSharedVariables impactent seulement l'option shared_variables, donc nous vous recommandons d'utiliser shared_variables.

Phug::setOptions([
    'globals' => [
        'haut' => 1,
        'droite' => 1,
        'bas' => 1,
        'gauche' => 1,
    ],
    'shared_variables' => [
        'droite' => 2,
        'bas' => 2,
        'gauche' => 2,
    ],
]);

Phug::share([
    'bas' => 3,
    'gauche' => 3,
]);

Phug::display('="$haut, $droite, $bas, $gauche"', [
    'gauche' => 4,
]);

Phug::resetSharedVariables();

Phug::display('="$haut, $droite, $bas, $gauche"', [
    'gauche' => 5,
]);

Le premier display va afficher :

1, 2, 3, 4

Le second display va afficher :

1, 1, 1, 5

Comme vous pouvez le constater dans cet exemple, les variables locales ont toujours la précédence et shared_variables a la précédence sur globals.

Le même code ressemblerait à ça en utilisant pug-php :

$pug = new Pug([
    'globals' => [
        'haut' => 1,
        'droite' => 1,
        'bas' => 1,
        'gauche' => 1,
    ],
    'shared_variables' => [
        'droite' => 2,
        'bas' => 2,
        'gauche' => 2,
    ],
]);

// ou $pug->setOptions([...])

$pug->share([
    'bas' => 3,
    'gauche' => 3,
]);

$pug->display('=haut + ", " + droite + ", " + bas + ", " + gauche', [
    'gauche' => 4,
]);

$pug->resetSharedVariables();

$pug->display('=haut + ", " + droite + ", " + bas + ", " + gauche', [
    'gauche' => 5,
]);

cache_dir boolean | string

Si réglée sur true, les templates compilés seront mis en cache. L'option filename est alors nécessaire pour déterminer la clé de cache (ce qui est fait automatiquement en utilisant renderFile ou displayFile). false par défaut.

Cette option peut aussi prendre une valeur string qui sera alors utilisée comme chemin du dossier où les fichiers de cache seront stockés.

pug-symfony et laravel-pug gèrent automatiquement cette option. Si vous utilisez l'un ou l'autre, vous n'avez pas à vous en soucier.

pug-php fournit aussi un alias cache pour cette option pour correspondre à pug-php 2 et pugjs. Cela permet aussi une meilleure sémantique lorsque vous passez à cette option une valeur booléenne, alors que cache_dir reste plus appropriée quand vous passez une chaîne.

Vous pouvez aussi utiliser la commande compile-directory pour mettre en cache un dossier complet.

Nous vous conseillons d'utiliser cette commande au déploiement de vos applications en production, vous pouvez alors optimiser les performances du cache en passant l'option up_to_date_check à false.

Langage

paths array

Spécifie la liste des chemins à utiliser pour include et extend avec des chemins absoluts. Exemple :

Phug::setOption('paths', [
  __DIR__.'/paquet',
  __DIR__.'/ressources/vues',
]);

Phug::render('include /dossier/fichier.pug');

Comme /dossier/fichier.pug commence par un slash, il est considéré comme un chemin absolut. Phug va d'abord essayer de le trouver dans le premier dossier : __DIR__.'/paquet', s'il n'existe pas, il passe au dossier suivant : __DIR__.'/ressources/vues'.

extensions array

Liste des extensions de ficheir que Phug va considérer comme fichier pug. ['', '.pug', '.jade'] par défaut.

Ce qui signifie :

//- mon-fichier.pug
p Truc
// fichier-non-pug.js
alert('truc');
//- index.pug
//-
  mon-fichier.pug peut être importé (include or extend)
  avec ou sans extension, et son contenu sera parsé
  en tant que contenu pug.
  (ce n'est pas testable dans cet éditeur car les
  includes sont émulées)
include mon-fichier.pug
//- include mon-fichier
//-
  fichier-non-pug.js va être inclus en texte brut
include fichier-non-pug.js

Donc l'option extensions vous permet de passer une autre liste d'extensions que Phug gèrera et ajoutera automatiquement lorsqu'elles manquent au chemin d'un import (include/extend).

default_doctype string

Le doctype à utiliser s'il n'est pas spécifié en argument. "html" par défaut.

Ce qui signifie :

doctype
//- va automatiquement se transformer en :
doctype html

default_tag string

Par défaut, quand vous ne spécifiez pas de balise, Phug va générer une balise div :

.truc
#machin(a="b")
(c="d") Salut

Le même code avec Phug::setOption('default_tag', 'section') va générer :

<section class="truc"></section>
<section id="machin" a="b"></section>
<section c="d">Salut</section>

attributes_mapping array

Cette option vous permet de remplacer des attributs par d'autres :

Phug::setOption('attributes_mapping', [
  'foo' => 'bar',
  'bar' => 'foo',
  'biz' => 'zut',
]);
Phug::display('p(foo="1" bar="2" biz="3" zut="4" hop="5")');

Va afficher :

<p bar="1" foo="2" zut="3" zut="4" hop="5"></p>

Profiling

Phug embarque un module de profiling pour surveiller, deboguer ou limiter la consommation de mémoire et le temps d'exécution.

memory_limit integer

Fixe une limite mémoire. -1 par défaut signifie pas de limite. Mais si l'option debug vaut true, elle passe automatiquement à 50*1024*1024 (50Mo).

Si le profiler détecte une utilisation de mémoire supérieure à la limite, il va jeter une exception. Mais soyez conscient que si la limite est supérieure à la limite de la machine ou de PHP, la limite de Phug n'aura aucun effet.

execution_max_time integer

Fixe une limite du temps d'exécution. -1 par défaut signifie pas de limite. Mais si l'option debug vaut true, elle passe automatiquement à 30*1000 (30 secondes).

Si le profiler détecte que Phug tourne depuis plus de temps que la limite autorisée, il va jeter une exception. Mais soyez conscient que si la limite de PHP est inférieure, celle de Phug n'aura aucun effet.

enable_profiler boolean

Quand cette option est réglée à true, Phug va générer une frise temporelle que vous pourrez consulter dans votre navigateur pour voir quel token/node prend le plus de temps à être parsé, compilé, rendu.

Quand c'est activé, un sous-set d'options supplémentaire est disponible, voici leurs valeurs par défaut :

'profiler' => [
    'time_precision' => 3,     // précision décimale des chronos
    'line_height'    => 30,    // hauteur/ligne de la frise
    'display'        => true,  // affiche le résultat
                               // (true ou false)
    'log'            => false, // enregistre le résultat dans un fichier
                               // (le chemin du ficheir ou false)
],

detailed_dump boolean

Le lexer, le parser et le compiler ont tous une méthode ->dump() qui permet de voir l'état de chaque processus.

En réglant detailed_dump sur true vous pouvez afficher plus de détails (pour l'instant, cette option est uniquement disponible pour le parser).

Erreurs

error_reporting callable | int

Permet de gérer l'affichage des erreurs PHP qui surviennent lors de l'exécution d'un template. Par défaut, les erreurs levées par la configuration PHP courante (voir error_reporting) sont transformées en exceptions que Phug peut alors retracer l'origine dans le code du template pug. Les autres erreurs sont masquées.

Vous pouvez passer un niveau d'erreur personnalisé qui prendra le pas sur la configuration PHP.

$renderer = new Renderer([
    'error_reporting' => E_ALL ^ E_NOTICE,
]);

$renderer->render('p=$foo["bar"]', ['foo' => []]);
// Affiche <p></p> car E_NOTICE est ignoré

$renderer = new Renderer([
    'error_reporting' => E_ALL,
]);

$renderer->render('p=$foo["bar"]', ['foo' => []]);
// Jète une erreur E_NOTICE encapsulée dans une exception Phug

Vous pouvez également passer un callback pour une gestion fine :

$renderer = new Renderer([
    'error_reporting' => function ($number, $message, $file, $line) {
        if (strpos($message, 'hidden') !== false) {
            return null; // Aucune erreur affichée
        }

        if ($number === E_NOTICE) {
            return false; // Affiche l'erreur dans le template
        }

        // Arrête l'exécution et jète une exception pour toute autre erreur
        throw new \ErrorException($message, 0, $number, $file, $line);
    },
]);

error_handler callable

Règle un callback to gérer les exceptions Phug. null par défaut.

html_error boolean

Affiche les erreurs en HTML (false par défaut si Phug est exécuté en CLI, true s'il est exécuté via un navigateur).

color_support boolean

Permet d'activer la couleur dans la sortie en ligne de commande (pour les erreurs notamment), par défaut nous essayons de détecter si la console utilisée supporte la couleur.

error_context_lines integer

Nous donnons du contexte en affichant le code source lié à une erreur 7 lignes au dessus et en dessous de la ligne d'erreur par défaut. Mais vous pouvez passez n'importe quel nombre à cette option pour avoir plus ou moins de contexte.

exit_on_error

Quand l'option debug est true, les erreurs non gérées quittent le processus en utilisant exit(1), pour désactiver ce comportement, vous pouvez régler l'option exit_on_error sur false.

Événements

Les événements sont un moyen très puissant d'intercepter différentes étapes du processus pour récupérer, modifier ou manipuler des objets et paramètres.

Exemple:

$renderer = new \Phug\Renderer([
    'on_render' => function (\Phug\Renderer\Event\RenderEvent $event) {
        // Get new parameters
        $parameters = $event->getParameters();
        // If you pass laurel in your parameters
        if (isset($parameters['laurel'])) {
            // Then you will need hardy
            $parameters['hardy'] = 'Scandale à Hollywood';
        }

        // Set new parameters
        $event->setParameters($parameters);
    },
]);

$renderer->display('p=$hardy', [
    'laurel' => true,
]);

Affichera :

<p>Scandale à Hollywood</p>

The same works with pug-php:

$renderer = new Pug([
    'on_render' => function (\Phug\Renderer\Event\RenderEvent $event) {
        // Récupération des paramètres actuels
        $parametres = $event->getParameters();
        // Si vous avez passé laurel en paramètre
        if (isset($parametres['laurel'])) {
            // Alors vous aurez aussi besoin de hardy
            $parametres['hardy'] = '45 Minutes from Hollywood';
        }

        // Applique les nouveaux paramètres
        $event->setParameters($parametres);
    },
]);

$renderer->display('p=hardy', [
    'laurel' => true,
]);

Notez que toutes les options en on_* sont des options initiales, ce qui signifie que vous ne pouvez pas les appliquer après l'initialisation du renderer ou en utilisant les façades (Phug::setOption() ou Pug\Facade::setOption()).

Cependant, vous pouvez attacher/détacher des événements de la manière suivante (en utilisant ou non les façades) :

function ajouteHardy(\Phug\Renderer\Event\RenderEvent $event) {
    // Récupération des paramètres actuels
    $parametres = $event->getParameters();
    // Si vous avez passé laurel en paramètre
    if (isset($parametres['laurel'])) {
        // Alors vous aurez aussi besoin de hardy
        $parametres['hardy'] = '45 Minutes from Hollywood';
    }

    // Applique les nouveaux paramètres
    $event->setParameters($parametres);
}

Phug::attach(\Phug\RendererEvent::RENDER, 'ajouteHardy');

Phug::display('p=$hardy', [
    'laurel' => true,
]);

Phug::detach(\Phug\RendererEvent::RENDER, 'ajouteHardy');

Phug::display('p=$hardy', [
    'laurel' => true,
]);

Affichera <p>Scandale à Hollywood</p> puis <p></p>.

Donc pour toutes les options on_* ci-dessous, nous vous donnerons le nom de l'option initiale, la constante d'événement (pour utiliser avec attach/detach) et la classe de l'événement avec un lien vers la documentation API qui vous donne toutes les méthodes disponibles sur cet événement (toutes les valeurs que vous pouvez récupérer et modifier).

Avant de lister tous les événements, voici une vue d'ensemble de la chronologie des processus :

Chronologie des processus Phug

Les lignes continues sont les processus actifs, les pointillés sont les processus en attente d'un autre processus.

Vous pouvez donc voir que le rendu commence avant les autres événements bien que le processus actif du rendu ne démarre que lorsque les autres processus sont terminés.

Il en va de même pour la compilation qui attend d'abord que le parser lui donne l'arbre complet des nœuds avant de démarrer la "vraie" compilation (c-à-d. la transformation des nœuds en éléments), puis elle va attendre le processus de formattage avant d'appeler l'événement output.

Les étapes de parsing, lexing et reading sont des processus parallèles, le lecteur (reader) identifie des morceaux de texte, par exemple 2 espaces en début de ligne, le lexer convertit la chaîne en token, puis le parser sait alors qu'il doit indenter un niveau et ajouter ensuite les nœuds suivants en tant qu'enfants du nœud du dessus. Cet enchaînement est ensuite répété token par token jusqu'à ce que toute la source soit consommée.

on_render callable

Est déclenché avant qu'un fichier ou une chaîne soit rendu ou affiché.

Dans certains cas vous pourriez hésiter entre on_render et on_compile, vous devriez peut-être consulter l'option on_compile.

Constante d'événement : \Phug\RendererEvent::RENDER

Type d'événement : \Phug\Renderer\Event\RenderEvent

Paramètres utilisables/modifiables :

  • input: code source si render/display a été appelé
  • path: fichier source si renderFile/displayFile a été appelé
  • method: la méthode qui a été appelée "render", "display", "renderFile" ou "displayFile".
  • parameters: variables locales passée pour le rendu de la vue

on_html callable

Est déclenché après qu'un fichier ou une chaîne soit rendu ou affiché.

Constante d'événement : \Phug\RendererEvent::HTML

Type d'événement : \Phug\Renderer\Event\HtmlEvent

Paramètres utilisables/modifiables :

  • renderEvent: lien vers le RenderEvent initial (voir ci-dessus)
  • result: résultat retourné (par render ou renderFile)
  • buffer: tampon de sortie (ce que display ou displayFile est sur le point d'afficher) généralement le code HTML, mais ce peut être aussi du XML ou n'importe quel format personnalisé
  • error: l'exception capturée si une erreur s'est produite

on_compile callable

Est déclenché avant qu'un fichier ou une chaîne soit compilé.

Cette option est differente de on_render par les aspects suivants :

  • les méthodes compile() et compileFile() déclenchen un événement compile mais pas d'événement render,
  • l'événement compile est déclenché avant le render,
  • et les méthodes render(File) et display(File) vont toujours déclenché un événement render mais ne déclencheront pas d'événement compile si un template compilé est servi depuis le cache (configuration de cache active et template à jour).

Le processus de compilation transforme du code pug en code PHP qui est toujours le même pour un template donné quelles que soientles valeurs des variables locales, alors que le processus de rendu exécute ce code PHP pour obtenir du HTML, XML ou n'importe quelle chaîne finale où les variables locales sont remplacées par leurs valeurs. C'est pour ça que l'événement render a aussi un paramètres parameters avec les valeurs des variables locales que vous pouvez récupérer et modifier.

Constante d'événement : \Phug\CompilerEvent::COMPILE

Type d'événement : \Phug\Compiler\Event\CompileEvent

Paramètres utilisables/modifiables :

  • input: contenu source de la chaîne/le fichier compilé
  • path: chemin du fichier source si compileFile/renderFile/displayFile a été appelé

on_output callable

Est déclenché après qu'un fichier ou une chaîne soit compilé.

Constante d'événement : \Phug\CompilerEvent::OUTPUT

Type d'événement : \Phug\Compiler\Event\OutputEvent

Paramètres utilisables/modifiables :

  • compileEvent: lien vers l'événement CompileEvent initial (voir ci-dessus)
  • output: code PHP généré qui peut être exécuté pour obtenir le code final

on_node callable

Est déclenché pour chaque nœud avant sa compilation.

Pour bien comprendre ce qu'est un nœud, vous pouvez utiliser le mode parse dans l'éditeur de code, voici un exemple :

doctype
html
  head
    title=$var
  body
    h1 Texte
    footer
      | Texte
      =date('Y')

Vous pouvez voir que le parser de Phug transforme le code pug en arbre de nœuds. Puis le compiler va compiler chacun de ces nœuds en éléments récursivement. Et le formatter transformera l'arbre d'éléments en code PHP compilé.

Constante d'événement : \Phug\CompilerEvent::NODE

Type d'événement : \Phug\Compiler\Event\NodeEvent

Paramètres utilisables/modifiables :

  • node: l'instance de nœud qui est sur le point d'être compilée

on_element callable

Est déclenché pour chaque nœud après sa compilation.

Constante d'événement : \Phug\CompilerEvent::ELEMENT

Type d'événement : \Phug\Compiler\Event\ElementEvent

Paramètres utilisables/modifiables :

  • nodeEvent: lien vers l'événement NodeEvent initial (voir ci-dessus)
  • element: l'élément compilé

on_format callable

Est déclenché pour chaque élément avant d'être formatté.

L'étape de formattage est un processus qui prend un arbre d'éléments (objet représentant la sortie telle que stockée après compilation, à ne pas confondre avec l'arbre de nœuds qui représente la source d'entrée et est le résultat du parsing - analyse - avant la compilation) et convertit ces éléments en code PHP compilé capable d'effectué le rendu.

Le formattage est récursif, en lui passant un élément, il formatte aussi les enfants contenus dans cet élément.

Avant le processus de formattage

p=$var

Après le processus de formattage

p=$var

Constante d'événement : \Phug\FormatterEvent::FORMAT

Type d'événement : \Phug\Formatter\Event\FormatEvent

Paramètres utilisables/modifiables :

  • element: l'élément compilé (implémente ElementInterface)
  • format: le format utilisé (implémente FormatInterface) sera par défaut BasicFormat ou le format pour votre doctype (par exemple, si vous avez mis doctype html) le format ici sera HtmlFormat (voir les options format et default_format)

on_stringify callable

Est déclenché pour chaque élément après avoir été formatté.

Constante d'événement : \Phug\FormatterEvent::STRINGIFY

Type d'événement : \Phug\Formatter\Event\StringifyEvent

Paramètres utilisable/modifiable :

  • output: output string (HTML and PHP code) Paramètres utilisable :
  • formatEvent: référence à l'événement de formattage source (FormatEvent), modifier des propriétés de cet événement n'aura aucun effet car il a déjà été déclenché.

on_new_format callable

Est déclenché quand le format change (par exemple quand vous définissez le doctype).

Constante d'événement : \Phug\FormatterEvent::NEW_FORMAT

Type d'événement : \Phug\Formatter\Event\NewFormatEvent

Paramètres utilisables/modifiables :

  • formatter: le formatter courant (implémente Formatter)
  • format: l'instance du nouveau format (implémente FormatInterface)

on_dependency_storage callable

Est déclenché quand une dépendance est extraire du dependency storage.

Le registre des dépendances dependency storage est utilisé pour stocker les fonctions utiles lors du rendu et les intégrer aux code PHP compilé, c'est utilisé par exemple dans les assignements :

p&attributes(['machin' => 'true'])

Ces méthodes sont stockées par défaut dans $pugModule (voir l'option dependencies_storage pour le changer).

Constante d'événement : \Phug\FormatterEvent::DEPENDENCY_STORAGE

Type d'événement : \Phug\Formatter\Event\DependencyStorageEvent

Paramètres utilisables/modifiables :

  • dependencyStorage: variable utilisée pour stocker pour stocker la méthode (par exemple : $pugModule['Phug\\Formatter\\Format\\BasicFormat::attributes_assignment'])

on_parse callable

Est déclenché avant le processus d'analyse (parsing).

Constante d'événement : \Phug\ParserEvent::PARSE

Type d'événement : \Phug\Parser\Event\ParseEvent

Paramètres utilisables/modifiables :

  • input: contenu source de la chaîne/le fichier compilé
  • path: chemin du fichier source si compileFile/renderFile/displayFile a été appelé
  • stateClassName: nom de la classe de l'objet d'état qui va être créée pour le parsing
  • stateOptions: array d'options qui va être passé à la création de l'état

on_document callable

Est déclenché quand le parser a analysé un document en entier (fin du parsing).

Constante d'événement : \Phug\ParserEvent::DOCUMENT

Type d'événement : \Phug\Parser\Event\NodeEvent

Paramètres utilisables/modifiables :

  • node: le document en tant qu'instance de nœud

on_state_enter callable

Est déclenché quand le parser entre dans un nœud.

Constante d'événement : \Phug\ParserEvent::STATE_ENTER

Type d'événement : \Phug\Parser\Event\NodeEvent

Paramètres utilisables/modifiables :

  • node: le nœud dans lequel le parser est entré

on_state_leave callable

Est déclenché quand le parser resort d'un nœud.

Constante d'événement : \Phug\ParserEvent::STATE_LEAVE

Type d'événement : \Phug\Parser\Event\NodeEvent

Paramètres utilisables/modifiables :

  • node: le nœud duquel le parser est resorti

on_state_store callable

Est déclenché quand le parser enregistre et attache un nœud à l'arbre du document.

Constante d'événement : \Phug\ParserEvent::STATE_STORE

Type d'événement : \Phug\Parser\Event\NodeEvent

Paramètres utilisables/modifiables :

  • node: le nœud que le parser a enregistré

on_lex callable

Est déclenché quand le lexer commence à transformer le code source Pug en tokens.

Constante d'événement : \Phug\LexerEvent::LEX

Type d'événement : \Phug\Lexer\Event\LexEvent

Paramètres utilisables/modifiables :

  • input: contenu source de la chaîne/le fichier compilé
  • path: chemin du fichier source si compileFile/renderFile/displayFile a été appelé
  • stateClassName: nom de la classe de l'objet d'état qui va être créée pour le lexing
  • stateOptions: array d'options qui va être passé à la création de l'état

on_lex_end callable

Est déclenché quand le lexer à terminé de transformer le code source Pug en tokens.

Constante d'événement : \Phug\LexerEvent::LEX_END

Type d'événement : \Phug\Lexer\Event\EndLexEvent

Paramètres utilisables/modifiables :

  • lexEvent: lien vers lévénement LexEvent initial

on_token callable

Est déclenché à chaque fois que le lexer est sur le point de retourné un token et de l'envoyer au parser.

Constante d'événement : \Phug\LexerEvent::TOKEN

Type d'événement : \Phug\Lexer\Event\TokenEvent

Paramètres utilisables/modifiables :

  • token: le jeont créé par le lexer
  • tokenGenerator: null par défaut, mais si vous passez un iterator à cette propriété, il remplacera le jeton

Quelques exemples :

$renderer = new \Phug\Renderer([
  'on_token' => function (\Phug\Lexer\Event\TokenEvent $event) {
    $token = $event->getToken();
    if ($token instanceof \Phug\Lexer\Token\TagToken) {
      $token->setName('a');
    }
  },
]);
$renderer->display('div'); // <a></a>

$renderer = new \Phug\Renderer([
  'on_token' => function (\Phug\Lexer\Event\TokenEvent $event) {
    if ($event->getToken() instanceof \Phug\Lexer\Token\TagToken) {
      $text = new \Phug\Lexer\Token\TextToken();
      $text->setValue('Salut');
      $event->setToken($text);
    }
  },
]);
$renderer->display('div'); // Salut

$renderer = new \Phug\Renderer([
  'on_token' => function (\Phug\Lexer\Event\TokenEvent $event) {
    if ($event->getToken() instanceof \Phug\Lexer\Token\TextToken) {
      $event->setTokenGenerator(new \ArrayIterator([
        (new \Phug\Lexer\Token\TagToken())->setName('div'),
        (new \Phug\Lexer\Token\ClassToken())->setName('machin'),
        (new \Phug\Lexer\Token\IdToken())->setName('truc'),
      ]));
    }
  },
]);
$renderer->display('| Salut'); // <div id="truc" class="machin"></div>

function replaceTextToken(\Phug\Lexer\Token\TextToken $token) {
  if (preg_match('/^(\D+)(\d+)$/', $token->getValue(), $match) {
    list(, $chars, $digit) = $match;
    for ($i = 0; $i < $digit; $i++) {
      yield (new \Phug\Lexer\Token\TagToken())->setName($chars);
    }
  }
}

$renderer = new \Phug\Renderer([
  'on_token' => function (\Phug\Lexer\Event\TokenEvent $event) {
    $token = $event->getToken();
    if ($token instanceof \Phug\Lexer\Token\TextToken) {
      $event->setTokenGenerator(replaceTextToken($token));
    }
  },
]);
$renderer->display("|i2\n|bk3"); // <i></i><i></i><bk></bk><bk></bk><bk></bk>

Add-ons

Voici une liste de points d'entrée pour ajouter des contenus et des comportements personnalisés :

macros array

L'option macros ou la méthode macro permettent d'ajouter des méthodes à la fois à la façade Phug et au instances de Phug\Renderer ( et si vous utilisez Pug-php, elles seront aussi ajoutées à la açade Pug\Facade et aux instances de Pug).

Phug::macro('displayMessage', function ($message) {
  static::display('p=$message', [
    'message' => $message,
  ]);
});

Phug::displayMessage('Salut');

Ce qui va afficher :

<p>Salut</p>

Ou un exemple en utilisant l'option :

Phug::setOption('macros', [
  'un' => function () {
    return 1;
  },
  'deux' => function () {
    return 2;
  },
]);

echo Phug::un() + Phug::deux();

Affiche : 3.

modules array

Les modules peuvent ajouter n'importe quoi en manipulant les options et les événements. Par exemple si vous utilisez Pug-php avec des options par défaut, vous avez déjà le module js-phpize pour Phug activé. Ce serait équivalent de l'installer avec composer et de l'ajouter à Phug de la manière suivante :

$renderer = new \Phug\Renderer([
    'modules' => [
        \JsPhpize\JsPhpizePhug::class,
    ],
]);

$renderer->display('p=nom.substr(0, 3)', [
    'nom' => 'Bobby',
]);

Ce code affiche <p>Bob</p> mais il échouerait sans le module ajouté car les expressions JS ne sont pas gérées nativement.

Vous pouvez aussi créer vos propres modules en héritant (extend) l'une des classes suivantes :

Elles étendent toutes la classe \Phug\Util\AbstractModule

Voici un exemple :

class MyModule extends \Phug\AbstractCompilerModule
{
    public function __construct(\Phug\Util\ModuleContainerInterface $container)
    {
        // Ici vous pouvez changer des options
        $container->setOption('default_tag', 'p');

        parent::__construct($container);
    }

    public function getEventListeners()
    {
        // Ici vous pouvez attacher des événements
        return [
            \Phug\CompilerEvent::NODE => function (\Phug\Compiler\Event\NodeEvent $event) {
                // Faire quelque chose avant de compiler un nœud
                $node = $event->getNode();
                if ($node instanceof \Phug\Parser\Node\MixinNode) {
                    $tag = new \Phug\Parser\Node\ElementNode(
                        $event->getNode()->getToken()
                    );
                    $attribute = new \Phug\Parser\Node\AttributeNode();
                    $attribute->setName('mixin');
                    $attribute->setValue('"'.$node->getName().'"');
                    $tag->getAttributes()->attach($attribute);
                    $event->setNode($tag);
                }
            },
        ];
    }
}

$renderer = new \Phug\Renderer([
    'modules' => [
        MyModule::class,
    ],
]);

$renderer->display('mixin foo()');

Ce qui affichera :

<p mixin="foo"></p>

Les modules sont délégués au renderer/compiler/formatter/parser/lexer selon l'interface implémentée (ou la classe abstraite héritée).

Mais vous pouvez spécifier explicitement la cible avec les options suivantes :

compiler_modules array

Modules réservés au compiler (voir modules).

formatter_modules array

Modules réservés au formatter (voir modules).

parser_modules array

Modules réservés au parser (voir modules).

lexer_modules array

Modules réservés au lexer (voir modules).

includes array

Inclut simplement des fichiers pug avant toute compilation :

// On ajoute aux includes existantes pour ne pas les écraser s'il y en a
Phug::setOption('includes', Phug::getOption('includes') + [
    'mixins.pug',
]);

Phug::display('+truc()');

Si le fichier mixins.pug contient une déclaration de mixin truc ce code va correctement l'appeler.

C'est un bon moyen de rendre une librairie de mixins disponible dans n'importe quel template.

filter_resolvers array

Ajoute un moyen dynamique de résoudre un filtre par son nom lorsqu'il n'existe pas déjà.

Phug::setOption('filter_resolvers', Phug::getOption('filter_resolvers') + [
    function ($nom) {
        if (mb_substr($nom, 0, 3) === 'go-') {
            return function ($contenu) use ($nom) {
                return '<a href="'.mb_substr($nom, 3).'">'.$contenu.'</a>';
            };
        }
    },
]);

Phug::display(':go-page');
// <a href="page"></a>
Phug::display(':go-action Action');
// <a href="action">Action</a>

Le resolver est utilisé seulement si le nom de filtre donné n'existe pas au que les précédents resolvers n'ont rien retourné. Si aucun resolver ne retourne rien, une erreur Unknown filter est lancée.

keywords array

Vous permet de créer vos propre mot-clé de language :

Phug::setOption('keywords', Phug::getOption('keywords') + [
    'monMotCle' => function ($valeur, \Phug\Formatter\Element\KeywordElement $element, $nom) {
        $enfants = isset($element->nodes) ? count($element->nodes) : 0;

        return "C'est un mot-clé $nom avec la valeur $valeur et $enfants enfants.";
    },
]);


Phug::display('
div
  monMotCle maValeur
    span 1
    span 2
');

Affiche :

<div>C'est un mot-clé monMotCle avec la valeur maValeur et 2 enfants.</div>

Quand le callback du mot-clé retourne une string, elle remplace le mot-clé entier avec ses enfants.

Mais vous pouvez aussi retourner un array avec begin et end qui va préserver les nœuds enfants :

Phug::setOption('keywords', Phug::getOption('keywords') + [
    'monMotCle' => function ($valeur) {
        return [
            'begin' => '<div class="'.$valeur.'">',
            'end'   => '</div>',
        ];
    },
]);


Phug::display('
monMotCle maValeur
  span 1
  span 2
');

Affiche :

<div class="maValeur">
  <span>1</span>
  <span>2</span>
</div>

Ou vous pouvez spécifier un array avec beginPhp et endPhp pour entourer les enfants d'une chaîne de début et de fin toute deux elles-mêmes entourées de <?php ?>.

Phug::setOption('keywords', Phug::getOption('keywords') + [
    'repete' => function ($compte) {
        return [
            'beginPhp' => 'for ($i = 0; $i < '.$compte.'; $i++) {',
            'endPhp'   => '}',
        ];
    },
]);


Phug::display('
repete 3
  section
');

Affiche :

<section></section>
<section></section>
<section></section>

php_token_handlers array

Ce paramètre vous permet d'intercepter n'importe quel token PHP et de le remplacer par un autre code PHP. Cela fonctionne aussi avec les expressions à l'intérieur des boucles each, des conditions if, etc. et même si les expressions proviennent d'une transformation. Par exemple si vous utilisez js-phpize et écrivez a(href=route('profil', {id: 3})), alors {id: 3} est convertit en array('id' => 3) et le jeton (token) array( peut être intercepté avec l'identifiant PHP T_ARRAY.

Phug::setOption('php_token_handlers', [
    T_DNUMBER => 'round(%s)',
]);

Phug::display('
- $nombreDecimal = 9.54
- $texte = "9.45"
strong=$nombreDecimal
| !=
strong=$texte
');

Affiche :

<strong>10</strong>!=<strong>9.45</strong>

Si vous passez une chaîne de caractères, sprintf est utilisé pour la gérée, donc si la chaîne contient %s, ce sera remplacé par la chaîne d'entrée du token.

Vous pouvez aussi utiliser une fonction callback :

Phug::setOption('php_token_handlers', [
    '(' => function ($tokenString, $index, $tokens, $checked, $formatInstance) {
        return '__appelle_quelque_chose('.mt_rand(0, 4).', ';
    },
]);

echo Phug::compile('b=($variable)');

Ceci va appeler la fonction de calback pour chaque token ( trouvé dans l'expression et le remplacer par le résultat retourné par la function (par exemple __appelle_quelque_chose(2, ).

La fonction de callback reçoit 5 arguments :

  • $tokenString le token d'entrée en tant que string;
  • $index la position du token dans l'expression;
  • &$tokens un array avec tous les tokens de l'expression, passé par référence, ce qui veut dire que vous pouvez modifier/ajouter/supprimer des tokens (ne fonctionne que pour les tokens qui viennent après le token actuel);
  • $checked vaut true si l'expression est vérifiée (=exp() est vérifiée, ?=exp() ne l'est pas);
  • $formatInstance l'instance qui formatte l'expression courante (implémente FormatInterface).

Soyez prudent, nous utilisons php_token_handlers pour gérer les expressions vérifiées. Ce qui veut dire que si vous remplacez la gestion du token PHP T_VARIABLE par votre propre traitement comme dans l'exemple ci-dessous :

Phug::setOption('php_token_handlers', [
    T_VARIABLE => function ($variable) {
        if (mb_substr($variable, 0, 5) === '$env_') {
            return '$_'.strtoupper(mb_substr($variable, 5));
        }

        return $variable;
    },
]);

Phug::display('
b=$variableNormale
i=$env_super["METHODE"]
', [
    'variableNormale' => 'machin',
    '_SUPER' => [
        'METHODE' => 'truc',
    ],
]);

Affiche :

<b>machin</b><i>truc</i>

Mais si vous faites ça, vous écrasez la gestion initiale des expressions vérifiées :

Phug::display('p=$manquant'); // affiche <p></p>

Phug::setOption('php_token_handlers', [
    T_VARIABLE => function ($variable) {
        return $variable;
    },
]);

Phug::display('p=$manquant'); // Exception: Undefined variable: manquant

Mais vous pouvez toujours ré-appeler la méthode native de gestion des variables avant ou après vos propres traitements :

Phug::setOption('php_token_handlers', [
    T_VARIABLE => function ($tokenString, $index, $tokens, $checked, $formatInstance) {
        // Traitement avant le processus des expressions vérifiées
        $tokenString .= 'Suffixe';
        // Processus des expressions vérifiées
        $tokenString = $formatInstance->handleVariable($tokenString, $index, $tokens, $checked);
        // Traitement après le processus des expressions vérifiées
        $tokenString = 'strtoupper('.$tokenString.')';

        return $tokenString;
    },
]);

Phug::display('p=$machin', [
    'machinSuffixe' => 'truc',
]);

Affiche :

<p>TRUC</p>

Mixins

mixin_keyword

Determine le mot-clé de début des déclarations de mixin.

Valeur par défaut: "mixin"

Vous pouvez le changer ppour une autre chaîne ou un array pour autoriser plueirus mots-clé comme: ["mixin", "component"]

Le chaîne peut contenir des expressions régulières, ça signifie que vous pouvez utiliser [a-z] to identifier les lettres minuscules, \d pour les chiffres etc. mais ça signifie également que vous avez besoin d'échapper des caractères spéciaux tels que []?!.{}()^$/ si vous voulez les utiliser en tant que texte.

mixin_call_keyword

Determine le mot-clé de début des appels de mixin.

Valeur par défaut: "\\+"

Vous pouvez le changer ppour une autre chaîne ou un array pour autoriser plueirus mots-clé comme: ["\\+", "@"]

Le chaîne peut contenir des expressions régulières, ça signifie que vous pouvez utiliser [a-z] to identifier les lettres minuscules, \d pour les chiffres etc. mais ça signifie également que vous avez besoin d'échapper des caractères spéciaux tels que []?!.{}()^$/ si vous voulez les utiliser en tant que texte.

mixin_merge_mode string

Alias de allowMixinOverride dans Pug-php.

Détermine si une nouvelle déclaration de mixin avec un nom existant va remplacer la précédente ou être ignorée :

Phug::setOption('mixin_merge_mode', 'replace');

Phug::display('
mixin machin
  p A

mixin machin
  p B

+machin
');

// Affiche <p>B</p>

Phug::setOption('mixin_merge_mode', 'ignore');

Phug::display('
mixin machin
  p A

mixin machin
  p B

+machin
');

// Affiche <p>A</p>

Cette option est réglée sur "replace" par défaut.

Formattage

patterns array

Definit comment sont transformées des parties spécifiques des éléments en PHP.

Valeur par défaut :

[
  'class_attribute'        => '(is_array($_pug_temp = %s) ? implode(" ", $_pug_temp) : strval($_pug_temp))',
  'string_attribute'       => '
        (is_array($_pug_temp = %s) || is_object($_pug_temp) && !method_exists($_pug_temp, "__toString")
            ? json_encode($_pug_temp)
            : strval($_pug_temp))',
  'expression_in_text'     => '(is_bool($_pug_temp = %s) ? var_export($_pug_temp, true) : $_pug_temp)',
  'html_expression_escape' => 'htmlspecialchars(%s)',
  'html_text_escape'       => 'htmlspecialchars',
  'pair_tag'               => '%s%s%s',
  'transform_expression'   => '%s',
  'transform_code'         => '%s',
  'transform_raw_code'     => '%s',
  'php_handle_code'        => '<?php %s ?>',
  'php_display_code'       => '<?= %s ?>',
  'php_block_code'         => ' {%s}',
  'php_nested_html'        => ' ?>%s<?php ',
  'display_comment'        => '<!-- %s -->',
  'doctype'                => '<!DOCTYPE %s PUBLIC "%s" "%s">',
  'custom_doctype'         => '<!DOCTYPE %s>',
  'debug_comment'          => "\n// PUG_DEBUG:%s\n",
  'debug'                  => function ($nodeId) {
    return $this->handleCode($this->getDebugComment($nodeId));
  },
]

Les formats peuvent ajouter des patterns (comme le fait XmlFormat) :

class XmlFormat extends AbstractFormat
{
    //...

    const DOCTYPE = '<?xml version="1.0" encoding="utf-8" ?>';
    const OPEN_PAIR_TAG = '<%s>';
    const CLOSE_PAIR_TAG = '</%s>';
    const SELF_CLOSING_TAG = '<%s />';
    const ATTRIBUTE_PATTERN = ' %s="%s"';
    const BOOLEAN_ATTRIBUTE_PATTERN = ' %s="%s"';
    const BUFFER_VARIABLE = '$__value';

    public function __construct(Formatter $formatter = null)
    {
        //...

        $this->addPatterns([
            'open_pair_tag'             => static::OPEN_PAIR_TAG,
            'close_pair_tag'            => static::CLOSE_PAIR_TAG,
            'self_closing_tag'          => static::SELF_CLOSING_TAG,
            'attribute_pattern'         => static::ATTRIBUTE_PATTERN,
            'boolean_attribute_pattern' => static::BOOLEAN_ATTRIBUTE_PATTERN,
            'save_value'                => static::SAVE_VALUE,
            'buffer_variable'           => static::BUFFER_VARIABLE,
        ])

Vous pouvez voir par exemple BOOLEAN_ATTRIBUTE_PATTERN = ' %s="%s"' qui implique que input(checked) devient <input checked="checked">.

Et HtmlFormat le réécrit à son tour :

class HtmlFormat extends XhtmlFormat
{
    const DOCTYPE = '<!DOCTYPE html>';
    const SELF_CLOSING_TAG = '<%s>';
    const EXPLICIT_CLOSING_TAG = '<%s/>';
    const BOOLEAN_ATTRIBUTE_PATTERN = ' %s';

    public function __construct(Formatter $formatter = null)
    {
        parent::__construct($formatter);

        $this->addPattern('explicit_closing_tag', static::EXPLICIT_CLOSING_TAG);
    }
}

BOOLEAN_ATTRIBUTE_PATTERN = ' %s' donc input(checked) devient <input checked>.

De la même manière vous pouvez étendre un format pour créer votre propre format personnalisé et les supplanter avec les options formats et default_format.

Les patterns peuvent être des string%s est replacé par la valeur d'entrée ou des fonctions de callback qui reçoivent la valeur d'entrée en argument.

Certains patterns ont des entrées multiples (comme pair_tag qui prend $ouverture, $contenu et $fermeture).

Exemple d'utilisation : vous pouvez intercepter et modifier les expressions :


Phug::setOption('patterns', [
  'transform_expression' => 'strtoupper(%s)',
]);

Phug::display('p="AbcD"'); // Affiche <p>ABCD</p>

Ou vous pouvez changer la fonction d'échappement :

Phug::setOption('patterns', [
  'html_expression_escape' => 'htmlentities(%s)',
  'html_text_escape'       => 'htmlentities',
]);

pattern callable

L'option pattern est la façon dans les patterns sont gérés.

Valeur par défaut :

function ($pattern) {
  $args = func_get_args();
  $function = 'sprintf';
  if (is_callable($pattern)) {
    $function = $pattern;
    $args = array_slice($args, 1);
  }

  return call_user_func_array($function, $args);
}

Cette fonction va prendre au moins un argument (le pattern) et autant de valeurs que nécessaire pour ce pattern comme arguments restants.

Vous pouvez voir dans le comportement par défaut que si un pattern est callable (exécutable), nous l'appelons simplement avec les valeurs d'entrée : $pattern($valeur1, $valeur2, ...), sinon nous appelons sprintf($pattern, $valeur1, $valeur2, ...)

En changeant l'option pattern, vous pouvez gérer patterns comme bon vous semble et supporter n'importe quels autres types de pattern.

formats array

Array des classes de format par doctype, la valeur par défaut est :

[
  'basic'        => \Phug\Formatter\Format\BasicFormat::class,
  'frameset'     => \Phug\Formatter\Format\FramesetFormat::class,
  'html'         => \Phug\Formatter\Format\HtmlFormat::class,
  'mobile'       => \Phug\Formatter\Format\MobileFormat::class,
  '1.1'          => \Phug\Formatter\Format\OneDotOneFormat::class,
  'plist'        => \Phug\Formatter\Format\PlistFormat::class,
  'strict'       => \Phug\Formatter\Format\StrictFormat::class,
  'transitional' => \Phug\Formatter\Format\TransitionalFormat::class,
  'xml'          => \Phug\Formatter\Format\XmlFormat::class,
]

Vous pouvez ajouter/modifier/supprimer n'importe quel format par doctype :

class MachinFormat extends \Phug\Formatter\Format\HtmlFormat
{
    const DOCTYPE = '#MACHIN';
    // Ici vous pouvez changer les options/méthodes/patterns
}

Phug::setOption('formats', [
    'machin' => MachinFormat::class,
] + Phug::getOption('formats'));
// Ajoute machin mais garde Phug::getOption('formats')
// Comme ça les deux arrays fusionnent

Phug::display('
doctype machin
test
');

// Affiche #MACHIN<test></test>

Vous pouvez aussi supprimer un format de la manière suivante :

$formats = Phug::getOption('formats');
unset($formats['xml']); // supprime le format XML

Phug::setOption('formats', $formats);

Phug::display('
doctype xml
test
');

// Affiche <!DOCTYPE xml><test></test>
// Au lieu de <?xml version="1.0" encoding="utf-8" ?><test></test>

default_format string

C'est le format utilisé lorsqu'il n'y a pas de doctype ; \Phug\Formatter\Format\BasicFormat::class par défaut.

$renderer = new \Phug\Renderer([
  'default_format' => \Phug\Formatter\Format\XmlFormat::class,
]);
$renderer->display('input'); // <input></input>

$renderer = new \Phug\Renderer([
  'default_format' => \Phug\Formatter\Format\HtmlFormat::class,
]);
$renderer->display('input'); // <input>

dependencies_storage string

Nom de la variable name qui contiendra les dépendances dans le code PHP compilé ; "pugModule" par défaut.

formatter_class_name string

Vous permet d'étendre la classe Formatter

class CustomFormatter extends \Phug\Formatter
{
    public function format(\Phug\Formatter\ElementInterface $element, $format = null)
    {
        // Ajotue des espaces partout
        return parent::format($element, $format).' ';
    }
}

Phug::display('
span machin
span truc
');

// <span>machin</span><span>truc</span>

$renderer = new Phug\Renderer([
    'formatter_class_name' => CustomFormatter::class,
]);

$renderer->display('
span machin
span truc
');

// <span>machin </span> <span>truc </span>

Rendu

scope_each_variables boolean | string

Par défaut (valeur false), les variables de clé et valeur créées par les boucles each ... in ou for ... in sont locales à la boucle (depuis phug/compiler 0.5.26).

- $valeur = 'a'
- $cle = 'b'
each $valeur, $cle in ['X', 'Y', 'Z']
  p= $cle . $valeur
div= $cle . $valeur

Avec scope_each_variables réglé à false, les variables utilisées dans each/for vont simplement écraser les variables globales du même nom :

- $valeur = 'a'
- $cle = 'b'
each $valeur, $cle in ['X', 'Y', 'Z']
  p= $cle . $valeur
div= $cle . $valeur

L'option scope_each_variables utilise sous le capot une variable $__eachScopeVariables pour enregistrer les nom et valeur des variables d'itération (la valeur et optionnellement la clé) et pouvoir les restaurer après la boucle.

Vous pouvez aussi passer un nom de variable à scope_each_variables pour personnaliser ce stockage, par example avec 'scope_each_variables' => 'beforeEach' :

- $valeur = 'a'
- $cle = 'b'
each $valeur, $cle in ['X', 'Y', 'Z']
  p= $beforeEach['cle'] . $beforeEach['valeur']
div= $cle . $valeur

adapter_class_name string

Cette option requiert une réinitialisation de l'adapter pour être prise en compte. Donc soit vous pouvez l'utiliser en option initiale (passée dans l'array des options lors de l'instanciation d'un nouveau Renderer ou un nouveau Pug si vous utilisez Pug-php) sinon vous pouvez simplement utiliser la méthode ->setAdapterClassName() pour changer cette option puis réinitialiser l'adapter.

Phug::getRenderer()->setAdapterClassName(\Phug\Renderer\Adapter\StreamAdapter::class);
// Phug::getRenderer()->getAdapter() instanceof \Phug\Renderer\Adapter\StreamAdapter

Il y a 3 adapters dispnibles et vous pouvez en créer d'autres en étendant l'un d'eux ou la classe AbstractAdapter.

Le rôle de l'adapter est de prendre le code compilé formatté et de le transformer en code final rendu. Donc le plus souvent, il s'agit d'exécuter du code PHP pour obtenir du code HTML.

FileAdapter

FileAdapter est le seul adapter à implémenter l'interface CacheInterface donc lorsque vous activez ou utiliser n'importe quelle fonctionnalité de cache, cet adapter est automatiquement sélectionné si l'adapter courant n'implémente pas CacheInterface. ->display() avec le FileAdapter est équivalent à :

file_put_contents('fichier.php', $codePhp);
include 'fichier.php';

EvalAdapter

EvalAdapter est l'adapter par défaut et utilise eval. Vous pouvez avoir entendu que eval est dangereux. Et oui, si vous ne filtrez pas les entrées utilisateur/externes que la chaîne que vous passez à eval peut contenir, c'est risqué. Mais ça n'arrive pas lors d'un rendu de template. Vos variables locales ou globales ne sont jamais exécutées, seul le code Pug converti en code PHP l'est, donc si vous n'écrivez pas de code dangereux dans votre code Pug, il n'y a rien de dangereux dans le code PHP final. C'est parfaitement aussi sûr que les 2 autres adapters, vous obtiendrez exactement les mêmes exécutions et résultats quelque soit l'adapter utilisé.

Regardez l'exemple suivant :

p?!=$contenuDangereux
[
  'contenuDangereux' => 'file_get_contents("index.php")',
]

Comme vous le voyez, les variables peuvent contenir n'importe quoi et être affichées n'importe comment, elles ne seront jamais évaluée en PHP, seulement affichées. Le danger n'apparaît que si vous l'écrivez directement dans vos templates Pug, c'est donc le même danger qu'avec n'importe quel moteur de templates, ou que si vous l'écriviez directement dans vos fichiers PHP.

EvalAdapter est aussi l'adapter le plus rapide et le plus facile à utiliser. Dans ce mode ->display() est équivalent à :

eval('?>'.$codePhp);

StreamAdapter

StreamAdapter Le flux (stream) est une alternative entre les deux. Dans ce mode ->display() est équivalent à :

include 'pug.stream://data;'.$codePhp;

Le stream a des contraintes. La taille du flux est limitée par la mémoire RAM. Et la configuration server (comme php.ini) peut interdire les inclusions de flux.

stream_name string

Par défaut "pug". Détermine le nom du flux quand vous utilisez l'adapter StreamAdapter (voir ci-dessus).

stream_suffix string

Par défaut ".stream". Détermine le suffixe du flux quand vous utilisez l'adapter StreamAdapter (voir ci-dessus).

Système de fichiers

tmp_dir string

Le dossier à utiliser pour stocker des fichiers temporaires. sys_get_temp_dir() par défaut.

tempnam callable

La fonction à utiliser pour créer un fichier temporaire. "tempnam" par défaut.

get_file_contents callable

La fonction à utiliser pour récupérer le contenu d'un fichier. "file_get_contents" par défaut.

$storage = new Memcached();
$storage->addServer('localhost', 11211);
Phug::setOption('get_file_contents', function ($path) use ($storage) {
  return $storage->get($path);
});

Dans cet exemple, au lieu de chercher les templates (qu'ils soient rendus, inclus ou étendus) dans le fichier correspondant au chemin, on le cherche dans un stockage Memcached.

up_to_date_check boolean

true par défaut, si réglé sur false, les fichiers de cache n'expirent jamais jusqu'à ce que le cache soit manuellement vidé.

keep_base_name boolean

Si true, le nom du template sera appliqué en préfixe au nom du fichier de cache. Cela peut être utile pour déboguer si vous avez besoin de voir rapidement de quel template provient un fichier de cache.

locator_class_name string

Le locator est utilisé par le compiler pour localiser les fichiers à compiler, inclure ou étendre.

Par défaut, nous utilisons FileLocator.

Mais vous pouvez le changer par n'importe quelle classe qui implémente LocatorInterface.

Lexing

Les options de lexing sont gérées et altérées par le lexer à un niveau très bas du processus. Il n'y a pas d'intérêt particulier à les changer (à l'exception de l'encodage) mais les récupérer lors de certains événements peut être intéressant :

$lexer = Phug::getRenderer()->getCompiler()->getParser()->getLexer();
$lexer->attach(\Phug\LexerEvent::TOKEN, function (\Phug\Lexer\Event\TokenEvent $event) use ($lexer, &$output) {
    $state = $lexer->getState();
    $state->getLevel(); // level
    $state->getIndentStyle(); // indent_style
    $state->getIndentWidth(); // indent_width
    $state->getReader()->getEncoding(); // encoding
});

Attention: ne confondez pas les options ci-dessous avec des options de formattage, les options ci-dessous n'ont aucun effet sur la sortie, elle n'influe que sur la manière d'interpréter l'entrée.

level integer

Nombre de crans d'indentation (espaces ou tabulations).

indent_style string

Chaîne d'indentation (espaces, tabulations ou n'importe quelle chaîne personalisée).

indent_width indent_width

Nombre d'occurences de la chaînes pour passer un niveau d'indentation.

allow_mixed_indent integer

true par défaut. Si réglé à false, mélanger des tabulations et des espaces lancera une exception.

encoding string

Encodage du code d'entrée ("UTF-8" par défaut).

Étendre les classes système

Les classes système peuvent être remplacées grâce aux options. Cela vous permet d'étendre les classes et méthodes de Phug. Ces options sont initiales donc vous devez réinitialiser le renderer si vous les changer après un rendu.

compiler_class_name string

Permet de remplacer le Compiler

parser_class_name string

Permet de remplacer le Parser

lexer_class_name string

Permet de remplacer le Lexer

lexer_class_name string

Permet de remplacer le Lexer

lexer_state_class_name string

Permet de remplacer la classe Lexer\State

parser_state_class_name string

Permet de remplacer la classe Parser\State

node_compilers array

Permet de changer les compilers pour chaque type de nœud, la map par défaut est :

[
    \Phug\Parser\Node\AssignmentListNode::class => \Phug\Compiler\NodeCompiler\AssignmentListNodeCompiler::class,
    \Phug\Parser\Node\AssignmentNode::class     => \Phug\Compiler\NodeCompiler\AssignmentNodeCompiler::class,
    \Phug\Parser\Node\AttributeListNode::class  => \Phug\Compiler\NodeCompiler\AttributeListNodeCompiler::class,
    \Phug\Parser\Node\AttributeListNode::class  => \Phug\Compiler\NodeCompiler\AttributeNodeCompiler::class,
    \Phug\Parser\Node\BlockNode::class          => \Phug\Compiler\NodeCompiler\BlockNodeCompiler::class,
    \Phug\Parser\Node\YieldNode::class          => \Phug\Compiler\NodeCompiler\YieldNodeCompiler::class,
    \Phug\Parser\Node\CaseNode::class           => \Phug\Compiler\NodeCompiler\CaseNodeCompiler::class,
    \Phug\Parser\Node\CodeNode::class           => \Phug\Compiler\NodeCompiler\CodeNodeCompiler::class,
    \Phug\Parser\Node\CommentNode::class        => \Phug\Compiler\NodeCompiler\CommentNodeCompiler::class,
    \Phug\Parser\Node\ConditionalNode::class    => \Phug\Compiler\NodeCompiler\ConditionalNodeCompiler::class,
    \Phug\Parser\Node\DoctypeNode::class        => \Phug\Compiler\NodeCompiler\DoctypeNodeCompiler::class,
    \Phug\Parser\Node\DocumentNode::class       => \Phug\Compiler\NodeCompiler\DocumentNodeCompiler::class,
    \Phug\Parser\Node\DoNode::class             => \Phug\Compiler\NodeCompiler\DoNodeCompiler::class,
    \Phug\Parser\Node\EachNode::class           => \Phug\Compiler\NodeCompiler\EachNodeCompiler::class,
    \Phug\Parser\Node\KeywordNode::class        => \Phug\Compiler\NodeCompiler\KeywordNodeCompiler::class,
    \Phug\Parser\Node\ElementNode::class        => \Phug\Compiler\NodeCompiler\ElementNodeCompiler::class,
    \Phug\Parser\Node\ExpressionNode::class     => \Phug\Compiler\NodeCompiler\ExpressionNodeCompiler::class,
    \Phug\Parser\Node\FilterNode::class         => \Phug\Compiler\NodeCompiler\FilterNodeCompiler::class,
    \Phug\Parser\Node\ForNode::class            => \Phug\Compiler\NodeCompiler\ForNodeCompiler::class,
    \Phug\Parser\Node\ImportNode::class         => \Phug\Compiler\NodeCompiler\ImportNodeCompiler::class,
    \Phug\Parser\Node\MixinCallNode::class      => \Phug\Compiler\NodeCompiler\MixinCallNodeCompiler::class,
    \Phug\Parser\Node\MixinNode::class          => \Phug\Compiler\NodeCompiler\MixinNodeCompiler::class,
    \Phug\Parser\Node\TextNode::class           => \Phug\Compiler\NodeCompiler\TextNodeCompiler::class,
    \Phug\Parser\Node\VariableNode::class       => \Phug\Compiler\NodeCompiler\VariableNodeCompiler::class,
    \Phug\Parser\Node\WhenNode::class           => \Phug\Compiler\NodeCompiler\WhenNodeCompiler::class,
    \Phug\Parser\Node\WhileNode::class          => \Phug\Compiler\NodeCompiler\WhileNodeCompiler::class,
]

element_handlers array

Permet de changer le formattage pour chaque type d'élément, la map par défaut est :

[
    \Phug\Formatter\Element\AssignmentElement::class => [$this, 'formatAssignmentElement'],
    \Phug\Formatter\Element\AttributeElement::class  => [$this, 'formatAttributeElement'],
    \Phug\Formatter\Element\CodeElement::class       => [$this, 'formatCodeElement'],
    \Phug\Formatter\Element\CommentElement::class    => [$this, 'formatCommentElement'],
    \Phug\Formatter\Element\ExpressionElement::class => [$this, 'formatExpressionElement'],
    \Phug\Formatter\Element\DoctypeElement::class    => [$this, 'formatDoctypeElement'],
    \Phug\Formatter\Element\DocumentElement::class   => [$this, 'formatDocumentElement'],
    \Phug\Formatter\Element\KeywordElement::class    => [$this, 'formatKeywordElement'],
    \Phug\Formatter\Element\MarkupElement::class     => [$this, 'formatMarkupElement'],
    \Phug\Formatter\Element\MixinCallElement::class  => [$this, 'formatMixinCallElement'],
    \Phug\Formatter\Element\MixinElement::class      => [$this, 'formatMixinElement'],
    \Phug\Formatter\Element\TextElement::class       => [$this, 'formatTextElement'],
    \Phug\Formatter\Element\VariableElement::class   => [$this, 'formatVariableElement'],
]

$this est l'instance de formattage courante.

token_handlers array

Permet de changer la manière dont les tokens du lexer sont parsés, la map par défaut est :

[
    \Phug\Lexer\Token\AssignmentToken::class             => \Phug\Parser\TokenHandler\AssignmentTokenHandler::class,
    \Phug\Lexer\Token\AttributeEndToken::class           => \Phug\Parser\TokenHandler\AttributeEndTokenHandler::class,
    \Phug\Lexer\Token\AttributeStartToken::class         => \Phug\Parser\TokenHandler\AttributeStartTokenHandler::class,
    \Phug\Lexer\Token\AttributeToken::class              => \Phug\Parser\TokenHandler\AttributeTokenHandler::class,
    \Phug\Lexer\Token\AutoCloseToken::class              => \Phug\Parser\TokenHandler\AutoCloseTokenHandler::class,
    \Phug\Lexer\Token\BlockToken::class                  => \Phug\Parser\TokenHandler\BlockTokenHandler::class,
    \Phug\Lexer\Token\YieldToken::class                  => \Phug\Parser\TokenHandler\YieldTokenHandler::class,
    \Phug\Lexer\Token\CaseToken::class                   => \Phug\Parser\TokenHandler\CaseTokenHandler::class,
    \Phug\Lexer\Token\ClassToken::class                  => \Phug\Parser\TokenHandler\ClassTokenHandler::class,
    \Phug\Lexer\Token\CodeToken::class                   => \Phug\Parser\TokenHandler\CodeTokenHandler::class,
    \Phug\Lexer\Token\CommentToken::class                => \Phug\Parser\TokenHandler\CommentTokenHandler::class,
    \Phug\Lexer\Token\ConditionalToken::class            => \Phug\Parser\TokenHandler\ConditionalTokenHandler::class,
    \Phug\Lexer\Token\DoToken::class                     => \Phug\Parser\TokenHandler\DoTokenHandler::class,
    \Phug\Lexer\Token\DoctypeToken::class                => \Phug\Parser\TokenHandler\DoctypeTokenHandler::class,
    \Phug\Lexer\Token\EachToken::class                   => \Phug\Parser\TokenHandler\EachTokenHandler::class,
    \Phug\Lexer\Token\ExpansionToken::class              => \Phug\Parser\TokenHandler\ExpansionTokenHandler::class,
    \Phug\Lexer\Token\ExpressionToken::class             => \Phug\Parser\TokenHandler\ExpressionTokenHandler::class,
    \Phug\Lexer\Token\FilterToken::class                 => \Phug\Parser\TokenHandler\FilterTokenHandler::class,
    \Phug\Lexer\Token\ForToken::class                    => \Phug\Parser\TokenHandler\ForTokenHandler::class,
    \Phug\Lexer\Token\IdToken::class                     => \Phug\Parser\TokenHandler\IdTokenHandler::class,
    \Phug\Lexer\Token\InterpolationStartToken::class     => \Phug\Parser\TokenHandler\InterpolationStartTokenHandler::class,
    \Phug\Lexer\Token\InterpolationEndToken::class       => \Phug\Parser\TokenHandler\InterpolationEndTokenHandler::class,
    \Phug\Lexer\Token\ImportToken::class                 => \Phug\Parser\TokenHandler\ImportTokenHandler::class,
    \Phug\Lexer\Token\IndentToken::class                 => \Phug\Parser\TokenHandler\IndentTokenHandler::class,
    \Phug\Lexer\Token\MixinCallToken::class              => \Phug\Parser\TokenHandler\MixinCallTokenHandler::class,
    \Phug\Lexer\Token\MixinToken::class                  => \Phug\Parser\TokenHandler\MixinTokenHandler::class,
    \Phug\Lexer\Token\NewLineToken::class                => \Phug\Parser\TokenHandler\NewLineTokenHandler::class,
    \Phug\Lexer\Token\OutdentToken::class                => \Phug\Parser\TokenHandler\OutdentTokenHandler::class,
    \Phug\Lexer\Token\TagInterpolationStartToken::class  => \Phug\Parser\TokenHandler\TagInterpolationStartTokenHandler::class,
    \Phug\Lexer\Token\TagInterpolationEndToken::class    => \Phug\Parser\TokenHandler\TagInterpolationEndTokenHandler::class,
    \Phug\Lexer\Token\KeywordToken::class                => \Phug\Parser\TokenHandler\KeywordTokenHandler::class,
    \Phug\Lexer\Token\TagToken::class                    => \Phug\Parser\TokenHandler\TagTokenHandler::class,
    \Phug\Lexer\Token\TextToken::class                   => \Phug\Parser\TokenHandler\TextTokenHandler::class,
    \Phug\Lexer\Token\VariableToken::class               => \Phug\Parser\TokenHandler\VariableTokenHandler::class,
    \Phug\Lexer\Token\WhenToken::class                   => \Phug\Parser\TokenHandler\WhenTokenHandler::class,
    \Phug\Lexer\Token\WhileToken::class                  => \Phug\Parser\TokenHandler\WhileTokenHandler::class,
]

scanners array

Permet de changer la manière de scanner et détecter chaque séquence de caractères pour obtenir le token correspondant, la map par défaut est :

[
    'new_line'    => \Phug\Lexer\Scanner\NewLineScanner::class,
    'indent'      => \Phug\Lexer\Scanner\IndentationScanner::class,
    'import'      => \Phug\Lexer\Scanner\ImportScanner::class,
    'block'       => \Phug\Lexer\Scanner\BlockScanner::class,
    'yield'       => \Phug\Lexer\Scanner\YieldScanner::class,
    'conditional' => \Phug\Lexer\Scanner\ConditionalScanner::class,
    'each'        => \Phug\Lexer\Scanner\EachScanner::class,
    'case'        => \Phug\Lexer\Scanner\CaseScanner::class,
    'when'        => \Phug\Lexer\Scanner\WhenScanner::class,
    'do'          => \Phug\Lexer\Scanner\DoScanner::class,
    'while'       => \Phug\Lexer\Scanner\WhileScanner::class,
    'for'         => \Phug\Lexer\Scanner\ForScanner::class,
    'mixin'       => \Phug\Lexer\Scanner\MixinScanner::class,
    'mixin_call'  => \Phug\Lexer\Scanner\MixinCallScanner::class,
    'doctype'     => \Phug\Lexer\Scanner\DoctypeScanner::class,
    'keyword'     => \Phug\Lexer\Scanner\KeywordScanner::class,
    'tag'         => \Phug\Lexer\Scanner\TagScanner::class,
    'class'       => \Phug\Lexer\Scanner\ClassScanner::class,
    'id'          => \Phug\Lexer\Scanner\IdScanner::class,
    'attribute'   => \Phug\Lexer\Scanner\AttributeScanner::class,
    'assignment'  => \Phug\Lexer\Scanner\AssignmentScanner::class,
    'variable'    => \Phug\Lexer\Scanner\VariableScanner::class,
    'comment'     => \Phug\Lexer\Scanner\CommentScanner::class,
    'filter'      => \Phug\Lexer\Scanner\FilterScanner::class,
    'expression'  => \Phug\Lexer\Scanner\ExpressionScanner::class,
    'code'        => \Phug\Lexer\Scanner\CodeScanner::class,
    'markup'      => \Phug\Lexer\Scanner\MarkupScanner::class,
    'expansion'   => \Phug\Lexer\Scanner\ExpansionScanner::class,
    'dynamic_tag' => \Phug\Lexer\Scanner\DynamicTagScanner::class,
    'text_block'  => \Phug\Lexer\Scanner\TextBlockScanner::class,
    'text_line'   => \Phug\Lexer\Scanner\TextLineScanner::class,
]

assignment_handlers array

Permet de changer la manière de gérer les assignements et permet d'en rajouter de nouveaux, exemple :

Phug::display('img&foo()', [], [
    'assignment_handlers' => [
        function (AssignmentElement $assignment) {
            if ($assignment->getName() === 'foo') {
                $assignment->detach();

                yield new AttributeElement('data-foo', '123');
            }
        },
    ],
]);

Affiche :

<img data-foo="123" />

attribute_assignments array

Permet de changer la manière de gérer les attributs, exemple :


Phug::display('img&attributes(["foo" => "bar", "biz" => true])', [], [
    'attribute_assignments' => [
        'foo' => function () {
            return 'not-bar';
        },
    ],
]);

Affiche :

<img foo="not-bar" biz="biz" />

CLI

Phug et Pug-php peuvent être exécutés en ligne de commande :

./vendor/bin/phug render 'p=$msg' '{"msg": "Salut"}'
./vendor/bin/pug render 'p=msg' '{"msg": "Salut"}'

./vendor/bin/pug n'est disponible que si vous installez Pug-php, ./vendor/bin/phug est toujours disponible que vous utilisiez l'un ou l'autre.

Options globales

2 options globales sont disponibles pour toutes les commandes :

--output-file (ou -o) redirige la sortie vers le fichier spécifié, cela permet par exemple d'enregister le rendu HTML dans un fichier :

./vendor/bin/phug render-file mon-fichier-source.pug --output-file mon-fichier-de-destination.html

--bottstrap (ou -b) permet d'inclure un fichier PHP à exécuter avant la commande. Par exemple, vous pouvez y définir vos variables de manière dynamique.

Mettons que vous avez le fichier suivant : definition-variables.php

Phug::share([
  'heure' => date('H:i'),
]);
./vendor/bin/phug render 'p=$heure' -b definition-variables.php -o page.html

page.html va contenir un paragraphe avec l'heure à l'intérieur (exemple <p>17:47</p>).

Le fichier de démarrage peut exécuter n'importe quel code PHP et a accès à toutes les classes grâce à l'autoload de Composer.

Si un fichier s'appelle phugBootstrap.php dans le dossier courant, alors il sera utilisé comme fichier de démarrage par défaut.

Ces deux options peuvent être définies avec un espace ou le symbole égal, donc toutes les commandes ci-dessous sont équivalentes :

./vendor/bin/phug render-file a.pug --output-file a.html
./vendor/bin/phug render-file a.pug --output-file=a.html
./vendor/bin/phug render-file a.pug -o a.html
./vendor/bin/phug render-file a.pug -o=a.html

Commandes

Les commandes sont les mêmes pour phug et pug et elles vont appeler les mêmes méthodes. La seule différence est que phug va les appeler sur la façade Phug (qui utilise Phug\Renderer sans extension ni réglages particuliers) et pug va les appeler sur la façade Pug\Facade (qui utilise Pug\Pug et embarque js-phpize et les réglages par défaut de pug-php). Pour les deux, vous pouvez utiliser --bootstrap pour régler plus d'options, ajouter des extension, partager des variables, etc.

render (ou display)

Appelle ::render() et prend 1 argument requis : le code pug d'entrée (en tant que chaîne de caractères), et 2 arguments optionnels : les variables locales (au format JSON) et les options (au format JSON)

./vendor/bin/phug render 'p(foo="a")' '{}' '{"attributes_mapping":{"foo":"bar"}}'

Va afficher :

<p bar="a"></p>

render-file (ou display-file)

Appelle ::renderFile(), c'est exactement la même chose que render sauf qu'il prend un chemin defichier en premier argument :

./vendor/bin/phug render-file /dossier/fichier.pug '{"maVariable":"valeur"}' '{"self":true}'

render-directory (ou display-directory)

Appelle ::renderDirectory(), effectue le rendu de chacun des fichiers contenus dans un dossier et dans ses sous-dossiers. Cette commande prend 1 argument requis : le dossier d'entrée, et 3 arguments optionnels :

  • le dossier de sortie (si non spécifié, le dossier d'entrée est utilisé, donc les fichiers rendus sont générés à côté des fichiers d'entrée)
  • extension des fichiers de sortie (.html par défaut)
  • variables locales (au format JSON)
./vendor/bin/phug render-directory ./templates ./pages '.xml' '{"foo":"bar"}' 

En supposant que vous avez le dossier ./templates suivant :

templates
  une-vue.pug
  sous-dossier
    autre-vue.pug

Vous allez obtenir le dossier .pages suivant :

pages
  une-vue.xml
  sous-dossier
    autre-vue.xml

Et dans cet exemple, tous les fichiers rendus auront $foo = "bar" comme variables locales disponibles.

compile

Appelle ::compile(). Compile du code pug sans le rendre (l'exécuter). Elle prend 1 argument requis : le code pug et 1 optionnel : un nom de fichier (peut aussi être fourni via les options), il sera utiliser pour résoudre les imports relatifs.

./vendor/bin/phug compile 'a(href=$lien) Aller' 'fichier.pug' -o fichier.php

Ceci va écrire dans file.php un contenu semblable à :

<a href="<?= htmlspecialchars($lien) ?>">Aller</a>

compile-file

Appelle ::compileFile(). Compile un fichier pug sans le rendre.

./vendor/bin/phug compile-file vues/index.pug -o public/index.php

compile-directory (ou cache-directory)

Appelle ::cacheDirectory() (via ::textualCacheDirectory()). Compile chaque fichier d'un dossier et de ses sous-dossiers.

C'est le moyen parfait de mettre en cache tous vos fichiers pug lorsque vous déployer une nouvelle version de votre application sur un serveur de production.

Si vous appeler cette commande à chaque fois que vous mettez quelque chose de nouveau en production, vous pouvez alors désactiver la vérification de cache en utilisant l'option 'up_to_date_check' => false pour optimiser les performances.

./vendor/bin/phug compile-directory vues cache '{"option":"valeur"}'

Seul le premier argument est requis (le dossier d'entrée où sont stockés vos fichiers pug).

En deuxième argument optionnel, vous pouvez spécifier le chemin du dossier de cache, sinon le chemin spécifié via l'option cache_dir sera utilisé, si non spécifié, le dossier temporaire du système sera utilisé.

Le troisième argument (aussi optionnel) permet de passer des options au format JSON si besoin.

commandes personnalisées

Vous pouvez créer des commandes personnalisées grâce à l'option commands :

Par exemple si vous écrivez ceci dans un fichier phugBootstrap.php (dans le dossier dans lequel vous exécutez les commandes, généralement à la racine de votre projet) ou dans n'importe quel autre fichier de votre projet que vous chargez avec l'option de CLI --bootstrap :

<?php

Phug::setOption('commands', [
  'renderDate' => function () {
    return Phug::render('p=date("d/m/Y")');
  },
]);

Vous pourrez alors lancer la commande suivante :

./vendor/bin/phug render-date

Et ceci affichera la date dans un paragraphe :

<p>09/02/2018</p>

L'option commands doit être un array listant les commandes personnalisées, chaque commande peut être décrit de 3 manière différentes :

<?php

Phug::setOption('commands', [
  'cacheFile',
    // Rend la méthode Phug::cacheFile()
    // disponible avec son nom en casse kebab :
    // ./vendor/bin/phug cache-file

  'storeFile' => 'cacheFile',
    // Rend la méthode Phug::cacheFile()
    // avec un autre nom :
    // ./vendor/bin/phug store-file

  'maFonction' => function () {
    return 'Salut';
  },
    // Exécute la fonction avec le nom donné :
    // ./vendor/bin/phug ma-fonction
]);

La commande peut également appeler un macro.

Détecter les changements et compiler automatiquement

Pour utiliser la commande watch, vous aurez besoin d'installer phug/watcher :

composer require phug/watcher

Et vous pouvez utiliser la commande --init pour créer un fichier phugBoostrap.php utilisé par défaut comme fichier de démarrage par le programme phug.

./vendor/bin/watcher --init

Dans ce fichier, vous pouvez modifier la liste des dossiers à surveiller (./views et ./templates par défaut) et vous pouvez changer le chemin du cache (par défaut, un dossier phug-cache est créé dans le dossier temporaire du système).

Puis ce fichier active l'extension du watcher et régler les options de Phug.

Pour fonctionner correctement, les options utilisées doivent être les mêmes qie dans votre application. Pour les garder synchronisées vous pouvez utiliser un fichier de configuration commun.

Par exemple, mettons que votre app a la structure suivante :

- vendor
- config
  - phug.php
- bootstrap
  - cli.php
  - web.php
- views
  - home.pug
- cache
  - views
composer.json
index.php

Alors vous pouvez avoir les contenus suivants :

phug.php

<?php return [
  'cache_dir' => __DIR__.'/../cache/views',
  'paths'     => [
    __DIR__.'/../views',
  ],
  // N'importe quelle autre option utilisée dans
  // votre application
  'debug'     => true,
];

cli.php

<?php

$options = include __DIR__.'/../config/phug.php';

if (!file_exists($options['cache_dir']) && !@mkdir($options['cache_dir'], 0777, true)) {
    throw new \RuntimeException(
        $options['cache_dir'].' cache directory could not be created.'
    );
}

Phug::addExtension(\Phug\WatcherExtension::class);

Phug::setOptions($options);

web.php

<?php

include_once __DIR__.'/../vendor/autolod.php';

$options = include __DIR__.'/../config/phug.php';

Phug::setOptions($options);

index.php

<?php

include_once __DIR__.'/bootstrap/web.php';

Phug::displayFile('home');

Et vous pouvez lancer le watcher avec :

./vendor/bin/phug watch -b bootstrap/cli.php

Quand vous éditerez un fichier dans le dossier views et l'enregistrer, le cache sera rafraîchit automatiquement (tant que la commande sera en cours d'exécution).

Si votre démarrage CLI utiliser l'emplacement par défaut (phugBootstrap.php), vous pouvez simplement faire :

./vendor/bin/phug watch

Recharger le navigateur automatiquement en cas de changement

L'auto-rechargement du navigateur a aussi besoin du paquet phug/watcher (voir plus haut pour l'installer).

Il permet de démarrer un serveur de développement et un watcher en parallèle sur 2 ports différents avec la commande suivante :

./vendor/bin/phug listen 9000 index.php

Ceci va démarrer un serveur de développement comme l'aurait fait :

php -S localhost:9000 index.php

En supposant que vous avez chargé l'extension \Phug\WatcherExtension dans index.php, une balise <script> est aussi ajoutée au rendu pour surveiller les changements et rafraîcher la page lorsqu'il y en a (en commiunicant sur un second port, par défaut 8066).

Par exemple avec une installation basique :

composer require phug/watcher
./vendor/bin/watcher --init

Et le contenu suivant dans index.php :

<?php

include_once __DIR__ . '/vendor/autoload.php';

include_once __DIR__ . '/phugBootstrap.php';

Phug::displayFile('views/basic.pug');

Puis en exécutant ./vendor/bin/phug listen 9000 index.php, vous pourrez charger http://localhost:9000 dans un navigateur web et la page se rafraichira tout seule si un changement survient dans le dossier views.

Tests unitaires et couverture

Vous pouvez essayer notre outil CLI expérimental de test qui utilise PHPUnit et Xdebug incluant des reporteurs de couverture (coverage) de code pour fichiers Pug et des aides tout-en-un pour PHP et Pug :

https://github.com/phug-php/tester

Questions fréquentes

Pourquoi ai-je une erreur avec les variables en CASSE_MAJUSCULE ?

Cela peut arriver lorsque vous This can happen when you use utilisez le style JS (module js-phpize ou Pug-php) simplement parce que rien dans la syntaxe JS ne permet de distinguer une constante d'une variable donc nous avons choisi de suivre la convention la plus utilisée : un nom tout en majuscules est une constante, tout le reste est une variable :

:php
  $fooBar = 9;
  define('FOO_BAR', 8);
  $d = 9;
  define('D', 8);
  $_ = '_';
  define('_K', 5);

p=fooBar
p=FOO_BAR
p=d
p=D
p=_
p=_K

Il est toujours possible de désactiver les constantes de la manière suivante :

<?php

use Pug\Pug;

include 'vendor/autoload.php';

$pug = new Pug([
    'module_options' => [
        'jsphpize' => [
            'disableConstants' => true,
        ],
    ],
]);

$pug->display('p=FOO', [
    'FOO' => 'variable',
]);

Comment utiliser les namespaces dans un template pug ?

Par défaut, les templates sont exécutés sans namespace et vous aurez besoin d'écrire les chemins complets pour accéder aux fonctions et classes :

p=\QuelquePart\unefonction()
p=appelle_depuis_le_namespace_racine()
- $a = new \QuelquePart\UneClasse()

Vous ne pouvez pas définir un namespace au début d'un template pug car ce n'est pas le début du fichier PHP compilé (nous ajoutons du code de debug, les dépendances, les fonctions de mixins, etc.)

Cependant vous pouvez appliquer globalement un namespace à tous vos templates de la manière suivante :

Phug::setOption('on_output', function (OutputEvent $event) {
  $event->prependCode('namespace QuelquePart;');
});

Avec cet intercepteur de l'événement output, le précédent code devient :

p=unefonction()
p=\appelle_depuis_le_namespace_racine()
- $a = new UneClasse()

Comment exécuter des scripts JS à l'intérieur des templates?

Il y a différentes approches possibles :

  • Tout d'abord, évitez de mélanger du PHP et du JS dans votre back-end donc si vous avez déjà une application PHP et trouvez un paquet node.js qui vous intéresse, vérifiez d'abord qu'il n'existe pas d'équivalent en PHP.

  • Si vous n'avez pas besoin d'appeler de fonctions, méthodes ou objets PHP dans vos templates, alors vous pouvez utiliser le paquet pugjs natif de npm. Pug-php a une option pour ça :

<?php

use Pug\Pug;

include 'vendor/autoload.php';

$pug = new Pug([
    'pugjs' => true,
]);

// Moteur Phug ignoré, pugjs utilisé à la place
$pug-&gt;display('p=9..toString()');

// Dans ce mode, vous pouvez `require` n'importe quel
// fichier JS ou paquet npm :
$pug->display('
- moment = require("moment")
p=moment("20111031", "YYYYMMDD").fromNow()
');
  • Vous pouvez passer une fonction comme n'importe quelle autre variable via share ou render qui peut appeler un programme CLI (comme node ou n'importe quoi d'autre) :
$pug->share('afficheDate', function ($date) {
    return shell_exec('node votre-script.js ' . escapeshellarg($date));
});
$pug->share('afficheDate', function ($date) {
    $v8 = new V8Js('valeurs', array('date' => '2016-05-09'));

    return $v8->executeString('appelleUneFonctionJs(valeurs.date)');
});

Comment utiliser des fonctions helper avec l'option pugjs ?

Quand vous utiliser Pug-php avec l'option pugjs à true toutes les données passées à la view sont encodées en JSON. Donc vous perdez vos typage de classe et vous perdez les fonctions de closure.

Néanmoins, vous pouvez écrire des fonctions JS à l'intérieur de vos templates et utiliser n'importe quelle locale ou variable partagée dedans :

-
  function asset(file) {
    return assetDirectory + '/' + file + '?v' + version;
  }

script(href=asset('app'))
[
  'assetDirectory' => 'assets',
  'version' => '2.3.4',
]

Comment désactiver les erreurs en production ?

En production, vous devriez régler l'option debug à false. Puis vous devriez utiliser un gestionnaire d'exceptions global pour votre application PHP qui cache les erreurs à l'utilisateur.

L'idéal est de les enregistrer (dans un fichier de log par exemple) en utilisant la gestion d'exception (voir set_exception_handler).

Un moyen plus radical est de les cacher complètement avec error_reporting(0); ou la même configuration dans php.ini.

Comment inclure dynamiquement des fichiers ?

Les mot-clés include et extend n'accepte que des chemins statiques : include monFichier mais pas des variables : include $maVariable.

Mais les mots-clés personnalisés arrivent à la rescousse :

Phug::addKeyword('dyninclude', function ($args) {
    return array(
        'beginPhp' => 'echo file_get_contents(' . $args . ');',
    );
});

Cela permet d'include des fichiers en tant que texte brut :

- $fichierDeStyle = 'machin.css'
- $fichierDeScript = 'machin.js'

style
  // Inclut machin.css en contenu inline
  dyninclude $fichierDeStyle
  
script
  // Inclut machin.js en contenu inline
  dyninclude $fichierDeScript

Attention : vous devez vérifier le contenu des variables avant leur inclusion. Si $fichierDeStyle contient "../../config.php" et que config.php contient des mots de passes de BDD, des secrets de session, etc. ces informations privées vont s'afficher.

Vous devez être encore plus prudent si vous autorisez l'inclusion de fichiers PHP :

Phug::addKeyword('phpinclude', function ($args) {
    return array(
        'beginPhp' => 'include ' . $args . ';',
    );
});

Cela peut être utilie et sécurisé si par exemple vous faites ceci :

each $module in $modulesUtilisateur
  - $module = 'modules/'.preg_replace('/[^a-zA-Z0-9_-]/', '', $module).'.php'
  phpinclude $module

Dans cet exemple, en supprimant tous les caractères exceptés les lettres, chiffres et tirets, peu importe ce que contient $modulesUtilisateur et d'où ça vient, vous ête sûr d'inclure un fichier qui existe dans le dossier modules. Donc vous avez juste à vérifier ce que vous mettez dans ce dossier.

Finalement vous pouvez aussi inclure des fichiers dynamiquement et les rendre avec Phug (ou n'importe quel transformer) :

Phug::addKeyword('puginclude', function ($args) {
    return array(
        'beginPhp' => 'Phug::display(' . $args . ');',
    );
});
- $path = '../dossier/template'
puginclude $path

Cela inclut ../dossier/template.pug comme l'extension est concaténée dans le callback du mot-clé.

Comment gérer l'internationalisation ?

Les fonctions de traduction telles que __() pour Laravel ou _() pour gettext peuvent être appelées comme n'importe quelle autre fonction dans un expression ou un bloc de code.

Le parser gettext ne supporte pas les fichiers pug mais le mode python donne de bons résultats avec les fichiers pug.

Les méthodes d'initialisation comme textdomain peuvent être appelée dans des blocs de code. Par exemple - textdomain("domain") peut être la première ligne de votre fichier pug.

Enfin, soyez sûr que l'extension nécessaire (comme gettext) est bien installée sur l'instance PHP qui rend vos templates pug.

Comment vider le cache ?

Si vous utilisez laravel-pug le cache est géré par Laravel et donc vous pouvez vous référer à la documentation du framework pour les opérations sur le cache. Le vide se fait par exemple avec php artisan cache:clear

Sinon en production, vous devriez utiliser la commande cache-directory et désactiver l'option up_to_date_check.

En environement de développement, si vous avez le moindre problème avec le cache, vous pouvez juste désactiver sans risque le cache avec un code comme celui-là :

Phug::setOption('cache', $prod ? 'dossier/de/cache' : false);

Quel est l'équivalent des filtres Twig ?

Si vous connaissez Twig, vous connaissez peut-être cette syntaxe :

<div>{{ param1 | filtre(param2, param3) }}</div>

Ou si vous connaissez AngularJS, ces filtres :

<div>{{ param1 | filtre : param2 : param3 }}</div>

Les filtres de Pugsont un peu différents puisqu'ils ne sont autorisés que en dehors des expressions :

div
  :filter(param2=param2 param3=param3)
    param1

À l'intérieur du contenu d'une balise, c'est techniquement utilisable même si cette syntaxe ne serait sûrement pas pertinente dans ce cas.

Pour les valeurs des attributs ou à l'intérieur des arguments de mixin, il n'y a rien de disponible semblable aux filtres parce que des simples fonctions marchent déjà très bien :

<div>{{ filter(param1, param2, param3) }}</div>

Les filtres de Twig/AngularJS ne sont rien de plus qu'une inversion du premier argument et du nom de la fonction. La plupart des filtres Twig sont disponible en tant que fonctions natives de PHP (split : explode, replace : strtr, nl2br : nl2br, etc.).

De plus, vous pouvez passer des fonctions comme closures à l'intérieur de vos variables locales ou des variables partagées.

Et souvenez-vous que le symbole | existe déjà en PHP, c'est l'opérateur binaire OR.

Comment résoudre Warning: include() read X bytes more data than requested (Y read, Z max) ?

Ceci arrive probablement parce que vous avez dans votre php.ini le réglage mbstring.func_overload avec le flag 2 activé.

Comme c'est un réglage obsolète, la meilleure chose à faire est de le régler à 0 et de remplacer manuellement les fonctions dans votre application plutôt que d'utiliser l'overload.

Si vous avez vraiment besoin de garder ce réglage, vous pouvez toujours utiliser le FileAdapter qui n'est pas sensible à ce problème :

Phug::setOption('adapter_class_name', FileAdapter::class);

Comment résoudre Maximum function nesting level of 'X' reached ?

Cette erreur signifie que vous avez dépassé le réglage xdebug.max_nesting_level du php.ini. La valeur par défaut est 100 et peut s'avérer insuffisante. Avec beaucoup d'includes et de mixins imbriqués dans vos templates, vous pourriez atteindre les 500 nesting levels (niveaux d'imbrication). Donc tout d'abord, essayez d'augmenter ce réglage.

Si vous avez toujours l'erreur, alors vous avez probablement une récursion infinie. Cela peut arriver avec pug quand un template a.pug inclut b.pug et que ce dernier inclut lui-même a.pug (bien sûr il en va de même pour les fichiers PHP). Si vous n'avez pas de conditions pour éviter que la récursion soit infinie, vous allez avoir une erreur nesting level ou timeout exceeded. Cela peut aussi arriver quand un mixin s'appelle lui-même (ou indirectement via d'autres mixins) et la même chose est possible avec les fonctions PHP.

Note : xdebug est une extension de débogage, donc n'oubliez pas de la d'asctiver en production.

Devrais-je utiliser render ou renderFile ?

Dans les anciennes versions de Pug-php, il n'y avait que la méthode render qui convertissait un fichier si l'argument donné correspondait au chemin d'un fichier existant, sinon elle convertissait l'argument en tant que chaîne pug. Alors que Tale-pug convertissait toujours un fichier.

Aucun de ces comportement n'était aligné avec Pugjs donc on a choisit de le changer dans Phug. Avec Phug, render ne prend que des chaîne, pas de fichier, et renderFile a été ajotué pour convertir des fichiers.

Pug-php garde l'ancien comportement pour le moment pour faciliter la mise à niveau mais il est fortement recommandé d'utiliser l'option strict pour obtenir le nouveau comportement :

$pug = new Pug([
  'strict' => true,
]);

De cette manière $pug->render() va toujours prender une chaîne pug en premier argument peu importe que l'argument corresponde our non a un chemin de fichier. Cela peut éviter des comportements inattendus.

Si pour des raisons de compatibilité, vous ne pouvez pas utiliser cette option, alors vous devriez éviter d'utiliser render et utiliser renderString pour une chaîne pug et renderFile pour un fichier.

Comment déboguer un code venant de la documentation qui ne fonctionne pas dans mon application ?

D'abord, nous supposons que vous utiliser la dernière version de Phug (ou Pug-php >= 3).

Vous pouvez vérifier votre version de Phug avec composer show phug/phug et la mettre à jour avec : composer update

La dernière version stable de Phug est Latest Stable Version

Si vous utiliser une version de Pug-php < 3.0 (vérifiez avec composer show phug/phug) cette documentation n'est pas exacte pour vous et nous vous recommandons vivement de mettre à jour Pug-php à la version Latest Stable Version

Si vous utilisez Tale-jade ou Tale-pug, nous vous conseillons de migrer à Phug.

Ensuite, les exemples dans cette documentation sont pour Phug si ce n'est pas précisé autrement. Si vous utilisez Pug-php vous devez l'adapter. Par exemple :

Phug::setOption('cache_dir', 'dossier');
Phug::display('template.pug', $vars);

Devrait être écrit avec l'une des 2 syntaxes suivantes pour que les fonctionnalités de Pug-php fonctionnent :

\Pug\Facade::setOption('cache_dir', 'dossier');
\Pug\Facade::display('template.pug', $vars);

\Pug\Facade permet de remplacer facilement Phug en gardant la syntaxe. Mais vous pourriez préférer le style instancié :

$pug = new Pug([
  'cache_dir' => 'dossier',
]);
$pug->display('template.pug', $vars);

Alternatives

Il existes des méthodes alternatives pour avoir des templates pug avec un back-end PHP.

V8Js

http://php.net/manual/fr/book.v8js.php

Vous pouvez utiliser le moteur V8 depuis PHP. Vous pourriez alors avoir besoin de setModuleLoader pour charger le module node de pug et ses dépendances automatiquement.

Ensuite vous pouvez charger et exécuter pug avec executeString en lui passant des données PHP. Cela peut s'avérer le moyen le plus rapide d'obtenir le comportement exact de pugjs avec des données PHP.

À notre connaissance, aucune solution prêt à l'emploi de ce type n'existe. Si vous en avez fait ou en connaissez une, n'hésitez pas à cliquer sur le bouton [Modifier] pour nous soumettre une pull-request.

Pug-php avec l'option pugjs

Quand vous installez Pug-php, on vous propose l'installation du paquet node pug-cli. Si vous entrez Y, npm sera utilisé si disponible sur la machine pour installer le paquet officiel pug-cli, et Pug-php a une option pour l'utiliser à la place de son moteur :

<?php

include 'vendor/autoload.php';

$pug = new Pug([
  'pugjs' => true,
]);

$html = $pug->render('p=9..toString()');

Ici vous appelez le paquet natif de pug en utilisant directement node.js, c'est pour ça que vous pouvez utiliser n'importe quelle syntaxe JS.

Si vous utilisez cette option, soyez conscient que nous ne sommes plus responsables de ce qu'il se passe à l'intérieur des templates, ce n'est plus PHP qui est utilisé et la documentation de référence est alors https://pugjs.org

Optionnellement, vous pouvez spécifier le chemin vers les programmes node et pug-cli avec les outils suivants :

$pug = new Pug([
  'pugjs' => true,
  'nodePath' => __DIR__ . '/../bin/node',
]);
NodejsPhpFallback::setModulePath('pug-cli', __DIR__ . '/../node_modules/pug-cli');
 Modifier