Skip to content

3. Les bases de PHP

3.1. L'arborescence des scripts

Les scripts de ce chapitre sont dans deux dossiers du dossier [scripts-console] du code du cours :

php8-oct-2026
└── scripts-console
    ├── bases
    │   ├── phpinfo.php
    │   ├── bases-01.php
    │   ├── bases-02.php
    │   ├── ...
    │   ├── bases-17.php
    │   └── infos.txt
    └── json
        ├── famille.json
        ├── json-01.php
        ├── json-02.php
        └── json-03.php

Ce sont des scripts « console » : ils s'exécutent dans un terminal, avec la commande [php nom-du-script.php], et écrivent leurs résultats dans ce même terminal. Les scripts web, exécutés par le serveur [Apache] de [Laragon] et affichés par un navigateur, viendront plus tard.

3.2. Configuration de PHP

PHP arrive préconfiguré par un fichier texte [php.ini]. Toutes ces configurations peuvent être changées par programmation. La configuration de PHP influence grandement l'exécution des scripts. Il est donc important de la connaître. Le script suivant [phpinfo.php] le permet :

1
2
3
4
5
6
<?php

declare(strict_types=1);

// affiche la configuration de PHP
phpinfo();

Commentaires :

  • ligne 3 : cette ligne sera présente au début de tous les scripts du cours. Nous l'expliquerons au paragraphe consacré aux changements de types ;
  • ligne 6 : la fonction [phpinfo] affiche la configuration de PHP .

Ouvrons un terminal dans le dossier [scripts-console/bases] et exécutons le script. En voici un extrait :

> php phpinfo.php
phpinfo()
PHP Version => 8.5.11
…
Server API => Command Line Interface
…
PHP API => 20250925
PHP Extension => 20250925
Zend Extension => 420250925
PHP Integer Size => 64 bits
Debug Build => no
Zend Memory Manager => enabled
IPv6 Support => enabled
…
display_errors => STDOUT => STDOUT
error_reporting => 30719 => 30719
memory_limit => 512M => 512M
max_execution_time => 0 => 0
…

La fonction [phpinfo] affiche ici près de 1 500 lignes de configuration. Nous n'allons pas les commenter car la plupart concerne un usage avancé de PHP. Retenons-en quelques-unes :

  • [PHP Version] : la version de PHP utilisée, ici la 8.5 ;
  • [Server API] : la façon dont PHP a été lancé. [Command Line Interface] (CLI) désigne l'interpréteur exécuté dans un terminal. Lorsque PHP est exécuté par le serveur [Apache], cette ligne change, et la configuration peut être différente ;
  • [display_errors] et [error_reporting] : PHP affiche toutes les erreurs, tous les avertissements (Warning) et toutes les notes de dépréciation (Deprecated). La valeur 30719 est celle de la constante [E_ALL] depuis PHP 8.4. C'est la configuration qu'il faut avoir pendant l'apprentissage : un avertissement signale presque toujours une erreur de programmation .

Une ligne importante de l'affichage complet est la ligne [Loaded Configuration File] : elle indique quel fichier [php.ini] a été utilisé pour configurer le PHP qui exécute vos scripts. Avec [Laragon], c'est un fichier du genre [C:\laragon\bin\php\php-8.5.x-Win32-vs17-x64\php.ini]. On peut aussi l'obtenir directement avec la commande [php --ini]. Si vous souhaitez modifier la configuration d'exécution de PHP, c'est ce fichier qu'il vous faut modifier ([Laragon] y donne accès par son menu, rubrique [PHP > php.ini]). De nombreux commentaires sont présents dans ce fichier pour expliquer le rôle des différentes configurations.

3.3. Un premier exemple

3.3.1. Le code

Ci-dessous, on trouvera un programme [bases-01.php] présentant les premières caractéristiques de PHP :

<?php

// contrôle strict des types des paramètres et des résultats des fonctions
declare(strict_types=1);

// ceci est un commentaire
// variable utilisée sans avoir été déclarée
$nom = "dupont";
// un affichage écran
print "nom=$nom\n";
// un tableau avec des éléments de type différent
$tableau = ["un", "deux", 3, 4];
// son nombre d'éléments
$n = count($tableau);
// une boucle
for ($i = 0; $i < $n; $i++) {
    print "tableau[$i]=$tableau[$i]\n";
}
// initialisation de 2 variables avec le contenu d'un tableau
[$chaine1, $chaine2] = ["chaine1", "chaine2"];
// concaténation des 2 chaînes
$chaine3 = $chaine1 . $chaine2;
// affichage résultat
print "[$chaine1,$chaine2,$chaine3]\n";
// utilisation fonction
affiche($chaine1);
// le type d'une variable peut être connu
afficheType("n", $n);
afficheType("chaine1", $chaine1);
afficheType("tableau", $tableau);
// le type d'une variable peut changer en cours d'exécution
$n = "a changé";
afficheType("n", $n);
// une fonction peut rendre un résultat
$res1 = f1(4);
print "res1=$res1\n";
// une fonction peut rendre un tableau de valeurs
[$res1, $res2, $res3] = f2();
print "(res1,res2,res3)=[$res1,$res2,$res3]\n";
// on aurait pu récupérer ces valeurs dans un tableau
$t = f2();
for ($i = 0; $i < count($t); $i++) {
    print "t[$i]=$t[$i]\n";
}
// des tests
for ($i = 0; $i < count($t); $i++) {
    // n'affiche que les chaînes
    if (gettype($t[$i]) === "string") {
        print "t[$i]=$t[$i]\n";
    }
}
// opérateurs de comparaison == et ===
if ("2" == 2) {
    print "avec l'opérateur ==, la chaîne 2 est égale à l'entier 2\n";
} else {
    print "avec l'opérateur ==, la chaîne 2 n'est pas égale à l'entier 2\n";
}
if ("2" === 2) {
    print "avec l'opérateur ===, la chaîne 2 est égale à l'entier 2\n";
} else {
    print "avec l'opérateur ===, la chaîne 2 n'est pas égale à l'entier 2\n";
}
// une comparaison dont le résultat a changé avec PHP 8
if (0 == "abc") {
    print "avec l'opérateur ==, l'entier 0 est égal à la chaîne abc\n";
} else {
    print "avec l'opérateur ==, l'entier 0 n'est pas égal à la chaîne abc\n";
}
// d'autres tests
for ($i = 0; $i < count($t); $i++) {
    // n'affiche que les entiers > 10
    if (is_int($t[$i]) && $t[$i] > 10) {
        print "t[$i]=$t[$i]\n";
    }
}
// une boucle while
$t = [8, 5, 0, -2, 3, 4];
$i = 0;
$somme = 0;
while ($i < count($t) && $t[$i] > 0) {
    print "t[$i]=$t[$i]\n";
    $somme += $t[$i];   // $somme = $somme + $t[$i]
    $i++;               // $i = $i + 1
}
print "somme=$somme\n";

// fin programme
exit;

//----------------------------------
function affiche(string $chaine): void
{
    // affiche $chaine
    print "chaine=$chaine\n";
}

//----------------------------------
function afficheType(string $name, mixed $variable): void
{
    // affiche le type de $variable
    print "type[variable $" . $name . "]=" . gettype($variable) . "\n";
}

//----------------------------------
function f1(int $param): int
{
    // ajoute 10 à $param
    return $param + 10;
}

//----------------------------------
function f2(): array
{
    // rend 3 valeurs
    return ["un", 0, 100];
}

Les résultats :

> php bases-01.php
nom=dupont
tableau[0]=un
tableau[1]=deux
tableau[2]=3
tableau[3]=4
[chaine1,chaine2,chaine1chaine2]
chaine=chaine1
type[variable $n]=integer
type[variable $chaine1]=string
type[variable $tableau]=array
type[variable $n]=string
res1=14
(res1,res2,res3)=[un,0,100]
t[0]=un
t[1]=0
t[2]=100
t[0]=un
avec l'opérateur ==, la chaîne 2 est égale à l'entier 2
avec l'opérateur ===, la chaîne 2 n'est pas égale à l'entier 2
avec l'opérateur ==, l'entier 0 n'est pas égal à la chaîne abc
t[2]=100
t[0]=8
t[1]=5
somme=13

Commentaires :

  • ligne 4 : demande à PHP de vérifier strictement les types déclarés des paramètres et des résultats des fonctions. Nous y reviendrons au paragraphe consacré aux changements de types. Pour l'instant, retenons que tous les scripts du cours commencent ainsi ;
  • ligne 8 : en PHP, on ne déclare pas les variables. Celles-ci ont un type dynamique qui peut varier au cours du temps. [$nom] représente la variable d'identifiant [nom] ;
  • ligne 10 : pour écrire à l'écran, on peut utiliser l'instruction [print] ou l'instruction [echo] ;
  • ligne 12 : la notation [[...]] permet de définir un tableau. La notation [$tableau[$i]] représente l'élément n° [$i] du tableau [$tableau], le premier élément ayant le n° 0. La notation plus ancienne [array("un", "deux", 3, 4)] est toujours acceptée, mais nous ne l'utiliserons pas ;
  • ligne 14 : la fonction [count($tableau)] rend le nombre d'éléments du tableau [$tableau] ;
  • lignes 16-18 : une boucle. Celle-ci n'ayant qu'une instruction, les accolades sont facultatives. Dans la suite de ce document, nous mettrons systématiquement les accolades quel que soit le nombre d'instructions ;
  • ligne 17 : les chaînes de caractères sont entourées de guillemets " ou d'apostrophes '. À l'intérieur de guillemets, les variables [$variable] sont évaluées (on dit aussi « interpolées »), mais pas à l'intérieur d'apostrophes ;
  • ligne 20 : on peut affecter les éléments d'un tableau à plusieurs variables en une seule opération. Ici [$chaine1="chaine1"] et [$chaine2="chaine2"]. Cette écriture, appelée déstructuration, remplace la fonction [list] des anciennes versions de PHP : [list($chaine1, $chaine2) = ...] est toujours accepté ;
  • ligne 22 : l'opérateur . (point) est l'opérateur de concaténation de chaînes ;
  • lignes 91-95 : le mot clé [function] définit une fonction. Une fonction peut être définie n'importe où dans le script, même après son utilisation. Ici, [string $chaine] indique que le paramètre [$chaine] doit être une chaîne de caractères et [: void] que la fonction ne rend aucun résultat. Ces déclarations de types sont facultatives en PHP, mais nous les utiliserons systématiquement : elles documentent le code et permettent à PHP (et à l'éditeur) de détecter des erreurs ;
  • ligne 98 : le type [mixed] (PHP 8.0) signifie « n'importe quel type ». Le paramètre [$variable] peut recevoir un entier, une chaîne, un tableau… ;
  • ligne 101 : la fonction prédéfinie [gettype($variable)] rend une chaîne de caractères représentant le type de [$variable]. Les résultats montrent que ce type peut changer au cours du temps : [$n] est d'abord un entier (integer) puis une chaîne (string) ;
  • ligne 105 : une fonction rend un résultat par l'instruction [return]. Le type de ce résultat est déclaré après les parenthèses : [: int]. Le code appelant peut ignorer ou récupérer les résultats d'une fonction ;
  • ligne 112 : une fonction peut rendre plusieurs valeurs en les rassemblant dans un tableau ;
  • lignes 53-62 : l'opérateur [===] compare deux éléments de façon stricte : il faut qu'ils aient le même type et la même valeur pour être égaux. L'opérateur [] est moins strict : deux éléments peuvent être égaux sans être du même type. Dans ce cas, la comparaison se fait après conversion des deux éléments dans un même type. Il est assez facile « d'oublier » la présence de ces conversions implicites et d'aboutir ainsi à des résultats imprévus, tels que de découvrir qu'une condition est vraie alors que vous l'attendiez fausse. Pour éviter cet écueil, nous utiliserons systématiquement l'opérateur de comparaison [=] (et son contraire [!==]) ;
  • lignes 64-68 : un exemple de ces conversions implicites. En PHP 7, la chaîne "abc" était convertie en le nombre 0, et l'expression [0 == "abc"] était vraie ! Depuis PHP 8.0, lorsqu'on compare un nombre à une chaîne qui ne représente pas un nombre, c'est le nombre qui est converti en chaîne, et l'expression est fausse. Le comportement de [] a donc changé d'une version à l'autre : raison de plus pour lui préférer [=] ;
  • ligne 72 : la fonction [is_int] rend [true] si son paramètre est un entier. Il existe de même [is_string], [is_float], [is_bool], [is_array]… L'opérateur [&&] est le ET logique. On peut également utiliser les opérateurs logiques [||] (OU) et [!] (NON). PHP connaît aussi les mots clés [and] et [or], mais leur priorité, plus faible que celle de l'affectation, réserve des surprises : nous ne les utiliserons pas ;
  • ligne 80 : une boucle [while]. Elle s'arrête dès que la condition est fausse, ici dès que le tableau est parcouru ou qu'on rencontre un élément négatif ou nul ;
  • ligne 88 : l'instruction [exit] arrête l'exécution du script. Les fonctions qui suivent ne sont pas exécutées à cet endroit mais elles sont connues de tout le script .

