Symfony ExpressionLanguage : Comment utiliser ce composant ?
Le composant Symfony ExpressionLanguage : qu'est-ce que c'est ? Quand et comment l'utiliser ? Comment créer des expressions lors de cas plus complexes ?
La définition du manuel étant déjà très claire, je me contente simplement de vous la partager.
La gestion des flux a été introduite en PHP 4.3.0. comme méthode de généralisation des fichiers, sockets, connexions réseau, données compressées et autres opérations du même type, qui partagent des opérations communes. Dans sa définition la plus simple, un flux est une ressource qui présente des capacités de flux. C'est-à-dire que ces objets peuvent être lus ou recevoir des écritures de manière linéaire, et disposent aussi de moyens d'accéder à des positions arbitraires dans le flux.
Un protocole est une spécification de plusieurs règles pour un type de communication. Il peut également être utile pour vérifier que les informations soient correctement reçues.
Dans une conversation téléphonique quand l'interlocuteur décroche, il commence par dire "Allô" afin de spécifier qu'il est prêt à recevoir des informations.
La référence d'un flux (style URL) s'écrit de la forme scheme://target
.
Donc oui
https://blog.eleven-labs.com/
peut être ouvert comme un flux qui pointe vers une ressource distante.
Dans le contexte présent un wrapper
est un gestionnaire de protocole (de style URL).
Voici la liste des scheme
(wrappers) supportés par PHP :
Utiliser stream_get_wrappers()
pour avoir la liste des protocoles supportés par votre serveur.
var_dump(stream_get_wrappers());
Un transport en PHP ce n'est ni plus ni moins qu'un moyen de transférer des données. Pour cela PHP utilise les sockets
.
ℹ️ Il ne faut pas oublier que les sockets
sont aussi des flux 😜.
Sockets type WEB
Utiliser stream_get_transports()
pour avoir la liste des protocoles de transport supportés par votre serveur.
var_dump(stream_get_transports());
À noter que les paths des
sockets
web s'écrivent sous la forme{PROTOCOL}://{DOMAIN / IP v4, v6}:{PORT}
Voici plusieurs exemples :
Sockets type UNIX
Voila un petit tour d'horizon des différents protocoles que PHP met nativement, ou par extensions, à votre disposition.
👨🚀 Il est également possible de créer sont propre wrapper
, afin d'encapsuler la logique de transmission des données !
Comme par exemple :
Les contextes de flux sont une autre notion importante de la gestion des flux. Le contexte est un ensemble d'options qui sera passé en argument aux diverses fonctions de traitements de flux (ex: stream_*
, fopen
, copy
, file_get_contents
...).
$context = stream_context_create( [ 'http' => [ 'protocol_version' => '1.1', 'timeout' => 10, 'user_agent' => 'Wilson Browser', 'method' => 'GET', ], ], [] ); $result = file_get_contents('http://../page', false, $context);
La requête générée pour récupérer la page sera donc en
GET HTTP 1.1
avec un user agentWilson Browser
et un timeout à 10 secondes.
Vous pouvez également utiliser stream_context_set_default
afin de configurer les options par défaut des gestionnaires de flux.
stream_context_set_default([ 'http' => [ 'timeout' => 10, 'user_agent' => 'Wilson Browser', ], 'ftp' => [...] ]);
⚠️ Attention à l'utilisation de cette dernière, car elle configure les options de toutes les requêtes HTTP faites par la couche de flux de PHP.
Une autre partie assez intéressante des flux étant la possibilité d'ajouter des fonctions de filtre sur les données qui transiteront dans le flux.
Utiliser stream_get_filters()
pour avoir la liste des filtres supportés par votre serveur.
var_dump(stream_get_filters());
Il existe 2 syntaxes pour configurer un filtre sur un flux.
L'utilisation de stream_filter_append
/stream_filter_prepend
.
$fp = fopen('php://output', 'w'); stream_filter_append($fp, 'string.toupper', STREAM_FILTER_WRITE); fwrite($fp, "Code de lancement: 151215"); fclose($fp);
Grâce au flux php://filter
file_put_contents('php://filter/string.toupper/resource=php://output', 'Code de lancement: 151215');
Les 2 exemples ci-dessus vont afficher
CODE DE LANCEMENT: 151215
👨🚀 Là aussi il est possible de créer son propre filter
grâce à php_user_filter !
Voici un petit filtre geek.
class l33t_filter extends php_user_filter {
function filter($in, $out, &$consumed, $closing)
{
$common = ["a", "e", "s", "S", "A", "o", "O", "t", "l", "ph", "y", "H", "W", "M", "D", "V", "x"];
$leet = ["4", "3", "z", "Z", "4", "0", "0", "+", "1", "f", "j", "|-|", "\\/\\/", "|\\/|", "|)", "\\/", "><"];
while ($bucket = stream_bucket_make_writeable($in)) {
$bucket->data = str_replace($common, $leet, $bucket->data);
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
}
stream_filter_register('l33t_filter', 'l33t_filter') or die('Failed to register filter Markdown');
file_put_contents('php://filter/l33t_filter/resource=php://output', 'Salut ça va?');
L'exemple du dessus convertira
Salut ça va?
enZ41u+ ç4 v4?
On peut imaginer des filtres html>markdown, un emoji converter, un dictionnaire de mots blacklistés, etc.
PHP met également à notre disposition des flux d'Input
/Output
.
C'est le flux d'entrée standard (ligne de commande)
ℹ️ stdin: est en lecture seule
Exemple
//index.php copy( 'php://stdin', 'php://filter/string.toupper/resource=php://stdout' );
La commande ci-dessous écrira string
dans le flux stdin
et ici on copie simplement ce que l'on reçoit dans la sortie standard après avoir appliqué un filtre toupper
.
$ echo 'string' | php index.php #affichera STRING
$ cat file.txt | php index.php #affichera le contenu du fichier en majuscule
Sont les flux de sortie standards (ligne de commande)
ℹ️ stdin: est en lecture seule
Exemple
//error.php error_reporting(E_ALL); ini_set("display_errors", 0); echo 'Hello '.$_GET['user'];
Avec ce script nous allons reporter toutes les erreurs (E_ALL) mais ne pas les afficher aux visiteurs.
Dans un navigateur web ce script affichera :
Hello
Et les erreurs seront dirigées vers le flux php://stderr
qui est bien souvent configuré par votre file handler (nginx/apache...) grâce au paramètre error_log.
👨🚀 En ligne de commande php://output
php://stderr
sont par défaut envoyés dans php://stdout
Lançons ce script avec la commande suivante :
$ php error.php
Ce qui donnera :
PHP Notice: Undefined index: user in /var/www/error.php on line 5
PHP Stack trace:
PHP 1. {main}() /var/www/error.php:0
Hello %
Utilisons maintenant la redirection de flux GNU/Linux
$ php error.php > out.txt
la console affichera :
PHP Notice: Undefined index: user in /var/www/error.php on line 5
PHP Stack trace:
PHP 1. {main}() /var/www/error.php:0
tandis que le fichier out.txt
contiendra :
Hello
Mais on peut également rediriger la sortie d'erreur
$ php error.php 2> errors.txt
la console affichera :
Hello
tandis que le fichier errors.txt
contiendra :
PHP Notice: Undefined index: user in /var/www/error.php on line 5
PHP Stack trace:
PHP 1. {main}() /var/www/error.php:0
ℹ️ On peut également combiner les 2 php error.php > out.txt 2> errors.txt
>
et2>
écrase le fichier ou le crée.>>
et2>>
écrit à la fin du fichier ou le crée.2>&1
et2>>&1
redirige les 2 flux (avec le même comportement pour>
et>>
)
Permet de lire les données brutes du corps de la requête.
⚠️ N'est pas disponible avec enctype="multipart/form-data".
Permet d'écrire dans le buffer de sortie de la même façon que print
echo
.
// les 2 écritures suivantes feront la même chose file_put_contents('php://output', 'Some data'); echo 'Some data';
N'oubliez pas qu'avec php://output
vous pouvez utiliser les filtres, le contexte et même pourquoi pas réécrire au début.
Permet d'écrire dans un gestionnaire de fichiers. php://memory
stockera toujours en mémoire tandis que php://temp
stockera en mémoire, puis sur disque après avoir attendu la limite prédéfinie (défaut 2Mo)
👨🚀
php://temp/maxmemory:200
stockera sur disque une fois que 200 octets seront écrit dans le flux.
Permet d'ajouter un filtre lors de l'ouverture d'un autre flux. Exemple :
// Applique un filtre lors de la lecture file_get_contents('php://filter/read=string.rot13/resource=example.txt'); // Applique un filtre lors de l'écriture file_put_contents('php://filter/write=string.rot13/resource=example.txt', 'new data'); // Applique le filtre lors de l'écriture mais aussi lors de la lecture $res = fopen('php://filter/string.rot13/resource=example.txt', 'w+');
N'ayant pas trouvé d'informations utiles je vous laisse consulter la documentation
Prenons l'exemple d'une copie de fichier :
$file = file_get_contents('http://.../textfile.txt'); file_put_contents(__DIR__.'/downloaded_textfile.txt', $file);
⚠️ Avec ce code nous allons télécharger entièrement le fichier textfile.txt
dans la mémoire avant de l'écrire dans le fichier de destination !
Maintenant si l'on change légèrement le code on obtient :
DO
copy( 'http://.../textfile.txt', __DIR__.'/downloaded_textfile.txt' );
ℹ️ On peut faire le même traitement avec des ressources :
stream_copy_to_stream( fopen('http://.../textfile.txt', 'r'), fopen(__DIR__.'/downloaded_textfile.txt', 'w+') );
Voici la consommation mémoire pour un fichier de 5Mo.
Code | memory_get_usage(true) | memory_get_peak_usage(true) |
---|---|---|
file_get_content | 8Mo | 13Mo |
copy | 2Mo | 2Mo |
stream_copy_to_stream | 2Mo | 2Mo |
La différence de consommation mémoire est due au fait que copy
et stream_copy_to_stream
vont directement écrire la source dans la destination.
👨🚀 N'hésitez pas à utiliser les wrappers
/transports
cités au début de l'article.
copy( 'http://.../image.jpg', 'ssh2.scp://user:pass@server:22/home/download/image.jpg' );
Copie le fichier depuis le web sur un serveur en utilisant
scp
viassh
.
Un autre exemple fréquemment rencontré lors de la création de fichier temporaire :
$tmpFile = tempnam(sys_get_temp_dir(), 'php' . rand());
⚠️ Ici le script va créer un fichier dans le dossier temporaire de php.
tempnam()
retourne le path
et non la ressource.👨🚀 Préférez donc l'utilisation de :
php://temp
ou php://temp/maxmemory:100
qui stockera en mémoire puis sur disque une fois la limite atteinte.php://memory
stocker en mémoiretmpfile()
crée un fichier temporaire avec un nom unique, ouvert en écriture et lecture (w+), et retourne un pointeur de fichier.$tmp = fopen('php://temp', 'w+');
Ce fichier sera automatiquement effacé :
Bien que très puissant et présent dans PHP depuis la version 4.3, ce composant est souvent méconnu, sous exploité, voire mal utilisé. C'est pourquoi j'en fais la promotion ici. J'espère avoir suscité votre intérêt !
📝 Je n'ai volontairement pas abordé les flux de type socket
car, ils mériteraient un article à eux seuls.
Auteur(s)
Anthony MOUTTE
_Développeur / Concepteur @ ElevenLabs_🚀 je suis très intéressé par la recherche et développement ainsi que les bonnes pratiques.
Vous souhaitez en savoir plus sur le sujet ?
Organisons un échange !
Notre équipe d'experts répond à toutes vos questions.
Nous contacterDécouvrez nos autres contenus dans le même thème
Le composant Symfony ExpressionLanguage : qu'est-ce que c'est ? Quand et comment l'utiliser ? Comment créer des expressions lors de cas plus complexes ?
Découvrez comment réaliser du typage générique en PHP : introduction et définition du concept, conseils et explications pas-à-pas de cas pratique.
Découvrez un cas d'usage d'intégration d'un CRM avec une application e-commerce, en asynchrone, avec Hubspot et RabbitMQ