2023-09-26
PHP 8.0+
6.1.1
Quand vous codez une application web, le rendu des vues peut être un véritable défi surtout si vous souhaitez n'utiliser que du PHP natif et éviter de faire appel à un langage externe de génération de code.
C'est l'unique objectif de PhpEcho
: fournir un moteur de rendu en PHP natif sans aucune autre dépendance.
PhpEcho
est très simple à utiliser, il colle parfaitement à la syntaxe utilisée par PHP pour le rendu du HTML/CSS/JS.
Il est basé sur une approche objet n'utilisant qu'une seule et unique classe pour accomplir la tâche.
Comme vous pouvez vous en douter, l'utilisation du PHP natif offre des performances inégalées.
Besoin d'aucun cache pour avoir des performances stratosphériques.
Pas de parsage additionnel, pas de nouvelle syntaxe à apprendre !
Si vous avez déjà quelques bases en PHP, c'est amplement suffisant pour l'utiliser.
Pour résumer, vous n'avez qu'à pointer vers un fichier de vue et passer à l'instance un tableau clé-valeur disponible au rendu.
La classe gère :
- l'inclusion de fichiers
- l'extraction et l'échappement des valeurs stockées dans l'instance courante
- l'échappement de n'importe quelle valeur sur demande (y compris clé-valeur des tableaux multidimensionnels)
- le rendu brut et sans échappement d'une valeur sur demande
- la possibilité d'écrire directement du code HTML au lieu de passer par des inclusions de fichier
- la gestion et le rendu de toutes les instances de classe implémentant la fonction magique
__toString()
- l'accès à la balise globale
<head></head>
de n'importe quel bloc enfant - détection d'inclusion infinie sur option
Vous serez également en mesure d'étendre les fonctionnalités du moteur en créant vos propres assistants tout en laissant votre EDI les lister rien qu'en utilisant la syntaxe PHPDoc.
- Installation
- Configuration
- Paramètres
- Principes and généralités
- Démarrage
- Blocs enfants
- Accès à la balise HEAD
- Valeurs utilisateurs
- Échappement automatique des valeurs
- Tableau d'instances de PhpEcho
- Utilisation d'une vue par défaut
- HTML au format HEREDOC
- Utilisation de l'id de bloc auto-généré
- Utilisation du composant
ViewBuilder
- Utilisation avancée : création de ses propres assistants
- Étudions quelques assistants
composer require rawsrc/phpecho
Pour l'utiliser, une fois que vous avez déclaré la classe en utilisant include_once
ou
n'importe quel autoloader, vous devez indiquer au moteur avant toute chose le répertoire
racine de toutes les vues (chemin résolu).
Veuillez noter que le seul séparateur de répertoire autorisé est /
(slash).
<?php
use rawsrc\PhpEcho\PhpEcho;
// eg: from the webroot directory
PhpEcho::setTemplateDirRoot(__DIR__.DIRECTORY_SEPARATOR.'View');
Par défaut, le moteur recherche en premier les valeurs demandées dans le tableau
local des valeurs attachées à chaque instance de PhpEcho
, puis si la clé demandée
n'est pas trouvée, il grimpe tous les blocs parents jusqu'à la racine.
Veuillez noter que la recherche n'est limitée qu'au premier niveau de chaque
tableau de valeurs.
Suite : Valeurs utilisateur
Par défaut le moteur est en mode production et n'interceptera pas les boucles infinies d'inclusion de blocs vues. Si vous souhaitez détecter la présence de ces boucles, il suffit de définir l'option ainsi :
<?php
use rawsrc\PhpEcho\PhpEcho;
PhpEcho::setDetectInfiniteLoop(true);
La détection de ces boucles consomme du temps et des ressources serveur, cette option n'est à utiliser qu'en mode développement et doit être désactivée en production.
Pour assurer le pilotage du rendu, il est possible de stocker dans n'importe quelle
instance de PhpEcho
autant de paramètres que nécessaire. Il y a deux niveaux de
paramètres : local et global.
Notez bien que les paramètres ne sont jamais échappés.
Si le paramètre est inexistant, le moteur déclenchera une Exception
.
// pour un bloc spécifique (paramètre local)
$this->setParam('document.isPopup', true);
$is_popup = $this->getParam('document.isPopup'); // true
$has = $this->hasParam('document.isPopup'); // true
$this->unsetParam('document.isPopup');
// pour tous les blocs (paramètre global)
PhpEcho::setGlobalParam('document.isPopup', true);
$is_popup = PhpEcho::getGlobalParam('document.isPopup'); // true
$has = PhpEcho::hasGlobalParam('document.isPopup');
PhpEcho::unsetGlobalParam('document.isPopup');;
Si vous souhaitez la valeur du paramètre local en premier et ensuite global si inexistant :
$is_popup = $this->getAnyParam(name: 'document.isPopup', seek_order: 'local');
Si vous souhaitez la valeur du paramètre global en premier et ensuite local si inexistant :
$is_popup = $this->getAnyParam(name: 'document.isPopup', seek_order: 'global');
Pour vérifier l'existence d'un paramètre dans les deux contextes :
$this->hasAnyParam('document.isPopup'); // contexte local puis global
Définition d'un paramètre dans les deux contextes simultanément :
$this->setAnyParam('document.isPopup', true); // the value is available in both contexts (local and global)
Suppression d'un paramètre dans les deux contextes simultanément :
$this->unsetAnyParam('document.isPopup');
- Toutes les valeurs extraites d'une instance de
PhpEcho
sont échappées et sûres dans un contexte HTML - Dans un fichier vue ou dans un assistant de code, l'instance courante de
PhpEcho
est accessible via$this
- Pour des vues complexes, la classe
ViewBuilder
est fournie avec le moteur PhpEcho
est fourni avec plusieurs générateurs de code appelés assistants pour vous rendre la vie meilleure.
En tant que développeur, vous savez que la complexité et la taille des applications web vont croissants.
Pour y arriver, vous devez diviser les vues en petits blocs qui composeront un rendu plus complexe.
Avec PhpEcho
, les blocs s'injectent et se lient les uns aux autres et composent des blocs plus vastes réutilisables.
Vous devez bien comprendre la structure d'une page HTML, c'est un arbre gigantesque. PhpEcho
suit exactement la même approche.
Il est fortement recommandé de garder les fichiers de rendu dans un répertoire séparé (gabarit, pages, blocs).
Habituellement, l'architecture est générique et assez simple :
- une page est basée sur un gabarit,
- une page contient autant de blocs que nécessaire,
- un bloc peut être composé d'autres blocs et ainsi de suite.
Notez : l'unité de travail de PhpEcho
est le bloc. Les autres composants sont
construits sur des blocs et sont eux-mêmes vus comme des blocs.
Simple, n'est-ce pas ?
Voici la partie classique de la section vue d'une application web :
www
|--- Controller
|--- Model
|--- View
| |--- block
| | |--- contact.php
| | |--- err404.php
| | |--- footer.php
| | |--- header.php
| | |--- home.php
| | |--- navbar.php
| | |--- login.php
| | |--- ...
| |--- layout
| | |--- err.php
| | |--- main.php
| | |--- ...
| |--- page
| | |--- about.php
| | |--- cart.php
| | |--- err.php
| | |--- homepage.php
| | |--- login.php
| | |--- ...
|--- bootstrap.php
|--- index.php
<?php
use rawsrc\PhpEcho\PhpEcho;
$block = new PhpEcho();
$block['foo'] = 'abc " < >'; // définition d'une paire clé-valeur dans l'instance
// pour obtenir la valeur échappée suffit de la demander
$x = $block['foo']; // $x = 'abc " < >'
// échappement à la demande en utilisant un assistant
$y = $block('hsc', 'any value to escape'); // ou
$y = $block->hsc('any value to escape'); // utilisation de la saisie assistée
// récupération de la valeur brut (non échappée)
$z = $block->raw('foo'); // $z = 'abc " < >'
// le type de la valeur est préservé, sont échappés toutes les chaînes de caractères et instances avec la méthode magique __toString()
$block['bar'] = new stdClass();
$bar = $block['bar'];
Génération de la page d'accueil en utilisant plusieurs blocs PhpEcho
séparés en plusieurs fichiers.
Pour bien comprendre comment les fichiers sont trouvés, le chemin partiel de chaque bloc est préfixé
par le chemin complet du répertoire racine des vues défini avec PhpEcho::setTemplateDirRoot()
.
<?php declare(strict_types=1);
use rawsrc\PhpEcho\PhpEcho;
$homepage = new PhpEcho('layout/main.php', [
'header' => new PhpEcho('block/header.php', [
'user' => 'rawsrc',
'navbar' => new PhpEcho('block/navbar.php'),
]),
'body' => new PhpEcho('block/home.php'),
'footer' => new PhpEcho('block/footer.php'),
]);
echo $homepage;
Comme vous pouvez le voir, vous composez votre vue qu'avec des blocs que vous
devez garder indépendants le plus possible les uns des autres. Dans le contexte des vues,
absolument tous les composants ne sont que des instances de PhpEcho
.
Tout est automatiquement câblé en arrière-plan et échappé par le moteur quand cela est nécessaire.
Comme PhpEcho
est très souple, vous composez votre vue bloc par bloc.
On va créer un simple formulaire de connexion basé sur la description ci-dessus.
En premier, création d'un fichier de mise en page appelé main.php
dans View/layout
avec
des valeurs requises :
- une description (texte)
- un titre (texte)
- un bloc
PhpEcho
en charge du rendu du corps de la page
<?php /** @var rawsrc\PhpEcho\PhpEcho $this */ // MAIN LAYOUT ?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="description" content="<?= $this['description'] ?>">
<title><?= $this['title'] ?></title>
</head>
<body>
<?= $this['body'] ?>
</body>
</html>
Comme toutes les instances de PhpEcho
sont préservées et transformées en texte que quand
cela est nécessaire, vous pouvez les appeler directement dans le code comme indiqué ci-dessus.
Ensuite, on créé un bloc vue appelé login.php
dans le répertoire View/block
contenant le code
HTML du formulaire :
Notez bien que $this['url_submit']
et $this['login']
sont automatiquement échappées
<?php /** @var rawsrc\PhpEcho\PhpEcho $this */ // LOGIN FORM BLOCK ?>
<p>Please login : </p>
<form method=post action="<?= $this['url_submit'] ?>">
<label>User</label>
<input type="text" name="login" value="<?= $this['login'] ?>"><br>
<label>Password</label>
<input type="password" name="pwd" value=""><br>
<input type="submit" name="submit" value="CONNECT">
</form>
Enfin, on code une page page/login.php
basée sur layout/main.php
et on y injecte
le corps de la page en utilisant le bloc block/login.php
.
<?php declare(strict_types=1); // LOGIN PAGE
use rawsrc\PhpEcho\PhpEcho;
echo new PhpEcho('layout/main.php', [
'title' => 'My first use case of PhpEcho',
'description' => 'PhpEcho, PHP template engine, easy to learn and use',
'body' => new PhpEcho('block/login.php', [
'login' => 'rawsrc',
'url_submit' => 'any/path/for/connection',
]),
]);
Code équivalent :
<?php declare(strict_types=1);
use rawsrc\PhpEcho\PhpEcho;
$page = new PhpEcho('layout/main.php');
$page['title'] = 'My first use case of PhpEcho';
$page['description'] = 'PhpEcho, PHP template engine, easy to learn and use';
$body = new PhpEcho('block/login.php');
$body['login'] = 'rawsrc';
$body['url_submit'] = 'any/path/for/connection';
$page['body'] = $body;
echo $page;
Comme vous pouvez le constater, PhpEcho
est super flexible. Plein de moyens sont
disponibles pour générer le rendu HTML/CSS/JS. La syntaxe est toujours claire, lisible et
facilement compréhensible.
Pour composer une vue à base de blocs enfants, il y a plusieurs moyens pour les déclarer :
$this->renderBlock()
: le bloc enfant est anonyme dans le contexte parental et n'est plus manipulable une fois rendu$this->addBlock()
: le bloc enfant est nommé et peut être manipulable dans le contexte parental directement par son nom$this->renderByDefault()
: le bloc enfant est nommé et si le bloc parent ne fournit aucun bloc spécifique avec le même nom alors le moteur utilisera celui défini par défaut
Notez bien encore que la vue complète doit être perçue comme un énorme arbre et que tous les blocs sont tous reliés entre eux.
Vous ne devez jamais déclarer un bloc totalement indépendant au sein d'un autre bloc.
Ceci n'est pas autorisé :
<?php /** @var rawsrc\PhpEcho\PhpEcho $this */ ?>
<p>Please login : </p>
<form method=post action="<?= $this['url_submit'] ?>">
<label>User</label>
<input type="text" name="login" value="<?= new rawsrc\PhpEcho\PhpEcho('block/login_input_text.php') ?>"><br>
<label>Password</label>
<input type="password" name="pwd" value=""><br>
<input type="submit" name="submit" value="CONNECT">
</form>
cela doit être remplacé par une des méthodes décrites ci-dessus :
<?php /** @var rawsrc\PhpEcho\PhpEcho $this */ ?>
<p>Please login : </p>
<form method=post action="<?= $this['url_submit'] ?>">
<label>User</label>
<input type="text" name="login" value="<?= $this->renderBlock('block/login_input_text.php') ?>"><br>
<label>Password</label>
<input type="password" name="pwd" value=""><br>
<input type="submit" name="submit" value="CONNECT">
</form>
Ainsi, vous ne coupez par l'arbre ;-)
Quand vous codez les vues d'un site, vous allez utiliser plein de petits blocs qui seront insérés à la
bonne place au moment du rendu. Comme beaucoup le savent, la meilleure architecture est de s'efforcer
de garder les blocs indépendants les uns des autres. Parfois, vous aurez le besoin d'ajouter des dépendances
directement dans l'en-tête de la page. Dans toutes les instances de PhpEcho
, vous disposez d'une méthode
nommée addhead()
qui est prévue pour.
Imaginez que vous êtes dans les tréfonds du DOM, vous avez besoin de déclarer un lien vers votre librairie. Dans le code de votre bloc, vous n'avez qu'à rajouter :
<?php $this->addHead('<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous">') ?>
ou utiliser un assistant script
qui va sécuriser toutes vos valeurs :
<?php $this->addHead('script', [
'src' => "https://code.jquery.com/jquery-3.4.1.min.js",
'integrity' => "sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=",
'crossorigin' => "anonymous"]) ?>
Maintenant dans le bloc qui gère le rendu de l'en-tête, il suffit juste de coder :
<?php /** @var rawsrc\PhpEcho\PhpEcho $this */ ?>
<head>
<?= $this->getHead(); ?>
</head>
Le moteur va compiler les données en provenance de tous les bloc enfants et compléter l'en-tête en conséquence.
Le moteur est capable de rechercher des valeurs selon différentes méthodes.
Quand vous demandez le rendu d'une valeur spécifique identifiée par une clé, le moteur va essayer
de la chercher dans les valeurs stockées localement, puis si rien n'est trouvé, il va chercher dans
les blocs parents en grimpant ainsi de suite jusqu'à atteindre la racine (le tout premier bloc PhpEcho
).
Ce fonctionnement est paramétrable en utilisant la méthode setSeekValueMode(string $mode)
.
Par défaut, le mode est défini sur parents
.
Les différents modes sont :
current
: le moteur va chercher uniquement la clé dans le tableau local des valeursparents
: le moteur va chercher la clé dans le tableau local des valeurs et si non trouvée, il va grimper les blocs un à un jusqu'à la racineroot
: le moteur va chercher la clé dans le tableau local des valeurs et si non trouvée, il va la chercher dans le bloc racine uniquement
Veuillez noter que la recherche de la clé ne se fait qu'au premier niveau du tableau des variables.
// ex : on a un tableau de valeurs déclaré dans un bloc
[
'k.abc' => 'v.abc',
'k.def' => [
'k.ghi' => 'v.ghi', // second niveau
'k.jkl' => 'v.jkl', // second niveau
],
];
// les clés visibles pour le bloc sont 'k.abc' et 'k.def' (premier niveau),
// les clés dans le sous-tableau ne sont pas automatiquement accessibles à moins d'utiliser foreach()
// si vous demandez directement $block['k.ghi'] le moteur ne cherchera pas dans le sous-tableau
Voici comment cela fonctionne :
<?php declare(strict_types=1);
use rawsrc\PhpEcho\PhpEcho;
// $page is the root of the tree
$page = new PhpEcho('layout/main.php');
$page['title'] = 'Mon premier projet PhpEcho';
$page['description'] = 'PhpEcho, un moteur de rendu PHP, facile à apprendre et à utiliser';
$page['login'] = 'rawsrc';
$page['url_submit'] = 'une/url/de/connexion';
$body = new PhpEcho('block/login.php'); // ce bloc attend deux valeurs (login et url_submit)
$page['body'] = $body; // $body['login'] and $body['url_submit'] sont bien définies
// les deux sont extraites de bloc racine : $page
echo $page;
Par défaut, si la clé est inexistante, le moteur déclenchera une Exception
.
Il est possible de changer ce comportement, en indiquant au moteur de retourner null
à la place
en utilisant la méthode setNullIfNotExists(bool $p)
.
L'échappement automatique des valeurs fonctionne aussi bien pour les clés que pour les valeurs. Soyez attentifs :
<?php
// supposons que nous avons ce genre de données :
$data = ['"name"' => 'rawsrc'];
// on injecte ces données dans une instance de PhpEcho
$block = new PhpEcho('dummy_block.php', ['my_data' => $data]);
// dans un contexte HTML nous devons tester la valeur de la clé
// un truc dans ce genre :
?>
<?php foreach ($this['my_data'] as $key => $value) {
// code erroné
if ($key === '"name"') { // ceci ne sera jamais vrai, la clé ayant été échappée
echo $value; // $value est automatiquement échappé
}
// code correct
if ($key === '"name"') {
echo $value; // $value est automatiquement échappé
}
}
Ou vous le faites manuellement en utilisant l'assistant raw()
et pensez bien à échapper la valeur :
foreach ($this->raw('my_data') as $key => $value) {
if ($key === '"name"') {
echo $this->hsc($value); // $value est manuellement échappée
}
}
Ou vous pouvez encore créer un assistant en charge de ne pas échapper les clés mais juste les valeurs.
Cela sera vu dans la section "Usage avance de PhpEcho
", plus bas.
Il est possible de définir plusieurs stratégies pour le rendu des vues, surtout en ce qui concerne le niveau de détail (la granularité) des mises en pages complexes. Supposons un code de rendu de la sorte :
<?php /** @var rawsrc\PhpEcho\PhpEcho $this */ ?>
<body>
<?= $this['body'] ?>
</body>
ou de cette sorte :
<?php /** @var rawsrc\PhpEcho\PhpEcho $this */ ?>
<body>
<?= $this['preloader'] ?>
<?= $this['top_header'] ?>
<?= $this['navbar'] ?>
<?= $this['navbar_mobile'] ?>
<?= $this['body'] ?>
<?= $this['footer'] ?>
<?= $this['copyright'] ?>
</body>
Le premier est très concis et le second très détaillé.
Pour préserver une certaine flexibilité tout en gardant le code concis, il est
possible d'utiliser un tableau d'instances de PhpEcho
pour une clé :
use rawsrc\PhpEcho\PhpEcho;
$page['body'] = [
new PhpEcho('block/preloader.php'),
new PhpEcho('block/top_header.php'),
new PhpEcho('block/navbar.php'),
new PhpEcho('block/navbar_mobile.php'),
new PhpEcho('block/body.php'),
new PhpEcho('block/footer.php'),
new PhpEcho('block/copyright.php'),
];
Les blocs sont rendus dans l'ordre dans lequel ils apparaissent. Depuis la version 6.1.0, le moteur est capable de rendre les blocs dans les tableaux multidimensionnels dans l'ordre dans lequel ils sont stockés.
Il est possible de définir un bloc PhpEcho
par défaut qui sera rendu si aucun
bloc n'est explicitement défini pour la clé visée :
<?php /** @var rawsrc\PhpEcho\PhpEcho $this */ ?>
<body>
<?= $this->renderByDefault('preloader', 'block/preloader.php') ?>
<?= $this->renderByDefault('top_header', 'block/top_header.php') ?>
<?= $this->renderByDefault('navbar', 'block/navbar.php') ?>
<?= $this->renderByDefault('navbar_mobile', 'block/navbar_mobile.php') ?>
<?= $this['body'] ?>
<?= $this->renderByDefault('footer', 'block/footer.php') ?>
<?= $this->renderByDefault('copyright', 'block/copyright.php') ?>
</body>
Toutes les clés sont facultatives sauf body
.
Il est aussi possible d'écrire directement du code HTML au format HEREDOC en lieu et place du mécanisme d'inclusion de fichiers. Il est obligatoire de définir les valeurs à passer au code html en amont de la génération du rendu.
Reprenons le code précédent et injectons directement le code HTML du fichier du
formulaire de connexion dans layout/main.php
en utilisant la notation HEREDOC.
<?php
use rawsrc\PhpEcho\PhpEcho;
$page = new PhpEcho('layout/main.php', [
'title' => 'My first use case of PhpEcho',
'description' => 'PhpEcho, PHP template engine, easy to learn and use',
]);
// ATTENTION: on définit les valeurs avant de les passer au bloc HTML
$body = new PhpEcho(vars: [
'login' => 'rawsrc',
'url_submit' => 'any/path/for/connection',
]);
// on définit directement le code du formulaire
$body->setCode(<<<html
<p>Please login : </p>
<form method=post action="{$body['url_submit']}>">
<label>User</label>
<input type="text" name="login" value="{$body['login']}"><br>
<label>Password</label>
<input type="password" name="pwd" value=""><br>
<input type="submit" name="submit" value="CONNECT">
</form>
html
);
$page['body'] = $body;
echo $page;
// Notez le codage dans ce cas : `$body` remplace `$this`
Chaque instance de PhpEcho
possède un id auto-généré qui peut être utilisé dans
n'importe quel balise html. Ceci va ainsi définir un contexte fermé qui va permettre
de travailler uniquement sur ce bloc sans interférer avec les autres.
On souhaiterait tester du code CSS sur le bloc sans interférer avec les autres parties de la page.
<?php /** @var rawsrc\PhpEcho\PhpEcho $this */ ?>
<?php $id = $this->getId() ?>
<style>
#<?= $id ?> label {
color: blue;
float: left;
font-weight: bold;
width: 30%;
}
#<?= $id ?> input {
float: right;
}
</style>
<div id="<?= $id ?>">
<p>Please login</p>
<form method="post" action="<?= $this['url_submit'] ?>>">
<label>Login</label>
<input type="text" name="login" value="<?= $this['login'] ?>"><br>
<label>Password</label>
<input type="password" name="pwd" value=""><br>
<input type="submit" name="submit" value="CONNECT">
</form>
</div>
Nous avons maintenant un contexte fermé défini par <div id="<?= $id ?>">
.
Dans un contexte orienté objet, il est souvent plus facile de manipuler la vue comme un objet aussi.
Revenons à l'exemple de la page de connexion.
Il est possible de la reprendre avec le composant ViewBuilder
:
<?php
namespace YourProject\View\Page;
use rawsrc\PhpEcho\PhpEcho;
use rawsrc\PhpEcho\ViewBuilder;
class Login extends ViewBuilder
{
public function build(): PhpEcho
{
$layout = new PhpEcho('layout/main.php');
$layout['description'] = 'dummy.description';
$layout['title'] = 'dummy.title';
$layout['body'] = new PhpEcho('block/login.php', [
'login' => 'rawsrc',
'url_submit' => 'any/path/for/connection',
/**
* Notez que le ViewBuilder implémente l'interface ArrayAccess,
* vous avez plein de moyens pour passer vos valeurs à la vue.
*/
]);
return $layout;
}
}
Dans un contrôleur qui doit renvoyer la page de connexion, vous pouvez coder quelque chose dans ce style :
<?php declare(strict_types=1);
namespace YourProject\Controller\Login;
use YourProject\View\Page\Login;
class Login
extends YourAbstractController
{
public function invoke(array $url_data = []): void
{
$page = new YourProject\View\Page\Login;
// on passe les valeurs attendues par la vue
$page['name'] = 'rawsrc';
$page['postal.code'] = 'foo.bar';
// un exemple de fin de traitement tiré d'un framework
$this->task->setResponse(Response::html($page));
}
}
Vous avez la possibilité de générer vos propres assistants aussi simplement qu'une fonction contextuelle.
PhpEcho
est fourni avec une petite bibliothèque d'assistants : stdPhpEchoHelpers.php
Chaque assistant est une fonction contextuelle (Closure
) qui peut retourner absolument ce que vous voulez.
Chaque assistant peut être lié à une instance de PhpEcho
ou totalement autonome.
Si le lien est avérée, à l'intérieur du code de la fonction contextuelle, vous disposez d'un accès au contexte
d'exécution de l'appelant en utilisant directement $this
. Si l'assistant est autonome, alors ce n'est qu'une
simple fonction paramétrée.
Si l'assistant doit disposer d'un accès au contexte de l'instance PhpEchp
qui l'appelle, alors il faut le déclarer
avec PhpEcho::addBindableHelper()
sinon PhpEcho::addHelper()
.
Cet assistant compare deux valeurs scalaires et renvoie " checked "
si elles sont identiques.
$checked = function($p, $ref) use ($is_scalar): string {
return $is_scalar($p) && $is_scalar($ref) && ((string)$p === (string)$ref) ? ' checked ' : '';
};
PhpEcho::addHelper(name: 'checked', helper: $checked, result_escaped: true);
Cet assistant est une fonction contextuelle autonome, elle n'a pas besoin d'un accès à l'instance de PhpEcho
qui l'appelle.
Comme tout est échappé par défaut dans PhpEcho
, on peut considérer que le mot " checked "
est inoffensif et n'as pas besoin
d'être échappé en plus d'où le troisième paramètre : result_escaped: true
.
Pour utiliser cet assistant dans vos vues, 2 manières :
$this('checked', 'your value', 'ref value'); // based on __invoke
$this->checked('your value', 'ref value'); // based on __call
Cet assistant renvoie la valeur brut d'une clé du tableau clé-valeurs présente dans chaque instance de PhpEcho
.
$raw = function(string $key) {
/** @var PhpEcho $this */
return $this->getOffsetRawValue($key);
};
PhpEcho::addBindableHelper(name: 'raw', closure: $raw, result_escpaed: true);
Comme cet assistant extrait une valeur contenue dans l'instance PhpEcho
qui l'appelle, il a besoin d'un
accès au contexte d'exécution du parent. C'est pourquoi il est déclaré en utilisant PhpEcho::addBindableHelper()
.
Comme nous voulons une valeur non échappée, on doit indiquer explicitement au moteur qu'elle est déjà échappée.
Bien sûr elle ne l'est pas, mais c'est ce que nous recherchons.
Pour utiliser cet assistant dans vos vues, 2 manières :
$this('raw', 'key');
$this->raw('key');
Il y a deux manières pour définir un assistant :
PhpEcho::addHelper(string $name, Closure $helper, bool $result_escaped = false)
PhpEcho::addBindableHelper(string $name, Closure $helper, bool $result_escaped = false)
Quand vous codez un assistant lié qui fera lui-même appel à un autre assistant lié et pour être sûr
que les deux assistants partagent le même contexte, vous devez utiliser cette syntaxe :
$existing_helper = $this->bound_helpers['$existing_helper_name'];
Reportez-vous à l'assistant $root_var
(regardez comment le lien vers un autre assistant lié $root
est créé).
Comme annoncé ci-dessus, la bibliothèque standard stdPhpEchoHelpers.php
contient des assistants
pour le traitement des données ainsi que pour la génération du HTML. Comme les assistants sont des petits
bouts de code, la lecture de leur code source vous aidera facilement à saisir leur utilité.
Exemples :
- Création d'un
<input>
en utilisant l'assistantvoidTag()
:
$this->voidTag('input', ['type' => 'text', 'name' => 'name', 'required', 'value' => ' < > " <script></script>']);
Pas d'inquiétude concernant les caractères dangereux, tous sont échappés. Voici le code HTML gténéré :
<input type="text" name="name" required value=" < > " <script></script>">
Il est aussi possible de le faire en utilisant l'assistant attributes()
:
<input <?= $this->attributes(['type' => 'text', 'name' => 'name', 'required', 'value' => ' < > " <script></script>']) ?>>
Comme vous pouvez le voir, il y a une tonne de méthodes pour arriver au résultat souhaité.
Revenons au problème précédent des clés échappées. Voici le code d'un assistant qui préserve les clés et échappe les valeurs en une seule fois.
<?php
use rawsrc\PhpEcho\PhpEcho;
/**
* Return an array of raw keys and escaped values for HTML
* Careful: keys are not safe in HTML context
*
* @param array $part
* @return array
*/
$hsc_array_values = function(array $part) use (&$hsc_array_values): array {
$hsc = PhpEcho::getHelperBase('hsc');
$to_escape = PhpEcho::getHelperBase('toEscape')
$data = [];
foreach ($part as $k => $v) {
if ($to_escape($v)) {
if (is_array($v)) {
$data[$k] = $hsc_array_values($v);
} else {
$data[$k] = $hsc($v);
}
} else {
$data[$k] = $v;
}
}
return $data;
};
PhpEcho::addBindableHelper('hscArrayValues', $hsc_array_values, true);
rawsrc