On remarquera que le script ne se termine pas par la balise [?>] de fin de code PHP. Elle n'est pas indispensable. De plus, dans un contexte web, elle peut poser problème si elle est suivie par des espaces ou des marques de fin de ligne, difficiles à détecter car non visibles dans un éditeur de texte. La recommandation [PSR-12] demande d'ailleurs de l'omettre dans les fichiers ne contenant que du PHP. Aussi, dans la suite du document, nous l'omettrons systématiquement.

Note : dans ce document, nous utiliserons le mot clé [print] pour afficher du texte sur la console. Une autre méthode pour faire la même chose est d'utiliser le mot clé [echo]. Il y a de subtiles différences entre ces deux mots clés, mais dans le contexte de ce document il n'y en aura aucune. Si donc vous préférez utiliser [echo], alors faites-le.

3.3.2. Usage de VS Code

Pour travailler sur les scripts du cours, on ouvre le dossier [php8-oct-2026] dans [VS Code] (menu [Fichier > Ouvrir le dossier]). L'arborescence des fichiers apparaît dans l'explorateur, à gauche de la fenêtre. Pour exécuter un script :

  • on ouvre un terminal intégré (menu [Terminal > Nouveau terminal]). Il s'ouvre dans le dossier [php8-oct-2026] ;
  • on se place dans le dossier du script : [cd scripts-console/bases] ;
  • on lance le script : [php bases-01.php]. Les résultats s'affichent dans le terminal. On peut aussi, dans l'explorateur, cliquer droit sur le dossier [bases] et choisir [Ouvrir dans le terminal intégré] .

Si la commande [php] n'est pas reconnue, c'est que le dossier de PHP n'est pas dans le PATH de Windows : [Laragon] l'y ajoute lorsqu'on coche l'option correspondante de son menu [Outils > Path]. La commande [php -v] doit afficher la version 8.5.

L'extension [PHP Intelephense] analyse le code au fur et à mesure de la frappe et signale les problèmes par un trait ondulé sous le code fautif. Le survol de ce trait avec la souris affiche le message. Tous les problèmes du dossier sont rassemblés dans le panneau [Problèmes] (menu [Affichage > Problèmes]). Parmi les avertissements utiles :

  • [Undefined variable] : une variable est utilisée sans avoir été initialisée. C'est souvent une faute de frappe sur le nom d'une variable ;
  • [Undefined function] : appel d'une fonction qui n'existe pas, là encore souvent une faute de frappe ;
  • [Expected type 'int'. Found 'string'.] : un paramètre effectif n'a pas le type déclaré par la fonction. C'est l'un des grands bénéfices des déclarations de types : l'erreur est signalée avant même l'exécution ;
  • [Expected 2 arguments. Found 1.] : le nombre de paramètres effectifs ne correspond pas à la définition de la fonction .

Le survol d'un nom de fonction affiche sa signature (paramètres, types, résultat) et sa documentation, y compris pour les fonctions prédéfinies de PHP. Pour qu'[Intelephense] connaisse les nouveautés récentes du langage, on vérifie dans les paramètres de [VS Code] (menu [Fichier > Préférences > Paramètres], rechercher « intelephense php version ») que la version de PHP indiquée est la 8.5. En phase d'apprentissage du langage, il est conseillé de corriger tous les avertissements signalés. On apprend ainsi beaucoup de choses.

[Intelephense] sait aussi mettre en forme un fichier (clic droit, [Mettre en forme le document]) selon les recommandations de codage [PSR-12]. Les PSR (PHP Standard Recommendations) sont des recommandations pour produire un code standard et ainsi faciliter l'interopérabilité et la maintenance de codes écrits par différentes personnes. Elles sont publiées par le [PHP-FIG] sur le site www.php-fig.org. Les recommandations [PSR-1] et [PSR-12] fixent les règles d'écriture du code. Voici celles de [PSR-1] :

Élément
Règle
Constantes de classe
Les constantes de classe DOIVENT être écrites en majuscules avec le caractère _ comme séparateur.
Ex : [const TAUX_TVA]
Méthodes
Les noms de méthodes DOIVENT être écrits en camelCase.
Ex : [public function executeBatchImpots()]
Propriétés
Les noms de propriétés DEVRAIENT être écrits de façon cohérente dans un même ensemble de code, en StudlyCaps, camelCase ou under_score.
Ex : [$SalaireAnnuel], [$salaireAnnuel], [$salaire_annuel]
Effets de bord
Un fichier DEVRAIT soit déclarer de nouveaux symboles (classes, fonctions, constantes…) sans autre effet de bord, soit exécuter du code avec des effets de bord, mais NE DEVRAIT PAS faire les deux.
Types
Les noms de types (classes, interfaces…) DOIVENT être écrits en StudlyCaps. Chaque type est dans un fichier à lui et dans un espace de noms d'au moins un niveau.
Ex : [class EtudiantBoursier]

La règle des effets de bord dit que dans un fichier PHP, on doit trouver :

  • soit la déclaration de types (classes, interfaces), de fonctions ou de constantes ;
  • soit du code exécutable sans déclaration de nouveaux types .

Par facilité, les exemples de ce chapitre ne la respectent pas : chaque script est un fichier unique qui définit ses fonctions et les utilise. Répartir le code dans plusieurs fichiers serait trop lourd pour des exemples basiques. Pour l'application présentée comme fil rouge de ce document, on cherchera, dès qu'elle sera écrite avec des classes, à observer au mieux ces recommandations.

3.4. La portée des variables

3.4.1. Exemple 1

Le script [bases-02.php] est le suivant :

<?php

declare(strict_types=1);

// portée des variables

function f1(): void
{
    // on utilise la variable globale $i
    global $i;
    $i++;
    $j = 10;
    print "f1[i,j]=[$i,$j]\n";
}

function f2(): void
{
    // on utilise la variable globale $i
    global $i;
    $i++;
    $j = 20;
    print "f2[i,j]=[$i,$j]\n";
}

function f3(): void
{
    // on utilise une variable locale $i
    $i = 4;
    $j = 30;
    print "f3[i,j]=[$i,$j]\n";
}

// tests
$i = 0;
$j = 0;  // ces deux variables ne sont connues d'une fonction f
// que si celle-ci déclare explicitement par l'instruction global
// qu'elle veut les utiliser
f1();
f2();
f3();
print "test[i,j]=[$i,$j]\n";

Les résultats :

1
2
3
4
5
> php bases-02.php
f1[i,j]=[1,10]
f2[i,j]=[2,20]
f3[i,j]=[4,30]
test[i,j]=[2,0]

Commentaires :

  • lignes 34-35 : définissent 2 variables [$i] et [$j] du programme principal. Ces variables ne sont pas connues à l'intérieur des fonctions. Ainsi, ligne 12, la variable [$j] de la fonction [f1] est une variable locale à la fonction [f1] et est différente de la variable [$j] du programme principal ;
  • ligne 10 : une fonction peut accéder à une variable [$variable] du programme principal via le mot clé [global]. Ici, l'instruction désigne la variable globale [$i] du programme principal ;
  • ligne 28 : la fonction [f3] n'a pas déclaré [$i] globale : sa variable [$i] est une variable locale, sans rapport avec celle du programme principal .

L'usage de [global] est déconseillé : une fonction qui modifie des variables globales est difficile à comprendre et à tester. Il est préférable qu'une fonction reçoive ses données par ses paramètres et rende ses résultats par [return]. C'est ce que nous ferons dans la suite.

3.4.2. Exemple 2

Le script [bases-03.php] est le suivant :

<?php

declare(strict_types=1);

// la portée d'une variable est globale aux blocs de code
$i = 0;
{
    $i = 4;
    $i++;
}
print "i=$i\n";

Les résultats :

> php bases-03.php
i=5

Commentaires :

Dans certains langages, une variable définie à l'intérieur d'accolades a la portée de celles-ci : elle n'est pas connue à l'extérieur de celles-ci. Les résultats ci-dessus montrent qu'il n'en est rien en PHP. La variable [$i] utilisée ligne 8 à l'intérieur des accolades est la même que celle utilisée ligne 6 et ligne 11 à l'extérieur de celles-ci.

3.5. Les changements de types

Les variables en PHP n'ont pas un type constant. Celui-ci peut changer en cours d'exécution selon la valeur affectée à la variable. Dans des opérations impliquant des données de divers types, l'interpréteur PHP fait des conversions implicites pour ramener les opérandes dans un type commun. Ces conversions implicites, si elles ne sont pas connues du développeur, peuvent être une source d'erreurs difficiles à repérer. On présente ci-dessous un script [bases-04.php] montrant des conversions implicites et explicites. Commençons par sa première partie :

<?php

// types stricts dans le passage des paramètres
declare(strict_types=1);

// changements implicites de types
// type --> bool
print "Conversion vers un booléen------------------------------\n";
showBool("abcd", "abcd");
showBool("", "");
showBool("[1, 2, 3]", [1, 2, 3]);
showBool("[]", []);
showBool("null", null);
showBool("0.0", 0.0);
showBool("0", 0);
showBool("4.6", 4.6);
showBool("\"0\"", "0");

function showBool(string $prefixe, mixed $var): void
{
    print "(bool) $prefixe : ";
    // la conversion de $var en booléen se fait automatiquement dans le test qui suit
    if ($var) {
        print "true";
    } else {
        print "false";
    }
    print "\n";
}

Commentaires :

  • ligne 4 : demande une vérification stricte du type des paramètres d'une fonction lorsque celui-ci est précisé (et du type de son résultat). Nous expliquons cette instruction un peu plus loin ;
  • ligne 19 : la fonction [showBool] a pour but de montrer la transformation implicite (automatique) que fait l'interpréteur PHP lorsqu'une valeur de tout type doit être transformée en booléen (ligne 23) ;
  • ligne 19 : le paramètre [$prefixe] doit être de type [string]. Le paramètre [$var] est de type [mixed] : le paramètre effectif pourra donc être de tout type. La fonction [showBool] ne rend aucune valeur ([void]) ;
  • ligne 23 : dans l'instruction [if ($var)], la valeur de [$var] doit être transformée en booléen pour que le [if] soit évalué. De façon étonnante, l'interpréteur PHP a une réponse pour tout type de valeur qu'on lui donne ;
  • lignes 9-17 : la valeur du paramètre de la fonction [showBool] sera successivement :
    • ligne 9 : une chaîne non vide : résultat [true] (la casse n'est pas importante, TRUE=true) ;
    • ligne 10 : une chaîne vide : résultat [false] ;
    • ligne 11 : un tableau non vide : résultat [true] ;
    • ligne 12 : un tableau vide : résultat [false] ;
    • ligne 13 : la valeur [null] (absence de valeur) : résultat [false] ;
    • ligne 14 : le nombre réel 0 : résultat [false] ;
    • ligne 15 : le nombre entier 0 : résultat [false] ;
    • ligne 16 : un nombre réel (ou entier) différent de 0 : résultat [true] ;
    • ligne 17 : un piège : la chaîne "0" est convertie en [false], alors que toute autre chaîne non vide est convertie en [true] .

C'est ce que montrent les affichages écran obtenus :

Conversion vers un booléen------------------------------
(bool) abcd : true
(bool)  : false
(bool) [1, 2, 3] : true
(bool) [] : false
(bool) null : false
(bool) 0.0 : false
(bool) 0 : false
(bool) 4.6 : true
(bool) "0" : false

Continuons le code du script :

// changements implicites de type string vers un type numérique
// string --> nombre
print "Conversion chaîne vers nombre------------------------------\n";
showNumber("12");
showNumber("45.67");
showNumber("12abcd");
try {
    showNumber("abcd");
} catch (TypeError $erreur) {
    print "(abcd): erreur [" . $erreur->getMessage() . "]\n";
}

function showNumber(string $var): void
{
    $nombre = $var + 1;
    var_dump($nombre);
    print "($var): $nombre\n";
}

Commentaires :

  • ligne 43 : la fonction [showNumber] admet un paramètre de type [string] et ne rend pas de résultat ([void]) ;
  • ligne 45 : ce paramètre est utilisé dans une opération arithmétique, ce qui va forcer l'interpréteur PHP à essayer de transformer [$var] en nombre :
    • ligne 34 : va transformer la chaîne "12" en nombre entier 12 ;
    • ligne 35 : va transformer la chaîne "45.67" en nombre réel 45.67 ;
    • ligne 36 : la chaîne "12abcd" commence par un nombre. PHP utilise ce nombre, 12, mais émet un avertissement (Warning) ;
    • ligne 38 : la chaîne "abcd" ne contient aucun nombre. En PHP 7, elle était transformée en 0 avec un simple avertissement. Depuis PHP 8.0, l'opération est impossible : PHP lance une erreur de type [TypeError] qui, si elle n'était pas interceptée, arrêterait le script ;
  • lignes 37-41 : la structure [try { ... } catch (TypeError $erreur) { ... }] permet d'intercepter cette erreur et de continuer l'exécution. [$erreur->getMessage()] est le message de l'erreur. Les exceptions seront étudiées dans un chapitre ultérieur ; il suffit ici de savoir que le code du bloc [catch] est exécuté lorsque le code du bloc [try] provoque une erreur [TypeError] .

Voici les résultats de l'exécution :

Conversion chaîne vers nombre------------------------------
int(13)
(12): 13
float(46.67)
(45.67): 46.67

Warning: A non-numeric value encountered in .../php8-oct-2026/scripts-console/bases/bases-04.php on line 45
int(13)
(12abcd): 13
(abcd): erreur [Unsupported operand types: string + int]

La fonction [var_dump] affiche le type et la valeur de son paramètre : int(13) est l'entier 13, float(46.67) le réel 46.67.

On remarquera que l'instruction [declare(strict_types=1)] n'a pas empêché la conversion de la chaîne "12" en nombre. C'est un point important : le mode strict ne concerne que le passage des paramètres aux fonctions et les résultats qu'elles rendent. Il ne change rien aux conversions faites par les opérateurs (+, ., ==…) ni à celle faite par le [if]. Voici ce qu'il change :

  • sans [declare(strict_types=1)] (mode dit « coercitif »), si une fonction attend un [int] et qu'on lui passe la chaîne "12", PHP convertit la chaîne en l'entier 12 ;
  • avec [declare(strict_types=1)] (mode strict), le même appel provoque une erreur [TypeError]. Le paramètre effectif doit être exactement du type attendu. Une seule exception : un [int] est accepté là où un [float] est attendu .

L'instruction doit être la première du fichier et ne vaut que pour les appels de fonctions faits dans ce fichier. Nous l'utiliserons dans tous les scripts : elle transforme des conversions silencieuses, sources d'erreurs, en erreurs signalées. Nous verrons un exemple au paragraphe consacré au passage des paramètres.

Continuons le code du script avec des changements de type explicites :

// changements explicites de type
// vers int
print "Conversion explicite vers int------------------------------\n";
showInt("12.45");
showInt(67.8);
showInt(true);
showInt(null);

function showInt(mixed $var): void
{
    print "paramètre : ";
    var_dump($var);
    print "résultat de la conversion : ";
    var_dump((int) $var);
}

Commentaires :

  • ligne 58 : la fonction [showInt] reçoit un paramètre de n'importe quel type ([mixed]) et ne rend pas de résultat. Elle convertit le paramètre [$var] en entier (ligne 63). De façon générale, pour changer une variable [$var] en un type T, on écrit [(T) $var] où T peut être : int, bool, float, string, array, object. Les écritures (integer), (boolean), (double) et (binary) des anciennes versions de PHP sont obsolètes : PHP 8.5 les signale comme dépréciées. L'écriture (real), elle, a été supprimée dès PHP 8.0 ;
  • ligne 53 : convertit la chaîne "12.45" en l'entier 12 ;
  • ligne 54 : convertit le réel 67.8 en l'entier 67 (la partie décimale est perdue) ;
  • ligne 55 : convertit le booléen [true] en l'entier 1 (le booléen [false] en l'entier 0) ;
  • ligne 56 : convertit la valeur [null] en l'entier 0 .

