Skip to Content

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 (pas this) ; ->/::.
  • Les idiomes fonctionnels existent : array_map/array_filter/array_reducemap/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

TypeScriptPHP 8.4Note
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->propFlè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 === $bUtilisez toujours ===.
a ?? b$a ?? $bNull-coalescing identique.
a?.b?.c$a?->b?->cNullsafe identique.
x ? a : b$x ? $a : $bTernaire identique.
// comment /* */idemCommentaires identiques.

2. Fonctions

TypeScriptPHP 8.4
function f(a: number): number { }function f(int $a): int { }
(x) => x * 2fn ($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

TypeScriptPHP 8.4
numberint ou float (distincts)
stringstring
booleanbool
T[] / Array<T>array + /** @var T[] */ (PHPStan)
Record<string, T>array + /** @var array<string, T> */
T | null?T ou T|null
A | BA|B
A & BA&B
unknownmixed
nevernever
voidvoid
enum / union de littérauxenum (backed)
interfaceinterface
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 :

TypeScriptPHP 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.lengthcount($arr)
arr.includes(v)in_array($v, $arr, true)
arr.push(v)$arr[] = $v;
'k' in objarray_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)

TypeScriptPHP 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 de arr.map(f). Idem array_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

TypeScriptPHP 8.4
str.lengthstrlen($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. Traitez DateTimeImmutable comme vous traiteriez une date immuable en TS.

7. Les 7 faux-amis à ne jamais oublier

  1. Concaténation : . — jamais +.
  2. Égalité : === — jamais ==.
  3. Typage nominal : pour « compatible par contrat », implémenter l’interface.
  4. Pas d’async/await : le différé passe par Messenger/workers.
  5. array joue liste et dictionnaire ; pour une forme fixe, préférez un DTO.
  6. $this (avec $), -> pour l’instance, :: pour le statique.
  7. 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 ?

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) ?

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.