Phug
Phug (moteur de rendu de Pug-php et Tale-pug)
Dernière version stable | 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 :
- voir comment l'installer dans le chapitre suivant
- consulter le projet original pugjs
- voir toutes les fonctionnalités de ce language
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.
Laravel : bkwld/laravel-pug
Symfony : pug-php/pug-symfony
Phalcon : pug-php/pug-phalcon
CodeIgniter : ci-pug/ci-pug
Yii 2 : pug/yii2
Slim 3 : pug/slim
Silex : exemple d'implementation
Lumen : bkwld/laravel-pug fonctionne aussi avec Lumen
Zend Expressive infw/pug
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é
- WordPress : wordless
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="<code>")
div(unescaped!="<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 = '<strong>OK</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 < 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 <échapé> !'
Le code peut aussi être écrit sur la même ligne et supporte toutes sortes d'expressions.
p= 'Ce code est ' . '<échapé> !'
Note: si vous utilisez les expressions JavaScript, la concaténations
doivent utiliser l'opérateur +
:
p= 'Ce code est ' + ' <é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 = '<strong>'
- $fin = '</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
<!--[if IE 8]>
<html lang="fr" class="lt-ie9">
<![endif]-->
<!--[if gt IE 8]><!-->
<html lang="fr">
<!--<![endif]-->
body
p Supporter les anciennes versions des navigateurs, c'est pénible.
</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 > < " et &
ou écrire des choses comme
<machin></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
<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 = "<span>echappé !</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 = "<em>Il faut quand même faire attention.</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 < 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 < 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 <em>texte</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.
<html>
body
p L'ouverture et fermeture de la balise html
p sont chacun considérés comme une ligne de HTML litéral.
</html>
Attention: Avec pugjs, indenter le contenu (body dans
cet exemple) n'a pas d'incidence. Avec pugjs, seules les
lignes commençant par <
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 <
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
<p>
Texte non traité #{'sauf pour les interpolations'}
</p>
<p>
Titre
<a href="/lien">
Boutton
</a>
</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 :
- 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.
- 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é
- 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 :
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
ourenderFile
) - buffer: tampon de sortie (ce que
display
oudisplayFile
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()
etcompileFile()
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 :
\Phug\AbstractRendererModule
\Phug\AbstractCompilerModule
\Phug\AbstractFormatterModule
\Phug\AbstractParserModule
\Phug\AbstractLexerModule
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 questring
;$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
vauttrue
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
où %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'],
]
Où $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 :
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->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
ourender
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));
});
- Vous pouvez utiliser le moteur V8Js (http://php.net/manual/fr/book.v8js.php) :
$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
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
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');
Ressources
Liste de filtres personalisés compatibles avec Phug : http://pug-filters.selfbuild.fr/
Liste des projets de l'écosystème Phug : https://gist.github.com/kylekatarnls/8720155b06b016f8128ff511b8695532
Code source de ce site web : https://github.com/phug-php/website