PHP 8++: l'antisèche
Concepts-clés à connaître
PHP (Hypertext Preprocessor) est un langage de scripting puissant inspiré du C qui souffre parfois d’une mauvaise réputation.
Certaines critiques sont tout à fait constructives mais font essentiellement référence à PHP 4 ou 5 alors que PHP 7, 8 et plus ont ouvert la voie à quelque chose de nouveau.
Le langage a maintenant un typage plus strict, tout un tas de sucres syntaxiques et bien d’autres fonctionnalités que vous apprécierez, surtout si vous venez d’autres langages de programmation.
PHP est un langage interprêté
Contrairement à d'autres langages, PHP a besoin d'un serveur externe (Apache, Nginx), d'un compilateur et d'un interprètateur pour fonctionner.Cet interprètateur lit le fichier
.php
, le parse, l'exécute, et injecte le résultat dans dans la réponse HTTP envoyée au navigateur (le client).
Les extensions PHP et les fonctions réutilisables
PHP fonctionne avec des extensions le plus souvent écrites en C mais certaines sont rédigées tout simplement en PHP comme des librairies externes. Certaines compilent avec le binaire de PHP et d'autres doivent être insérées dans le fichier de configurationphp.ini
en tant que directives pour PHP.
Cycle : Lexing
La plupart du code PHP est transformé en tokens (tokenizing), mais certains symboles comme?
, =
, ou ;
dont déjà dans la forme souhaitée. Le lexer génère des lexemes qui sont un mélange de nombres et de lettres représentant les tokens et leurs valeurs associées.
Cycle de vie : Parsing / AST
Le parser valide les streams de tokens générés par le lexer avec des règles de grammaire pour générer un arbre (AST) avec l'extension php-ast.Cycle de vie : Compilation / opcodes
La compilation va traverser l'arbre généré par le parser (AST) et générer à son tour des opcodes en faisant quelques optimisations.Ces opcodes ne sont pas illisibles comme du code machine mais ils diffèrent du code d'origine fortement, avec par exemple le résultat d'opérations intermédiaires comme des
true
/false
pour des conditions comme $a === $b
.
Cycle de vie : Opcache
L'Opcache va capturer les opcodes pour éviter d'avoir à refaire sans arrêt les mêmes opérations de transformation du code que vous écrivez et ainsi faire gagner en performance l'application.Cycle de vie : Interprêtation
Le Zend Engine (VM) génère l'affichage avec les opcodes et exécute les instructions.Cycle de vie : PHP 8 - compilation JIT / opcache
La compilation Just-In-Time va de pair avec l'Opcache (qui permet de sauter les étapes de parsing et d'interprêtation) et optimise les performances d'applications très gourmandes en CPU et en RAM (exemple: le Machine Learning, calculs mathématiques avancés, 3D, etc). Le JIT doit être configuré finement et ne conviendra pas à tous les usages..Composer
Composer est LE gestionnaire de paquets pour PHP. Une approche intelligente pour gérer les dépendences d'un projet.Types de données avec PHP 8
Entiers
Nombres comme1, 2, 3, 4, 5, 6, ..., n
Booléens
true
ou false
Chaînes de caractères
Séquences de caractères comme "Éléphants roses"Doubles
Floats comme38.2
Tableaux
Collections de valeurs comme["a","b","c",]
Objets
Instances de classes PHP qui peuvent elles-mêmes contenir beaucoup de fonctions et de valeurs.Ressources
Références spéciales à des ressources externes comme des connexions à des bases de données ou des services cloud.NULL
Type spécial avec pour seule valeur possible NULL.Mixed
Tout venant!Itérable
Tableaux ou traversablesCommon Escape Characters
\n
Line
\t
Tabulations horizontales
\v
Tabulations verticales
\r
Retour charriot
\e
Echappement
Basic PHP Syntax
Utiliser les balises PHP
Un fichier.php
contient des balises d'ouverture et de fermeture :
<?php
// my_script.php
?>
Commenter du code
<?php
// a comment using "//", you can also use "#"
/*
Handle multiple lines
with '/'
and '*'
*/
?>
PHP est sensible à la casse
$lower
n'est pas la même variable que $Lower
.
Le point-virgule est obligatoire
Contrairement à CSS ou JavaScript,;
est obligatoire à la fin des expressions et de certaines instructions :
<?php
echo 'test';
?>
Indentation
Contrairement à Python, PHP ignore l'indentation et les espaces vides.Simple guillemets
<?php
echo 'test';
?>
Double guillemets
Les doubles guillemets permettent l'interpolation des variables et l'échappement de certains caractères :<?php
$my_var = 'test';
echo "\t\t\t This is a $my_var \n";
?>
Variables prédéfinies
Aussi appelées superglobales, les variables prédéfinies permettent certains traitements spécifiques :
$GLOBALS
Contient toutes les variables du scope global.
$_REQUEST
Un tableau associatif de toutes les variables passées dans $_GET
, $_POST
et $_COOKIE
.
$_POST
Un tableau associatif de toutes les variables passées au script le plus souvent via des formulaires (le HTTP Content-Type application/x-www-form-urlencoded
ou multipart/form-data
)
$_COOKIE
Un tableau associatif de toutes les variables passées au script via les cookies HTTP.
$_GET
Un tableau associatif de toutes les variables passées au script via des paramètres dans l'URL.
$_SERVER
Variables d'environnement et d'exécution.
Retrouvez plus de superglobales ici
Constantes magiques
__DIR__
Le répertoire courant du fichier.
__FILE__
Le chemin complet vers le fichier.
__LINE__
Le numéro de la ligne dans le fichier.
__CLASS__
Nom de la classe courante, incluant le namespace si défini.
__FUNCTION__
Nom de la fonction courante.
__METHOD__
Nom de la méthode courante.
__NAMESPACE__
Nom du namespace courant.
__TRAIT__
Nom du trait courant
Boucle, boucle, boucle
for
<?php
for ($i = 0; $i < 23; $i++) {
echo $i;
}
?>
foreach
<?php
$letters = ["a", "b", "c",]
foreach ($letters as $letter) {
echo "$letter \n";
}
?>
while
Attention aux conditions mal écrites qui peuvent partir en boucles infinies:
<?php
$j = 1;
while($j <= 5) {
echo "$j \n";
$j++;
}
?>
do while
à utiliser quand les tests dépendent du résultat d'un calcul effectué dans la boucle:
<?php
$i = 0;
do {
echo $i;
} while ($i > 0);
?>
Opérateurs
Assignement
Assignement par valeur =
<?php
$a = 110;
$a += 1;
$b = "Hello ";
$b .= "World";
?>
Assignement par référence &
<?php
$a = 111;
$b = &$a; // $b and $a point to the same data, there's no copy
?>
Arithmétique
+
Addition
-
Soustraction
*
Multiplication
/
Division
**
Exponentielle
%
Modulo (reste de la division)
Logique
AND
Et
&&
Et
OR
Ou
||
Ou
!
Négation
xor
$a xor $b
signifie $a
ou $b
mais pas les deux.
Comparaison
===
Identique
!==
Non identique
<>
Non égal
<
Plus petit que
>
Plus grand que
<=
Plus petit que ou égal
>=
Égal ou plus grand que
<=>
Plus petit que, égal à, ou plus grand que
Peu courant ou moins connu
|
ou mais inclusif
^
xor
~
négation
...
Spread operator pour fusionner des éléments
<?php
$arr = [1, 2, 3];
$arr2 = [...$arr, 4, 5, 6];
?>
_
<?php
$price = 1_1_1;
echo $price; // 111
?>
Conditions
Classiques
<?php
if (CONDITION1) {
} elseif (CONDITION2) {
} else {
}
?>
Switch
<?php
switch ($n) {
case 1:
$r = "You are alone";
break;
case 2:
$r = "It's a double situation.";
break;
case 3:
$r = "It's the third case.";
break;
default:
$r = "I don't know.";
}
echo $r;
?>
Operateur Null Coalescing (@since PHP 7 mais je le mets quand même 😘)
<?php
$search = $_GET['search'] ?? 'does not exist or is null';
?>
Match expression (@since PHP 8)
<?php
echo match ($n) {
1 => "You are alone",
2 => "It's a double situation.",
3 => "It's the third case.",
default => "I don't know.",
};?>
Ternary condition
<?php
$a = (expression1) ? expression2 : expression3;
?>
Elvis
<?php
$a = (expression1) ?: expression2; // I never use it, but it exists...
// the same as $a = (expression1) ? expression1 : expression2;
?>
Scopes
3 scopes
global, local and static:<?php
function test() {
$b = "b"; // local
global $c;
$c = "c"; // global
static $d = 0;
$d++;// $d increment on each function call
}
?>
Formats de date et time
D
Jours Mon à Sund
Jours 01 à 31j
Jours 1 à 31L
Année avec un 29 février (valeur 1 ou 0)l
Jours Sunday à SaturdayN
Jours 1 (Mon) à 7 (Sat)w
Jours 0 (Sun) à 6 (Sat)M
Mois Jan à Decm
Mois 01 à 12n
Mois 1 à 12F
Mois Janvier to DécembreY
Années 4 chiffres (e.g., 2022)y
Year two digits (e.g., 22)A
AM and PMa
am et pmG
Heures 0 à 23g
Heures 1 à 12H
Heures 00 à 23h
Heures 01 à 12i
Minutes 00 à 59s
Secondes 00 à 59Voir la documentation complète.
Filtres
Les filtres sont particulièrement efficaces pour valider et nettoyer des entrées issues de champs de formulaire. Au lieu d’appliquer des regex alambiquées pour contrôler un email, on peut faire :
$email = "elon.musk@tesla.com";
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
// process
}
Syntaxes PHP avancées
Nowdoc et Heredoc permettent de gérer le multi-lignes.
Heredoc
<?php
echo <<<EOT
Hello, this is a test
Thank you for your time
EOT;
?>
Nowdoc
<?php
$my_var = 'test';
echo <<<EOT
Hello, this is a $my_var
Thank you for your time
EOT;
?>
Travailler avec des tableaux
Commencez peut-être par ce guide (en). Les tableaux ne sont pas spécifiques à PHP mais on les utilise tout le temps.
Impossible de faire l’impasse dessus si vous voulez apprendre PHP.
Travailler avec des générateurs
Commencer avec le système de fichiers
Lire le contenu d'un fichier comme une châine de caractères
<?php
echo file_get_contents($file);
?>
Écrire dans un fichier
<?php
$file = 'file.txt';
$text = "Test write\n";
file_put_contents($file, $text, FILE_APPEND | LOCK_EX);
?>
Supprimer un fichier
unlink($file);
Travailler un CSV
Source:<?php
$row = 1;
if (($handle = fopen("test.csv", "r")) !== FALSE) {
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
$num = count($data);
echo "<p> $num fields in line $row: <br /></p>\n";
$row++;
for ($c=0; $c < $num; $c++) {
echo $data[$c] . "<br />\n";
}
}
fclose($handle);
}
?>
Débuter avec les formulaires
Les formulaires sont probablement l’un des points d’entrée les plus prisés par les hackers surtout avec les applications écrite en PHP car bien souvent celles-ci seront reliées à d’autres instances comme une base de données.
Les sites webs qui intègrent un paywall avec différents formulaires sont des cibles de choix.
En PHP, les données varient bien souvent en fonction de l’URL (GET) ou de requêtes HTTP POST.
Un formulaire de recherche, par exemple, fera appel aux paramètres de l’URL alors qu’un formulaire de login enverra une requête de type POST au serveur.
Le traitement du formulaire fera donc appel aux superglobales $_GET
et $_POST
pour capturer les valeurs.
Débuter avec les bases de données
Vous travaillerez bien souvent avec une base. Peu importe le système choisi, vous devrez utiliser les bons outils pour vous connecter et interagir avec la base sinon vous pouvez laisser passer des injections SQL.
Démarrer avec PDO
<?php
try {
$pdo = new PDO("mysql:host=localhost;dbname=db_username;charset=utf8mb4", "db_username", "db_password");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
if ($pdo) {
$statement = $pdo->query("SELECT some_field FROM some_table");
$row = $statement->fetch(PDO::FETCH_ASSOC);
echo htmlentities($row['some_field']);
}
} catch(\PDOException $e) {
throw new \PDOException($e->getMessage(), (int)$e->getCode());
}
?>
Sécuriser un input avec PDO
PDO est particulièrement pratique et efficace dans cet exercice :<?php
$q = $pdo->prepare('SELECT name FROM users WHERE id = :id');
$id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT);
$q->bindParam(':id', $id, PDO::PARAM_INT);
$q->execute();
?>
Source: PHP The Right Way - PDO (en)
PHP CLI
On peut exécuter PHP dans un terminal de différentes façons :
Exécuter un fichier .php
php my_script.php
Démarrer un shell interactif
php -a
Interactive shell
php > echo "hi";
hi
php >
Passer des instructions en tant qu'arguments
php -r 'echo "hi";'
Vous pouvez écrire des scripts PHP en vue d’une utilisation dans le terminal :
Passer des arguments aux scripts PHP
php my_script.php "test"
<?php
// my_script.php
print_r($argv[1]);// test
?>
Démarrer un serveur local
php -S localhost:1001
Nouveautés que j’aime dans PHP 8
PHP 8 c’est grandiose.
Attention aux migrations depuis d’anciennes versions, même depuis PHP 7.4. PHP 8 parle fort et ne passe plus aucune erreur sous silence. Ainsi l’opérateur @
n’existe plus et le niveau de reporting est maximum par défaut : E_ALL
!
Ce n’est pas la liste complète mais j’aime ces fonctionnalités :
Arguments nommés
On peut zapper les paramètres optionnels désormais et c'est beaucoup plus lisible que les arguments positionnels :<?php
function say_hello(string $message = 'Hi', string $name) {
echo "$message $name";
}
say_hello(name: "Zed");
?>
Opérateur Null-safe
On supprimer BEAUCOUP d'écritures inutiles à base deif/else
:
<?php
return $user->getJob()?->getSalary()?->bitcoins;
?>
Types d'Union
On peut spécifier plusieurs types maintenant pour le cast:<?php
function convert(int|float $value): int|float {}
?>
Promotion de propriétés de constructeur
On économise des initialisations redondantes qui sont autant d'écritures inutiles :<?php
class Character {
public function __construct(private string $name) {}
}
?>
0 == 'ki' // false
Retourne true
avant PHP 8 !
Le compilateur JIT
Voir la première section : Concepts-clés à connaîtrestr_contains()
<?php
if (str_contains($string, 'target')) {
// code
}
?>
Nouveautés que j’aime dans PHP 8.1
Ce n’est pas la liste complète mais j’aime ces nouveautés :
Enumérations
Un Enum est un objet un peu particulier qui accepte un ou un nombre limité de valeurs possibles :<?php
enum Colors
{
case Red;
case Blue;
case Green;
}
?>
Array unpacking pour les tableaux avec des clés non numériques
Réservé aux tableaux numérique dans PHP 7 et 8, on peut désormais faire :<?php
$arr1 = ['a' => 1];
$arr2 = ['b' => 2];
$results = ['a' => 0, ...$arr1, ...$arr2];
print_r($results);
?>
Le return type Never
Le return type Never permet de fixer que la fonction n'aura pas de retour et fera même un die ou un exit:<?php
function redirect_uri(string $uri): never {
header('Location: ' . $uri);
exit();
}
?>
Pure Intersection Types (&
)
Les Pure Intersection Types permettent de spécifier plusieurs types pour un même paramètre (la réciproque des union types en quelque sorte puisqu'ici ce sera du ET plutôt):
<?php
function count_and_iterate(Iterator&Countable $value) {
foreach ($value as $val) {
echo $val;
}
count($value);
}
?>
Capter des erreurs dans PHP 8
Dans PHP 8, on n’a plus l’obligation de capturer les exceptions:
class CustomException extends \Exception {}
try {
// code
} catch (CustomException) { // instead of catch (CustomException $exception)
Log::error("Error");// Log is a custom class too (not native)
}
Il faut noter que PHP en jette beaucoup plus fréquemment des exceptions:
Most of the internal functions now throw an Error exception if the validation of the parameters fails.
Divers
Destructuration
<?php
$array = ['first', 'second', 'third',];
[, , $c] = $array;
// $c = 'third'
?>
Fonctions fléchées
<?php
$y = 111;
$f = fn($x) => $x + $y;
echo $f(222);// 333
?>
Expressions Throw
<?php
$err = fn () => throw new CustomErrors();
?>
::class
Charmant:
<?php
$my_class = new MyClass();
var_dump($my_class::class);// instead of using get_class()
?>
Virgule possible à la fin de la liste des paramètres des fonctions
<?php
public static function(
string $name,
int $number,
) {
// code
}
?>
PSR
Les PSR sont des standards d’écriture pour le dév PHP. Vous avez peut-être déjà vu PSR-0
ou PSR-4
, en particulier avec Composer et l’autoload. Ce sont des versions particulières des standards.
POO
La programmation orientée objet est fun et très puissante mais assez difficile à maîtriser au final. La bonne nouvelle est qu’on la retrouve dans pratiquement tous les langages de programmation donc c’est un bon investissement si vous ne la connaissez pas encore.
Certains développeurs ne conçoivent pas de développer sans et d’autres se cantonnent au code procédural. Dans tous les cas, impossible de faire l’impasse dessus.
Apprenez les Design Patterns (en)
Le cache
Mes solutions favorites pour le cache PHP
Débug et profilage
console.log PHP data
<?php
function console_log( $data ){
echo '<script>';
echo 'console.log('. json_encode( $data ) .')';
echo '</script>';
}
?>
xdebug
xdebugHelpers
print_r()
: accepte 1 paramètrevar_dump()
: accepte plusieurs paramètresvar_dump(debug_backtrace())
: génère une backtracedebug_print_backtrace()
: imprime a backtrace
Profilage
Le profilage permet d'éviter les fuites de mémoires et les problèmes de performance :Des tests, tests, et encore des tests
Les professionnels écrivent des tests unitiaires, d'intégration, d'acceptance, et bien d'autres variants pour éviter les régressions :Gérer les requêtes HTTP
Divers chemins sont possibles mais je préfère nettement le module http-client: composer require symfony/http-client
.
Ce client HTTP bas niveau supporte les streams et cURL. On peut récupérer les données en synchrone OU asynchrone.
Voir l’antisèche http-client.