Written by
Yoann Pigné
Updated on
Updated on
Frameworks Web en PHP
Prérequis
Quelques technologies nécessaires :
- HTTP
- REST
- Sécurité
Hypertext Transfer Protocol (HTTP)
HTTP est un protocole applicatif pour les systèmes distribués. Il est générique et sans état.
- Message de requête (du client au serveur)
- Message de réponse (du serveur au client)
- Messages en format texte / Multipurpose Internet Mail Extensions (MIME)
Structure d’un message générique
generic-message = start-line
*(message-header CRLF)
CRLF
[ message-body ]
start-line = Request-Line | Status-Line
En-têtes et entités HTTP
message-header = field-name : [ field-value ]
field-name = token
field-value = *( field-content | LWS )
field-content = <the OCTETs making up the field-value
and consisting of either *TEXT or combinations
of token, separators, and quoted-string>
Exemple d’en-têtes de requête
Host: tools.ietf.org
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: UTF-8,*;q=0.5
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17
Requêtes HTTP
Les requêtes sont envoyées du client au serveur.
Request = Request-Line
*(( general-header
| request-header
| entity-header ) CRLF)
CRLF
[ message-body ]
Ligne de requête
Request-Line = Method SP Request-URI SP HTTP-Version CRLF
Exemple
GET /project/65142b1a71134a60df97ad24 HTTP/2
Méthodes HTTP
OPTIONS
: Informations sur les options de communication disponibles.GET
: Récupère une ressource identifiée par l’URI de la requête.HEAD
: CommeGET
mais sans le corps du message en réponse.POST
: Envoie des données pour être traitées par le serveur.PUT
: Remplace ou crée une ressource.DELETE
: Supprime une ressource.TRACE
: Diagnostic de la requête.CONNECT
: Utilisé avec un proxy pour établir un tunnel.
Réponses HTTP
Response = Status-Line
*(( general-header
| response-header
| entity-header ) CRLF)
CRLF
[ message-body ]
Exemple
HTTP/1.1 200 OK
HTTP/2 ne définit pas de Reason-Phrase :
HTTP/2 200
Codes de statut
1xx
: Information - Requête reçue, en cours de traitement.2xx
: Succès - L’action a été reçue, comprise et acceptée.3xx
: Redirection - Action supplémentaire nécessaire.4xx
: Erreur client - Mauvaise syntaxe ou requête invalide.5xx
: Erreur serveur - Problème interne du serveur.
Representational State Transfer (REST)
REST
est un style d’architecture logicielle pour les systèmes distribués sur HTTP.
- Chaque ressource est accessible via une requête unique (URI).
- Les requêtes sont sans état.
- Les ressources sont accessibles individuellement ou sous forme de collections.
- APIs RESTful.
API RESTful et Méthodes HTTP
Ressource | GET | PUT | POST | DELETE |
---|---|---|---|---|
Collection URI | Liste | Remplace | Crée | Supprime |
Élément URI | Récupère | Remplace | Non utilisé | Supprime |
Sécurité
Trop de vulnérabilités existent… Mais les développeurs sont responsables de leur code !
Attaques courantes
- Cross-Site Request Forgery (CSRF)
- Cross-Site Scripting (XSS)
- Injection SQL
Mesures de protection
- Tokens Anti-CSRF
- Expiration des sessions
- Échappement des entrées utilisateur
Réduisez les vulnérabilités… Utilisez des frameworks !
MVC (Model-View-Controller)
Le modèle MVC permet de :
- Améliorer la séparation des responsabilités.
- Faciliter les tests unitaires.
- Améliorer la collaboration en équipe.
Modèle
- Contient les données.
- Gère la persistance (base de données).
- Indépendant des autres composants.
Vue
- Représente les données.
- Interface utilisateur.
- Peut connaître le modèle.
Contrôleur
- Gère les requêtes utilisateur.
- Met à jour le modèle.
- Déclenche les vues.
MVC en PHP
Un simple script PHP (mal conçu)
Affiche des actualités et permet de commenter.
<?php
$pdo = new \PDO("mysql:host=127.0.0.1;port=3306;dbname=database", 'user', 'password');
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$news_id = $_POST['news_id'];
$stmt = $pdo->query("INSERT INTO commentaires SET news_id='$news_id',
auteur='" . $_POST['auteur'] . "', texte='" . $_POST['texte'] . "', date=NOW()");
header("location: " . $_SERVER['SCRIPT_NAME'] . "?news_id=$news_id");
exit;
} else {
$news_id = $_GET['news_id'];
}
?>
<html><head><title>Les news</title></head>
<body>
<div id="news">
<?php
$news_req = $pdo->query("SELECT * FROM news WHERE id='$news_id'");
$news = $news_req->fetch();
?>
<h1>À la une : <?php echo $news['titre'] ?></h1>
<h4>postée le <?php echo $news['date'] ?></h4>
<p><?php echo $news['texte_nouvelle'] ?> </p>
<?php
$comment_req = $pdo->query("SELECT * FROM commentaires
WHERE news_id='$news_id'");
$nbre_comment = $comment_req->rowCount();
?>
<h3><?php echo $nbre_comment ?> commentaires relatifs à cette news</h3>
<ul>
<?php while ($comment = $comment_req->fetch()) { ?>
<li><b><?php echo $comment['auteur'] ?></b>
a écrit le <?php echo $comment['date'] ?>
<p><?php echo $comment['texte'] ?></p>
<?php } ?>
</ul>
<form method="POST" action="<?php echo $_SERVER['SCRIPT_NAME'] ?>" name="ajoutcomment">
<input type="hidden" name="news_id" value="<?php echo $news_id ?>">
<label>Votre nom : </label><input type="text" name="auteur"><br />
<label>Votre commentaire : </label> <textarea name="texte" rows="5" cols="20" >
</textarea><br />
<input type="submit" name="submit" value="Envoyer">
</form>
</div>
</body></html>
Problèmes du code
- Sécurité : Injection SQL possible, aucune validation des entrées.
- Responsabilités mélangées : Requête SQL, affichage et logique dans un même fichier.
- Difficulté de maintenance : Difficile d’ajouter de nouvelles fonctionnalités.
Version MVC : Une meilleure approche
Modèle
// simpleModel.php
function dbconnect() {
static $connect = null;
if ($connect === null) {
try {
$connect = new PDO("mysql:dbname=" . getenv('DB_NAME') . ";host=" . getenv('DB_HOST'), getenv('DB_USER'), getenv('DB_PASSWORD') );
$connect->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) { echo 'Connection failed :( : ' . $e->getMessage(); exit;}
}
return $connect;
}
function get_news($id) {
try{
$sql = "SELECT * FROM news WHERE id= :id";
$sth = dbconnect()->prepare($sql);
$sth->execute(array(':id' => $id));
if($sth->errorCode() == 0) {
return $sth->fetch();
}
else {
return array();
}
}catch (PDOException $e) { echo 'Select comments failed: ' . $e->getMessage(); exit;}
}
function get_comments($news_id) {
try {
$sql = "SELECT * FROM commentaires WHERE news_id= :news_id";
$sth = dbconnect()->prepare($sql);
$sth->execute(array(':news_id' => $news_id));
if($sth->errorCode() == 0) {
return $sth->fetchAll();
}
else {
return array();
}
}catch (PDOException $e) { echo 'Select comments failed: ' . $e->getMessage(); exit;}
}
function insert_comment($comment) {
$connect = dbconnect();
try{
$sql = "INSERT INTO commentaires SET news_id= :news_id , " .
"auteur= :auteur , " .
"texte= :texte , " .
"date=NOW()";
$sth = $connect->prepare($sql);
$sth->execute(array(':news_id' => (int)$comment['news_id'],
':auteur' => $connect->quote($comment['auteur']),
':texte' => $connect->quote($comment['texte']),
)
);
} catch(PDOException $e) { echo 'Insert failed: ' . $e->getMessage(); exit;}
}
Vue
<!-- simpleView.php -->
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Les news</title>
</head>
<body>
<h1>Les News</h1>
<div id="news">
<?php if ($news): ?>
<h2><?= htmlspecialchars($news['titre']) ?> postée le <?= htmlspecialchars($news['date']) ?></h2>
<p><?= nl2br(htmlspecialchars($news['texte_nouvelle'])) ?></p>
<h3><?= $nbre_comment ?> commentaire(s) relatif(s) à cette nouvelle</h3>
<dl>
<?php foreach ($comments as $comment): ?>
<dt><?= htmlspecialchars($comment['auteur']) ?>, le <?= htmlspecialchars($comment['date']) ?>:</dt>
<dd><?= nl2br(htmlspecialchars($comment['texte'])) ?></dd>
<?php endforeach; ?>
</dl>
<?php else: ?>
<p>Cette news n'existe pas.</p>
<?php endif; ?>
<h3>Un commentaire ?</h3>
<form method="POST" action="<?= htmlspecialchars($_SERVER['SCRIPT_NAME']) ?>" name="ajoutcomment">
<input type="hidden" name="news_id" value="<?= $news_id ?>">
<input type="text" name="auteur" placeholder="Votre nom" required><br>
<textarea name="texte" placeholder="Saisissez votre commentaire" required></textarea><br>
<input type="submit" name="submit" value="Envoyer">
</form>
</div>
</body>
</html>
Contrôleur
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
insert_comment($_POST);
header("Location: ?news_id=" . $_POST['news_id']);
exit;
}
$news = get_news($_GET['news_id']);
$comments = get_comments($_GET['news_id']);
$nbre_comment = sizeof($comments);
require ('simpleView.php');
Frameworks PHP MVC
Quelques frameworks populaires :
- Laravel
- Symfony
- CodeIgniter
- Laminas (Zend)
- Yii
- CakePHP
- Slim
- Phalcon
- FuelPHP
- Fat-Free Framework