C'est ce que montrent les affichages écran :

1
2
3
4
5
6
7
8
9
Conversion explicite vers int------------------------------
paramètre : string(5) "12.45"
résultat de la conversion : int(12)
paramètre : float(67.8)
résultat de la conversion : int(67)
paramètre : bool(true)
résultat de la conversion : int(1)
paramètre : NULL
résultat de la conversion : int(0)

Nous continuons l'étude du script avec la conversion explicite de valeurs en type [float] :

// vers float
print "Conversion explicite vers float------------------------------\n";
showFloat("12.45");
showFloat(67);
showFloat(true);
showFloat(null);

function showFloat(mixed $var): void
{
    print "paramètre : ";
    var_dump($var);
    print "résultat de la conversion : ";
    var_dump((float) $var);
}

Commentaires :

  • ligne 73 : la fonction [showFloat] reçoit un paramètre de type quelconque et ne rend pas de résultat ;
  • ligne 78 : la valeur de ce paramètre est transformée explicitement en [float] ;
  • ligne 68 : la chaîne "12.45" est transformée en le nombre réel 12.45 ;
  • ligne 69 : l'entier 67 est transformé en le nombre réel 67 ;
  • ligne 70 : le booléen [true] est transformé en le nombre réel 1 (la valeur [false] en le nombre 0) ;
  • ligne 71 : la valeur [null] est transformée en le nombre réel 0 .

C'est ce que montrent les résultats écran :

1
2
3
4
5
6
7
8
9
Conversion explicite vers float------------------------------
paramètre : string(5) "12.45"
résultat de la conversion : float(12.45)
paramètre : int(67)
résultat de la conversion : float(67)
paramètre : bool(true)
résultat de la conversion : float(1)
paramètre : NULL
résultat de la conversion : float(0)

Nous terminons la présentation du script en étudiant des conversions vers le type [string] :

// vers string
print "Conversion explicite vers string------------------------------\n";
showString(5);
showString(6.7);
showString(false);
showString(null);

function showString(mixed $var): void
{
    print "paramètre : ";
    var_dump($var);
    print "résultat de la conversion : ";
    var_dump((string) $var);
}

Commentaires :

  • ligne 88 : la fonction [showString] reçoit un paramètre de type quelconque et ne rend pas de résultat ;
  • ligne 93 : la valeur du paramètre est transformée en un type [string] ;
  • ligne 83 : l'entier 5 sera transformé en la chaîne "5" ;
  • ligne 84 : le réel 6.7 sera transformé en la chaîne "6.7" ;
  • ligne 85 : le booléen [false] sera transformé en chaîne vide ;
  • ligne 86 : la valeur [null] sera transformée en chaîne vide .

Voici les résultats écran :

1
2
3
4
5
6
7
8
9
Conversion explicite vers string------------------------------
paramètre : int(5)
résultat de la conversion : string(1) "5"
paramètre : float(6.7)
résultat de la conversion : string(3) "6.7"
paramètre : bool(false)
résultat de la conversion : string(0) ""
paramètre : NULL
résultat de la conversion : string(0) ""

3.6. Les tableaux

3.6.1. Tableaux classiques à une dimension

Il existe deux notations pour les tableaux en PHP :

$tableau = ["un", 2, "trois"];
$contraires = ["petit" => "grand", "beau" => "laid", "cher" => "bon marché"];

Le tableau 1 est appelé tableau (ou liste) et le tableau 2 un dictionnaire ou tableau associatif où les éléments sont notés clé => valeur. La notation [$contraires["beau"]] désigne la valeur associée à la clé "beau". C'est donc ici la chaîne "laid". Le tableau 1 n'est qu'une variante du dictionnaire et pourrait être noté :

$tableau = [0 => "un", 1 => 2, 2 => "trois"];

On a ainsi [$tableau[2]="trois"]. Finalement, il n'y a que des dictionnaires. Dans le cas d'un tableau classique de n éléments, les clés sont les nombres entiers de l'intervalle [0,n-1].

Le script [bases-05.php] montre des opérations de manipulation d'un tableau de valeurs :

<?php

declare(strict_types=1);

// tableaux classiques
// initialisation
$tab1 = [0, 1, 2, 3, 4, 5];
// parcours - 1
print "tab1 a " . count($tab1) . " éléments\n";
for ($i = 0; $i < count($tab1); $i++) {
    print "tab1[$i]=$tab1[$i]\n";
}
// parcours - 2
print "tab1 a " . count($tab1) . " éléments\n";
foreach ($tab1 as $clé => $valeur) {
    print "tab1[$clé]=$valeur\n";
}
// ajout d'éléments
$tab1[] = $i++;
$tab1[] = $i++;
// parcours - 3
print "tab1 a " . count($tab1) . " éléments\n";
$i = 0;
foreach ($tab1 as $élément) {
    print "tab1[$i]=$élément\n";
    $i++;
}
// suppression dernier élément
array_pop($tab1);
// parcours - 4
print "tab1 a " . count($tab1) . " éléments\n";
for ($i = 0; $i < count($tab1); $i++) {
    print "tab1[$i]=$tab1[$i]\n";
}
// suppression premier élément
array_shift($tab1);
// parcours - 5
print "tab1 a " . count($tab1) . " éléments\n";
for ($i = 0; $i < count($tab1); $i++) {
    print "tab1[$i]=$tab1[$i]\n";
}
// ajout en fin de tableau
array_push($tab1, -2);
// parcours - 6
print "tab1 a " . count($tab1) . " éléments\n";
for ($i = 0; $i < count($tab1); $i++) {
    print "tab1[$i]=$tab1[$i]\n";
}
// ajout en début de tableau
array_unshift($tab1, -1);
// parcours - 7
print "tab1 a " . count($tab1) . " éléments\n";
for ($i = 0; $i < count($tab1); $i++) {
    print "tab1[$i]=$tab1[$i]\n";
}
// premier et dernier éléments (PHP 8.5)
print "premier élément : " . array_first($tab1) . ", dernier élément : " . array_last($tab1) . "\n";

Les résultats :

> php bases-05.php
tab1 a 6 éléments
tab1[0]=0
tab1[1]=1
tab1[2]=2
tab1[3]=3
tab1[4]=4
tab1[5]=5
tab1 a 6 éléments
tab1[0]=0
tab1[1]=1
tab1[2]=2
tab1[3]=3
tab1[4]=4
tab1[5]=5
tab1 a 8 éléments
tab1[0]=0
tab1[1]=1
tab1[2]=2
tab1[3]=3
tab1[4]=4
tab1[5]=5
tab1[6]=6
tab1[7]=7
tab1 a 7 éléments
tab1[0]=0
tab1[1]=1
tab1[2]=2
tab1[3]=3
tab1[4]=4
tab1[5]=5
tab1[6]=6
tab1 a 6 éléments
tab1[0]=1
tab1[1]=2
tab1[2]=3
tab1[3]=4
tab1[4]=5
tab1[5]=6
tab1 a 7 éléments
tab1[0]=1
tab1[1]=2
tab1[2]=3
tab1[3]=4
tab1[4]=5
tab1[5]=6
tab1[6]=-2
tab1 a 8 éléments
tab1[0]=-1
tab1[1]=1
tab1[2]=2
tab1[3]=3
tab1[4]=4
tab1[5]=5
tab1[6]=6
tab1[7]=-2
premier élément : -1, dernier élément : -2

Commentaires :

  • ligne 15 : le tableau est parcouru avec un [foreach]. Cet élément syntaxique permet de parcourir un dictionnaire, donc un tableau, selon deux syntaxes : [foreach ($dictionnaire as $clé => $valeur)] et [foreach ($tableau as $valeur)]. La première syntaxe ramène une paire (clé, valeur) à chaque itération alors que la seconde ne ramène que la valeur. Le cours PHP 7 utilisait ici la fonction [each], déjà obsolète à l'époque : elle a été supprimée en PHP 8.0. [foreach] est la façon normale de parcourir un tableau ;
  • ligne 19 : la notation [$tableau[] = valeur] ajoute l'élément [valeur] comme dernier élément de [$tableau]. Ici, [$i] vaut 6 à la sortie de la première boucle [for] : on ajoute 6, puis 7 ;
  • ligne 24 : un parcours avec la seconde syntaxe de [foreach]. On remarquera que les noms de variables peuvent contenir des caractères accentués ;
  • ligne 29 : la fonction [array_pop($tableau)] supprime le dernier élément de [$tableau] ;
  • ligne 36 : la fonction [array_shift($tableau)] supprime le premier élément de [$tableau]. Les éléments restants sont renumérotés à partir de 0 ;
  • ligne 43 : la fonction [array_push($tableau, valeur)] ajoute [valeur] comme dernier élément de [$tableau] ;
  • ligne 50 : la fonction [array_unshift($tableau, valeur)] ajoute [valeur] comme premier élément de [$tableau] ;
  • ligne 57 : les fonctions [array_first] et [array_last], apparues avec PHP 8.5, rendent le premier et le dernier élément d'un tableau (ou [null] si le tableau est vide) .

3.6.2. Le dictionnaire ou tableau associatif

Le script [bases-06.php] est le suivant :

<?php

declare(strict_types=1);

// dictionnaires
$conjoints = ["Pierre" => "Gisèle", "Paul" => "Virginie", "Jacques" => "Lucette", "Jean" => ""];
// parcours - 1
print "Nombre d'éléments du dictionnaire : " . count($conjoints) . "\n";
foreach ($conjoints as $clé => $valeur) {
    print "conjoints[$clé]=$valeur\n";
}
// tri du dictionnaire sur la clé
ksort($conjoints);
// parcours - 2
foreach ($conjoints as $clé => $valeur) {
    print "conjoints[$clé]=$valeur\n";
}
// liste des clés du dictionnaire
$clés = array_keys($conjoints);
for ($i = 0; $i < count($clés); $i++) {
    print "clés[$i]=$clés[$i]\n";
}
// liste des valeurs du dictionnaire
$valeurs = array_values($conjoints);
for ($i = 0; $i < count($valeurs); $i++) {
    print "valeurs[$i]=$valeurs[$i]\n";
}
// un tableau classique est-il une liste ? et un dictionnaire ?
print "valeurs est une liste : " . var_export(array_is_list($valeurs), true) . "\n";
print "conjoints est une liste : " . var_export(array_is_list($conjoints), true) . "\n";
// recherche d'une clé
existe($conjoints, "Jacques");
existe($conjoints, "Lucette");
existe($conjoints, "Jean");
// valeur par défaut si la clé n'existe pas : opérateur ??
print "conjoint de Lucette : " . ($conjoints["Lucette"] ?? "inconnu") . "\n";
// suppression d'une clé-valeur
unset($conjoints["Jean"]);
print "Nombre d'éléments du dictionnaire : " . count($conjoints) . "\n";
foreach ($conjoints as $clé => $valeur) {
    print "conjoints[$clé]=$valeur\n";
}
// fin
exit;

