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.
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
| Python | JavaScript | Java | PHP | |
|---|---|---|---|---|
| Concaténation | a + b | a + b | a + b | $a . $b |
| ĂgalitĂ© stricte | a == b | a === b | a.equals(b) | $a === $b |
| Coalescence de null | a or b | a ?? b | (aucun) | $a ?? $b |
| AccÚs sûr au membre | (aucun) | a?.b | (aucun) | $a?->b |
| Lambda | lambda x: x * 2 | x => x * 2 | x -> x * 2 | fn($x) => $x * 2 |
| Dictionnaire littéral | {'k': 1} | {k: 1} | Map.of("k", 1) | ['k' => 1] |
| Interpolation | f"{x}" | `${x}` | (aucune) | "{$x}" |
| Membre dâinstance | obj.name | obj.name | obj.name | $obj->name |
| Membre statique | Cls.name | Cls.name | Cls.name | Cls::$name |
| Ternaire | a if c else b | c ? a : b | c ? 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 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Ă©.