Grégory Bourguin
SysReIC - LISIC - ULCO
PHP (Les bases)

PHP

Dans cette partie, nous allons étudier comment créer un Serveur Web Dynamique en utilisant le langage PHP.

PHP est un langage de scripts généraliste qui, lorsqu'il est intégré à un serveur Web (comme Apache), permet de générer dynamiquement des pages web (contenant potentiellement du HTML, CSS et Javascript).

NB: PHP peut aussi être utilisé pour créer une API REST qui génère du JSON.

Environnement de Travail

Pour créer notre serveur web en php, il nous faut un serveur web capable d'exécuter les scripts PHP... De plus, un serveur PHP est généralement couplé à un serveur de base de données comme MySQL.

Pour toutes ces raisons, nous allons utiliser une solution "classique" qui fournit à la fois un serveur Apache, un serveur MysSQL, et divers outils d'administration tels que phpMyAdmin : XAMPP.

Installer XAMPP

Votre premier travail consiste donc à télécharger et installer la (dernière) version de XAMPP qui correspond à votre système d'exploitation.

Pour vérifier que tout est ok, utilisez l'interface de XAMPP pour démarrer les serveurs Apache et MySQL (si ce n'est pas déjà fait). Dans le menu d'administration (Dashboard) vous pouvez cliquer sur le lien PHPInfo (en haut à droite de la page web) et admirer les informations de configuration du serveur Apache, ainsi que sur phpMyAdmin pour accéder au contenu du serveur MySQL.

NB: sous Windows, il faut lancer le programme en tant qu'Administrateur.

Premier Script PHP

Les serveurs Apache vont chercher les fichiers demandés par l'utilisateur dans un dossier qu'on appelle DocumentRoot.

Vous pouvez voir la valeur de DocumentRoot en cliquant PHPInfo dans le DashBoard.


Trouvez le dossier correspondant à DocumentRoot (a priori C:/xampp/htdocs/ sous Windows), et créez un sous dossier nommé web3.

Dans web3 utilisez un éditeur de code (par exemple Atom) et créez le fichier index.php, avec pour contenu :

index.php
<?php 
echo "<h1>PHP dit Hello World</h1>" ; 
?>

Dans un navigateur (sur la machine serveur), allez à l'adresse http://localhost/web3/ .