function existe(array $conjoints, string $mari): void
{
    // vérifie si la clé $mari existe dans le dictionnaire $conjoints
    if (isset($conjoints[$mari])) {
        print "La clé [$mari] existe associée à la valeur [$conjoints[$mari]]\n";
    } else {
        print "La clé [$mari] n'existe pas\n";
    }
}

Les résultats :

> php bases-06.php
Nombre d'éléments du dictionnaire : 4
conjoints[Pierre]=Gisèle
conjoints[Paul]=Virginie
conjoints[Jacques]=Lucette
conjoints[Jean]=
conjoints[Jacques]=Lucette
conjoints[Jean]=
conjoints[Paul]=Virginie
conjoints[Pierre]=Gisèle
clés[0]=Jacques
clés[1]=Jean
clés[2]=Paul
clés[3]=Pierre
valeurs[0]=Lucette
valeurs[1]=
valeurs[2]=Virginie
valeurs[3]=Gisèle
valeurs est une liste : true
conjoints est une liste : false
La clé [Jacques] existe associée à la valeur [Lucette]
La clé [Lucette] n'existe pas
La clé [Jean] existe associée à la valeur []
conjoint de Lucette : inconnu
Nombre d'éléments du dictionnaire : 3
conjoints[Jacques]=Lucette
conjoints[Paul]=Virginie
conjoints[Pierre]=Gisèle

Commentaires :

Le code précédent applique à un dictionnaire ce qui a été vu auparavant pour un simple tableau. Nous ne commentons que les nouveautés :

  • ligne 13 : la fonction [ksort] (key sort) permet de trier un dictionnaire dans l'ordre naturel de la clé ;
  • ligne 19 : la fonction [array_keys($dictionnaire)] rend la liste des clés du dictionnaire sous forme de tableau ;
  • ligne 24 : la fonction [array_values($dictionnaire)] rend la liste des valeurs du dictionnaire sous forme de tableau ;
  • ligne 29 : la fonction [array_is_list] (PHP 8.1) rend [true] si les clés du tableau sont 0, 1, 2… dans cet ordre, c'est-à-dire si le tableau est une liste. La fonction [var_export] rend ici la représentation PHP d'une valeur ("true" ou "false") ;
  • ligne 46 : la fonction [existe] reçoit un tableau ([array]) et une chaîne ([string]) ;
  • ligne 49 : la fonction [isset($variable)] rend [true] si la variable [$variable] existe et n'a pas la valeur [null], [false] sinon. Appliquée à un élément de tableau, elle permet de savoir si une clé existe. La fonction [array_key_exists($clé, $tableau)] fait la même chose, mais rend [true] même si la valeur associée à la clé est [null] ;
  • ligne 36 : l'opérateur [??] (« null coalescent ») rend son opérande gauche s'il existe et n'est pas [null], son opérande droit sinon. C'est une façon concise d'obtenir une valeur par défaut pour une clé absente, sans avertissement ;
  • ligne 38 : la fonction [unset($variable)] supprime la variable [$variable], ici un élément du dictionnaire .

3.6.3. Les tableaux à plusieurs dimensions

Le script [bases-07.php] est le suivant :

<?php

declare(strict_types=1);

// tableaux classiques multidimensionnels
// initialisation
$multi = [[0, 1, 2], [10, 11, 12, 13], [20, 21, 22, 23, 24]];
// parcours
for ($i1 = 0; $i1 < count($multi); $i1++) {
    for ($i2 = 0; $i2 < count($multi[$i1]); $i2++) {
        print "multi[$i1][$i2]=" . $multi[$i1][$i2] . "\n";
    }
}
// dictionnaires multidimensionnels
// initialisation
$multi = ["zéro" => [0, 1, 2], "un" => [10, 11, 12, 13], "deux" => [20, 21, 22, 23, 24]];
// parcours
foreach ($multi as $clé => $valeur) {
    for ($i2 = 0; $i2 < count($valeur); $i2++) {
        print "multi[$clé][$i2]={$multi[$clé][$i2]}\n";
    }
}

Résultats :

> php bases-07.php
multi[0][0]=0
multi[0][1]=1
multi[0][2]=2
multi[1][0]=10
multi[1][1]=11
multi[1][2]=12
multi[1][3]=13
multi[2][0]=20
multi[2][1]=21
multi[2][2]=22
multi[2][3]=23
multi[2][4]=24
multi[zéro][0]=0
multi[zéro][1]=1
multi[zéro][2]=2
multi[un][0]=10
multi[un][1]=11
multi[un][2]=12
multi[un][3]=13
multi[deux][0]=20
multi[deux][1]=21
multi[deux][2]=22
multi[deux][3]=23
multi[deux][4]=24

Commentaires :

  • ligne 7 : les éléments du tableau [$multi] sont eux-mêmes des tableaux ;
  • ligne 11 : à l'intérieur d'une chaîne entre guillemets, l'interpolation simple [$multi[$i1][$i2]] ne fonctionne pas : PHP n'évaluerait que [$multi[$i1]]. On sort donc l'expression de la chaîne et on utilise la concaténation ;
  • ligne 16 : le tableau [$multi] devient un dictionnaire (clé, valeur) où chaque valeur est un tableau ;
  • ligne 20 : une autre solution : à l'intérieur d'une chaîne entre guillemets, la notation [{$expression}] permet d'évaluer une expression complexe. Nous la retrouverons au paragraphe suivant .

3.7. Les chaînes de caractères

3.7.1. Notation

Le script [bases-08.php] est le suivant :

<?php

declare(strict_types=1);

// notation des chaînes
$chaine1 = "un";
$chaine2 = 'un';
print "[$chaine1,$chaine2]\n";
// entre apostrophes, les variables ne sont pas évaluées
print '[$chaine1,$chaine2]' . "\n";
// la notation {$...} délimite l'expression à évaluer
$personne = ["nom" => "dupont", "âge" => 34];
print "{$personne['nom']} a {$personne['âge']} ans\n";
// une chaîne sur plusieurs lignes (heredoc)
$texte = <<<FIN
    nom : $chaine1
    âge : {$personne['âge']}
    FIN;
print "$texte\n";

Résultats :

1
2
3
4
5
6
> php bases-08.php
[un,un]
[$chaine1,$chaine2]
dupont a 34 ans
nom : un
âge : 34

Commentaires :

  • lignes 6-7 : une chaîne peut être entourée de guillemets ou d'apostrophes ;
  • ligne 8 : entre guillemets, les variables sont évaluées ;
  • ligne 10 : entre apostrophes, elles ne le sont pas : la chaîne est affichée telle quelle. De même, [\n] n'y désigne pas une fin de ligne, d'où la concaténation de ["\n"] ;
  • ligne 13 : la notation [{$...}] permet d'évaluer dans une chaîne des expressions plus complexes qu'une simple variable, ici des éléments d'un dictionnaire. La notation [${variable}] des anciennes versions de PHP est dépréciée depuis PHP 8.2 : on ne l'utilisera pas ;
  • lignes 15-18 : la syntaxe [heredoc] permet d'écrire une chaîne sur plusieurs lignes. La chaîne commence après [<<<FIN] et se termine par le marqueur [FIN] (le nom est libre). Les variables y sont évaluées comme entre guillemets. Depuis PHP 7.3, le marqueur de fin peut être indenté : cette indentation est alors retirée de toutes les lignes de la chaîne .

3.7.2. Comparaison

Le script [bases-09.php] est le suivant :

<?php

// respect strict du type des paramètres des fonctions
declare(strict_types=1);

// fonction de comparaison
function compareChaines(string $chaine1, string $chaine2): void
{
    // compare chaine1 et chaine2
    if ($chaine1 === $chaine2) {
        print "[$chaine1] est égal à [$chaine2]\n";
    } else {
        print "[$chaine1] est différent de [$chaine2]\n";
    }
}

// tests de comparaisons de chaînes
compareChaines("abcd", "abcd");
compareChaines("", "");
compareChaines("1", "");

// recherche d'une sous-chaîne (PHP 8)
$fichier = "taxpayersdata.txt";
print "[$fichier] contient [data] : " . var_export(str_contains($fichier, "data"), true) . "\n";
print "[$fichier] commence par [tax] : " . var_export(str_starts_with($fichier, "tax"), true) . "\n";
print "[$fichier] se termine par [.json] : " . var_export(str_ends_with($fichier, ".json"), true) . "\n";
// position d'une sous-chaîne
print "position de [data] dans [$fichier] : " . strpos($fichier, "data") . "\n";
// longueur, majuscules
print "longueur de [$fichier] : " . strlen($fichier) . ", en majuscules : " . strtoupper($fichier) . "\n";
// attention : strlen compte des octets, mb_strlen des caractères
print "strlen(\"été\")=" . strlen("été") . ", mb_strlen(\"été\")=" . mb_strlen("été") . "\n";

Résultats :

> php bases-09.php
[abcd] est égal à [abcd]
[] est égal à []
[1] est différent de []
[taxpayersdata.txt] contient [data] : true
[taxpayersdata.txt] commence par [tax] : true
[taxpayersdata.txt] se termine par [.json] : false
position de [data] dans [taxpayersdata.txt] : 9
longueur de [taxpayersdata.txt] : 17, en majuscules : TAXPAYERSDATA.TXT
strlen("été")=5, mb_strlen("été")=3

Commentaires :

  • ligne 10 : on aurait pu utiliser le comparateur [] plutôt que [=]. Ce dernier opérateur est plus contraignant en ce sens qu'il impose que les deux opérandes soient de même type. À noter qu'ici, il pouvait être remplacé par l'opérateur [] puisque le type des deux paramètres est fixé à [string]* dans la signature de la fonction. Nous garderons cependant l'habitude d'utiliser *[=] ;
  • ligne 24 : la fonction [str_contains($chaîne, $sousChaîne)] (PHP 8.0) rend [true] si [$sousChaîne] est présente dans [$chaîne] ;
  • ligne 25 : la fonction [str_starts_with] (PHP 8.0) teste le début d'une chaîne ;
  • ligne 26 : la fonction [str_ends_with] (PHP 8.0) teste la fin d'une chaîne ;
  • ligne 28 : la fonction [strpos($chaîne, $sousChaîne)] rend la position de [$sousChaîne] dans [$chaîne], le premier caractère ayant la position 0, et [false] si la sous-chaîne est absente. Avant PHP 8, on testait la présence d'une sous-chaîne avec [strpos(...) !== false], une écriture piégeuse puisque la position 0 est convertie en [false] : [str_contains] est bien plus lisible ;
  • ligne 30 : la fonction [strlen] rend la longueur d'une chaîne et [strtoupper] la passe en majuscules ;
  • ligne 32 : [strlen] compte des octets, pas des caractères. Les fichiers du cours sont encodés en UTF-8, où un caractère accentué occupe deux octets : "été" a 3 caractères mais 5 octets. Les fonctions de l'extension [mbstring] (mb_strlen, mb_substr, mb_strtoupper…) travaillent, elles, sur des caractères .

3.7.3. Liens entre chaînes et tableaux

Le script [bases-10.php] est le suivant :

<?php

declare(strict_types=1);

// chaîne vers tableau
$chaine = "1:2:3:4";
$tab = explode(":", $chaine);
// parcours tableau
print "tab a " . count($tab) . " éléments\n";
for ($i = 0; $i < count($tab); $i++) {
    print "tab[$i]=$tab[$i]\n";
}
// tableau vers chaîne
$chaine2 = implode(":", $tab);
print "chaine2=$chaine2\n";
// ajoutons un champ vide
$chaine .= ":";
print "chaîne=$chaine\n";
$tab = explode(":", $chaine);
// parcours tableau
print "tab a " . count($tab) . " éléments\n";
for ($i = 0; $i < count($tab); $i++) {
    print "tab[$i]=$tab[$i]\n";
} // on a maintenant 5 éléments, le dernier étant vide
// ajoutons de nouveau un champ vide
$chaine .= ":";
print "chaîne=$chaine\n";
$tab = explode(":", $chaine);
// parcours tableau
print "tab a " . count($tab) . " éléments\n";
for ($i = 0; $i < count($tab); $i++) {
    print "tab[$i]=$tab[$i]\n";
} // on a maintenant 6 éléments, les deux derniers étant vides

Résultats :

> php bases-10.php
tab a 4 éléments
tab[0]=1
tab[1]=2
tab[2]=3
tab[3]=4
chaine2=1:2:3:4
chaîne=1:2:3:4:
tab a 5 éléments
tab[0]=1
tab[1]=2
tab[2]=3
tab[3]=4
tab[4]=
chaîne=1:2:3:4::
tab a 6 éléments
tab[0]=1
tab[1]=2
tab[2]=3
tab[3]=4
tab[4]=
tab[5]=

