Professional Documents
Culture Documents
Tamba KOLANI
Master Génie Logiciel
Email : koltemb@gmail.com
Programme
Éléments de syntaxe langage PHP
Base du langage
PHP orienté Objet
PDO (PHP Data Objects) et bases de données
Architecture d’un site
Les PSR
PHP et Design patterns
modèle MVC, contrôleur unique
Introduction a AJAX
Les frameworks (Symfony, Laravel, ...)
Application Web Restful
Evaluation
Examen final : 40%
Projet : 60%
Éléments de syntaxe langage PHP
Architecture client-serveur
Une architecture client-serveur représente l’environnement dans lequel des
applications de machines clientes communiquent avec des applications de machines
de type serveurs.
L’exemple classique est le navigateur Web d’un client qui demande (on parle de
“requête”) le contenu d’une page Web à un serveur Web qui lui renvoie le résultat
(on parle de “réponse”).
Le client : C’est le visiteur d’un site Web. Il demande la page Web au serveur.
En pratique, vous êtes des clients quand vous surfez sur le Web. Plus
précisément c’est votre navigateur Web (Firefox, Chrome, Safari, IE, Edge,
…) qui est le client car c’est lui qui demande la page Web.
Le serveur : Ce sont les ordinateurs qui délivrent les sites Web aux
internautes, c’est-à-dire aux clients.
?>
Une variable dynamique prend la valeur d'une variable et l'utilise comme nom d'une
autre variable. On utilise pour cela "$$"
<?php
$a = "Bonjour";
$$a = "SUPINFO";
?>
Notion de fonction
Une fonction peut être définie en utilisant la syntaxe suivante
<?php
function mafonction($arg_1, $arg_2, /* ..., */ $arg_n)
{
echo "Exemple de fonction.\n";
return $valeur;
}
?>
Ici on peut typer les variables passées en argument afin de forcer à entrer les valeurs
convenable aux paramètres lors de l’appel de la fonction
<?php
function mafonction(int $arg_1, int $arg_2){
$valeur = $arg_1 + $arg_2;
return $valeur;
}
?>
Notion de tableau
Les tableaux en PHP peuvent s’indexer par des entiers ou des chaînes de caractères
Pour créer un tableau vide, on utilise la syntaxe
<?php
$mon_tableau = array();
?>
Notez l’existence des boucles foreach pour parcourir les paires clé/valeur des
tableaux.
<?php
foreach ($mon_tableau as $cle => $valeur){
//commandes
}
?>
La boucle foreach va boucler sur les associations du tableau. Pour chaque association,
foreach va mettre la clé de l’association dans la variable $cle et la valeur dans $valeur
puis exécuter les commandes. Par exemple
<?php
foreach ($coordonnees as $cle => $valeur){
echo "$cle : $valeur\n";
}
?>
Remarque : La boucle foreach est indispensable pour parcourir les indices et valeurs
d’un tableau indexé par des chaînes de caractères.
Il existe aussi bien sûr une boucle for classique si le tableau est indexé uniquement
par des entiers
<?php
for ($i = 0; $i < count($mon_tableau); $i++) {
echo $mon_tableau[$i];
}
?>
private $marque;
private $couleur;
private $immatriculation;
// un constructeur
public function __construct($m, $c, $i) {
$this->marque = $m;
$this->couleur = $c;
$this->immatriculation = $i;
}
// un getter
public function getMarque() {
return $this->marque;
}
// un setter
public function setMarque($m) {
$this->marque = $m;
}
/*
Les autres getters et setters
*/
Méthode POST
Requêtes préparées
La plupart des bases de données supportent le concept des requêtes préparées. C’est
une sorte de modèle compilé pour le SQL que vous voulez exécuter, qui peut être
personnalisé en utilisant des variables en guise de paramètres. Les requêtes préparées
offrent deux fonctionnalités essentielles :
La requête ne doit être analysée (ou préparée) qu'une seule fois, mais peut être
exécutée plusieurs fois avec des paramètres identiques ou différents. Lorsque
la requête est préparée, la base de données va analyser, compiler et optimiser
son plan pour exécuter la requête. Pour les requêtes complexes, ce processus
peut prendre assez de temps, ce qui peut ralentir vos applications si vous devez
répéter la même requête plusieurs fois avec différents paramètres. En utilisant
les requêtes préparées, vous évitez ainsi de répéter le cycle
analyse/compilation/optimisation. Pour résumer, les requêtes préparées
utilisent moins de ressources et s'exécutent plus rapidement.
Les paramètres pour préparer les requêtes n'ont pas besoin d'être entre
guillemets ; le pilote gère cela pour vous. Si votre application utilise
exclusivement les requêtes préparées, vous pouvez être sûr qu'aucune
injection SQL n'est possible (Cependant, si vous construisez d'autres parties
de la requête en vous basant sur des entrées utilisateurs, vous continuez à
prendre un risque).
Les requêtes préparées sont tellement pratiques que c'est l'unique fonctionnalité que
PDO émule pour les pilotes qui ne les prennent pas en charge. Ceci assure de pouvoir
utiliser la même technique pour accéder aux données, sans se soucier des capacités
de la base de données.
Exemples
<?php
$stmt = $dbh-
>prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);
L’exemple suivant substitue un nom et une valeur pour les marqueurs point
d’interrogation (?).
<?php
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (?, ?)");
$stmt->bindParam(1, $name);
$stmt->bindParam(2, $value);
Cet exemple récupère des données basées sur la valeur d'une clé fournie par un
formulaire. L'entrée utilisateur est automatiquement échappée, il n'y a donc aucun
risque d'attaque par injection SQL.
<?php
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?");
$stmt->execute([$_GET['name']]);
foreach ($stmt as $row) {
print_r($row);
}
?>
PDO::ERRMODE_EXCEPTION
À partir de PHP 8.0.0, c'est le mode par défaut. En plus de définir le code d'erreur,
PDO lancera une exception PDOException et y définit les propriétés afin de
représenter le code d'erreur et les informations complémentaires. Cette configuration
est également utile lors du débogage, car elle va « contourner » le point critique de
votre code, vous montrer rapidement le problème rencontré (souvenez-vous : les
transactions sont automatiquement annulées si l'exception fait que votre script se
termine).
Le mode "exception" est également très utile car ainsi, vous pouvez structurer votre
gestionnaire d'erreur plus clairement qu'avec les alertes traditionnelles PHP et, ce,
avec moins de code que lorsque vous exécutez votre code en mode silence, et que
vous vérifiez systématiquement les valeurs retournées après chaque appel à la base
de données.
Exemples
<?php
$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
$user = 'dbuser';
$password = 'dbpass';
try {
$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo 'Échec lors de la connexion : ' . $e->getMessage();
}
?>
/*
L'utilisation des blocs try/catch autour du constructeur est toujours
valide
même si nous définissons le ERRMODE à WARNING sachant que PDO::__construct
va toujours lancer une exception PDOException si la connexion échoue.
*/
try {
$dbh = new PDO($dsn, $user, $password, array(PDO::ATTR_ERRMODE => PDO:
:ERRMODE_WARNING));
} catch (PDOException $e) {
echo 'Échec de la connexion : ' . $e->getMessage();
exit;
}
// Ceci fera que PDO lancera une erreur de niveau E_WARNING au lieu d'une
exception (lorsque la table n'existe pas)
$dbh->query("SELECT wrongcolumn FROM wrongtable");
?>
Exercice
Créer une classe Etudiant et réaliser le CRUD avec PDO. On réaliser une interface
web pour cela.
Chaque PSR est suggéré par les membres et voté selon un protocole établi pour agir
de manière cohérente et conformément aux processus convenus.
Fichiers
Balises PHP
Le code PHP DOIT utiliser la balise longue <?php ?> ou la balise courte echo <?=
?> ; Il NE DOIT PAS utiliser d'autre variation.
Encodage de caractères
Le fichier PHP DOIT utiliser UNIQUEMENT l'encodage UTF-8 sans BOM.
Effets de bord
Un fichier DEVRAIT déclarer de nouveaux symboles (classes, fonctions, constantes,
etc.) et ne pas causer d'autre effet de bord, ou il DEVRAIT exécuter de la logique
avec effet de bord, mais NE DEVRAIT PAS faire les deux.
Les termes “effet de bord” signifient l'exécution de logique qui n'est pas directement
liée à une déclaration de classe, fonction, constante...etc., seulement en incluant le
fichier.
“Effets de bord” inclut mais n'est pas limité à : génération de sortie, utilisation
explicite de require ou include, se connecter à un service externe, modifier les
paramètres ini, émettre des erreurs ou exceptions, modifier des variables globales ou
statiques, lire ou écrire dans un fichier...
L'exemple ci-dessous est un exemple contenant déclaration et effet de bord, un
exemple de ce qu'il faut éviter :
<?php
// effet de bord : modification des paramètres ini
ini_set('error_reporting', E_ALL);
// effet de bord : chargement d'un fichier
include "file.php";
// effet de bord : génère une sortie
echo "<html>\n";
// déclaration
function foo(){
// code de la fonction
}
PHP
L'exemple suivant correspond à un fichier qui contient des déclarations mais pas
d'effets de bord :
<?php
// déclaration
function foo()
{
// code de la fonction
}
// déclaration conditionnelle n'est *pas* un effet de bord
if (! function_exists('bar')) {
function bar()
{
// code de la fonction
}
}
PHP
Noms de Namespace et Class
Les Namespaces et classes DOIVENT suivre un PSR "autoloading” [PSR-0, PSR-
4].
Cela signifie que chaque classe est un fichier par lui-même, et se trouve dans un
namespace d'au moins un niveau, un nom de "vendor".
Les noms de classes DOIVENT être déclarés en UpperCamelCase.
Par exemple:
<?php
namespace Vendor\Model;
class Foo
{
}
PHP
Constantes, Propriétés et Méthodes de classes
Le terme “classe” se réfère à toutes les classes, interfaces et traits.
Constantes
Les constantes de classes DOIVENT être déclarées en majuscules avec séparateur
underscore. Par exemple :
<?php
namespace Vendor\Model;
class Foo
{
const VERSION = '1.0';
const DATE_APPROVED = '2012-06-01';
}
PHP
Propriétés
Le guide officiel évite de façon volontaire toute recommandation concernant
l'utilisation de $UpperCamelCase, $camelCase, ou $under_score pour les noms de
propriétés.
Méthodes
Les noms de méthodes DOIVENT être déclarés en camelCase().
PSR-2
Il existait le PSR-2, qui indiquait quelques bonnes pratiques de codage pour les
variables et fonctions. Ce standard a été déprécié à ce stade.
PSR-4 - Autoloader
Les mots-clés “DOIT”, “NE DOIT PAS”, “REQUIS”, “PEUT”, “POURRAIT NE
PAS”, “DEVRAIT”, “NE DEVRAIT PAS”, “RECOMMANDE” et
“OPTIONNEL” dans ce document correspondent à la terminologie décrite dans la
RFC 2119.
Résumé
Cette PSR décrit une spécification pour mettre en place l'autoloading de classes
depuis un chemin de fichiers. Il est totalement interopérable, et peut être utilisé en
complément de tout autre standard d'autoloading, incluant PSR-0. Cette PSR décrit
également où placer les fichiers qui seront chargés automatiquement en accord avec
la spécification.
Spécification
Le terme “classe” se réfère à toutes les classes, interfaces et traits, et autres structures
similaires.
Un nom de classe totalement qualifié a la forme suivante :
\<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>
1. Un nom de classe totalement qualifié DOIT avoir un namespace principal,
également appelé “vendor namespace”.
2. Un nom de classe totalement qualifié PEUT avoir un ou plusieurs noms de
"sous namespaces".
3. Un nom de classe totalement qualifié DOIT se terminer par un nom de classe.
4. Les underscores n'ont pas de signification dans le nom de classe totalement
qualifié.
5. Les caractères alphabétiques dans le nom de classe totalement qualifié
PEUVENT être toute combinaison de majuscules et minuscules.
6. Tous les noms de classes DOIVENT être référencés dans un modèle sensible
à la casse.
7. Lors du chargement d'un fichier qui correspond à un nom de classe totalement
qualifié…
8. Une série continue d'un ou plusieurs namespaces et sous namespaces, sans
compter le séparateur d'espace de noms principal, dans le nom de classe
pleinement qualifié (un «préfixe d'espace de noms») correspond à au moins un
«répertoire de base».
9. Les noms de sous-namespaces contigus après le «préfixe d'espace de noms»
correspondent à un sous-répertoire dans un «répertoire de base», dans lequel
les séparateurs d'espaces de noms représentent des séparateurs de répertoires.
Le nom du sous-répertoire DOIT correspondre à la casse des noms de sous-
namespaces.
10. Le nom de la classe correspond à un nom de fichier se terminant par .php. Le
nom de fichier DOIT correspondre à la casse du nom de classe.
11. Les implémentations d'autoloader NE DOIVENT PAS lancer d'exceptions,
NE DOIVENT PAS déclencher d'erreurs de quelque niveau que ce soit et
NE DEVRAIENT PAS renvoyer de valeur.
Le routeur
Dans la structure MVC, un seul et unique fichier est le point d'entrée de l'application,
quelle que soit la page affichée. Il est systématiquement appelé, et envoie la demande
au bon contrôleur. Il est chargé de trouver le bon chemin pour que l'utilisateur
récupère la bonne page, d'où le nom de routeur.
Cas pratique
http://url_du_site/controleur/methode
Cette url permettra à notre routeur de comprendre qu'il doît pointer vers le
contrôleur mentionné en premier paramètre et la méthode de ce contrôleur
mentionnée en deuxième paramètre.
Le fichier .htaccess
RewriteEngine On
Donnera : http://url_du_site/index.php?p=articles/lire
Le fichier index.php
Dans ce fichier il faut gérer les données de l'URL. Ce fichier sera mis à jour au fur et
à mis de ce cours
Avant d'écrire notre routeur, nous allons appeler dans ce fichier le contrôleur et le
modèle principaux qui serviront de base commune à tous les fichiers.
Afin d'assurer la portabilité du projet sur toutes les configurations, nous allons baser
nos appels sur un chemin généré automatiquement. Nous allons le sauvegarder dans
une constante que nous appellerons "ROOT".
Cette constante sera générée depuis une des informations stockées dans la variable
super globale "$_SERVER" qui contient le chemin complet vers notre fichier.
define('ROOT', str_replace('index.php','',$_SERVER['SCRIPT_FILENAME']));
On peut maintenant utiliser "ROOT" pour appeler nos fichiers comme ceci :
require_once(ROOT.'app/Model.php');
require_once(ROOT.'app/Controller.php');
Il faudra garder en tête qu'une action dans l'URL correspondra à une méthode
(fonction) dans un contrôleur.
Pour commencer, nous devons récupérer chacun des paramètres, s’ils existent, et les
affecter à des variables. En effet, la page d'accueil n'aura pas de paramètre par
exemple (http://nom_dusite/).
Nous allons utiliser la fonction explode de php pour séparer chacun des paramètres
et générer un tableau. Puis nous allons tester les valeurs et enfin affecter les variables,
si besoin.
?>
Le routeur est maintenant capable de lire une URL. Il faut maintenant diriger la
demande vers le bon contrôleur et dans ce contrôleur vers la bonne méthode. Etant
donné que nous développons en PHP orienté objet, chaque contrôleur correspondra
à une classe.
Dans la suite, nous manipulerons des articles. La page d'accueil des articles en
affichera la liste complète. Nous aurons donc une méthode "index" qui accèdera à
cette liste.
Si nous souhaitions accéder à cette méthode, sans routeur, nous écririons le code
suivant
<?php
require_once(ROOT.'controllers/Articles.php');
$articles = new Articles();
$articles->index();
?>
Avec notre routeur, nous avons ces informations dans des variables :
<?php
require_once(ROOT.'controllers/'.$controller.'.php');
$controller = new $controller();
$controller->$action();
?>
// On appelle la méthode
$controller->$action();
}else{
// Ici aucun paramètre n'est défini
}
?>
Il nous reste maintenant à gérer les erreurs et l'absence de paramètres. Une erreur,
c'est un contrôleur ou une action qui n'existent pas.
Nous avons une fonction php qui nous permet de vérifier si une méthode existe dans
une classe. Cette fonction s'appelle "method_exists()" et prend deux paramètres,
la classe ou une instance de la classe (ici le contrôleur) et la méthode (action). Intégrée
à notre routeur, on a donc :
<?php
if(method_exists($controller, $action)){
// On appelle la méthode
$controller->$action();
}else{
// On envoie le code réponse 404
http_response_code(404);
echo "La page recherchée n'existe pas";
}
?>
En cas d'absence de paramètres, nous appellerons un contrôleur par défaut que nous
appellerons "Main" et sa méthode "index".
Nous ajouterons donc en fin de fichier, dans le "else", les lignes suivantes
<?php
// Ici aucun paramètre n'est défini
// On appelle le contrôleur par défaut
require_once(ROOT.'controllers/Main.php');
// On instancie le contrôleur
$controller = new Main();
// On appelle la méthode index
$controller->index();
?>
Les contrôleurs
Ils se situent entre la base de données et les vues et sont chargés de demander et
traiter les données avant de les envoyer vers les vues.
Le contrôleur principal
Le contrôleur principal est le contrôleur qui contiendra les méthodes nécessaires à
tous les autres. Ceci nous évitera de répéter les mêmes méthodes plusieurs fois.
Il sera situé dans le dossier "app" et s'appellera "Controller.php".
Nous allons commencer par un contrôleur vide mais nous devrons rapidement y
inclure du contenu.
<?php
abstract class Controller{
}
?>
Cette classe étant abstraite, on ne pourra donc pas l’instancier, mais elle sera utilisée
par héritage dans tous les autres contrôleurs.
Le contrôleur "Articles"
Créons ce contrôleur avec une action par défaut qui est la méthode "index"
<?php
class Articles extends Controller{
/**
* Cette méthode affiche la liste des articles
* @return void
*/
public function index(){
echo "Ici nous aurons la liste des articles";
}
}
?>
Accéder ensuite à l’url suivant : http://url_du_site/articles
Nous allons donc créer les contrôleurs en suivant ce modèle, mais nous aurons
besoin de données, nous devrons donc charger des fichiers "model" pour pouvoir y
accéder.
Les modèles
Le modèle principal
Le modèle principal sera une classe abstraite "Model" qui sera incluse par héritage
dans tous nos modèles. Il servira principalement à initialiser la connexion à la base
de données.
<?php
abstract class Model{
// Informations de la base de données
private $host = "localhost";
private $db_name = "nom_de_la_base";
private $username = "nom_utilisateur";
private $password = "mot_de_passe";
// Propriété qui contiendra l'instance de la connexion
protected $_connexion;
// Propriétés permettant de personnaliser les requêtes
public $table;
public $id;
/**
* Fonction d'initialisation de la base de données
* @return void
*/
public function getConnection(){
// On supprime la connexion précédente
$this->_connexion = null;
// On essaie de se connecter à la base
try{
$this->_connexion = new PDO("mysql:host=".$this-
>host . ";dbname=" . $this->db_name, $this->username, $this->password);
$this->_connexion->exec("set names utf8");
}catch(PDOException $exception){
echo "Erreur de connexion : " . $exception->getMessage();
}
}
}
?>
Les méthodes communes
Il peut être utile de créer des méthodes communes à tous les modèles. Par
exemple, il sera fréquent de vouloir obtenir un unique enregistrement ou au
contraire tous les enregistrements d'une table donnée. Nous avons 2 propriétés
publiques (protégé de préférence) "table" et "id" qui nous permettront de créer ces
requêtes.
Nous allons les appeler "getOne" pour un enregistrement et "getAll" pour tous les
enregistrements.
/**
* Méthode permettant d'obtenir un enregistrement de la table choisie en f
onction d'un id
*
* @return void
*/
public function getOne(){
$sql = "SELECT * FROM ".$this->table." WHERE id=".$this->id;
$query = $this->_connexion->prepare($sql);
$query->execute();
return $query->fetch();
}
/**
* Méthode permettant d'obtenir tous les enregistrements de la table chois
ie
*
* @return void
*/
public function getAll(){
$sql = "SELECT * FROM ".$this->table;
$query = $this->_connexion->prepare($sql);
$query->execute();
return $query->fetchAll();
}
Le modèle "Article"
Une classe "Articles" existant déjà pour un contrôleur, nous allons nommer notre
modèle "Article.php" et le stocker dans "models". Cette classe "Article" va hériter
du modèle principal.
Nous allons devoir utiliser le constructeur pour instancier la base de données dès
que le modèle sera lui-même instancié. Pour ce faire nous allons ajouter la méthode
magique "__construct"
Notre contrôleur "Articles" nécessite l'affichage des derniers articles lors de l'appel
de la méthode "index".
Nous allons donc devoir demander à notre contrôleur d'utiliser le modèle "Article"
et de récupérer tous les enregistrements.
/**
* Permet de charger un modèle
*
* @param string $model
* @return void
*/
public function loadModel(string $model){
// On va chercher le fichier correspondant au modèle souhaité
require_once(ROOT.'models/'.$model.'.php');
// On crée une instance de ce modèle. Ainsi "Article" sera accessible
par $this->Article
$this->$model = new $model();
}
Dans tous les contrôleurs il nous suffira d'appeler un modèle de cette façon
$this->loadModel('Article');
/**
* Cette méthode affiche la liste des articles
*
* @return void
*/
public function index(){
// On instancie le modèle "Article"
$this->loadModel('Article');
Les vues
L'intérêt de toute application web est d'afficher les données de façon propre et
structurée aux utilisateurs. Nous allons donc devoir générer du code html qui
contiendra les données de la base de données.
Pour ce faire, nous allons créer des vues. Ces fichiers contiendront principalement
du HTML mais également un peu de PHP pour afficher les données.
Pour commencer, nous allons créer un dossier "articles" dans "views" et un premier
fichier "index.php" à l'intérieur. Nous utiliserons comme "convention" de nommer
les fichiers de vue de la même façon que la méthode qui les appellera.
Pour simplifier les choses, nous allons créer une méthode d'affichage des
vues dans le contrôleur principal. Cette méthode permettra de charger toutes les
vues, quel que soit le contrôleur qui va y faire appel.
Pour y parvenir nous utiliserons la fonction php "get_class" qui permet de récupérer
la classe du contrôleur. Nous la passerons en minuscule, convention utilisée pour les
noms de dossiers, et l'intègrerons au chemin d'accès au fichier de vues.
/**
* Afficher une vue
*
* @param string $fichier
* @param array $data
* @return void
*/
public function render(string $fichier, array $data = []){
// Récupère les données et les extrait sous forme de variables
extract($data);
Cette méthode devra appeler la vue "index" et lui passer les articles sous forme de
tableau.
Nous allons donc appeler la méthode "render" et lui faire passer les informations.
Les données devront être passées sous forme de tableau. Nous pourrons utiliser la
fonction php "compact" pour créer le tableau.
<?php
$this->render('index', ['articles' => $articles]);
?>
<?php
$this->render('index', compact('articles'));
?>
<?php
/**
* Cette méthode affiche la liste des articles
*
* @return void
*/
public function index(){
// On instancie le modèle "Article"
$this->loadModel('Article');
// On stocke la liste des articles dans $articles
$articles = $this->Article->getAll();
Passons maintenant à l'affichage de nos données. Nous devons créer du code HTML
et y insérer les données envoyées par le contrôleur.
Pour afficher les données, nous allons devoir boucler sur la variable "articles" et
afficher les informations à chaque boucle.
Cependant, il nous manque une chose, le lien pour aller lire l'article. Nous allons donc
devoir créer une nouvelle "route" pour y accéder, et cette route devra prendre un
paramètre complémentaire. Pour la lecture d'un article, nous définirons la route
comme suit
http://url_du_site/articles/lire/slug-de-l-article
Pour accéder à la lecture d'un article, nous avons besoin de passer un paramètre
supplémentaire à notre routeur, le "slug" de l'article.
Le routeur
Le problème est que notre routeur actuel ne prend que 2 paramètres. Comment
faire pour en ajouter un 3ème ?
Nous allons utiliser la fonction php "call_user_func_array" qui permet d'appeler
une fonction en lui faisant passer des paramètres sous forme de tableau. Cette
fonction prend en premier paramètre une classe et une méthode sous forme de
tableau.
<?php
...
call_user_func_array([$controller,$action], $params);
// la ligne ci-dessus remplacera la ligne ci-dessous
$controller->$action();
...
?>
Le problème qui se pose maintenant est que les paramètres sont en doublon, pour
les 2 premiers. Il faudra donc commencer par les supprimer. On utilise pour cela la
méthode unset() qui permet de supprimer un élément du tableau.
Notre routeur devient donc :
<?php
// On génère une constante contenant le chemin vers la racine publique du
projet
define('ROOT', str_replace('index.php','',$_SERVER['SCRIPT_FILENAME']));
// On appelle le modèle et le contrôleur principaux
require_once(ROOT.'app/Model.php');
require_once(ROOT.'app/Controller.php');
// On sépare les paramètres et on les met dans le tableau $params
$params = explode('/', $_GET['p']);
// Si au moins 1 paramètre existe
if($params[0] != ""){
// On sauvegarde le 1er paramètre dans $controller en mettant sa 1ère
lettre en majuscule
$controller = ucfirst($params[0]);
// On sauvegarde le 2ème paramètre dans $action si il existe, sinon
index
$action = isset($params[1]) ? $params[1] : 'index';
// On appelle le contrôleur
require_once(ROOT.'controllers/'.$controller.'.php');
// On instancie le contrôleur
$controller = new $controller();
if(method_exists($controller, $action)){
// On supprime les 2 premiers paramètres
unset($params[0]);
unset($params[1]);
// On instancie le contrôleur
$controller = new Main();
?>
Le modèle
Nous pouvons donc maintenant créer la méthode "lire" avec un paramètre "slug"
pour aller chercher un article.
Mais nous n'avons pas encore de méthode pour aller chercher un article à partir de
son slug, ce qui nous pose problème. Créons pour cela cette méthode dans le
modèle "Article". Cette méthode prendra en entrée un "slug" et retournera un
enregistrement de la base de données.
<?php
class Article extends Model{
public function __construct()
{
// Nous définissons la table par défaut de ce modèle
$this->table = "articles";
Le contrôleur
Notre modèle étant à jour, nous pouvons maintenant créer la méthode "lire" dans
le contrôleur "Articles".
<?php
/**
* Méthode permettant d'afficher un article à partir de son slug
*
* @param string $slug
* @return void
*/
public function lire(string $slug){
// On instancie le modèle "Article"
$this->loadModel('Article');
La vue
Dans notre projet, notre vue s'appelle "lire" et elle utilisera la variable "article". Le
fichier "lire.php" sera créé dans le dossier "views/articles" et contiendra
<?php
<h2><?= $article['titre'] ?></h2>
<p><?= $article['contenu'] ?></p>
Il est très courant d'avoir des éléments qui s'intègrent dans toutes les pages (entête,
pied de page...), il serait donc utile de pouvoir les intégrer automatiquement dans
toutes nos vues.
Sur le principe, notre "template" contiendra tous les éléments répétitifs des
pages, et nos vues viendront uniquement injecter leur contenu dans l'emplacement
réservé à cet effet
Ce fichier contiendra tout ce que nous voulons intégrer dans toutees nos pages, et
un espace réservé sous la forme d'une variable php (ici $content) pour le contenu
envoyé par les vues.
<header>
<h1>Bannière de haut de page du site</h1>
</header>
<main>
<?= $content ?>
</main>
<footer>
<p>Pied de page du site Copyright 2021</p>
</footer>
Nous allons demander à la vue de générer son contenu, puis le stocker dans une
variable et enfin l'envoyer au "template"
Le contrôleur devient :
<?php
abstract class Controller{
/**
* Afficher une vue
*
* @param string $fichier
* @param array $data
* @return void
*/
public function render(string $fichier, array $data = []){
extract($data);
// On génère la vue
require_once(ROOT.'views/'.strtolower(get_class($this)).'/'.$fichi
er.'.php');
// On stocke le contenu dans $content
$content = ob_get_clean();
// On fabrique le "template"
require_once(ROOT.'views/layout/default.php');
}
/**
* Permet de charger un modèle
*
* @param string $model
* @return void
*/
public function loadModel(string $model){
// On va chercher le fichier correspondant au modèle souhaité
require_once(ROOT.'models/'.$model.'.php');
// On crée une instance de ce modèle. Ainsi "Article" sera
accessible par $this->Article
$this->$model = new $model();
}
}
PHP et AJAX
Le langage PHP peut être couplé avec le JavaScript pour réaliser les applications
web dynamique. Les pages ici sont rafraîchies sans que l’url ne change. Le client
(navigateur) va pouvoir faire une requête vers le serveur et modifier la page
courante sans que l’url de la page courante ne change. On utilise pour cela la
technologie AJAX.
AJAX signifie Asynchronous Javascript and Xml. Il permet de créer une interface
fonctionnant sur un navigateur, avec le langage JavaScript, devenu extrêmement
rapide sur les dernières versions des navigateurs.
PHP fonctionne sur le serveur donc peut consulter une base de donnée et la mettre
à jour avec les données entrées par l'interface utilisateur, et exécute aussi les
traitements demandés par l'utilisateur qui requièrent des données stockées sur le
serveur.
Comment opère l'interaction entre l'interface en Ajax et les scripts en PHP sur le
serveur ?
Pratique