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 configuration php.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 comme 1, 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 comme 38.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 traver­sables

Common 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 prédéfinies

__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 Days Mon to Sun
d Days 01 to 31
j Days 1 to 31
L Leap year or not (1 or 0)
l Days Sunday to Saturday
N Days 1 (Mon) to 7 (Sat)
w Days 0 (Sun) to 6 (Sat)
M Months Jan to Dec
m Months 01 to 12
n Months 1 to 12
F Months January to December
Y Year four digits (e.g., 2022)
y Year two digits (e.g., 22)
A AM and PM
a am and pm
G Hours 0 to 23
g Hours 1 to 12
H Hours 00 to 23
h Hours 01 to 12
i Minutes 00 to 59
s Seconds 00 to 59

Voir 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
}

Voir tous les filtres.

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

Make sense of generators (en)

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.

Voir ce guide

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 de if/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ître
str_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 xdebug
Helpers
  • print_r(): accepte 1 paramètre
  • var_dump(): accepte plusieurs paramètres
  • var_dump(debug_backtrace()): génère une backtrace
  • debug_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.

A voir aussi