Commentaires :

  • ligne 7 : la fonction [explode($séparateur, $chaine)] permet de récupérer les champs de [$chaine] séparés par [$séparateur]. Ainsi [explode(":", $chaine)] permet de récupérer sous forme de tableau les éléments de [$chaine] qui sont séparés par la chaîne ":" ;
  • ligne 14 : la fonction [implode($séparateur, $tableau)] fait l'opération inverse de la fonction [explode]. Elle rend une chaîne de caractères formée des éléments de [$tableau] séparés par [$séparateur] ;
  • ligne 17 : l'opérateur [.=] ajoute une chaîne à la fin d'une autre. Les résultats montrent qu'un champ vide en fin de chaîne donne un élément vide dans le tableau rendu par [explode] .

3.7.4. Les expressions régulières

Le script [bases-11.php] est le suivant :

<?php

// type strict pour les paramètres de fonctions
declare(strict_types=1);

// expressions régulières en php
// récupérer les différents champs d'une chaîne
// le modèle : une suite de chiffres entourée de caractères quelconques
// on ne veut récupérer que la suite de chiffres
$modèle = "/(\d+)/";
// on confronte la chaîne au modèle
compareModele2Chaine($modèle, "xyz1234abcd");
compareModele2Chaine($modèle, "12 34");
compareModele2Chaine($modèle, "abcd");

// le modèle : une suite de chiffres entourée de caractères quelconques
// on veut la suite de chiffres ainsi que les champs qui suivent et précèdent
$modèle = "/^(.*?)(\d+)(.*?)$/";
// on confronte la chaîne au modèle
compareModele2Chaine($modèle, "xyz1234abcd");
compareModele2Chaine($modèle, "12 34");
compareModele2Chaine($modèle, "abcd");

// le modèle - une date au format jj/mm/aa
$modèle = "/^\s*(\d\d)\/(\d\d)\/(\d\d)\s*$/";
compareModele2Chaine($modèle, "10/05/97");
compareModele2Chaine($modèle, "  04/04/01  ");
compareModele2Chaine($modèle, "5/1/01");

// le modèle - un nombre décimal
$modèle = "/^\s*([+-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/";
compareModele2Chaine($modèle, "187.8");
compareModele2Chaine($modèle, "-0.6");
compareModele2Chaine($modèle, "4");
compareModele2Chaine($modèle, ".6");
compareModele2Chaine($modèle, "4.");
compareModele2Chaine($modèle, " + 4");

// fin
exit;

// --------------------------------------------------------------------------
function compareModele2Chaine(string $modèle, string $chaîne): void
{
    // compare la chaîne $chaîne au modèle $modèle
    // on confronte la chaîne au modèle
    $champs = [];
    $correspond = preg_match($modèle, $chaîne, $champs);
    // affichage résultats
    print "\nRésultats($modèle,$chaîne)\n";
    if ($correspond === 1) {
        for ($i = 0; $i < count($champs); $i++) {
            print "champs[$i]=$champs[$i]\n";
        }
    } else {
        print "La chaîne [$chaîne] ne correspond pas au modèle [$modèle]\n";
    }
}

Résultats :

> php bases-11.php

Résultats(/(\d+)/,xyz1234abcd)
champs[0]=1234
champs[1]=1234

Résultats(/(\d+)/,12 34)
champs[0]=12
champs[1]=12

Résultats(/(\d+)/,abcd)
La chaîne [abcd] ne correspond pas au modèle [/(\d+)/]

Résultats(/^(.*?)(\d+)(.*?)$/,xyz1234abcd)
champs[0]=xyz1234abcd
champs[1]=xyz
champs[2]=1234
champs[3]=abcd

Résultats(/^(.*?)(\d+)(.*?)$/,12 34)
champs[0]=12 34
champs[1]=
champs[2]=12
champs[3]= 34

Résultats(/^(.*?)(\d+)(.*?)$/,abcd)
La chaîne [abcd] ne correspond pas au modèle [/^(.*?)(\d+)(.*?)$/]

Résultats(/^\s*(\d\d)\/(\d\d)\/(\d\d)\s*$/,10/05/97)
champs[0]=10/05/97
champs[1]=10
champs[2]=05
champs[3]=97

Résultats(/^\s*(\d\d)\/(\d\d)\/(\d\d)\s*$/,  04/04/01  )
champs[0]=  04/04/01
champs[1]=04
champs[2]=04
champs[3]=01

Résultats(/^\s*(\d\d)\/(\d\d)\/(\d\d)\s*$/,5/1/01)
La chaîne [5/1/01] ne correspond pas au modèle [/^\s*(\d\d)\/(\d\d)\/(\d\d)\s*$/]

