Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

La syntaxe

PHP ressemble Ă  du C avec un $ devant chaque variable, -> pour les membres et . pour concatĂ©ner les chaĂźnes. Si vous lisez du Java, du JavaScript ou du C#, vous lisez dĂ©jĂ  du PHP, et le reste de ce chapitre est la liste des endroits oĂč vos doigts vont taper la mauvaise chose.

Un fichier, de haut en bas

<?php
declare(strict_types=1);

$name = 'world';
$count = 3;

echo "Hello, {$name}!", PHP_EOL;
echo 'Hello, ' . $name . '! Count: ' . $count . PHP_EOL;

Le fichier s’ouvre sur <?php. Tout ce qui prĂ©cĂšde cette balise, mĂȘme une ligne vide, part tel quel dans la sortie, parce que PHP a commencĂ© sa vie comme langage de gabarits. Un fichier qui ne contient que du code s’ouvre sur <?php et ne referme jamais la balise, parce qu’un ?> final suivi d’un saut de ligne oubliĂ© ferait fuir ce saut de ligne dans votre rĂ©ponse HTTP.

declare(strict_types=1); vient ensuite, sur sa propre ligne, et dĂ©sactive la conversion automatique des arguments scalaires dans les appels faits depuis ce fichier. Le systĂšme de types explique prĂ©cisĂ©ment ce que cette ligne couvre et ce qu’elle ne couvre pas ; pour l’instant, mettez-la dans chaque fichier, et considĂ©rez un fichier qui ne l’a pas comme un fichier portant un panneau d’avertissement.

Les variables commencent par $ et ne se dĂ©clarent jamais : vous affectez, et la variable existe, sans let, sans var et sans type devant. Les noms de variables sont sensibles Ă  la casse, alors que les noms de fonctions et de classes ne le sont pas, mĂȘme si personne ne s’appuie sur cette tolĂ©rance.

Les instructions se terminent par un point-virgule. echo prend une liste sĂ©parĂ©e par des virgules et l’affiche, print fait la mĂȘme chose avec un seul argument et se voit rarement, et PHP_EOL est le saut de ligne de la plateforme.

Les chaĂźnes

L’opĂ©rateur de concatĂ©nation est ., pas +. Écrire $a + $b avec deux chaĂźnes pousse PHP Ă  les additionner comme des nombres, ce qui donne une TypeError pour des chaĂźnes non numĂ©riques sous PHP 8.

Les guillemets simples vous donnent les octets que vous avez tapĂ©s, tandis que les guillemets doubles interpolent les variables et traduisent les sĂ©quences comme \n. Entourez d’accolades tout ce qui dĂ©passe une simple variable :

<?php
declare(strict_types=1);

$user = ['name' => 'Ada'];
$items = 3;

echo "Hi {$user['name']}, you have {$items} items\n";
echo 'Hi {$user[name]}\n'; // printed literally, backslash included

Pour du texte sur plusieurs lignes, un heredoc interpole et un nowdoc n’interpole pas. Le marqueur de fin peut ĂȘtre indentĂ© (depuis PHP 7.3), et cette indentation est retirĂ©e de chaque ligne :

<?php
declare(strict_types=1);

$title = 'Report';

$html = <<<HTML
    <h1>{$title}</h1>
    <p>Generated by PHP</p>
    HTML;

$raw = <<<'TXT'
    No {$interpolation} in here.
    TXT;

echo $html, PHP_EOL, $raw, PHP_EOL;

Trois flĂšches

PHP a trois symboles en forme de flÚche, et leurs rÎles ne se recouvrent jamais. -> entre dans une instance, comme dans $user->name ou $user->save(). :: entre dans une classe, comme dans User::create(), User::MAX_AGE ou self::$count. => associe une clé à une valeur dans un tableau littéral ou une branche de match, comme dans ['id' => 1].

<?php
declare(strict_types=1);

final class Money
{
    public const string CURRENCY = 'EUR'; // typed constant, PHP 8.3

    public function __construct(public readonly int $cents)
    {
    }

    public static function zero(): self
    {
        return new self(0);
    }
}

$price = new Money(1999);
echo $price->cents, ' ', Money::CURRENCY, ' ', Money::zero()->cents, PHP_EOL;
// 1999 EUR 0

Dans une mĂ©thode, l’instance courante est $this. Les classes et les objets ont leur propre chapitre, Les classes, et cet exemple montre seulement quelle flĂšche va oĂč.

L’égalitĂ©

Utilisez ===, et considĂ©rez == comme un opĂ©rateur hĂ©ritĂ©. Le double Ă©gal convertit les deux cĂŽtĂ©s vers un type commun avant de comparer, ce qui a produit les absurditĂ©s cĂ©lĂšbres du vieux PHP. Depuis PHP 8, un nombre comparĂ© Ă  une chaĂźne non numĂ©rique est comparĂ© comme une chaĂźne, donc 0 == 'foo' vaut false, mais '1' == '01' vaut toujours true : le comportement est plus sain qu’avant, et reste un jeu de devinettes. Le triple Ă©gal compare la valeur et le type, sans rien deviner.

La mĂȘme rĂšgle vaut pour !== contre !=, et pour in_array() et array_search(), qui prennent un troisiĂšme argument true pour comparer strictement.

<=> est l’opĂ©rateur vaisseau spatial : il renvoie -1, 0 ou 1, et existe pour les fonctions de tri.

Null, en trois opérateurs

<?php
declare(strict_types=1);

$config = ['debug' => null, 'owner' => null];

$debug = $config['debug'] ?? false;      // false: ?? treats null like missing
$config['level'] ??= 'info';             // assign only if null or missing
$length = $config['owner']?->name;       // null, no error: nullsafe chain
$mode = $config['debug'] ? 'on' : 'off'; // plain ternary, same as everywhere