NB: lorsque seul le nom du dossier est indiqué (ex. http://localhost/web3/), le serveur web renverra automatiquement le fichier nommé index.html, et s'il ne le trouve pas, il cherchera index.php C'est ce que nous faisons ici : demander http://localhost/web3/ revient en réalité à obtenir la page http://localhost/web3/index.php .

Vous devriez obtenir le résultat suivant :

Résultat

PHP dit Hello World

NB: Si cela ne fonctionne pas, il est possible que votre serveur ait été démarré sur un port non standard. Pour le vérifier, regardez l'adresse à laquelle vous aviez ouvert le DashBoard. Si l'adresse contient un chiffre comme dans http://localhost:8080/dashboard, cela signifie que votre serveur sert les pages web sur le port 8080. Vous devez alors aussi indiquer ce port particulier pour consulter vos propres pages (ex. http://localhost:8080/web3/)

Affichage des erreurs

Lorsque vous développez en PHP, il est possible que le serveur Web qui exécute vos pages n'indique pas les erreurs d'exécutions. Dans ce cas, si une erreur survient, le script s'arrête (plante), mais vous n'avez aucune information vous permettant de savoir ce qui s'est passé.

Ce fonctionnement est tout à fait logique lorsque votre site Web est en production : il serait malvenu qu'un site Web affiche des messages d'erreurs incompréhensibles (ou contenant des données sensibles) aux utilisateurs.

Par contre, pendant le développement, avoir le détail de ce qui a mal fonctionné peut largement vous aider dans le but de corriger le problème.

D'une manière générale :
  • En développmement : on laisse PHP afficher les erreurs dans la page.
  • En production : on empêche PHP d'envoyer ses messages d'erreur à l'utilisateur.

Dans ce cours nous serons en mode développement : si le PHP de votre serveur Web n'affiche pas les erreurs d'un de vos scripts, vous pouvez ajouter ces quelques lignes au tout début :

<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
?>

NB : au lieux d'ajouter ces lignes au début de chaque script qu'on veut tester, il est possible de demander au serveur Web d'afficher/cacher les erreurs en modifiant ses fichiers de configuration (ex. php.ini).

PHP + HTML/CSS/Javascript

Dans l'exemple précédent, vous pouvez constater que le résultat obtenu correspond (uniquement) au contenu de la chaine passée en paramètre de la fonction php echo.

Si vous demandez au navigateur d'afficher le code source de la page, vous pourrez remarquer que le code php a totalement disparu : il ne reste plus que le code html généré par l'appel à echo.

Lorsqu'on demande une page .php à un serveur web, le serveur envoie le fichier à l'interpréteur PHP (ou préprocesseur) et tout le code php qui se trouve entre les balises <?php et ?> est exécuté et remplacé dans la page web par son résultat.

L'interpréteur PHP ne comprend que le code php : tout code qui n'est pas du code php (ex. HTML, CSS, Javascript) est recopié à l'identique.

C'est le résultat de ce processus (le code interprété, résultat du pré-processing) qui est envoyé par le serveur en réponse au client (le navigateur).


On peut donc avoir des fichiers .php tels que : http://localhost/web3/01_balises.php
<html>
<head>
    <title>Exemple <?php echo " de pré-processing PHP" ?></title>
    <style>
        .bleu{
            color: <?php echo "blue" ?>;
        }
    </style>
</head>
<body>
    <h2 class="bleu">
        Voici un exemple de <span style="color:red"><?php echo "code généré par PHP" ?></span>
    </h2>
</body>
</html>
Résultat : le code dans les 3 balises <?php ... ?> a été interprété/remplacé.
Exemple de pré-processing PHP

Voici un exemple de code généré par PHP

Il est très important de bien comprendre que, comme dans cet exemple, le code php peut générer du HTML, du CSS et/ou du Javascript, mais il ne peut pas interagir avec ces langages : en effet, les scripts PHP sont traités exclusivement sur le serveur, alors que les autre langages s'exécutent sur le client, c.a.d. dans le navigateur.
Ils ne partagent donc aucunement le même contexte d'exécution (variables, etc.).

Les Bases du Langage

Code PHP

Le code php doit être écrit entre les balises <?php et ?>.
Chaque instruction doit se terminer par un ; .

Comme nous venons de le voir, tout ce qui est en dehors de ces balises est ignoré par PHP (ou plutôt, laissé tel quel).

La balise fermante ?> ne signifie pas la fin d'un script : l'interpréteur continuera son exécution avec le même contexte (variables, fonctions, etc.) dès qu'il rencontrera une nouvelle balise <?php.

Le code est donc exécuté séquentiellement de balise en balise en partageant (faisant évoluer) le même contexte d'exécution.

C'est ce que nous allons voir ci-après, en particulier avec l'utilisation de variables.

Sorties en PHP : echo

C'est l'instruction echo qui sert principalement à faire des sorties dans une page :

<?php 
echo "<p>Voici une sortie en PHP</p>" ; 
?>
Résultat

Voici une sortie en PHP

Notez que <?= est une variante courte de <?php echo :

<?= "<p>Voici une autre sortie en PHP</p>" ?>
Résultat

Voici une autre sortie en PHP

Les Variables

Les variables php doivent être déclarées avec le pattern $nom_variable.
Leur portée est limitée au bloc dans lequel elles sont déclarées.

Pour plus de détails : https://www.php.net/manual/fr/language.variables.basics.php

Le typage est dynamique. Il n'est pas obligatoire d'initialiser la valeur d'une variable, mais c'est fortement conseillé.

http://localhost/web3/02_variable.php
<?php
    $ma_variable = 5 ; // déclaration|affectation de variable
?>
<p>
    Voici la valeur de ma variable : <?= $ma_variable ?> 
</p>
<?php $ma_variable *= 2 ?>
<p>
    Et la voici après avoir été modifiée : <?= $ma_variable ?> 
</p>
Résultat

Voici la valeur de ma variable : 5

Et la voici après avoir été modifiée : 10

NB: Vous pouvez constater que le contexte d'exécution est bien partagé par les balises.

Informations sur une variable : var_dump(...)

Quand on développe un site web en php, il est parfois utile de pouvoir afficher simplement (par ex. pour débugger) les informations concernant une variable.

La fonction var_dump(data) affiche les informations concernant une data.

<?php $ok = false ; ?>
<p>Voici les informations sur $ok : <b><?php var_dump($ok) ?></b></p> 
Résultat

Voici les informations sur $ok : bool(false)

Existence d'une variable : isset / unset

Cela peut sembler bizarre a priori (développer en PHP ne rend pas amnésique), mais nous verrons qu'en PHP, il est souvent pratique de pouvoir vérifier si une variable existe. (Ce sera en particulier le cas lorsque nous aurons à traiter des variables correspondant à des champs de formulaires remplis, ou pas...).

La fonction isset(variable) renvoie true si variable existe et est différente de null, false sinon.
La fonction unset(variable) permet de "détruire" une variable.

<p>La variable existe ? <?php var_dump(isset($prenom)) ?></p>
<?php $prenom = "Greg" ?>
<p>La variable existe ? <?php var_dump(isset($prenom)) ?> : <?= $prenom ?></p>
<?php unset($prenom) ?>
<p>La variable existe ? <?php var_dump(isset($prenom)) ?></p>
Résultat

La variable existe ? bool(false)

La variable existe ? bool(true) : Greg

La variable existe ? bool(false)

Les Chaînes de Caractères : string

Les chaînes de caractères de PHP sont similaires à celles des autres langages.
On pourra cependant noter que l'opérateur de concaténation est ici le . .

<?php
    $greeting = "bonjour" ;
    $fname = "greg" ;
    $message = $greeting . " " . $fname . " !" ;
?>
<p>Le message : <b><?php echo $message ?></b></p>

<?php
    // PHP offre de nombreuse fonctions pour manipuler les string
    $formatted = ucfirst($greeting) . " " . strtoupper($fname) . " !!!" ;
?>
<p>
    Le message formaté : <b><?php echo $formatted ?></b> <br>
    Longueur : <?php echo strlen($formatted) ?> caractère(s) 
</p>
Résultat

Le message : bonjour greg !

Le message formaté : Bonjour GREG !!!
Longueur : 16 caractère(s)

Les Tableaux : array

Rien de tel qu'un petit exemple...

<?php 
    $tab = ['a', 'b', 'c', 'd'] ; 
?>
<p>Voici un tableau de taille <?php echo count($tab) ?> :</p>
<div style="font-family: Monaco ; font-size: 0.8em">
    <div style="white-space: pre ;"><?php var_dump($tab) ?></div>
</div><br>
    
<p>
    <?php 
        $case = 2 ;
        // on peut utiliser les variables dans un echo :
        echo "Le contenu de la case $case est $tab[$case]." ;
    ?>
</p>
Résultat

Voici un tableau de taille 4 :

array(4) { [0]=> string(1) "a" [1]=> string(1) "b" [2]=> string(1) "c" [3]=> string(1) "d" }

Le contenu de la case 2 est c.

Vous aurez remarqué que les tableaux de PHP sont associatifs (paires clé=>valeur).

Dans l'exemple précédent, les clés ne sont pas précisées : elles sont attribuées automatiquement (avec des nombres de 0 à count($tab)-1).


On peut donc aussi créer des tableaux en spécifiant les paires voulues :

<?php
    $phonetic_alphabet=array("Alpha" => "A", "Bravo" => "B", "Charlie" => "C", "Delta" => "D") ;
?>
<p>
    <?php $code = "Charlie" ; ?>
    Dans l'alphabet phonétique de l'OTAN, 
    le code "<?php echo $code ?>" signifie '<?php echo $phonetic_alphabet[$code] ?>'.
</p>
Résultat

Dans l'alphabet phonétique de l'OTAN, le code "Charlie" signifie 'C'.

Fonctions de recherche dans un tableau

PHP offre plusieurs fonctions permettant d'effectuer des recherches dans un tableau :

  • in_array(valeur, tableau) : renvoie true si valeur est dans tableau, false sinon.
  • array_key_exists(clé, tableau) : renvoie true si clé existe dans tableau, false sinon.
  • array_search(valeur, tableau) : renvoie la clé correspondante à valeur si valeur est dans tableau, false sinon.
<?php
    $week = ['lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'] ;
    
    $day = 'vendredi' ;
    $idx = array_search($day, $week) ; 
    
    // NB: il faudrait vérifier que la réponse n'est pas false...
    echo "$day est le jour n° " . ($idx+1). " dans la semaine" ;
?>
Résultat
vendredi est le jour n° 5 dans la semaine

Les Structures de Contrôle

Conditionnelle : if

La structure du if en PHP est classique. Les blocs sont délimités par des accolades.

Plusieurs formes sont disponibles selon les besoins :

  • if(condition) { ... }
  • if(condition) { ... } else { ... }
  • if(condition) { ... } elseif(condition) { ... } ... else { ... }

Opérateurs booléens

Les opérateur booléen sont notés and, or, xor, !, && et ||.
Pour plus de détails sur leurs différentes priorités, c'est ici.


Un exemple qui peut vous rappeler quelque chose :
<?php
    $a_le_permis = true ;
    $est_sobre = false ;
    $a_un_chauffeur = true ;
    
    $msg = "Rentre" ;
    
    if ( ($a_le_permis and $est_sobre) or $a_un_chauffeur){
        $msg .= ' en voiture' ;
    } else {
        $msg .= ' à pieds' ;
    }
    
   echo $msg ;
?>
Résultat
Rentre en voiture

Affectation ternaire

Il est aussi possible d'utiliser des opérateurs ternaires :
<?php
    $a_le_permis = true ;
    $est_sobre = false ;
    
    $msg = ($a_le_permis and $est_sobre) ? 'en voiture' : 'à pieds' ;
    
    $msg = 'Rentre ' . $msg ;
    echo $msg ;
?>
Résultat
Rentre à pieds

Tant que : while

<?php
    $notes = [18, 15, 6, 20, 12] ;
    $acc = 0 ;
    
    $i = 0 ;
    while($i < count($notes)){
        $acc += $notes[$i] ;
        $i++ ;
    }
    
    $moyenne = $acc / count($notes) ;
?>
La moyenne est : <?php echo $moyenne ?>
Résultat
La moyenne est : 14.2

Pour : for

<?php
    $notes = [18, 15, 6, 20, 12] ;
    $acc = 0 ;
    
    for($i = 0 ; $i < count($notes) ; $i++){
        $acc += $notes[$i] ;
    }
    
    $moyenne = $acc / count($notes) ;
?>
La moyenne est : <?php echo $moyenne ?>
Résultat
La moyenne est : 14.2

foreach

Pour parcourir les tableaux, il peut être plus intéressant d'utiliser un foreach.

foreach(iretable as $value){ ... }
<?php
    $notes = [18, 15, 6, 20, 12] ;
    $acc = 0 ;
    
    foreach($notes as $n){
        $acc += $n ;
    }
    
    $moyenne = $acc / count($notes) ;
?>
La moyenne est : <?php echo $moyenne ?>
Résultat
La moyenne est : 14.2
foreach(iretable as $key => $value){ ... }
<p>
    Alphabet phonétique de l'OTAN :
</p>
<ul style="list-style-type: square">
<?php
    $phonetic_alphabet=array("Alpha" => "A", "Bravo" => "B", "Charlie" => "C", "Delta" => "D") ;          
    
    foreach($phonetic_alphabet as $code => $char){
        echo "<li>\"$code\" : '$char'</li>" ;
    }
?>
</ul>
Résultat

Alphabet phonétique de l'OTAN :

  • "Alpha" : 'A'
  • "Bravo" : 'B'
  • "Charlie" : 'C'
  • "Delta" : 'D'

Les blocs php + HTML/CSS/Javascript

Le code HTML/CSS/Javascript qui est placé dans un bloc php (dans un { ... } ) est "recopié" par l'interpréteur seulement si ce bloc est exécuté.

Ce mécanisme peut être mis à profit lorsque l'on crée un script php qui intercale plusieurs "morceaux" de code php dans du HTML.

NB: cela peut aussi rendre le code difficile à lire...

Pour plus de lisibilité, on peut utiliser la syntaxe alternative dans laquelle les accolades sont remplacées par des : et la fermeture de bloc est assurée par (selon le cas) : endif, endwhile, endfor, endforeach, ou endswitch.


Exemple avec un bloc if : ... else : ... endif ; :
Tu vas rentrer 
<?php 
    $a_le_permis = false ;
?>    

<?php if($a_le_permis) : ?> 
    <!-- apparait seulement si la condition est true -->
    <span style="color: blue">en voiture :) </span> 