Résultats(/^\s*([+-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/,187.8)
champs[0]=187.8
champs[1]=
champs[2]=187.8

Résultats(/^\s*([+-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/,-0.6)
champs[0]=-0.6
champs[1]=-
champs[2]=0.6

Résultats(/^\s*([+-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/,4)
champs[0]=4
champs[1]=
champs[2]=4

Résultats(/^\s*([+-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/,.6)
champs[0]=.6
champs[1]=
champs[2]=.6

Résultats(/^\s*([+-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/,4.)
champs[0]=4.
champs[1]=
champs[2]=4.

Résultats(/^\s*([+-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/, + 4)
champs[0]= + 4
champs[1]=+
champs[2]=4

Commentaires :

Nous utilisons ici les expressions régulières pour récupérer les divers champs d'une chaîne de caractères. Les expressions régulières permettent de dépasser les limites de la fonction [explode]. Le principe est de comparer une chaîne de caractères à une autre chaîne appelée modèle à l'aide de la fonction [preg_match] : [$correspond = preg_match($modèle, $chaîne, $champs)] (ligne 48). La fonction [preg_match] rend l'entier 1 si le modèle peut être trouvé dans la chaîne, 0 sinon. Si oui, [$champs[0]] représente la sous-chaîne correspondant au modèle. Par ailleurs, si le modèle contient des sous-modèles entre parenthèses, [$champs[1]] est le morceau de [$chaîne] correspondant au 1er sous-modèle, [$champs[2]] est le morceau de [$chaîne] correspondant au 2e sous-modèle, etc. Revenons au script :

  • ligne 10 : le modèle désigne une suite de un ou plusieurs (+) chiffres (\d) placés n'importe où dans une chaîne. Par ailleurs, le modèle définit un sous-modèle entouré de parenthèses ;
  • ligne 12 : le modèle /(\d+)/ est comparé à la chaîne "xyz1234abcd". On voit que la sous-chaîne 1234 correspond au modèle. On aura donc [$champs[0]] égal à "1234". Par ailleurs, le modèle a un sous-modèle entre parenthèses. On aura [$champs[1]="1234"] ;
  • ligne 13 : le modèle /(\d+)/ est comparé à la chaîne "12 34". On voit que les sous-chaînes 12 et 34 correspondent au modèle. La comparaison s'arrête à la première sous-chaîne correspondant au modèle. On aura donc [$champs[0]=12] et [$champs[1]=12] ;
  • ligne 14 : le modèle /(\d+)/ est comparé à la chaîne "abcd". Aucune correspondance n'est trouvée ;
  • ligne 51 : [preg_match] rend un entier (et [false] en cas d'erreur dans le modèle) : on le compare strictement à 1 .

Explicitons les modèles utilisés dans la suite du code :

$modèle = "/^(.*?)(\d+)(.*?)$/";

correspond à début de chaîne (^), puis 0 ou plusieurs (*) caractères quelconques (.) puis 1 ou plusieurs (+) chiffres, puis de nouveau 0 ou plusieurs (*) caractères quelconques (.). Le modèle (.*) désigne 0 ou plusieurs caractères quelconques. Un tel modèle va correspondre à n'importe quelle chaîne. Ainsi, dans le modèle /^(.*)(\d+)(.*)$/, le premier sous-modèle (.*) absorberait tous les chiffres sauf le dernier, le seul laissé au sous-modèle (\d+). Le modèle (.*?)(\d+) désigne lui 0 ou plusieurs caractères quelconques jusqu'au sous-modèle suivant (?), ici \d+. Donc les chiffres ne sont maintenant plus absorbés par le modèle (.*). Le modèle ci-dessus correspond donc à [début de chaîne (^), une suite de caractères quelconques (.*?), une suite d'un ou plusieurs chiffres (\d+), une suite de caractères quelconques (.*?), la fin de la chaîne ($)].

$modèle = "/^\s*(\d\d)\/(\d\d)\/(\d\d)\s*$/";

correspond à [début de chaîne (^), une suite de 0 ou plusieurs espaces (\s*), 2 chiffres (\d\d), le caractère / (\/), 2 chiffres, /, 2 chiffres, une suite de 0 ou plusieurs espaces (\s*), la fin de chaîne ($)].

$modèle = "/^\s*([+-]?)\s*(\d+\.\d*|\.\d+|\d+)\s*/";

correspond à [début de chaîne (^), 0 ou plusieurs espaces (\s*), un signe + ou - ([+-]) présent 0 ou 1 fois (?), une suite de 0 ou plusieurs espaces (\s*), 1 ou plusieurs chiffres suivis d'un point décimal suivi de zéro ou plusieurs chiffres (\d+\.\d*) ou (|) un point décimal (\.) suivi d'un ou plusieurs chiffres (\d+) ou (|) un ou plusieurs chiffres (\d+), une suite de 0 ou plusieurs espaces (\s*)]. Le cours PHP 7 écrivait [+|-] : entre crochets, le caractère | n'a pas de signification particulière, et ce modèle acceptait aussi le signe | devant un nombre. [+-] est l'écriture correcte.

Note : le terme [espace] dans les expressions régulières désigne un ensemble de caractères : blanc, saut de ligne \n, tabulation \t, retour chariot \r, saut de page \f…

3.8. Les fonctions

3.8.1. Mode de passage des paramètres

Le script [bases-12.php] est le suivant :

<?php

// mode de passage des paramètres d'une fonction
// respect strict du type des paramètres
declare(strict_types=1);

function f(int &$i, int $j): void
{
    // $i sera obtenu par référence
    // $j sera obtenu par valeur
    $i++;
    $j++;
    print "f[i,j]=[$i,$j]\n";
}

// tests
$i = 0;
$j = 0;
// $i et $j sont passés à la fonction f
f($i, $j);
print "test[i,j]=[$i,$j]\n";

Résultats :

1
2
3
> php bases-12.php
f[i,j]=[1,1]
test[i,j]=[1,0]

Commentaires :

Le code ci-dessus montre les deux modes de passage de paramètres à une fonction. Prenons l'exemple suivant :

1
2
3
4
5
6
7
8
9
function f(int &$a, int $b): void
{
    …
}

// programme principal
$i = 10;
$j = 20;
f($i, $j);
  • ligne 1 : définit les paramètres formels [$a] et [$b] de la fonction [f]. Celle-ci manipule ces deux paramètres formels ;
  • ligne 9 : appel de la fonction [f] avec deux paramètres effectifs [$i] et [$j]. Les liens entre les paramètres formels ([$a], [$b]) et les paramètres effectifs ([$i], [$j]) sont définis par les lignes 1 et 9 :
    • [&$a] : le signe & indique que le paramètre formel [$a] prendra pour valeur l'adresse du paramètre effectif [$i]. Dit autrement, [$a] et [$i] sont deux références sur un même emplacement mémoire. Manipuler le paramètre formel [$a] revient à manipuler le paramètre effectif [$i]. C'est ce que montre l'exécution du code. Ce mode de passage convient aux paramètres de sortie. On appelle ce mode de passage, passage par référence ;
    • [$b] : le paramètre formel [$b] prendra pour valeur celle du paramètre effectif [$j]. C'est un passage par valeur. Les paramètres formels et effectifs sont deux variables différentes. Manipuler le paramètre formel [$b] n'a aucune incidence sur le paramètre effectif [$j]. C'est ce que montre l'exécution du code. Ce mode de passage convient aux paramètres d'entrée .

Soit la fonction [échange] qui admet deux paramètres formels [$a] et [$b]. La fonction échange la valeur de ces deux paramètres. Ainsi lors d'un appel [échange($i, $j)], le code appelant s'attend à ce que les valeurs des deux paramètres effectifs soient échangées. Ce sont donc des paramètres de sortie (ils sont modifiés). On écrira donc :

1
2
3
4
function échange(mixed &$a, mixed &$b): void
{
    [$a, $b] = [$b, $a];
}

Note : on pourrait penser que le passage par référence convient aussi aux données volumineuses, tableaux et dictionnaires, pour éviter leur copie. C'est inutile : PHP ne copie réellement un tableau passé par valeur que si la fonction le modifie. On réservera donc le passage par référence aux paramètres de sortie.

Le script suivant [bases-13.php] montre d'autres exemples :

<?php

// mode de passage des paramètres
// pas de declare(strict_types=1) dans ce script : on est en mode « coercitif »

function f(&$i, $j)
{
    // $i sera obtenu par référence
    // $j sera obtenu par valeur
    $i++;
    $j++;
    print "f[i,j]=[$i,$j]\n";
}

function g(int &$i, int $j): void
{
    // $i sera obtenu par référence
    // $j sera obtenu par valeur
    $i++;
    $j++;
    print "g[i,j]=[$i,$j]\n";
}

// tests
$i = 0;
$j = 0;
// $i et $j sont passés à la fonction f
f($i, $j);
print "test[i,j]=[$i,$j]\n";
// $i et $j sont passés à la fonction g
g($i, $j);
print "test[i,j]=[$i,$j]\n";
// on passe des paramètres de type float à f
$a = 5.0;
$b = 6.2;
f($a, $b);
print "test[a,b]=[$a,$b]\n";
// on passe des paramètres de type float à g
$a = 5.0;
$b = 6.0;
g($a, $b);
print "test[a,b]=[$a,$b]\n";

Commentaires :

  • ligne 4 : contrairement à tous les autres scripts du cours, ce script n'a pas l'instruction [declare(strict_types=1)] : PHP fonctionne en mode « coercitif » ;
  • lignes 6-13 : la fonction [f] étudiée dans le paragraphe précédent mais on n'a pas typé les paramètres ;
  • lignes 15-22 : la fonction [g] fait la même chose que la fonction [f] mais on précise le type des paramètres attendus. On attend deux paramètres de type [int]. On veut voir ce qui se passe lorsque le paramètre effectif passé à la fonction n'a pas le type attendu par celle-ci ;
  • lignes 25-26 : [$i] et [$j] sont deux entiers ;
  • ligne 28 : appel de la fonction [f] avec des paramètres du type attendu ;
  • ligne 31 : appel de la fonction [g] avec des paramètres du type attendu ;
  • lignes 34-35 : les variables [$a] et [$b] sont de type [float] ;
  • ligne 36 : appel de la fonction [f] avec des paramètres de type [float] ;
  • lignes 39-41 : appel de la fonction [g] avec des paramètres de type [float], qui n'est pas le type attendu .

Résultats :

1
2
3
4
5
6
7
8
9
> php bases-13.php
f[i,j]=[1,1]
test[i,j]=[1,0]
g[i,j]=[2,1]
test[i,j]=[2,0]
f[i,j]=[6,7.2]
test[a,b]=[6,6.2]
g[i,j]=[6,7]
test[a,b]=[6,6]
  • la fonction [f], dont les paramètres ne sont pas typés, a accepté les deux paramètres de type [float] et a travaillé avec : [$i] vaut 6.0, affiché 6, et [$j] vaut 7.2 ;
  • la fonction [g] a accepté les deux paramètres de type [float] mais elle les a transformés en type [int]. On le voit à la variable [$a], passée par référence, qui est devenue l'entier 6 .

Les réels 5.0 et 6.0 n'ont pas de partie décimale : leur conversion en entier ne perd aucune information. Que se passerait-il avec le réel 5.3 ? En PHP 7, il était silencieusement converti en l'entier 5. Depuis PHP 8.1, cette conversion qui perd de l'information est signalée comme dépréciée : elle deviendra une erreur dans une version future de PHP.

Faisons maintenant la même chose en mode strict. Le script [bases-13b.php] est identique au script [bases-13.php] si ce n'est qu'il commence par l'instruction :

declare(strict_types=1);

Cette instruction indique que les types des paramètres formels doivent être respectés. Si ce n'est pas le cas, une erreur est signalée. Les résultats de l'exécution deviennent alors :

> php bases-13b.php
f[i,j]=[1,1]
test[i,j]=[1,0]
g[i,j]=[2,1]
test[i,j]=[2,0]
f[i,j]=[6,7.2]
test[a,b]=[6,6.2]

Fatal error: Uncaught TypeError: g(): Argument #1 ($i) must be of type int, float given, called in .../php8-oct-2026/scripts-console/bases/bases-13b.php on line 42 and defined in .../php8-oct-2026/scripts-console/bases/bases-13b.php:16
Stack trace:
#0 .../php8-oct-2026/scripts-console/bases/bases-13b.php(42): g(5.0, 6.0)
#1 {main}
  thrown in .../php8-oct-2026/scripts-console/bases/bases-13b.php on line 16

L'interpréteur PHP a lancé une erreur [TypeError] pour indiquer que le 1er paramètre passé à la fonction [g] n'avait pas le bon type : g(): Argument #1 ($i) must be of type int, float given. Le message indique la ligne de l'appel fautif (42) et celle de la définition de la fonction (16). Comme cette erreur n'est pas interceptée (il n'y a pas de [try / catch]), le script s'arrête. Il est recommandé d'être strict sur les types des paramètres, chaque fois que c'est possible, pour détecter les erreurs d'appel de fonctions : c'est pourquoi tous les scripts du cours commencent par [declare(strict_types=1)].

3.8.2. Les paramètres des fonctions en PHP 8

PHP 8 a beaucoup enrichi la déclaration et l'appel des fonctions. Le script [bases-14.php] présente ces nouveautés :

<?php

// les paramètres des fonctions en PHP 8
declare(strict_types=1);

// 1 - paramètres avec valeur par défaut
function salutation(string $nom, string $formule = "Bonjour", bool $majuscules = false): string
{
    $message = "$formule $nom";
    return $majuscules ? strtoupper($message) : $message;
}

print salutation("Pierre") . "\n";
print salutation("Pierre", "Bonsoir") . "\n";
// 2 - arguments nommés (PHP 8.0) : on peut sauter un paramètre et les donner dans n'importe quel ordre
print salutation("Pierre", majuscules: true) . "\n";
print salutation(majuscules: true, formule: "Salut", nom: "Paul") . "\n";
// les fonctions prédéfinies acceptent aussi les arguments nommés
print str_pad(string: "7", length: 3, pad_string: "0", pad_type: STR_PAD_LEFT) . "\n";

// 3 - type union (PHP 8.0) : le paramètre peut être de l'un des types listés
function double(int|float $nombre): int|float
{
    return 2 * $nombre;
}

var_dump(double(4));
var_dump(double(4.5));

// 4 - type nullable : ?string est équivalent à string|null
function longueur(?string $chaîne): int
{
    // l'opérateur ?? rend son opérande droit si l'opérande gauche est null
    return strlen($chaîne ?? "");
}

print "longueur(\"abc\")=" . longueur("abc") . ", longueur(null)=" . longueur(null) . "\n";

// 5 - type mixed (PHP 8.0) et expression match (PHP 8.0)
function décrire(mixed $valeur): string
{
    // match compare avec === et rend une valeur
    return match (true) {
        is_int($valeur), is_float($valeur) => "le nombre $valeur",
        is_string($valeur) => "la chaîne [$valeur]",
        is_bool($valeur) => "le booléen " . ($valeur ? "true" : "false"),
        is_array($valeur) => "un tableau de " . count($valeur) . " élément(s)",
        default => "une valeur de type " . get_debug_type($valeur),
    };
}

foreach ([12, 3.5, "abc", false, [1, 2], null] as $valeur) {
    print décrire($valeur) . "\n";
}

// match sur une valeur
$enfants = 3;
$parts = match ($enfants) {
    0 => 0.0,
    1, 2 => $enfants * 0.5,
    default => 1 + ($enfants - 2) * 1.0,
};
print "$enfants enfant(s) apportent $parts part(s)\n";

// 6 - nombre variable de paramètres (variadique) et opérateur ... (spread)
function somme(int|float ...$nombres): int|float
{
    // $nombres est un tableau
    return array_sum($nombres);
}

print "somme()=" . somme() . "\n";
print "somme(1, 2, 3)=" . somme(1, 2, 3) . "\n";
$valeurs = [10, 20, 30.5];
// le tableau est « étalé » en paramètres effectifs
print "somme(...\$valeurs)=" . somme(...$valeurs) . "\n";
// l'opérateur ... permet aussi de fusionner des tableaux
$tous = [0, ...$valeurs, 40];
print "tous=[" . implode(",", $tous) . "]\n";

Résultats :

> php bases-14.php
Bonjour Pierre
Bonsoir Pierre
BONJOUR PIERRE
SALUT PAUL
007
int(8)
float(9)
longueur("abc")=3, longueur(null)=0
le nombre 12
le nombre 3.5
la chaîne [abc]
le booléen false
un tableau de 2 élément(s)
une valeur de type null
3 enfant(s) apportent 2 part(s)
somme()=0
somme(1, 2, 3)=6
somme(...$valeurs)=60.5
tous=[0,10,20,30.5,40]

Commentaires :

  • ligne 7 : les paramètres [$formule] et [$majuscules] ont une valeur par défaut. Ils peuvent être omis lors de l'appel (ligne 13), ils prennent alors leur valeur par défaut ;
  • ligne 10 : l'opérateur ternaire [condition ? valeur1 : valeur2] rend [valeur1] si la condition est vraie, [valeur2] sinon ;
  • ligne 16 : les arguments nommés (PHP 8.0). Le paramètre effectif est précédé du nom du paramètre formel suivi de :. On peut ainsi donner une valeur au 3e paramètre sans donner le 2e, qui garde sa valeur par défaut ;
  • ligne 17 : avec des arguments nommés, l'ordre des paramètres n'a plus d'importance. Le code y gagne en lisibilité : [majuscules: true] est plus parlant qu'un [true] isolé ;
  • ligne 19 : les fonctions prédéfinies de PHP acceptent aussi les arguments nommés. Leurs noms sont ceux de la documentation de php.net ;
  • ligne 22 : le type union [int|float] (PHP 8.0) indique que le paramètre peut être un [int] ou un [float]. Il en est de même pour le résultat ;
  • ligne 31 : le type [?string] est un type nullable : le paramètre est une chaîne ou la valeur [null]. C'est une écriture abrégée de [string|null]. Attention : en PHP 7, on écrivait souvent [string $chaîne = null] pour obtenir le même effet. Cette écriture (« nullable implicite ») est dépréciée depuis PHP 8.4 : il faut écrire [?string $chaîne = null] ;
  • ligne 34 : l'opérateur [??] déjà rencontré : si [$chaîne] vaut [null], on utilise la chaîne vide. En effet, depuis PHP 8.1, passer [null] à un paramètre non nullable d'une fonction prédéfinie, comme ici [strlen(null)], est déprécié ;
  • ligne 40 : le paramètre de type [mixed] accepte toute valeur, y compris [null] ;
  • lignes 43-49 : l'expression [match] (PHP 8.0). Elle compare la valeur entre parenthèses aux valeurs à gauche des flèches => et rend la valeur à droite de la première flèche dont la comparaison réussit. Avec [match (true)], on écrit à gauche des conditions : la première condition vraie l'emporte. Plusieurs valeurs peuvent être séparées par des virgules (ligne 44). [default] désigne tous les autres cas ;
  • ligne 48 : la fonction [get_debug_type] (PHP 8.0) rend le nom du type d'une valeur tel qu'on l'écrit dans le code : int, float, string, null, ou le nom de la classe d'un objet. [gettype] rendait, lui, des noms historiques comme integer ou double ;
  • lignes 58-62 : [match] appliqué à une valeur. Ici la variable [$enfants] est comparée aux valeurs 0, puis 1 et 2, sinon c'est le cas [default]. Contrairement à l'instruction [switch] que [match] remplace avantageusement, la comparaison se fait avec [===], [match] est une expression qui rend une valeur, et il n'y a pas d'instruction [break] à oublier. Si aucune branche ne convient et qu'il n'y a pas de [default], [match] lance une erreur [UnhandledMatchError] ;
  • ligne 66 : [int|float ...$nombres] est un paramètre variadique : la fonction accepte un nombre quelconque de paramètres effectifs (même aucun), que PHP rassemble dans le tableau [$nombres]. Un paramètre variadique est toujours le dernier ;
  • ligne 76 : l'opérateur [...] (spread) placé devant un tableau dans un appel « étale » le tableau : ses éléments deviennent les paramètres effectifs de la fonction. [somme(...$valeurs)] est équivalent à [somme(10, 20, 30.5)] ;
  • ligne 78 : le même opérateur permet d'insérer les éléments d'un tableau dans un autre tableau .

3.8.3. Résultats rendus par une fonction

Le script [bases-15.php] est le suivant :

<?php

// types en mode strict
declare(strict_types=1);

// résultats rendus par une fonction
// une fonction peut rendre plusieurs valeurs dans un tableau
[$res1, $res2, $res3] = f1(10);
print "[$res1,$res2,$res3]\n";
$res = f1(10);
for ($i = 0; $i < count($res); $i++) {
    print "f1 : res[$i]=$res[$i]\n";
}
// un tableau associatif peut aussi être « déstructuré »
["min" => $min, "max" => $max] = minMax([4, -2, 17, 8]);
print "minMax : [min=$min,max=$max]\n";

// une fonction peut rendre un objet
$res = f2(10);
print "f2 : [$res->res1,$res->res2,$res->res3]\n";
// objet de quelle nature ?
print "nature de l'objet : ";
var_dump($res);

// on fait la même chose avec la fonction f3
$res = f3(10);
print "f3 : [$res->res1,$res->res2,$res->res3]\n";
// objet de quelle nature ?
print "nature de l'objet : ";
var_dump($res);

// une fonction peut rendre un objet ou null
$res = f4(10);
// l'opérateur nullsafe ?-> rend null au lieu de provoquer une erreur si $res est null
print "f4(10) : res1=" . ($res?->res1 ?? "aucun") . "\n";
$res = f4(-10);
print "f4(-10) : res1=" . ($res?->res1 ?? "aucun") . "\n";

// fin
exit;

// fonction f1
function f1(int $valeur): array
{
    // rend un tableau ($valeur+1,$valeur+2,$valeur+3)
    return [$valeur + 1, $valeur + 2, $valeur + 3];
}

// fonction minMax
function minMax(array $nombres): array
{
    // rend un tableau associatif
    return ["min" => min($nombres), "max" => max($nombres)];
}

// fonction f2
function f2(int $valeur): object
{
    // rend un objet ($valeur+1,$valeur+2,$valeur+3)
    // obtenu en transformant un tableau associatif en objet
    return (object) ["res1" => $valeur + 1, "res2" => $valeur + 2, "res3" => $valeur + 3];
}

// fonction f3 - fait la même chose que la fonction f2
function f3(int $valeur): object
{
    // rend un objet ($valeur+1,$valeur+2,$valeur+3)
    $res = new stdClass();
    $res->res1 = $valeur + 1;
    $res->res2 = $valeur + 2;
    $res->res3 = $valeur + 3;
    // rend l'objet
    return $res;
}

// fonction f4 - rend un objet si $valeur est positive, null sinon
function f4(int $valeur): ?object
{
    return $valeur >= 0 ? f3($valeur) : null;
}

Résultats :

> php bases-15.php
[11,12,13]
f1 : res[0]=11
f1 : res[1]=12
f1 : res[2]=13
minMax : [min=-2,max=17]
f2 : [11,12,13]
nature de l'objet : object(stdClass)#1 (3) {
  ["res1"]=>
  int(11)
  ["res2"]=>
  int(12)
  ["res3"]=>
  int(13)
}
f3 : [11,12,13]
nature de l'objet : object(stdClass)#2 (3) {
  ["res1"]=>
  int(11)
  ["res2"]=>
  int(12)
  ["res3"]=>
  int(13)
}
f4(10) : res1=11
f4(-10) : res1=aucun

Commentaires :

  • le programme précédent montre qu'une fonction PHP peut rendre un ensemble de résultats et non un seul, sous la forme d'un tableau ou d'un objet. La notion d'objet est explicitée dans un chapitre ultérieur ;
  • lignes 43-47 : la fonction [f1] rend plusieurs valeurs sous la forme d'un tableau ([array]) ;
  • ligne 8 : le tableau rendu est déstructuré dans trois variables ;
  • ligne 15 : la déstructuration fonctionne aussi avec un tableau associatif : on indique pour chaque variable la clé de la valeur à récupérer. La fonction [minMax] rend en effet un dictionnaire (ligne 53). Les fonctions prédéfinies [min] et [max] rendent le plus petit et le plus grand élément d'un tableau ;
  • lignes 57-62 : la fonction [f2] rend plusieurs valeurs sous la forme d'un objet ([object]). Elle l'obtient en convertissant un tableau associatif en objet avec l'opérateur [(object)] : chaque clé du tableau devient une propriété de l'objet ;
  • lignes 65-74 : la fonction [f3] fait la même chose que la fonction [f2] mais elle crée explicitement un objet de type [stdClass] (ligne 68), puis lui ajoute des propriétés. L'attribut A d'un objet O est noté [$O->A] ;
  • ligne 23 : la fonction [var_dump] donne accès à la nature de l'objet et à son contenu. Dans les résultats, on voit que l'objet est de type [stdClass], que sa propriété [res1] est de type entier et a la valeur 11, etc. Le numéro #1 ou #2 qui suit le type est un numéro interne d'objet ;
  • ligne 77 : le type de résultat [?object] indique que la fonction [f4] rend un objet ou [null] ;
  • lignes 35-37 : l'opérateur nullsafe [?->] (PHP 8.0). Si [$res] vaut [null], l'expression [$res?->res1] vaut [null] au lieu de provoquer une erreur ; l'opérateur [??] remplace alors [null] par "aucun". Ici l'opérateur [??] suffirait à lui seul, car il ne signale pas l'absence de la propriété. L'opérateur [?->] montrera tout son intérêt avec les objets et leurs méthodes : [$client?->getAdresse()?->getVille()] .

Note : le cours PHP 7 présentait une fonction qui écrivait [$res->res1 = ...] sans avoir créé l'objet [$res]. PHP 7 créait alors un objet [stdClass] par défaut avec un simple avertissement. Depuis PHP 8.0, c'est une erreur fatale (Attempt to assign property on null) : un objet doit toujours être créé avant d'être utilisé.

3.8.4. Les fonctions sont aussi des valeurs

En PHP, une fonction peut être rangée dans une variable, passée en paramètre à une autre fonction ou rendue comme résultat. Le script [bases-17.php] montre les différentes façons d'écrire et d'utiliser ces fonctions :

<?php

// les fonctions sont aussi des valeurs
declare(strict_types=1);

$nombres = [4, -2, 17, 8, -5];

// 1 - une fonction anonyme affectée à une variable
$carré = function (int $x): int {
    return $x * $x;
};
print "carré(5)=" . $carré(5) . "\n";

// 2 - une fonction anonyme passée en paramètre à une fonction prédéfinie
// array_map applique la fonction à chaque élément du tableau
$carrés = array_map($carré, $nombres);
print "carrés=[" . implode(",", $carrés) . "]\n";

// 3 - une fonction anonyme utilisant une variable extérieure : mot clé use
$seuil = 5;
$grands = array_filter($nombres, function (int $x) use ($seuil): bool {
    return $x > $seuil;
});
print "nombres > $seuil : [" . implode(",", $grands) . "]\n";

// 4 - la fonction fléchée (PHP 7.4) : une seule expression, variables extérieures capturées automatiquement
$positifs = array_filter($nombres, fn(int $x): bool => $x >= 0);
print "positifs=[" . implode(",", $positifs) . "]\n";
// tri avec une fonction de comparaison : l'opérateur <=> rend -1, 0 ou 1
$triés = $nombres;
usort($triés, fn(int $a, int $b): int => $b <=> $a);
print "tri décroissant=[" . implode(",", $triés) . "]\n";

// 5 - recherche dans un tableau (PHP 8.4)
print "premier négatif : " . array_find($nombres, fn(int $x): bool => $x < 0) . "\n";
print "au moins un > 10 : " . var_export(array_any($nombres, fn(int $x): bool => $x > 10), true) . "\n";
print "tous > -10 : " . var_export(array_all($nombres, fn(int $x): bool => $x > -10), true) . "\n";

// 6 - la syntaxe callable de première classe (PHP 8.1) : nomFonction(...)
$majuscules = strtoupper(...);
print $majuscules("bonjour") . "\n";
$noms = ["dupont", "durand", "martin"];
print "noms=[" . implode(",", array_map(ucfirst(...), $noms)) . "]\n";

// 7 - l'opérateur pipe |> (PHP 8.5) : le résultat de gauche est passé à la fonction de droite
$résultat = "  Bonjour tout le monde  "
    |> trim(...)
    |> strtolower(...)
    |> (fn(string $s): string => str_replace(" ", "-", $s));
print "résultat=[$résultat]\n";
// équivalent sans l'opérateur pipe
$résultat = str_replace(" ", "-", strtolower(trim("  Bonjour tout le monde  ")));
print "résultat=[$résultat]\n";

Résultats :

> php bases-17.php
carré(5)=25
carrés=[16,4,289,64,25]
nombres > 5 : [17,8]
positifs=[4,17,8]
tri décroissant=[17,8,4,-2,-5]
premier négatif : -2
au moins un > 10 : true
tous > -10 : true
BONJOUR
noms=[Dupont,Durand,Martin]
résultat=[bonjour-tout-le-monde]
résultat=[bonjour-tout-le-monde]

Commentaires :

  • lignes 9-11 : une fonction anonyme (sans nom) est affectée à la variable [$carré]. On remarquera le ; final : c'est une affectation ;
  • ligne 12 : la fonction est appelée via la variable ;
  • ligne 16 : la fonction prédéfinie [array_map] applique la fonction passée en 1er paramètre à chaque élément du tableau passé en 2e paramètre, et rend le tableau des résultats ;
  • lignes 21-23 : la fonction prédéfinie [array_filter] rend les éléments du tableau pour lesquels la fonction passée en 2e paramètre rend [true]. Une fonction anonyme ne connaît pas les variables du code qui l'entoure : le mot clé [use ($seuil)] lui donne accès à la variable [$seuil]. On notera que [array_filter] conserve les clés des éléments retenus ; c'est sans effet ici car [implode] n'utilise que les valeurs ;
  • ligne 27 : une fonction fléchée (PHP 7.4) : [fn(paramètres) => expression]. Elle ne contient qu'une expression, dont la valeur est le résultat de la fonction. Elle a accès automatiquement (en lecture) aux variables du code qui l'entoure : pas besoin de [use]. C'est l'écriture la plus concise pour les petites fonctions passées en paramètre ;
  • ligne 31 : la fonction [usort] trie un tableau à l'aide d'une fonction de comparaison qui reçoit deux éléments [$a] et [$b] et rend un nombre négatif, nul ou positif selon que [$a] doit être placé avant, au même rang ou après [$b]. L'opérateur [<=>] (« vaisseau spatial ») rend précisément -1, 0 ou 1. En écrivant [$b <=> $a] au lieu de [$a <=> $b], on obtient un tri décroissant. On remarquera que [usort] modifie le tableau qu'on lui passe (passage par référence) : on a donc trié une copie [$triés] du tableau [$nombres] ;
  • ligne 35 : les fonctions [array_find], [array_any] et [array_all] sont apparues avec PHP 8.4. [array_find] rend le premier élément pour lequel la fonction rend [true] (ou [null] s'il n'y en a pas), [array_any] rend [true] si au moins un élément vérifie la condition, [array_all] si tous la vérifient ;
  • ligne 40 : la syntaxe callable de première classe (PHP 8.1) : [nomFonction(...)], avec réellement trois points entre les parenthèses, ne fait pas appel à la fonction mais rend la fonction elle-même, sous la forme d'un objet [Closure]. Elle peut alors être utilisée comme une fonction anonyme ;
  • ligne 43 : la fonction prédéfinie [ucfirst] (première lettre en majuscule) est passée à [array_map] ;
  • lignes 46-49 : l'opérateur pipe [|>] (PHP 8.5). L'expression [valeur |> fonction] appelle la fonction avec la valeur comme unique paramètre. Les appels s'enchaînent de gauche à droite, dans l'ordre où on les lit : on enlève les espaces de début et de fin avec [trim], on passe en minuscules avec [strtolower], puis on remplace les espaces par des tirets. Une fonction fléchée utilisée avec [|>] doit être entourée de parenthèses ;
  • ligne 52 : la même chose sans l'opérateur pipe. Les fonctions s'emboîtent et se lisent de l'intérieur vers l'extérieur, à l'envers de l'ordre d'exécution .

3.9. Les fichiers texte

Le script [bases-16.php] est le suivant :

<?php

// respect strict du type des paramètres des fonctions
declare(strict_types=1);

// exploitation séquentielle d'un fichier texte
// celui-ci est un ensemble de lignes de la forme login:pwd:uid:gid:infos:dir:shell
// chaque ligne est mise dans un dictionnaire sous la forme login => [uid, gid, infos, dir, shell]
// on fixe le nom du fichier
$INFOS = "infos.txt";
// on l'ouvre en création
$fic = fopen($INFOS, "w");
if ($fic === false) {
    print "Erreur d'ouverture du fichier $INFOS en écriture\n";
    exit;
}
// on génère un contenu arbitraire
for ($i = 0; $i < 100; $i++) {
    fputs($fic, "login$i:pwd$i:uid$i:gid$i:infos$i:dir$i:shell$i\n");
}
// on ferme le fichier
fclose($fic);

// on l'exploite - fgets garde la marque de fin de ligne
// cela permet de ne pas récupérer une chaîne vide lors de la lecture d'une ligne blanche
// on l'ouvre en lecture
$fic = fopen($INFOS, "r");
if ($fic === false) {
    print "Erreur d'ouverture du fichier $INFOS en lecture\n";
    exit;
}

// le dictionnaire des informations
$dico = [];
// les lignes font moins de 1000 caractères
// la lecture de la ligne s'arrête sur la marque de fin de ligne
// ou celle de fin de fichier
while (($ligne = fgets($fic, 1000)) !== false) {
    // on supprime la marque de fin de ligne si elle existe
    $ligne = cutNewLineChar($ligne);
    // on met la ligne dans un tableau
    $infos = explode(":", $ligne);
    // on récupère le login
    $login = array_shift($infos);
    // on néglige le pwd
    array_shift($infos);
    // on crée une entrée dans le dictionnaire
    $dico[$login] = $infos;
}
// on le ferme
fclose($fic);

// exploitation du dictionnaire
afficheInfos($dico, "login10");
afficheInfos($dico, "X");

// une autre façon de lire un fichier texte : la fonction file
$lignes = file($INFOS, FILE_IGNORE_NEW_LINES);
print "le fichier $INFOS a " . count($lignes) . " lignes, la dernière est [" . array_last($lignes) . "]\n";

// fin
exit;

// --------------------------------------------------------------------------
function afficheInfos(array $dico, string $clé): void
{
    // affiche la valeur associée à clé dans le dictionnaire $dico si elle existe
    if (isset($dico[$clé])) {
        // valeur existe - est-ce un tableau ?
        $valeur = $dico[$clé];
        if (is_array($valeur)) {
            print "[$clé," . implode(":", $valeur) . "]\n";
        } else {
            // $valeur n'est pas un tableau
            print "[$clé,$valeur]\n";
        }
    } else {
        // $clé n'est pas une clé du dictionnaire $dico
        print "la clé [$clé] n'existe pas\n";
    }
}

// --------------------------------------------------------------------------
function cutNewLineChar(string $ligne): string
{
    // on supprime la marque de fin de ligne de $ligne si elle existe
    // (\n sous Unix, \r\n sous Windows)
    return rtrim($ligne, "\r\n");
}

Le fichier [infos.txt] créé par le script :

1
2
3
4
5
6
login0:pwd0:uid0:gid0:infos0:dir0:shell0
login1:pwd1:uid1:gid1:infos1:dir1:shell1
login2:pwd2:uid2:gid2:infos2:dir2:shell2
…
login98:pwd98:uid98:gid98:infos98:dir98:shell98
login99:pwd99:uid99:gid99:infos99:dir99:shell99

Les résultats :

1
2
3
4
> php bases-16.php
[login10,uid10:gid10:infos10:dir10:shell10]
la clé [X] n'existe pas
le fichier infos.txt a 100 lignes, la dernière est [login99:pwd99:uid99:gid99:infos99:dir99:shell99]

Commentaires :

  • ligne 12 : [fopen(nom_fichier, "w")] ouvre le fichier [nom_fichier] en écriture (w=write). Si le fichier n'existe pas, il est créé. S'il existe, il est vidé. Le fichier est créé dans le dossier courant, celui depuis lequel on a lancé le script. Si la création échoue, [fopen] émet un avertissement et rend la valeur [false] ;
  • ligne 13 : on teste le résultat de [fopen] en le comparant strictement à [false] ;
  • ligne 19 : [fputs($fic, $chaîne)] écrit [$chaîne] dans le fichier [$fic]. Ici la chaîne se termine par la marque de fin de ligne [\n] ;
  • ligne 22 : [fclose($fic)] ferme le fichier [$fic] ;
  • ligne 27 : [fopen(nom_fichier, "r")] ouvre le fichier [nom_fichier] en lecture (r=read). Si l'ouverture échoue (le fichier n'existe pas par exemple), [fopen] rend la valeur [false] ;
  • ligne 34 : le dictionnaire est initialisé à vide avant d'être rempli. PHP créerait le tableau à la première affectation [$dico[$login] = ...], mais si le fichier était vide, la variable n'existerait pas et son utilisation provoquerait un avertissement ;
  • ligne 38 : [fgets($fic, 1000)] lit la ligne suivante du fichier dans la limite de 1000 caractères. Dans l'opération [while (($ligne = fgets($fic, 1000)) !== false) {...}], il y a deux opérations successives : 1) [$ligne = fgets(...)] 2) [while ($ligne !== false)]. Après que le dernier caractère du fichier a été lu, la fonction [fgets] rend la valeur [false] et la boucle [while] s'arrête. La fonction [fgets] tente ici de lire au plus 1000 caractères mais s'arrête dès qu'une marque de fin de ligne est rencontrée. Ici toutes les lignes ayant moins de 1000 caractères, [fgets] lit une ligne de texte, marque de fin de ligne incluse. Le cours PHP 7 écrivait [while ($ligne = fgets($fic, 1000))] : la boucle se serait arrêtée prématurément sur une dernière ligne réduite à "0", sans marque de fin de ligne, car la chaîne "0" est convertie en [false]. La comparaison stricte [!== false] évite ce piège ;
  • ligne 40 : la fonction [cutNewLineChar] élimine les éventuels caractères de fin de ligne ;
  • ligne 44 : la fonction [array_shift($tableau)] élimine le 1er élément de [$tableau] et le rend comme résultat ;
  • ligne 46 : on néglige ici le résultat rendu par [array_shift] ;
  • ligne 58 : une autre façon de lire un fichier texte. La fonction [file] rend le tableau de toutes les lignes du fichier. Avec l'option [FILE_IGNORE_NEW_LINES], les marques de fin de ligne sont retirées. C'est plus simple que la boucle [fgets], mais tout le fichier est chargé en mémoire : pour un fichier très volumineux, la lecture ligne à ligne reste préférable ;
  • ligne 71 : la fonction [is_array($variable)] rend [true] si [$variable] est un tableau, [false] sinon ;
  • ligne 84 : la marque de fin de ligne est la chaîne "\r\n" sur les systèmes Windows, "\n" sur les systèmes Unix. La fonction [rtrim($chaîne, $caractères)] enlève à la fin de [$chaîne] tous les caractères présents dans [$caractères], ici \r et \n. Sans second paramètre, [rtrim] enlève tous les « espaces » (blanc, tabulation, fin de ligne…) ; la fonction [trim] fait la même chose aux deux extrémités de la chaîne .

3.10. Encodage / décodage JSON

Le dossier [scripts-console/json] contient les scripts de ce paragraphe :

1
2
3
4
5
json
├── famille.json
├── json-01.php
├── json-02.php
└── json-03.php

L'encodage / décodage JSON (JavaScript Object Notation) est quelque chose que nous allons utiliser intensivement dans l'exercice qui sert de fil rouge au document. Les scripts [json-01.php, json-02.php, json-03.php] expliquent ce qui est à savoir pour la suite.

Le script [json-01.php] est le suivant :

<?php

declare(strict_types=1);

$array1 = ["nom" => "séléné", "prénom" => "bénédicte", "âge" => 34];
// encodage JSON du tableau array1 avec caractères Unicode échappés
print "encodage JSON du tableau array1 avec caractères Unicode échappés\n";
$json1 = json_encode($array1);
print "json1=$json1\n";
// encodage JSON du tableau array1 avec caractères Unicode non échappés
print "encodage JSON du tableau array1 avec caractères Unicode non échappés\n";
$json2 = json_encode($array1, JSON_UNESCAPED_UNICODE);
print "json2=$json2\n";
// décodage JSON dans tableau associatif
print "décodage JSON de json2 dans tableau associatif\n";
$array2 = json_decode($json2, true);
var_dump($array2);
foreach ($array2 as $key => $value) {
    print "$key:$value\n";
}
// décodage JSON dans objet
print "décodage JSON de json2 dans objet stdClass\n";
$objet2 = json_decode($json2);
var_dump($objet2);
print "prénom=$objet2->prénom\n";
print "nom=$objet2->nom\n";
print "âge=$objet2->âge\n";

Résultats :

> php json-01.php
encodage JSON du tableau array1 avec caractères Unicode échappés
json1={"nom":"s\u00e9l\u00e9n\u00e9","pr\u00e9nom":"b\u00e9n\u00e9dicte","\u00e2ge":34}
encodage JSON du tableau array1 avec caractères Unicode non échappés
json2={"nom":"séléné","prénom":"bénédicte","âge":34}
décodage JSON de json2 dans tableau associatif
array(3) {
  ["nom"]=>
  string(9) "séléné"
  ["prénom"]=>
  string(11) "bénédicte"
  ["âge"]=>
  int(34)
}
nom:séléné
prénom:bénédicte
âge:34
décodage JSON de json2 dans objet stdClass
object(stdClass)#1 (3) {
  ["nom"]=>
  string(9) "séléné"
  ["prénom"]=>
  string(11) "bénédicte"
  ["âge"]=>
  int(34)
}
prénom=bénédicte
nom=séléné
âge=34

Commentaires :

  • ligne 8 : la fonction [json_encode] transforme son paramètre en chaîne de caractères JSON. Dans la chaîne produite, les caractères accentués é et â ont été remplacés par leur code Unicode qui commence par \u ;
  • ligne 12 : on refait la même chose en demandant cette fois-ci à ce que les caractères Unicode soient conservés tels quels. La chaîne JSON résultante est beaucoup plus lisible ;
  • lignes 16-20 : on fait l'opération inverse. On transforme une chaîne JSON en tableau associatif : c'est le rôle du 2e paramètre [true] de [json_decode]. Les résultats montrent qu'on a bien récupéré un tableau associatif ;
  • lignes 23-27 : sans le paramètre [true], [json_decode] transforme la chaîne JSON en objet de type [stdClass]. L'attribut A d'un objet O est noté [$O->A] .

On peut encoder en JSON des tableaux à plusieurs niveaux, comme le montre le script [json-02.php] suivant :

<?php

declare(strict_types=1);

$array = [
    "nom" => "séléné", "prénom" => "bénédicte", "âge" => 34,
    "mari" => ["nom" => "icariù", "prénom" => "ignacio", "âge" => 35],
    "enfants" => [
        ["prénom" => "angèle", "age" => 8],
        ["prénom" => "andré", "age" => 2],
    ],
];
// encodage JSON du tableau à plusieurs niveaux
print "encodage JSON d'un tableau à plusieurs niveaux\n";
$json = json_encode($array, JSON_UNESCAPED_UNICODE);
print "json=$json\n";
// le même encodage, présenté de façon lisible
print "encodage JSON avec l'option JSON_PRETTY_PRINT\n";
print json_encode($array["mari"], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n";

Résultats :

1
2
3
4
5
6
7
8
9
> php json-02.php
encodage JSON d'un tableau à plusieurs niveaux
json={"nom":"séléné","prénom":"bénédicte","âge":34,"mari":{"nom":"icariù","prénom":"ignacio","âge":35},"enfants":[{"prénom":"angèle","age":8},{"prénom":"andré","age":2}]}
encodage JSON avec l'option JSON_PRETTY_PRINT
{
    "nom": "icariù",
    "prénom": "ignacio",
    "âge": 35
}

Commentaires :

Dans la chaîne JSON :

  • les tableaux non associatifs (les listes) sont entourés de crochets [] ;
  • les tableaux associatifs sont entourés d'accolades {} .

ligne 19 : les options de [json_encode] se combinent avec l'opérateur | (OU binaire). L'option [JSON_PRETTY_PRINT] produit une chaîne JSON indentée sur plusieurs lignes, plus lisible.

Le script [json-03.php] montre comment exploiter le fichier JSON [famille.json] suivant :

{
    "épouse": {
        "nom": "séléné",
        "prénom": "bénédicte",
        "âge": 34
    },
    "mari": {
        "nom": "icariù",
        "prénom": "ignacio",
        "âge": 35
    },
    "enfants": [
        {
            "prénom": "angèle",
            "age": 8
        },
        {
            "prénom": "andré",
            "age": 2
        }
    ]
}

Le script [json-03.php] est le suivant :

<?php

declare(strict_types=1);

// lecture du fichier JSON
$json = file_get_contents("famille.json");
// le contenu lu est-il du JSON valide ? (PHP 8.3)
if ($json === false || !json_validate($json)) {
    print "Le fichier [famille.json] est absent ou ne contient pas du JSON valide\n";
    exit;
}
// décodage JSON en objet
$famille1 = json_decode($json);
print "----famille1\n";
var_dump($famille1);
// décodage JSON en tableau associatif
print "----famille2\n";
$famille2 = json_decode($json, true);
var_dump($famille2);

Commentaires :

  • ligne 6 : la fonction [file_get_contents] lit le contenu du fichier nommé [famille.json] et le met dans la variable [$json]. Elle rend [false] si la lecture échoue ;
  • ligne 8 : la fonction [json_validate] (PHP 8.3) rend [true] si la chaîne passée en paramètre est du JSON valide. Auparavant, il fallait décoder la chaîne puis consulter la fonction [json_last_error] ;
  • lignes 13-19 : la chaîne est ensuite décodée en objet puis en tableau associatif .

Résultats :

> php json-03.php
----famille1
object(stdClass)#2 (3) {
  ["épouse"]=>
  object(stdClass)#1 (3) {
    ["nom"]=>
    string(9) "séléné"
    ["prénom"]=>
    string(11) "bénédicte"
    ["âge"]=>
    int(34)
  }
  ["mari"]=>
  object(stdClass)#3 (3) {
    ["nom"]=>
    string(7) "icariù"
    ["prénom"]=>
    string(7) "ignacio"
    ["âge"]=>
    int(35)
  }
  ["enfants"]=>
  array(2) {
    [0]=>
    object(stdClass)#4 (2) {
      ["prénom"]=>
      string(7) "angèle"
      ["age"]=>
      int(8)
    }
    [1]=>
    object(stdClass)#5 (2) {
      ["prénom"]=>
      string(6) "andré"
      ["age"]=>
      int(2)
    }
  }
}
----famille2
array(3) {
  ["épouse"]=>
  array(3) {
    ["nom"]=>
    string(9) "séléné"
    ["prénom"]=>
    string(11) "bénédicte"
    ["âge"]=>
    int(34)
  }
  ["mari"]=>
  array(3) {
    ["nom"]=>
    string(7) "icariù"
    ["prénom"]=>
    string(7) "ignacio"
    ["âge"]=>
    int(35)
  }
  ["enfants"]=>
  array(2) {
    [0]=>
    array(2) {
      ["prénom"]=>
      string(7) "angèle"
      ["age"]=>
      int(8)
    }
    [1]=>
    array(2) {
      ["prénom"]=>
      string(6) "andré"
      ["age"]=>
      int(2)
    }
  }
}
  • la première partie des résultats montre l'objet issu du décodage du fichier JSON [famille.json] : les objets JSON sont devenus des objets [stdClass], la liste JSON des enfants est devenue un tableau PHP ;
  • la seconde partie montre le tableau associatif issu du même décodage : tous les objets JSON sont devenus des tableaux associatifs .