Chapitre 2.8 — PHP ↔ TypeScript : le mémo de survie
Partie 2 : PHP moderne — Chapitre 8/8 Version de référence : PHP 8.4 pour Symfony 7.4.
⏱️ TL;DR
- Ce chapitre est une table de correspondance de référence : gardez-le sous la main pendant tout le cours.
- Les faux-amis à ne jamais oublier :
.(pas+) pour concaténer ;===(jamais==) ; typage nominal (pas structurel) ; pas d’async/await;array= liste et map ;$this(pasthis) ;->/::.- Les idiomes fonctionnels existent :
array_map/array_filter/array_reduce≈map/filter/reduce,...= spread,[$a, $b] = …= destructuring.- JSON :
json_encode/json_decode($s, true)↔JSON.stringify/JSON.parse.- À la fin : vous lisez et écrivez du PHP moderne en traduisant mentalement depuis TypeScript, presque sans friction.
🎯 Objectifs
- Disposer d’une table de traduction TS → PHP réutilisable.
- Mémoriser les faux-amis qui piègent les devs JS.
- Traduire les idiomes courants (itération, transformation, JSON, dates).
1. Syntaxe de base
| TypeScript | PHP 8.4 | Note |
|---|---|---|
const x = 1 | $x = 1; | $, ; obligatoire. Pas de const/let (variable simple). |
let x: number | $x (+ type sur propriété/param) | Pas d’annotation sur variable locale ; on type propriétés/params/retours. |
x.prop | $x->prop | Flèche ->. |
Cls.staticMethod() | Cls::staticMethod() | Double ::. |
`Hi ${name}` | "Hi $name" / "Hi {$obj->p}" | Guillemets doubles interpolent ; simples = littéral. |
'a' + 'b' | 'a' . 'b' | . concatène ; + = addition. |
a === b | $a === $b | Utilisez toujours ===. |
a ?? b | $a ?? $b | Null-coalescing identique. |
a?.b?.c | $a?->b?->c | Nullsafe identique. |
x ? a : b | $x ? $a : $b | Ternaire identique. |
// comment /* */ | idem | Commentaires identiques. |
2. Fonctions
| TypeScript | PHP 8.4 |
|---|---|
function f(a: number): number { } | function f(int $a): int { } |
(x) => x * 2 | fn ($x) => $x * 2 |
(x) => { const y = …; return y } | function ($x) use ($ext) { … return $y; } |
f(...args) (spread appel) | f(...$args) |
function f(a, ...rest) | function f($a, ...$rest) |
f(a, b, { verbose: true }) (options) | f($a, $b, verbose: true) (arguments nommés) |
const g = obj.method.bind(obj) | $g = $obj->method(...) (first-class callable) |
3. Types
| TypeScript | PHP 8.4 |
|---|---|
number | int ou float (distincts) |
string | string |
boolean | bool |
T[] / Array<T> | array + /** @var T[] */ (PHPStan) |
Record<string, T> | array + /** @var array<string, T> */ |
T | null | ?T ou T|null |
A | B | A|B |
A & B | A&B |
unknown | mixed |
never | never |
void | void |
enum / union de littéraux | enum (backed) |
interface | interface |
type Alias = … | ✗ pas d’alias de type ; on crée une classe/interface ou on documente en PHPStan |
génériques <T> | ✗ natif ; annotations PHPStan @template T |
4. Tableaux et objets (le grand piège)
En JS, [] (Array) et {} (Object) sont deux types. En PHP, array fait les deux :
| TypeScript | PHP 8.4 |
|---|---|
[1, 2, 3] | [1, 2, 3] (array indexé) |
{ a: 1, b: 2 } | ['a' => 1, 'b' => 2] (array associatif) |
obj.a | $arr['a'] |
Object.keys(o) | array_keys($arr) |
Object.values(o) | array_values($arr) |
Object.entries(o) | foreach ($arr as $k => $v) |
{ ...a, ...b } | [...$a, ...$b] (spread, 8.1 pour clés string) ou array_merge($a, $b) |
const [x, y] = arr | [$x, $y] = $arr; (destructuring) |
arr.length | count($arr) |
arr.includes(v) | in_array($v, $arr, true) |
arr.push(v) | $arr[] = $v; |
'k' in obj | array_key_exists('k', $arr) / isset($arr['k']) |
Quand la donnée a une forme fixe et connue, préférez une classe/DTO (typée) à un array associatif « fourre-tout ». Réservez les arrays associatifs aux structures dynamiques.
5. Itération et transformation (fonctionnel)
| TypeScript | PHP 8.4 |
|---|---|
arr.map(f) | array_map($f, $arr) |
arr.filter(f) | array_filter($arr, $f) |
arr.reduce(f, init) | array_reduce($arr, $f, $init) |
arr.forEach(f) | foreach ($arr as $v) { … } |
arr.find(f) | array_find($arr, $f) (PHP 8.4) |
arr.some(f) / arr.every(f) | array_any(...) / array_all(...) (PHP 8.4) |
arr.sort(cmp) | usort($arr, $cmp) (modifie en place) |
[...arr].reverse() | array_reverse($arr) |
arr.slice(0, 3) | array_slice($arr, 0, 3) |
arr.join(', ') | implode(', ', $arr) |
str.split(',') | explode(',', $str) |
⚠️ Piège : dans
array_map($f, $arr), l’ordre des arguments est (fonction, tableau) — l’inverse dearr.map(f). Idemarray_filter($arr, $f)prend (tableau, fonction). Ces incohérences historiques de l’ordre des paramètres sont la rançon de la vieille bibliothèque standard de PHP. En cas de doute, vérifiez la signature (l’IDE l’affiche).
6. Chaînes, JSON, dates
| TypeScript | PHP 8.4 |
|---|---|
str.length | strlen($str) (octets) / mb_strlen($str) (caractères) |
str.toUpperCase() | strtoupper($str) / mb_strtoupper($str) |
str.trim() | trim($str) |
str.includes(x) | str_contains($str, $x) |
str.startsWith(x) | str_starts_with($str, $x) |
str.replace(a, b) | str_replace($a, $b, $str) |
`${a}-${b}` | "$a-$b" ou sprintf('%s-%s', $a, $b) |
JSON.stringify(x) | json_encode($x) |
JSON.parse(s) | json_decode($s, true) (true = array assoc.) |
new Date() | new \DateTimeImmutable() |
date.toISOString() | $date->format(\DateTimeInterface::ATOM) |
⚠️ Piège dates : préférez
\DateTimeImmutableà\DateTime(mutable, source de bugs quand on le passe et qu’un code le modifie « à distance »). Symfony et Doctrine gèrent parfaitement l’immuable. TraitezDateTimeImmutablecomme vous traiteriez une date immuable en TS.
7. Les 7 faux-amis à ne jamais oublier
- Concaténation :
.— jamais+. - Égalité :
===— jamais==. - Typage nominal : pour « compatible par contrat », implémenter l’interface.
- Pas d’
async/await: le différé passe par Messenger/workers. arrayjoue liste et dictionnaire ; pour une forme fixe, préférez un DTO.$this(avec$),->pour l’instance,::pour le statique.declare(strict_types=1);en tête de chaque fichier.
8. Un exemple de « traduction » complète
TypeScript :
interface Booking { id: number; email: string; status: "pending" | "confirmed" }
function activeEmails(bookings: Booking[]): string[] {
return bookings
.filter(b => b.status === "confirmed")
.map(b => b.email);
}PHP 8.4 :
declare(strict_types=1);
enum BookingStatus: string {
case Pending = 'pending';
case Confirmed = 'confirmed';
}
final readonly class Booking {
public function __construct(
public int $id,
public string $email,
public BookingStatus $status,
) {}
}
/**
* @param Booking[] $bookings
* @return string[]
*/
function activeEmails(array $bookings): array
{
$confirmed = array_filter(
$bookings,
fn (Booking $b) => $b->status === BookingStatus::Confirmed,
);
return array_map(fn (Booking $b) => $b->email, $confirmed);
}Même intention, mêmes idées ; seule la syntaxe change (et l’union de littéraux devient une enum, mieux typée). Vous savez maintenant faire ce va-et-vient dans les deux sens.
✏️ Exercices
Exercice 1. Traduisez en PHP 8.4 : const total = items.reduce((sum, i) => sum + i.price, 0); (où items est un tableau d’objets avec une propriété price: number).
✅ Solution
$total = array_reduce(
$items,
fn (int $sum, Item $i) => $sum + $i->price,
0,
);Ordre des arguments : array_reduce(tableau, callback, initial). Le callback reçoit (accumulateur, élément), comme en JS.
Exercice 2. Repérez et corrigez les faux-amis dans ce code :
$parts = ['a', 'b'];
$joined = $parts[0] + '-' + $parts[1];
if ($user.role == 'admin') { echo "Bonjour ${user.name}"; }✅ Solution
$parts = ['a', 'b'];
$joined = $parts[0] . '-' . $parts[1]; // . pour concaténer, pas +
if ($user->role === 'admin') { // -> pour l'objet, === strict
echo "Bonjour {$user->name}"; // interpolation PHP : {$user->name}
}Trois faux-amis : +→., $user.role→$user->role + ==→===, et l’interpolation ${...} (JS) →{$...} (PHP).
Exercice 3. Donnez l’équivalent PHP de JSON.parse(body) puis, dans l’autre sens, de JSON.stringify({ ok: true }).
✅ Solution
$data = json_decode($body, true); // true → tableau associatif (sinon objet stdClass)
$json = json_encode(['ok' => true]); // '{"ok":true}'json_decode($s, true) renvoie un array associatif ; sans le true, il renvoie un objet stdClass. Pour un contrôle fin (erreurs, profondeur), on ajoute le flag JSON_THROW_ON_ERROR.
🧠 Quiz de révision
1. Quel opérateur concatène, et lequel teste l’égalité stricte ?
Concaténation : .. Égalité stricte : === (toujours, jamais ==).
2. Comment traduit-on un objet littéral JS { a: 1 } en PHP ?
{ a: 1 } en PHP ?Par un array associatif ['a' => 1] — ou, pour une forme fixe et typée, par une classe/DTO.
3. Équivalents PHP de arr.map(f) et arr.filter(f) ?
arr.map(f) et arr.filter(f) ?array_map($f, $arr) (fonction avant le tableau) et array_filter($arr, $f) (tableau avant la fonction) — attention à l’ordre inversé entre les deux.
4. Quelle classe de date privilégier, et pourquoi ?
\DateTimeImmutable (immuable), pour éviter les bugs de mutation « à distance » du \DateTime mutable.
5. Citez trois faux-amis du dev JS en PHP.
Au choix : concaténation . (pas +), === (pas ==), typage nominal (implémenter les interfaces), pas d’async/await, array = liste et map, $this/->/::, declare(strict_types=1).
Fin de la Partie 2. Vous avez récupéré le langage : types, enums, attributs, classes modernes, Composer/PSR-4, erreurs, et la table de traduction TS→PHP. Le code Symfony ne vous paraîtra plus « exotique ».
Prochaine étape : Partie 3 — Installation & environnement de dev — on installe PHP 8.4, Composer, la Symfony CLI, on met en place Docker/FrankenPHP, et on crée le squelette de BookLab.