<?php else : ?> 
    <!-- apparait seulement si la condition est false -->
    <span style="color: red">à pieds :(</span>
<?php endif ?>
Résultat
Tu vas rentrer à pieds :(

Un autre exemple avec un bloc foreach : ... endforeach ; :
<?php
    $phonetic_alphabet=array(
        "Alpha" => "A", 
        "Bravo" => "B", 
        "Charlie" => "C", 
        "Delta" => "D"
    ) ;     
?>

<style>
    .cell{ 
        width: 50%;
        border: 1px solid black ;
        padding: 3px;
    }
</style>

<table>
    <tr><th colspan="2">Alphabet phonétique de l'OTAN</th></tr>
    <?php foreach($phonetic_alphabet as $code => $char) : ?>
        <!-- ce code HTML est intégré à la boucle -->
        <tr>
            <td class="cell"><?php echo $code ?></td>
            <td class="cell" style="text-align: center"><?php echo $char ?></td>
        </tr>
    <?php endforeach ?>
</table>
Résultat
Alphabet phonétique de l'OTAN
Alpha A
Bravo B
Charlie C
Delta D

Les Fonctions

Les fonctions php ont une syntaxe proche de celle qu'on peut trouver dans la plupart des langages.
Comme pour les variables, le type des paramètres ne doit pas être indiqué.

Fonction sans paramètre

<?php
    // déclaration de la fonction
    function disBonjour(){
        $msg = "Hello World" ;
        echo "<p>$msg</p>" ;
    }
    
    // appel de la fonction
    disBonjour() ;
    disBonjour() ;
?>
Résultat

Hello World

Hello World

Fonction avec paramètre(s)

<?php
    // déclaration de la fonction
    function disBonjourA($nom, $ponctuation='!'){
        echo "<p>Hello $nom $ponctuation</p>" ;
    }
    
    // appel de la fonction
    disBonjourA('Greg') ;
    disBonjourA('Greg', ':)') ;
?>
Résultat

Hello Greg !

Hello Greg :)

Fonction avec valeur de retour (return)

<?php
    // déclaration d'une fonction qui retourne une chaine
    function getGreetingMessage($nom, $ponctuation='!'){
        $msg = "Hello $nom $ponctuation" ;
        return $msg ; // <- l'exécution s'arrête ici ! 
    }   
?>

<h2><?php echo getGreetingMessage('Greg') ?></h2>
Résultat

Hello Greg !

Inclusion de Fichiers

De manière à apporter plus de modularité et de ré-utilisabilité, PHP offre plusieurs fonctions qui permettent d'inclure/importer des fichiers externes dans un résultat.

L'inclusion d'un fichier signifie littéralement insérer la totalité de son code à l'endroit où est appelée l'inclusion.

L'exécution du script est alors réalisée comme si le code inclus y était collé.

Ce mécanisme permet de réutiliser non seulement des suites d'instructions php, des variables php, des fonctions php... MAIS AUSSI du code HTML/CSS/Javascript !

Cela s'avère très pratique par exemple lorsque l'on a besoin d'intégrer un header, un footer, une navbar, ... qui sont typiquement des instructions HTML/CSS/Javascript qui doivent se répéter à l'identique dans plusieurs pages d'un même site.


Il existe 4 instructions permettant d'inclure le code d'un fichier dans un script :

  • include(fichier) : inclut les instructions du fichier à l'endroit où include est appelé. Ne fait rien si le fichier n'est pas trouvé.
  • include_once(fichier) : comme include, mais ne fait rien si fichier a déjà été inclus (précédemment) dans le script. (Cela permet par exemple d'éviter des re-déclarations, une connexion à un serveur externe alors qu'elle a déjà été faite, etc.). Ne fait rien si le fichier n'est pas trouvé.
  • require(fichier) : a le même fonctionnement que include, mais déclenche une erreur d'exécution du script php si fichier n'est pas trouvé.
  • require_once(fichier) : a le même fonctionnement que include_once, mais déclenche une erreur d'exécution du script php si fichier n'est pas trouvé.

Un Exemple

On définit d'abord plusieurs fichiers destinés à être inclus.
Dans cet exemple ce seront les fichiers :

  • header.html : du code HTML pour le header du site.
  • footer.php : du code HTML + PHP pour le footer du site.
  • helpers.php : du code HTML + PHP (ici, l'ajout d'une balise link pour un fichier .css, et la déclaration d'une fonction PHP nommée generateTitle.
  • hepers.css : des déclarations CSS utilisées dans helpers.php.

Remarques:
Les fichiers .html (avec du HTML "pur") peuvent aussi être nommés .php (ex. header.php).
Les fichiers contenant du code PHP (+ HTML) doivent être nommés .php (ex. footer.php).

Voici le contenu de ces fichiers :

header.html
<h1>Cours de PHP</h1>
<hr>
footer.php
<div style="width: 100% ; padding: 5px ; background: black ; color: white">
    Ceci est une démo d'inclusions en PHP <?php echo phpversion() ?>
</div>
helpers.php
<link rel="stylesheet" href="helpers.css">

<?php
/**
 * Génère une balise h1 avec un titre formaté
 * @param $title
 */
function generateTitle($title){
    // formatage du titre
    $title = ucwords($title) ;

    // génération du code HTML
    echo "<h2 class='custom'>$title</h2>" ;
}
helpers.css
h2.custom{
    font-family: Courier;
    font-size: 1.2em;
    color: darkblue;
}

Il ne reste plus qu'à inclure ces fichiers aux endroits voulus : ici dans index.php.
(NB: dès que helpers.php a été inclus, la fonction generateTitle est disponible.)

index.php
<?php require_once "helpers.php" ?>

<!doctype html>
<head>
    <meta charset="UTF-8">
    <title>Test Inclusion</title>
</head>
<body>

<?php include "header.html" ?>

<?php generateTitle("introduction") ?>
<p>
    bla bla blaaa
</p>

<?php generateTitle("serveurs web dynamiques") ?>
<p>
    bla bla blaaa
</p>

<?php generateTitle("conclusion") ?>
<p>
    bla bla blaaa
</p>

<?php include "footer.php"; ?>

</body>
</html>
Résultat
Test Inclusion

Cours de PHP


Introduction

bla bla blaaa

Serveurs Web Dynamiques

bla bla blaaa

Conclusion

bla bla blaaa

Ceci est une démo d'inclusions en PHP 7.4.25

Les Objets en PHP

PHP fournit un modèle objet avec des mécanismes proches de ceux de Java.
Un bonne nouvelle puisque vous avez tout compris au cours de LOO ;).

Les classes

Comme en Java, les objets sont instances de classes dont les définitions contiennent des membres (attrituts/propriétés et méthodes/fonctions).

  • A partir de PHP 7.4, il est possible (et conseillé) de typer les attributs, paramètres, retours de méthodes/fonctions.
    ATTENTION : avec les plus anciennes versions, il ne faut pas mettre les types.
  • Les constructeurs sont nommés __construct.
  • L'instanciation est faite par new.
  • L'objet peut se référencer lui-même grâce à $this.
  • L'accès aux membres d'instance est faite par ->.
  • La visibilité est gérée par private, protected et public.

Dans ce cours, j'utiliserai les déclarations avec types.
Si votre version de PHP est antérieure à 7.4, ne mettez/recopiez pas les types !

Exemple de classe

Définition d'un classe People dans un fichoer People.php :
People.php
<?php

class People
{

    private string $name ; // "string" seulement pour PHP >= 7.4

    /**
     * People constructor.
     * @param $name
     */
    public function __construct($name)
    {
        $this->name = $name ;
    }

    /**
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @return string
     */
    public function __toString() : string
    {
        return ucwords($this->getName()) ;
    }
}


Utilisation de la classe :
ex_01_class.php
<?php
    include "People.php" ;

    $greg = new People("grégory bourguin") ;

?>
<p>
    Le contenu de l'attribut : <b><?php echo $greg->getName() ?></b>
</p>
<p>
    Utilisation de __toString() : <b><?php echo $greg ?></b>
</p>

<hr>
<?php
    $className = "People" ;
    $toto = new $className("toto") ; // désignation de la classe avec une chaine...
    echo "<p>Autre utilisation de People: <b>$toto</b></p>" ;
?>
Résultat

Le contenu de l'attribut : grégory bourguin

Utilisation de __toString() : Grégory Bourguin


Autre utilisation de People: Toto

Vous aurez remarqué que, similairement à Java, un echo sur un objet fait automatiquement appel à sa méthode __toString() (qu'on a ici surchargée).

Membres de Classe + Héritage

Comme en Java, les classes peuvent avoir des membres de classe (static).

  • on y accède par :: (au lieu de -> pour les membres d'instance).
  • un objet peut référencer sa classe grâce à self.

Le mécanisme d'héritage de PHP est aussi similaire à celui de Java.

  • pour hériter on utilise extends.
  • un objet peut faire appel à un membre de sa classe mère grâce à parent.

Exemple d'une classe Citizen qui hérite de People:
Citizen.php
<?php
require_once "People.php" ;

class Citizen extends People
{

    // attribut de classe (static)
    private static int $population = 0 ;

    // méthode de classe (static)
    public static function getPopulation(): int
    {
        return self::$population; // accès à l'attribut de classe
    }

    // -----------------------------------------------------------------------

    // attribut d'instance
    private ?string $address ; // le ? signifie que l'attribut peut être null

    /**
     * Citizen constructor.
     * @param $name
     */
    public function __construct($name)
    {
        parent::__construct($name); // appel à une méthode de la superclasse
        self::$population++ ; // accès à un membre static
    }

    /**
     * @param string|null $address
     */
    public function setAddress(?string $address): void
    {
        $this->address = $address;
    }

    /**
     * @return string|null
     */
    public function getAddress(): ?string
    {
        return $this->address;
    }

}

Utilisation de la classe :
ex_02_static_extends.php
<?php
    include "Citizen.php" ;

    $greg = new Citizen("grégory bourguin") ;

    // la syntaxe Heredoc pour une chaine sur plusieurs lignes
    $adress = <<<EOT
    666 Highway to Hell
    NEW HAVEN - USA
    EOT;

    $greg->setAddress($adress) ;
?>

<p>
    <?php echo "<b>$greg</b> a été créé." ?>
</p>

<p style="white-space: pre ; font-weight: bold"><?php echo $greg->getAddress() ?></p>

<p>
    Population : <b><?php echo Citizen::getPopulation() ?></b>
</p>
Résultat

Grégory Bourguin a été créé.

666 Highway to Hell NEW HAVEN - USA

Population : 1

Classes Abstraites / Interfaces

abstract

Toujours comme en Java, les classes abstraites de PHP sont des classes avec au moins une méthode abstraite.

Pour les classes comme pour les méthodes, on utilise le mot clé abstract.

Exemple d'une classe abstraite Animal :
Animal.php
<?php
abstract class Animal
{

    public abstract function scream() : string  ;

    public function __toString()
    {
        return "The " . get_called_class() . " goes " . $this->scream() ;
    }

}

...dont héritent des classes concrètes Dog et Bird :
Dog.php
<?php
require_once "Animal.php" ;

class Dog extends Animal
{
    public function scream(): string
    {
        return "WOOF" ;
    }
}
Bird.php
<?php
require_once "Animal.php" ;

class Bird extends Animal
{

    public function scream(): string
    {
        return "TWEET" ;
    }

}

Utilisation :
ex_03_abstract.php
<?php
    require_once "Dog.php" ;
    require_once "Bird.php" ;

    $dog = new Dog() ;
    $bird = new Bird() ;
?>

<p>
    <?php echo $dog ?><br>
    <?php echo $bird ?><br>
    But... <a href="https://youtu.be/jofNR_WkoCE" target="_blank">What does the Fox say ???</a>
</p>
Résultat

The Dog goes WOOF
The Bird goes TWEET
But... What does the Fox say ???

interface

Quand toutes le méthodes sont abstraites, on peut aussi définir des interfaces.

  • interface remplace abstract class.
  • implements remplace extends.

Les Exceptions

Encore une fois, PHP founit un mécanisme d'exceptions similaire à celui de Java constitué de 3 types de blocs :

  • try : contient le code susceptible de générer une Exception.
  • catch(Type ex) : récupère l'instance (d'une sous-classe) d'Exception.
    Il peut y avoir plusieurs blocs catch pour capturer plusieurs types d'Execption.
  • finally : un bloc qui sera exécuté quel qu'ait été le traitement du try ... catch.

Voici un exemple avec une classe Fraction dont la méthode setDenominateur (aussi utilisée dans le __construct) est susceptible de générer une exception :
Fraction.php
<?php
class Fraction
{
    private int $numerateur ;
    private int $denominateur ;

    public function __construct(int $numerateur, int $denominateur)
    {
        $this->setNumerateur($numerateur);
        $this->setDenominateur($denominateur);
    }

    /**
     * @return int
     */
    public function getNumerateur(): int
    {
        return $this->numerateur;
    }

    /**
     * @param int $numerateur
     */
    public function setNumerateur(int $numerateur): void
    {
        $this->numerateur = $numerateur;
    }

    /**
     * @return int
     */
    public function getDenominateur(): int
    {
        return $this->denominateur;
    }

    /**
     * @param int $denominateur
     * @throws Exception
     */
    public function setDenominateur(int $denominateur): void
    {
        if ($denominateur == 0){
            throw new Exception("Division par 0 !") ;
        }
        $this->denominateur = $denominateur;
    }

    /**
     * @return string
     */
    public function __toString()
    {
        return "$this->numerateur  / $this->denominateur" ;
    }

}

Utilisation :
ex_04_exception.php
<?php
require_once "Fraction.php" ;

$fraction = new Fraction(1, 2) ;

echo "<p>Voici une fraction : $fraction</p>" ;

try{
    $fraction->setDenominateur(0);
} catch (Exception $e){
    echo "
    <p>
        Une erreur s'est produite : 
        <span style='color: red'>" . $e->getMessage() ."</span>
    </p> 
    " ;
}
Résultat

Voici une fraction : 1 / 2

Une erreur s'est produite : Division par 0 !

Espace de nommage

Instruction namespace

Plus un projet grandit, plus il a de chance d'utiliser des classes différentes mais qui portent le même nom. Ceci est encore plus probable lorsque le projet propose/intègre des frameworks réutilisables.

En PHP, la notion de namespace permet de différencier des classes qui portent le même nom en y ajoutant un espace de nommage.

En suivant cette méthode, on peut imaginer que nos classes Animal, Dog et Bird précédemment définies fassent partie d'un namespace nommé web3\animals.

Il suffit alors d'utiliser l'instruction namespace au tout début de ces 3 classes.

Voici ce que cela donne pour Dog.php :
Dog.php
<?php
namespace web3\animals ;

require_once "Animal.php" ;

class Dog extends Animal
{
    public function scream(): string
    {
        return "WOOF" ;
    }
}

Pour utiliser Dog, il faut maintenant utiliser son nom complet : \web3\animals\Dog.
<?php
// .. je ne l'ai pas mis ici, mais il ne faut pas oublier d'inclure Dog.php

$chien = new \web3\animals\Dog() ;
echo $chien->scream() ;
?>

Instruction use

Il peut sembler lourd et fastidieux de préfixer le nom de la classe à chaque fois qu'on veut l'utiliser. Heureusment PHP offre une solution :

L'instruction use permet d'importer une classe afin de pouvoir ensuite l'utiliser sans préciser son namespace.

On pourra alors écrire :
<?php
// .. je ne l'ai pas mis ici, mais il ne faut pas oublier d'inclure Dog.php

use web3\animals\Dog ;

$chien = new Dog() ;
echo $chien->scream() ;
?>

Arborescence

Dans une application complexe, le nombre de classes peut vite grandir : il ne serait pas efficace de mettre tous les fichiers correspondants dans le même dossier.

Une bonne pratique est créer une arborescence de dossiers contenant les fichiers des classes et qui reflète la hiérarchie du namespace.

Les sites en PHP Objet placent généralement leurs classes dans un dossier nommé class situé à la racine du site.

L'idée est donc de mettre tous les fichiers PHP correspondant à des définitions de classes dans une arborescence à partir du dossier class, et qui reflète la hiérarchie définie par les namespace.

Dans notre exemple, on aurait alors quelque chose comme :
/
|-- test.php
|-- class
      |
      |-- web3
            |-- animals
                   |-- Animal.php
                   |-- Bird.php
                   |-- Dog.php
        

Et le contenu de test.php (qui utilise la classe Dog) :
test.php
<?php
require_once __DIR__ . "/class/web3/animals/Dog.php" ;

use web3\animals\Dog ;

$chien = new Dog() ;
echo $chien->scream() ;
Résultat
WOOF

NB: __DIR__ est une des " constantes magiques" de PHP qui contient le nom (complet) du dossier du fichier (ici test.php) dans laquelle l'instruction se trouve.

Autoloading

Un site PHP peut impliquer de nombreuses classes et donc l'inclusion de nombreux fichiers .php (puisqu'on a 1 classe par fichier).

Afin d'éviter une profusion de require (ou require_once, etc.), PHP offre la possibilité de mettre en oeuvre ce qu'on appelle l'autoloading.

La fonction PHP spl_autoload_register permet de créer une chaine d'autoload.

Il s'agit d'enregistrer une (série de) fonction(s) qui sera(ont) appelée(s) automatiquement pour tenter d'inclure le fichier PHP correspondant à une classe la première fois qu'elle est utilisée.

Étape 1 : une fonction d'inclusion

La première étape consiste à écrire une fonction qui sera capable d'inclure le fichier .php correspondant à une classe à partir de son nom complet (namespace).

Si on reprend l'exemple précédent, la classe Dog s'appelle en réalité :
web3\animals\Dog
... et elle se trouve dans le fichier :
/class/web3/animals/Dog.php.

Pour trouver le fichier à inclure à partir du nom web3\animals\Dog :
  • On remplace les \ avec le séparateur de dossiers /.
    web3\animals\Dog -> web3/animals/Dog
  • On ajoute l'extension .php.
    -> web3/animals/Dog.php
  • On ajoute le dossier "racine" /class/ dans lequel se trouvent toutes les classes.
    -> /class/web3/animals/Dog.php

Le nom de la fonction n'est pas (très) important : la technique d'autoloading permet d'ailleurs d'en définir plusieurs (donc il n'y a pas/plus un nom unique standardisé).

Nous l'appellerons ici autoload (en souvenir de l'ancienne technique avant PHP 7.2 qui utilisait 1 fonction nommée __autoload).

function autoload($class_name){

    // on remplace les \ du namespace par les / des répertoires
    $class_name = str_replace('\\', '/', $class_name) ;

    // on inclut le fichier correspondant
    require 'class/' .$class_name . '.php' ;
}

Étape 2 : enregistrer la fonction pour l'autoloading

La fonction PHP spl_autoload_register permet de fournir à PHP une (série de) fonction(s) qui sera(ont) automatiquement appelée(s) lors de la 1ere référence à une classe pour inclure son fichier .php

Il suffit donc de faire appel à cette fonction en lui passant en paramètre une fonction d'inclusion pour que PHP l'utilise automatiquement lorsqu'il recontrera une (nouvelle) classe.

Le code "complet" pour activer l'autoload :
<?php
function autoload($class_name){

    // on remplace les \ du namespace par les / des répertoires
    $class_name = str_replace('\\', '/', $class_name) ;

    // on inclut le fichier correspondant
    require 'class/' . $class_name . '.php' ;
}
spl_autoload_register('autoload') ;
?>

Étape 3 : Retirer les require (etc.) pour les fichiers de classes

Quand le spl_autoload_register a été exécuté (dans le flux d'exécution d'un script), vous n'avez plus à faire d'inclusion pour tous les fichiers qui définissent des classes.

L'idée est donc d'ajouter les instructions d'autoloading au tout début du script, et de retirer tous les require (etc.) qui référencent des fichiers classe.

Voici donc la version "nettoyée" de Dod.php :
Dog.php (sans require)
<?php
namespace web3\animals ;

class Dog extends Animal
{
    public function scream(): string
    {
        return "WOOF" ;
    }
}
... et un fichier test.php qui utilise l'autoloading :
test.php
<?php

function autoload($class_name){
    // on remplace les \ du namespace par les / des répertoires
    $class_name = str_replace('\\', '/', $class_name) ;

    // on inclut le fichier correspondant
    require 'class/' . $class_name . '.php' ;
}
spl_autoload_register('autoload') ;

$chien = new \web3\animals\Dog() ;
echo $chien->scream() ;
Résultat
WOOF

Étape 4 : Finalisation

Pour que l'autoloading soit utilisable dans plusieurs de vos scripts, l'idée est de le mettre dans un fichier .php qui sera inclus là où on en a besoin.

NB: oui, on va ajouter une inclusion, mais elle permet d'enlever toutes celles qui référencent des fichier class !

Le nom du fichier n'est pas important : on l'appellera ici init_autoload.php

init_autoload.php
<?php
function autoload($class_name){
    // on remplace les \ du namespace par les / des répertoires
    $class_name = str_replace('\\', '/', $class_name) ;

    // on inclut le fichier correspondant
    require 'class/' . $class_name . '.php' ;
}
spl_autoload_register('autoload') ;

... et il suffit maintenant d'inclure ce fichier au tout début des scripts qui utilisent des classes.

test.php (version finalisée)
<?php

require_once __DIR__ . "/init_autoload.php" ;

$chien = new \web3\animals\Dog() ;
echo $chien->scream() ;

L'archive ex05_autoload_v1.zip contient cet exemple.


L'archive ex05_autoload_v2.zip contient une variante de cet exemple :
on y remplace le fichier init_autoload.php par le fichier class/Autoloader.php.
Ce fichier définit la classe \Autoloader et sa méthode de classe register qui contient l'appel à spl_autoload_register.
NB : dans la suite du cours, j'aurai tendance à utiliser cette 2ème version.

TP 01

Templates et Buffering

Un site web est généralement constitué de nombreuses pages dont le contenu est différent, mais qui partagent la même section head, voire, dans la partie body, les mêmes header et footer.

Imaginez par exemple un site de e-commerce avec une page d'accueil index.php, une page de login login.php, un magasin store.php, un panier cart.php, etc. Pour l'unité du site, il est nécessaire de démarrer chaque page avec le même code, d'inclure les mêmes CSS, etc. Cela fait beaucoup d'include ou require qui sont copié/collés dans chaque page...

Pour pallier ce problème, une solution consiste à créer un template, c'est à dire une structure générique qui sera utilisée pour générer toutes les pages du site en répétant les parties communes et en permettant d'y injecter les différences inhérentes à chaque page.

Exemple de définition de template

La technique pour créer un (des) template n'est pas forcément liée aux objets, mais c'est une manière efficace de procéder.

Nous allons donc ici créer une classe nommée Template (NB: c'est VOTRE classe, donc elle pourrait porter un autre nom et être construite différemment !) avec une méthode render($content) dont le but est d'encapsuler un contenu de page ($content) dans la "coquille" du site constituée d'un head, header et footer communs à toutes les pages.

NB: pour simplifier l'exemple, je n'ai pas utilisé de CSS externe, n'ai pas créé de namespace, et n'ai pas mis en place d'autoloading... mais ce serait mieux de le faire !

Template.php
<?php
class Template
{

    public static function render(string $content) : void{?>

        <!doctype html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Test Buffering</title>

        <style>
            .black-ribbon{
                padding: 3px;
                background: black ;
                color: white ;
            }
            #injected-content{
                height: 100px;
                display: flex;
                justify-content: start;
                align-items: center;
                color: darkred;
            }
        </style>

        </head>
        <body>
            <?php include "header.php" ?>

            <div id="injected-content">
                <?php echo $content ?> <!-- INJECTION DU CONTENU -->
            </div>

            <?php include "footer.html" ?>

        </body>
        </html>

    <?php
    }

}

Vous pouvez remarquer que la méthode render injecte le contenu du paramètre $content dans le template HTML du site, entre l'inclusion de header.php et footer.html.

Pour l'exemple, le contenu de ces 2 fichiers est très simple :
header.php
<header class="black-ribbon">
    Ceci est un header
</header>
footer.html
<footer class="black-ribbon">
    Ceci est un footer
</footer>

Utilisation : injection par buffering

Il reste maintenant à définir une variable qu'on pourra utiliser en tant que paramètre $content de Template::render.

Le problème est que cette variable doit contenir une string correspondant au résultat d'un contenu généré par PHP (construit avec par exemple du code HTML pur, des include/require, des echo, etc.).

La solution réside dans la temporisation de sortie...

ob_start ... ob_get_clean

La fonction ob_start déclenche la temporisation de sortie de PHP.

En d'autres termes, après un appel à ob_start toutes les sorties d'un script PHP sont stockées dans un tampon/buffer au lieu d'être envoyée directement au navigateur.

La fonction ob_get_clean stoppe la temporisation de sortie de PHP, retourne le contenu du buffer/tampon, puis l'efface.


En résumé, ces 2 fonctions permettent de "détourner" la sortie de PHP pour la stocker dans une variable qui pourra être utilisée ultérieurement : c'est exactement ce qu'il nous faillait pour injecter le contenu d'une page dans notre template.

On peut alors par exemple écrire notre page d'accueil comme suit.

index.php
<!-- Ce serait mieux d'utiliser un autoloading... -->
<?php require_once __DIR__.DIRECTORY_SEPARATOR."Template.php" ; ?>

<!-- Démarre le buffering -->
<?php ob_start() ?>

<p style="font-size: 1.2em">
    Ce texte a été <b><?php echo "bufferisé" ?></b>
    avant d'être injecté dans mon template.
</p>

<!-- Récupère le contenu du buffer (et le vide) -->
<?php $content=ob_get_clean() ?>

<!-- Utilisation du contenu bufferisé -->
<?php Template::render($content) ?>
Résultat
Test Buffering
Ceci est un header

Ce texte a été bufferisé avant d'être injecté dans mon template.

Ceci est un footer

Bien entendu, tout l'intérêt est que le template peut être ainsi utilisé pour générer toutes les pages de votre site web !

On peut donc utiliser Template pour générer d'autres pages du site comme cart.php

cart.php
<!-- Ce serait mieux d'utiliser un autoloading... -->
<?php require_once __DIR__.DIRECTORY_SEPARATOR."Template.php" ; ?>

<!-- Démarre le buffering -->
<?php ob_start() ?>

<h2>Votre panier est vide...</h2>

<!-- Récupère le contenu du buffer (et le vide) -->
<?php $content=ob_get_clean() ?>

<!-- Utilisation du contenu bufferisé -->
<?php Template::render($content) ?>
Résultat
Test Buffering
Ceci est un header

Votre panier est vide...

Ceci est un footer

Remarque

Je ne vous ai montré ici que ob_start et ob_get_clean mais il est possible de faire plus de choses avec les fonctions de bufferisation de sortie.

Les Fichiers en PHP

PHP permet de manipuler (créer, lire, écrire, supprimer) des fichiers sur le serveur.

Il est important de se souvenir qu'il est nécessaire de faire en sorte que PHP (le serveur Web) possède bien les droits nécessaires sur les dossier et fichiers que vous souhaitez manipuler, ceci en accord avec les actions que vous voulez effectuer.

Lecture Simple : file_get_contents

La fonction file_get_contents charge en mémoire la totalité du contenu d'un fichier.

Cette méthode sera donc à utiliser uniquement quand la taille du fichier est "petite".

Exemple

Soit le fichier lets_bang.txt.

Le script simple_lecture.php utilise file_get_contents pour charger son contenu et l'afficher dans un div.

lecture_simple.php
<style>
    .lyrics{
        height: 150px ;
        padding: 10px;
        background: black;
        color: greenyellow;
        overflow: auto ;
        white-space: pre ;
    }
</style>
<?php
// Lecture du fichier dans le répertoire courant
$texte = file_get_contents(__DIR__."/lets_bang.txt") ;
?>
<div class="lyrics"><?php echo $texte ?></div>
Résultat
Let's Bang (Shaka Ponk) : Saturday night in a dancing house I say (yeah yeah yeah yeah) Storming boys staring at my eyes I say (yeah yeah yeah yeah) You're the one I like I wanna share my story tonight Big mama you're my kind Gimme a part of your precious time Hey you're the one I like I wanna share your love tonight I wanna smoke your special thing Let's Bang bang bang You'd better do your dance You'd better take that chance You'd better move your butt You gotta show me what you got You wanna check this man Jump on the line You gotta shake that ass If you wanna have mine It's soon the perfect time I'm sounding lighter You wanna hang, wanna hang So do the dance and Bang bang bang bang bang Do the dance mate and I lose cabeza Yeah yeah yeah yeah You get my ass with the troubleshoot styla Yeah yeah yeah yeah I wanna get naughty and no wanna doubt Yeah yeah yeah yeah The boys over the rainbow is just what I want Yeah ! Yeah ! Yeah ! Hey yo chic-chica you wanna hang out with the jerk? So bring your ass to table! Wanna hang wanna hang So do the dance and Bang bang bang bang bang ! I kill yo, I kill yo, I kill yo yo yo, I kill yo

Ecriture simple : file_put_contents

La fonction file_put_contents permet d'écrire dans un fichier.

Par défaut, cette fonction crée le fichier s'il n'existe pas et/ou remplace totalement son contenu.

Exemple

Dans cet exemple, on ecrit le contenu de $lyrics dans le fichier local eisbar.txt.
Si le fichier n'existe pas, il est créé. S'il existe, le contenu est remplacé.
On utilise ensuite file_get_contents pour relire le fichier et afficher son contenu. ecriture_simple.php
<?php
// EOT permet de définir du texte sur plusieurs lignes
$lyrics = <<<EOT
Eisbär (Grauzone) :

Ich möchte ein Eisbär sein
im kalten Polar
dann müßte ich nicht mehr schrei'n
alles wär' so klar

EOT;

$filename = __DIR__."/eisbar.txt" ;

// ecriture dans le fichier
file_put_contents($filename, $lyrics) ;

// (re)lecture du fichier
$texte = file_get_contents($filename) ;
?>

<div class="lyrics"><?php echo $texte ?></div>
Résultat
Eisbär (Grauzone) : Ich möchte ein Eisbär sein im kalten Polar dann müßte ich nicht mehr schrei'n alles wär' so klar

Ajout : FILE_APPEND

Le flag FILE_APPEND de file_put_contents permet de ne pas écraser le contenu du fichier mais de faire des ajouts à la fin.

Exemple

On complète ici le fichier eisbar.txt avec de nouvelles paroles : 4 x la même ligne...
ajout_simple.php
<?php
$lyrics = "\nEisbär'n müssen nie weinen" ;

$filename = __DIR__."/eisbar.txt" ;

// ajout de 4 lignes à la fin du fichier
for ($i=0 ; $i<4 ; $i++){
    file_put_contents($filename, $lyrics, FILE_APPEND) ;
}

// (re)lecture du fichier
$texte = file_get_contents($filename) ;
?>

<div class="lyrics" style="height: 240px"><?php echo $texte ?></div>
Résultat
Eisbär (Grauzone) : Ich möchte ein Eisbär sein im kalten Polar dann müßte ich nicht mehr schrei'n alles wär' so klar Eisbär'n müssen nie weinen Eisbär'n müssen nie weinen Eisbär'n müssen nie weinen Eisbär'n müssen nie weinen

Ouverture/Fermeture de flux avec fopen et fclose

Comme indiqué précédemment, les fonctions telles que file_get_contents chargent la totalité des données dans la mémoire, ce qui n'est pas envisageable lorsqu'on doit manipuler de gros fichiers.

NB: PHP dispose d'une taille mémoire maximale limitée (qui peut être modifiée dans sa configuration) pour travailler.

Lorsque l'on veut manipuler un fichier de manière plus "fine" (ligne par ligne, caractère par caractère, ...) PHP peut fournir non pas la totalité du contenu, mais une ressource qui représente un flux (stream) permettant de se déplacer dans le fichier sans le charger totalement ne mémoire.

fopen ouvre un flux sur un fichier ouvert dans un mode précisé en paramètre.

La liste des modes disponibles (et leur effet) est dans la documentation

Le flux récupéré peut ensuite être utilisé avec diverses autres fonctions.

Quand les traitements sont terminés, il faut fermer le flux.

fclose permet de fermer (libérer) un flux.

Nous allons voir comment utiliser ces méthodes ci-après avec par exemple fgets.

Lecture par Ligne : fgets

fgets permet de lire un flux ligne par ligne : chaque lecture renvoie une ligne (ou null si on est à la fin du fichier), et fait passer le "curseur" à la ligne suivante.

Exemple

Dans l'exemple suivant, on ouvre le fichier lets_bang.txt en lecture seule avec fopen, puis on récupère les $max premières lignes pour les afficher, avant de fermer le flux avec fclose.

lecture_lignes.php
<?php
// ouverture du fichier (ici en lecture seule)
$file = fopen(__DIR__."/lets_bang.txt", 'r') ;
$max = 5 ;
?>

Les <?php echo $max ?> première(s) ligne(s) de la chanson :
<div class="lyrics"><?php

    // Lecture et affichage des $max premières lignes
    for($i=1 ; $i<=$max ; $i++){
        $line = fgets($file) ; // récupération d'une ligne
        if ($line === null) break ; // on sort s'il n'y a plus de ligne à lire
        echo $i . " : " . $line ; // affichage
    }

// fermeture du fichier
fclose($file) ;
?></div>
Résultat
Les 5 première(s) ligne(s) de la chanson :
1 : Let's Bang (Shaka Ponk) : 2 : 3 : Saturday night in a dancing house I say (yeah yeah yeah yeah) 4 : Storming boys staring at my eyes I say (yeah yeah yeah yeah) 5 : You're the one I like

De nombreuses autres fonctions

Il existe de nombreuses autres fonctions permettant de manipuler les fichiers comme :

  • fputs : le pendant de fgets pour écrire une ligne.
  • fseek: pour déplacer le "curseur" dans le fichier.
  • unlink : supprime un fichier.
  • fgetc : travaille au niveau des caractères.
  • fgetcsv : pour lire des lignes dans un fichier au format CSV.
  • ... une liste plus exhaustive est diponible -> ici <-.

Fichiers JSON : json_encode et json_decode

PHP est aussi capable de manipuler des chaines de caractères au format JSON, et donc par extension de travailler avec des fichiers .json pour stocker/récupérer des données.

NB: d'une manière générale, pour stocker/récupérer des données, on travaillera de préférence en couplant PHP avec un (ou des) serveur(s) de Bases de Données.
Néanmoins, l'utilisation de fichiers JSON peut s'avérer pratique quand il y a peu d'information à manipuler.

json_encode et json_decode permettent d'encoder/décoder des objets au format JSON.

Exemple d'écriture d'un objet en JSON

Dans cet exemple, on crée le tableau associatif $data (un objet PHP), on l'encode en JSON dans une variable $json, puis on enregistre la chaine générée dans le fichier data.json.

put_json.php
<?php

// création d'un objet
$data = array(
   'fname' => "Grégory",
   'lname' => "Bourguin",
    'email' => "gregory.bourguin@univ-littoral.fr"
) ;

// encodage de l'objet
$json = json_encode($data, JSON_PRETTY_PRINT) ;
// JSON_PRETTY_PRINT met des \n dans la chaine pour qu'elle soit plus lisible

// enregistrement dans un fichier
file_put_contents(__DIR__."/data.json", $json) ;

Voici le contenu du fichier généré :
data.json
{
    "fname": "Gr\u00e9gory",
    "lname": "Bourguin",
    "email": "gregory.bourguin@univ-littoral.fr"
}

Exemple de lecture d'un objet encodé dans fichier JSON

L'exemple ci-dessous récupère le contenu du fichier JSON, reconstruit l'objet qui y a été sérialisé, et affiche ces données dans une page Web.

get_json.php
<?php
// lecture du fichier
$json = file_get_contents(__DIR__."/data.json") ;

// décodage
$data = json_decode($json) ;
?>

<!--utilisation de l'objet-->
<p>Prénom : <b><?php echo $data->fname ?></b></p>
<p>Nom :    <b><?php echo $data->lname ?></b></p>
<p>Email :  <b><?php echo $data->email ?></b></p>
Résultat

Prénom : Grégory

Nom : Bourguin

Email : gregory.bourguin@univ-littoral.fr

Exercice 01

Soit le fichier jokes.json contenant des blagues au format JSON.
(NB: ces blagues ont été téléchargées sur le site https://icanhazdadjoke.com/)

Créez le script PHP jokes.php qui :

  • récupère les blagues enregistrées dans le fichier jokes.json.
  • génère du code HTML qui affiche ces blagues.

Attention: même si cela ressemble à ce que nous avions fait dans le cours de Javascript, il faut ici lire les données et afficher les blagues exclusivement en PHP !

Résultat attendu :
Why did the cowboy have a weiner dog? Somebody told him to get a long little doggy.
Me: If humans lose the ability to hear high frequency volumes as they get older, can my 4 week old son hear a dog whistle? Doctor: No, humans can never hear that high of a frequency no matter what age they are. Me: Trick question... dogs can't whistle.
What did the dog say to the two trees? Bark bark.
I adopted my dog from a blacksmith. As soon as we got home he made a bolt for the door.
My dog used to chase people on a bike a lot. It got so bad I had to take his bike away.
What kind of dog lives in a particle accelerator? A Fermilabrador Retriever.
I went to the zoo the other day, there was only one dog in it. It was a shitzu.
“My Dog has no nose.” “How does he smell?” “Awful”
what do you call a dog that can do magic tricks? a labracadabrador
It was raining cats and dogs the other day. I almost stepped in a poodle.
At the boxing match, the dad got into the popcorn line and the line for hot dogs, but he wanted to stay out of the punchline.
What did the Zen Buddist say to the hotdog vendor? Make me one with everything.