var_dump($debug, $config['level'], $length, $mode);

?? est la coalescence de null, et il avale aussi « clĂ© indĂ©finie » et « variable indĂ©finie », ce qui en fait la façon idiomatique de lire une entrĂ©e optionnelle d’un tableau. ??= n’affecte que si le cĂŽtĂ© gauche est null ou absent, et ?-> (PHP 8.0) arrĂȘte une chaĂźne d’appels au premier null et renvoie null. Le ternaire abrĂ©gĂ© ?: existe aussi ($a ?: $b renvoie $a s’il est vrai au sens large) ; il convient aux boolĂ©ens et trompe avec tout le reste.

Le flux de contrîle, d’un seul coup

<?php
declare(strict_types=1);

$scores = ['ada' => 92, 'linus' => 71, 'grace' => 85];

foreach ($scores as $who => $score) {
    if ($score >= 90) {
        $grade = 'A';
    } elseif ($score >= 80) {
        $grade = 'B';
    } else {
        $grade = 'C';
    }
    echo "{$who}: {$grade}", PHP_EOL;
}

for ($i = 0; $i < 3; $i++) {
    echo $i;
}
echo PHP_EOL;

$attempts = 0;
while ($attempts < 3) {
    $attempts++;
}

$label = match (true) {
    $attempts === 0 => 'never tried',
    $attempts < 3 => 'a few tries',
    default => 'gave it everything',
};
echo $label, PHP_EOL;

Rien ici ne demande d’explication, Ă  part deux dĂ©tails. foreach est la boucle des tableaux et de tout itĂ©rable, et elle vous donne la clĂ© et la valeur ensemble avec as $key => $value. match est une expression qui compare avec ===, ne tombe pas dans la branche suivante et lĂšve une exception si rien ne correspond, alors que switch, que vous trouverez dans le code ancien, compare avec == et enchaĂźne les branches. ÉnumĂ©rations et match montre Ă  quoi sert match.

Trois panneaux cÎte à cÎte : une flÚche marquée -> pointe vers un objet unique, une flÚche marquée :: pointe vers le plan d'une classe, et une double flÚche marquée => relie une carte clé à une carte valeur

Tout le reste sur un écran

<?php
declare(strict_types=1);

namespace App\Billing;         // one per file, mirrors the folder

use App\Money;                 // import a class from another namespace
use function App\format_cents; // functions and constants can be imported too

const TAX_RATE = 0.2;          // compile-time constant, namespaced
define('LEGACY', true);        // runtime constant, global, older style

// one-line comment
# also a one-line comment, rarer
/* block
   comment */

/**
 * Docblock: read by editors, PHPStan and Psalm, not by PHP itself.
 */
function total(int ...$cents): int
{
    return array_sum($cents);
}

$parts = [100, 250];
echo total(...$parts), PHP_EOL; // 350: ... unpacks on both sides

... déplie un tableau en arguments et, dans une liste de paramÚtres, rassemble le reste dans un tableau. Les arguments nommés (total(cents: 5)) existent depuis 8.0. null est une valeur, en minuscules par convention, comme true et false. Les espaces de noms et use font exactement ce que vous attendez des packages Java ou des imports ES, et Namespaces, Composer et autoloading explique comment les fichiers sont trouvés.

CĂŽte Ă  cĂŽte

PythonJavaScriptJavaPHP
Concaténationa + ba + ba + b$a . $b
ÉgalitĂ© strictea == ba === ba.equals(b)$a === $b
Coalescence de nulla or ba ?? b(aucun)$a ?? $b
AccÚs sûr au membre(aucun)a?.b(aucun)$a?->b
Lambdalambda x: x * 2x => x * 2x -> x * 2fn($x) => $x * 2
Dictionnaire littéral{'k': 1}{k: 1}Map.of("k", 1)['k' => 1]
Interpolationf"{x}"`${x}`(aucune)"{$x}"
Membre d’instanceobj.nameobj.nameobj.name$obj->name
Membre statiqueCls.nameCls.nameCls.nameCls::$name
Ternairea if c else bc ? a : bc ? a : b$c ? $a : $b

Le piĂšge

Deux choses font trĂ©bucher un nouveau venu dans la premiĂšre heure. La premiĂšre est ==, une comparaison qui a l’air juste et passe les tests Ă©vidents, puis dĂ©cide que '1e1' == '10' et que null == false. Tapez === jusqu’à ce que ce soit un rĂ©flexe, et laissez strict_types attraper le reste.

La seconde est la bibliothĂšque standard, oĂč l’on trouve strpos($haystack, $needle) mais array_search($needle, $haystack), str_replace avec un tiret bas mais strlen sans, et array_key_exists Ă  cĂŽtĂ© de in_array. Le nommage n’a jamais Ă©tĂ© conçu, il s’est accumulĂ© au fil des versions. Les fonctions rĂ©centes sont cohĂ©rentes (str_contains, array_is_list, array_find), les anciennes ne changeront pas, et le remĂšde n’est pas la mĂ©moire mais un Ă©diteur avec complĂ©tion et un analyseur statique qui signale l’ordre d’arguments inversĂ©.

Une haute commode dont chaque tiroir porte un nom de fonction PHP dans un lettrage différent, certains avec des tirets bas et d'autres sans, et un petit éléphant tenant une lampe torche qui éclaire le bon tiroir

Une fois la syntaxe rĂ©glĂ©e, les questions intĂ©ressantes commencent, Ă  commencer par ce que garantit vraiment int. Le systĂšme de types y rĂ©pond avec plus de nuances que vous ne l’auriez souhaitĂ©.