Un langage de programmation de haut niveau est plus qu’un ensemble d’instructions qui vous permet de donner des ordres à un ordinateur, c’est également un outil pour penser et organiser vos idées sur des processus de calcul permettant de résoudre un problème donné. Comme tout langage, les langages de programmation permettent de combiner des idées simples pour des idées complexes. Un langage de programmation se base sur trois éléments essentiels :
echo 1 ;
echo 0 + 1 ;
echo 5 / 2 ;
echo 3 ** 2 ;
echo 1 + 2 * 5;
echo 10 / 3 + 7 * 5 + 1 ;
echo (5 * 2) * (5 + 1) ;
echo 12.5 - 7.3 + 5.0 / 3;
echo 64 % 2 ;
echo 9 % 2 ;
echo 'a' ;
echo 'a' . 'b';
echo 'une chaine' ;
echo 'une chaine' . ' '. 'concatenée' . ' '. 'avec une autre chaine' ;
echo 1 == 2 ;
echo 2 == 2 ;
echo 3 >= 2 ;
echo 2 != 2 ;
echo true ;
echo !true ;
echo true && false ;
echo true || false ;
echo 5 <= 2 && 25 > 12.5 ;
echo sqrt(3**4 + 5**2);
echoest une instruction qui permet d’afficher le résultat sur la sortie (écran)
En PHP, il n’est pas nécessaire de définir le type de données référencée par une variable, le type est déduit au runtime :
$message = "hello, world";
//Imprimer la valeur de la variable sur la sortie
echo $message;
//Imprimer le type de la variable sur la sortie
echo gettype($message);
echo gettype(new stdClass());
//Afficher le contenu et le type de la variable $message
var_dump($message);Types principaux : boolean, int,
double, string, array,
object, NULL (absence de valeur),
callable.
Voir tous les types du langage PHP.
AND et OR
:
Pas les mêmes précédences que && et
|| !
Précédence plus faible que les opérateurs d’affectation !
$c = 8 > 5 AND 15 < 10 //$c vaut true ! car évalué comme ($c = (8 > 5)) AND (15 < 10)$action = (empty($_POST['action'])) ? 'default' : $_POST['action'];Comparaison :
== : (essaie de convertir dans même type et)
compare les valeurs;=== : compare les valeurs et
les types.<?php
//Opérateurs de comparaison :
//@link https://www.php.net/manual/en/language.operators.comparison.php
if(1 == '1') echo "1 == '1' est VRAI" . PHP_EOL;
if(1 === '1') echo "1 === '1' est FAUX" . PHP_EOL;
if(1 !== '1') echo "1 !== '1' est VRAI". PHP_EOL;Pour créer des constantes, utiliser la primitive
define() :
define("CONSTANT", "Hello world.");
echo CONSTANT; // outputs "Hello world."
define('ANIMALS', [
'dog',
'cat',
'bird'
]);
echo ANIMALS[1]; // outputs "cat"
//Afficher toutes les constantes définies et accessibles dans ce script
print_r(get_defined_constants());Par convention, écrire les constantes en
MAJUSCULES. On utilisera souvent des constantes en WordPress, notamment pour la configuration.
Le flot de contrôle définit des chemins ou flots d’execution du programme. Le flot “naturel” d’un programme est donné par son code source, de haut en bas : les instructions sont exécutées dans l’ordre dans lequel elles sont écrites.
Il est possible de moduler ce flot en faisant des sauts (jumps) d’une instruction à l’autre. Il existe trois manières de moduler le flot :
while, do..while, for) ;if/else,
switch) ;continue, break et
return.Exécuter des instructions uniquement si une condition est vraie.
<?php
$age = 18;
//Structure if simple
if ($age >= 18) {
//Ce code est exécuté seulement si la condition est vraie
echo "Accès autorisé\n";
}
//Ensuite le reste du code est exécuté
echo "Bienvenue qui que vous soyez\n";L’expression placée entre parenthèses
($age >= 18) doit pouvoir s’évaluer à vrai
(true) ou faux (false).
<?php
$age = 16;
if ($age >= 18) {
echo "Accès autorisé";
} else {
echo "Accès refusé";
}if : condition vraie ;else : condition fausse.On parle de prédicat. Par exemple la phrase “il est en train de pleuvoir” est un prédicat que l’on peut évaluer à vrai ou faux (soit il pleut, soit il ne pleut pas)
En PHP, un prédicat est une expression de comparaison ou une expression logique. Pour cela, on utilise les opérateurs relationnels (de comparaison) et logiques :
$age >= 18 // true ou false
$quantity > 0 // true ou false
$isAdmin == true // true ou false
$age >= 18 && $hasTicket // ET
$age < 18 || $isStudent // OU
!$isBlocked // NON$a == $b // égal à
$a != $b // différent de
$a < $b // inférieur à
$a > $b // supérieur à
$a <= $b // inférieur ou égal
$a >= $b // supérieur ou égal<?php
$quantity = 3;
if ($quantity > 0) {
echo "Commande valide";
} else {
echo "Quantité invalide";
}<?php
$quantity = 2;
$inStock = true;
if ($quantity > 0 && $inStock) {
echo "Commande acceptée";
}Pour chaque expression, indiquer si elle vaut
true ou false:
//Valeurs de $a et $b données
$a = 5;
$b = 10;
$a < $b
$a == $b
$a != $b
$a >= 2 * 5
$b / 2 < 5Écrire le prédicat correspondant à la phrase
L'utilisateur est connecté et n’est pas bloqué à l’aide des
variables $isLoggedIn et $isBlocked
for #Répéter une ou plusieurs instructions
<?php
//Boucle for
for($i = 0 ; $i < 100; $i = $i + 1){
//Ici on répète "imprime le nombre" autant de fois que nécessaire
echo "$i ";
}for et if #<?php
//Indiquer si un nombre est pair ou impair
for($i = 0 ; $i < 100; $i = $i + 1){
if($i % 2 === 0){
echo "$i est un nombre pair";
}else{
echo "$i est un nombre impair";
}
}for syntaxe alternative #<?php
//Boucle for
for($i = 0 ; $i < 100; $i = $i + 1) :
//Embranchement avec if/else (tests)
if($i % 2 === 0) :
echo "$i est un nombre pair";
else :
echo "$i est un nombre impair";
endif;
endfor;Sera utile dans les templates ! Plus lisible, plus pratique pour produire du HTML
while et do-while #<?php
$i = 1;
while ($i <= 10) {
echo $i++; /* La valeur affichée est $i avant l'incrémentation
(post-incrémentation) */
}
$i = 0;
do {
echo $i;
} while ($i > 0);while et do-while, syntaxe
alternative #<?php
$i = 1;
while ($i <= 10) :
echo $i++;
endwhile;switch #Alternative à if/else si les cas sont énumérables et tests d’égalité uniquement
<?php
$i=2;
switch ($i) {
case 0:
echo "i égal 0";
break;
case 1:
echo "i égal 1";
break;
case 2:
echo "i égal 2";
break;
}$i=2;
switch ($i) :
case 0:
echo "i égal 0";
break;
case 1:
echo "i égal 1";
break;
case 2:
echo "i égal 2";
break;
endswitch;foreach #Syntaxe commode pour parcourir des collections d’items. Permet de se passer ou non de l’index.
<?php
$arr = array(1, 2, 3, 4);
//Parcourir un tableau en parcourant uniquement ses valeurs
foreach ($arr as $value) {
//$value est une variable locale à la boucle qui contiendra chaque valeur du tableau (1, puis 2, puis 3, puis 4)
//a chaque itération
echo $value . PHP_EOL;
}
//Parcourir un tableau en parcourant ses clefs ET ses valeurs
//Syntaxe: foreach(tableau as clef => valeur)
foreach($arr as $index => $value){
echo "Index: {$index} - Value: {$value} \n";
}foreach syntaxe alternative #Les accolades ouvrantes et fermantes de la boucle
sont remplacées par le caractère
: et par une instruction endforeach; :
<?php
$arr = array(1, 2, 3, 4);
// foreach($arr as $index => $value){
// echo "Index: {$index} - Value: {$value} \n";
// }
foreach($arr as $index => $value) :
echo "Index: {$index} - Value: {$value} \n";
endforeach;Lorsque l’on utilise PHP pour générer des pages HTML, les structures de contrôle usuelles offrent une syntaxe alternative afin d’améliorer (un peu) la lisibilité du code source de la page.
Prenons un exemple :
//Tout le HTML est généré depuis un code PHP
$items = ['pomme', 'poire', 'raisin'];
echo "<ul>";
foreach($items as $item){
echo "<li>$item</li>";
}
echo "</ul>";Dans un template, le script PHP est essentiellement constitué de texte (code HTML), le contenu est donc imprimé sur la sortie sans modification. Les balises PHP ne sont ouvertes que pour insérer des données dynamiques :
//Le HTML est écrit directement, PHP est seulement utilisé pour les données dynamiques
<?php $items = ['pomme', 'poire', 'raisin']; ?>
<ul>
<?php foreach ($items as $item) { ?>
<li><?php echo $item ?></li>
<?php } ?>
</ul>Syntaxe encore difficile à lire…
Idem que précédemment mais avec la syntaxe
alternative pour le foreach :
//Le HTML est écrit directement, PHP est seulement utilisé pour les données dynamiques
<?php $items = ['pomme', 'poire', 'raisin']; ?>
<ul>
<?php foreach ($items as $item) :?>
<li><?php echo $item ?></li>
<?php endforeach; ?>
</ul>Cette syntaxe alternative est plus lisible, notamment lorsque les pages à générer deviennent plus complexes.
La structure de contrôle if/else (embranchement) dispose
également d’une syntaxe alternative.
<?php $items = ['pomme', 'poire', 'raisin']; ?>
//...
//Syntaxe alternative pour le if
<?php if(!empty($items)) : ?>
<!-- Cette balise sera écrite dans la page web si la liste d'items n'est pas vide -->
<p> Il y a des items dans la liste ! </p>
<?php else : ?>
<!-- Cette balise sera écrite dans la page web sinon -->
<p> La liste est vide ! </p>
<?php endif; ?>En PHP, pas besoin de déclarer une variable avant de lui affecter une
valeur. Un nom de variable doit toujours commencer par
un dollar ($).
//La variable $message contient la valeur 'hello, world' de type 'string'
$message = 'hello, world';
//Un tableau (collection de valeurs)
$monTableau = [1, 2, 3];
$nombre = 12;
$nombreRationnel = -1.5;Pour inspecter le contenu d’une variable et/ou son type. Utile pour débuger et comprendre son programme et regarder les valeurs
$name = "Jane Doe";
//Ecrire la valeur sur la sortie
echo $name;
$grades = [12, 10, 19, 8];
//Imprimer le contenu d'un tableau de manière lisible
print_r($grades);
//Imprimer le contenu de n'importe quelle variable et des types des données
var_dump($grades);<?php
echo 'ceci est une chaîne simple';
echo 'Vous pouvez également ajouter des nouvelles lignes
dans vos chaînes
de cette façon';
// Echapper des caractères spéciaux avec l'antislash (ici la guillemet simple avec \') pour signifier qu'il fait partie de la chaîne
echo 'Arnold a dit : "I\'ll be back"';
echo 'Voulez-vous supprimer C:\*.*?';
$firstName = "Jane";
//Les simples quotes ne permettent aucune interprétation de caractères spéciaux dans la chaine
echo 'La variable $firstName ne sera pas interpolée ici (remplacée par sa valeur)\n';
//Les doubles quotes effectuent des interprétations de caractères spéciaux (interpolation, caractère de retour à la ligne, etc.)
echo "La variable $firstName sera interpolée ici (remplacée par sa valeur)\n";Une autre façon de délimiter une chaîne de caractères est la syntaxe
Heredoc : <<<. Après cet opérateur, un identifiant
est fourni (ici END), suivi d’une nouvelle ligne. La chaîne
de caractères en elle-même vient ensuite, suivie du même identifiant
pour fermer la notation.
<?php
// no indentation
echo <<<END
$$$$$$$\ $$\ $$\ $$$$$$$\
$$ __$$\ $$ | $$ |$$ __$$\
$$ | $$ |$$ | $$ |$$ | $$ |
$$$$$$$ |$$$$$$$$ |$$$$$$$ |
$$ ____/ $$ __$$ |$$ ____/
$$ | $$ | $$ |$$ |
$$ | $$ | $$ |$$ |
\__| \__| \__|\__|
END;$text= <<<FOOBAR
À d’autres la satiété ! Toi, tu gardes ton désir, désir exubérant qui déborde toujours : moi qui te poursuis sans cesse, je viens par-dessus le marché faire addition à tes tendres caprices.
Toi, dont le désir est si large et si spacieux, ne daigneras-tu pas une fois absorber mon désir dans le tien ? Ton désir sera-t-il toujours si gracieux aux autres sans jeter sur mon désir un rayon de consentement ?
La mer, qui est toute eau, reçoit pourtant la pluie encore, et ajoute abondamment à ses réservoirs : ainsi toi, riche de désir, ajoute à tes désirs la goutte du mien, et élargis ton caprice.
Ne te laisse pas accabler par tant de tentations, bonnes ou mauvaises : confonds-les toutes en une, et aime Will dans ce désir unique (1).
FOOBAR;
echo $text;sprintf, number_format, strtolower, strtoupper, ucwords, strpos, date, mktime
$x = 'bOnJoUr eT bIeNvEnUe';
echo "<p><code>strtolower('$x')</code> = " . strtolower($x) . '<br>';
echo "<code>ucwords('$x')</code> = " . ucwords($x) . '<br>';
echo "<code>ucwords(strtolower('$x'))</code> = " . ucwords(strtolower($x)) . ' </p>';
echo '<h5>Mise en forme avec <a href="http://php.net/manual/fr/function.sprintf.php"><code>sprintf()</code></a></h5>';
echo '<p>Mise en forme d\'une date : ' . sprintf('%02d/%02d/%04d', 1, 1, 1981) . '</p>';
echo '<h5>Mise en forme avec <a href="http://php.net/manual/fr/function.number-format.php"><code>number_format()</code></a></h5>';
$x = 1234.567;
echo "<p><code>number_format($x)</code> = " . number_format($x) . '<br>';
echo "<code>number_format($x,1)</code> = " . number_format($x, 1) . '<br>';
echo "<code>number_format($x,2,',',' ')</code> = " . number_format($x, 2, ',', ' ') . '</p>';
echo '<p><code>date("d/m/Y H:i:s", mktime(11, 45, 30, 4, 10, 2017))</code> = ' . date("d/m/Y H:i:s", mktime(11, 45, 30, 4, 10, 2017)) . '<br>';
echo 'Unix a fêté sa milliardième seconde le ' . date("d/m/Y H:i:s", 1000000000) . '</p>';
echo '<p>Date du jour au format JJ/MM/AAAA : ' . date('d/m/Y') . '<p>';Les tableaux en PHP sont très (trop) puissants et versatiles :
$array = [1, 2, 3, ['a','b','c', [1,2,3]]];
//La fonction count() permet de connaître le nombre d'éléments dans le tableau
echo "Taille du tableau : " . count($array) . PHP_EOL;En PHP, un tableau est une collection de couples clé/valeur :
[clé => valeur]
//Tableau : on déclare les clefs et les valeurs
$magasin = [
'citron' => 5,
'banane' => 12,
'poireau' => 20
];
//Parcourir un tableau en parcourant ses clefs ET ses valeurs
//Syntaxe: foreach(tableau as clef => valeur)
foreach($magasin as $product => $quantity){
//Equivalent "string literals" avec les back tick en JS
//Ici $product et $quantity vont être remplacées par
//leurs valeurs dans la chaîne.
echo "Produit: {$product} - Quantité: {$quantity} \n";
}
//Ajouter un élément
$magasin['riz'] = 12;
//Supprimer un élément
unset($magasin['riz']);$array = [1,2,3];
//count : Retourne le nombre d'éléments
count($array);
//implode: Construit une chaine à partir des éléments d'un tableau, avec un séparateur
$array = ['lastname', 'email', 'phone'];
//in_array : permet de tester si un élément est dans le tableau
if(in_array('email', $array)){
echo "email est dans le tableau";
}
//shuffle : mélange les éléments du tableau de manière aléatoire
shuffle($array);
var_dump(implode(",", $array)); // string(20) "lastname,email,phone"Voir toutes les fonctions natives de PHP pour manipuler le tableaux
NULL est un type spécial et une valeur qui indique
l’absence de valeur. La fonction isset() est
parfaitement adaptée pour tester la présence d’une valeur ou d’une clef
dans un tableau.
$var = NULL;
//Test avec la fonction isset :
if(!isset($var)){
echo "la variable ne contient aucune valeur !";
}Un aspect essentiel de la programmation est de pouvoir nommer les choses afin de pouvoir raisonner et donner du sens à nos programmes.
Une fois une procédure (suite d’instructions, action) clairement identifiée, la nommer pour en faire une boîte noire et l’utiliser sans avoir besoin de se soucier de comment elle accomplit sa tâche.
Une fonction (ou procédure) est une unité de programmation qui est définie par :
Une fonction, avant d’être appelée (exécutée), doit
être déclarée avec le mot clef function. Une
fonction peut prendre des arguments (ou pas) et retourne
toujours quelque chose (si pas d’instruction
return, elle retourne NULL)
On indique le type des données des arguments et de retour de la fonction (type hinting)
//Déclaration de la fonction
function sayHi(string $firstName, string $lastName) : string{
//Le corps de la fonction (les instructions à faire)
//Pour interpolation, il faut utiliser les guillemets doubles
//Les guillemets simples n'interprètent pas le contenu de la chaine
$msg = "Hello $firstName $lastName !";
//La fonction retourne son résultat (une chaîne de caractère)
return $msg;
}
//Appel de la fonction
echo sayHi('Jane', 'Doe');Il est recommandé d’utiliser le mode impératif à la 2eme personne
du présent pour nommer une fonction (ou infinitive en anglais). Une
fonction agit, elle manipule des données. Un
verbe est donc un bon nom de fonction. Par exemple,
search, create-new-game ou
enleve-voyelles représentent de très bons choix.
Il est aussi important de bien nommer les arguments pour que
l’utilisateur·ice de votre fonction comprenne facilement à quoi ils
correspondent. N’utilisez jamais de noms à un caractère comme
a ou i, sauf si la fonction est triviale ou
mathématique. Aujourd’hui nous disposons d’outils d’autocomplétion, nous
n’avons aucune excuse pour ne pas donner des noms longs mais
significatifs à nos variables et à nos fonctions.
PHP est un langage typé dynamiquement, ce qui apporte de la flexibilité mais peut conduire à des comportements inattendus ! (bugs).
Depuis PHP 7, il est possible (et recommandé) de déclarer les types des arguments et retours de fonction.
Cela assure à l’execution que la valeur passée en
argument à une fonction est du type attendu, sinon une erreur fatale
TypeError est lancée (si coercition désactivée) et
interrompt l’exécution du programme.
<?php
//Déclaration de fonction utilisant le *type hinting* : attend une chaîne et un entier, retourne une chaîne
function repeat(string $str, int $times): string {
if($times == 0)
return '';
return $str . repeat($str, $times - 1) ;
}
echo repeat("ha", 13);TOUJOURS utiliser le type hinting !
Attention, concernant les valeurs
scalaires ou primitives
(string, int, float, etc.), PHP
tente par défaut de convertir (coerce) vers le
type attendu (défini par le type hinting)
<?php
function sum(int $a, int $b): int {
return $a + $b;
}
var_dump(sum(1.5,2.5)); // Affiche int(3)strict #Pour empêcher PHP de faire cette conversion (qui peut provoquer des
comportement inattendus!), on peut activer
le mode typage strict par fichier, avec
l’instruction declare(strict_types=1);
Elle peut aussi éviter des problèmes non anticipés…
Dans ce mode, seule une variable correspondant exactement au
type attendu dans la déclaration sera acceptée sinon une
TypeError sera levée, sauf si la conversion
garantit l’intégrité de la donnée
(int -> float)
<?php
declare(strict_types=1);
var_dump(sum(1.5, 2.5));Sortie :
PHP Fatal error: Uncaught TypeError: sum(): Argument #1 ($a) must be of type int, float givenstrict dans les
fichiers contenant de l’exécution de code que vous
contrôlez;PHP est particulièrement adapté pour réaliser des sites web dynamiques.
Un site web écrit en PHP peut facilement interagir avec des bases de données et effectuer des opérations complexes côté serveur pour générer du contenu web dynamique personnalisé à chaque requête HTTP.
Pour envoyer des données à un service web (site web), on utilise un
formulaire, soit la
balise <form> HTML. Le formulaire contient des
balises <input> permettant au client de saisir
les données nécessaires au traitement de sa demande (voir cours Dev
Front)
Une balise form possède notamment deux attributs :
action : indiquer l’URL responsable du traitement du
formulaire (par exemple un script PHP de notre site). Si non précisé,
soumet le formulaire sur la même URL;method : indiquer la méthode HTTP à utiliser pour
soumettre le form (GET ou POST). Si non
précisée, utilise la méthode GET<!-- Formulaire sera posté sur l'URL {origine de la page web}/confirm-order.php, avec la méthode POST -->
<form action="/confirm-order.php" method="POST">
<label for="quantity">Quelle quantité de riz basmati (kg) souhaitez-vous ? </label>
<input type="number" name="quantity" id="quantity">
<input type="submit" value="Valider la commande" name="order">
</form>GET monsite.com avec son navigateur favori;monsite.com. Sur cette machine, le serveur web récupère la
requête et identifie le script PHP à appeler;GET ou
POST);On parle de programmation CGI lorsqu’un serveur web, au lieu de servir un fichier HTML statique, execute un programme dont la sortie est retournée au client
Pour générer la page web, le script PHP est exécuté par un autre programme : le serveur web (Apache, Nginx, Caddy, etc.). Le serveur web transmet au script PHP toutes les informations nécessaires pour que le script puisse faire son travail et générer sa réponse via des tableaux spéciaux appelés variables super globales.
Lire la page Variables externes à PHP de la documentation officielle.
Les variables Super Globales sont des variables fournies aux scripts PHP à l’exécution contenant toutes les informations sur l’environnement du script, la requête HTTP, le client, le serveur, etc.
Ce sont des tableaux associatifs PHP, qui commencent, par convention,
par un underscore (_).
#Un tableau associatif des valeurs passées au script courant via le protocole HTTP et la méthode POST
$_POST;
#Un tableau associatif des valeurs passées au script courant via le protocole HTTP et la méthode GET
$_GET;
#$_SERVER est un tableau contenant des informations telles que les en-têtes, les chemins et les emplacements de script. Crée par le serveur web (programme)
$_SERVER;
#Un tableau associatif des valeurs téléchargées au script courant via le protocole HTTP et la méthode POST
$_FILES;
#Un tableau associatif de variables, passé au script courant, via des cookies HTTP.
$_COOKIE;
#Un tableau associatif qui contient par défaut le contenu des variables $_GET, $_POST et $_COOKIE.
$_REQUEST;Ne modifiez jamais vous-même ces tableaux, accédez-y en lecture seulement.
<!-- Formulaire sera posté sur l'URL {origine de la page web}/confirm-order.php, avec la méthode POST -->
<form action="/confirm-order.php" method="POST">
<label for="quantity">Quelle quantité de riz basmati (kg) souhaitez-vous ? </label>
<input type="number" name="quantity" id="quantity">
<input type="submit" value="Valider la commande" name="order">
</form>action indique que le formulaire est soumis
à l’URL /confirm-order.php, qui déclenchera ici l’execution
du script confirm-order.php;method indique que formulaire est soumis
avec la méthode HTTP POST, les données sont envoyées dans
le corps (body) de la requête HTTP;Pour récupérer les données côté serveur, on utilise la variable
superglobale $_POST :
<?php
//Script confirm-order.php
$quantity = $_POST['quantity'];
//Je peux travailler avec la valeur fournie par le client, par exemple préparer sa commandeIl faudra évidemment prendre des précautions pour manipuler les données soumises au serveur, comme on le verra par la suite.
Si on avait utilisé la méthode GET pour soumettre le formulaire, on aurait du utiliser le tableau
$_GET
Accéder à la démo sur le dépôt du cours.
On peut traiter le formulaire envoyé par le client :
Le traitement du formulaire se fait grâce aux variables super globales qui mettent à disposition du script PHP les données envoyées par le client.
Le service web peut alors effectuer sa logique basée sur le contenu du formulaire et rendre le service adéquat à son client.
#Retourne vrai si la clé foobar existe dans $_POST (valeur soumise pour ce champ avec l'attribut name égal à 'foobar')
$bool = isset($_POST['foobar']);
#Échapper les balises HTML
htmlentities($untrustedString);
#Génère une chaîne de requête en encodage URL
http_build_query($data);
#Analyse une URL et retourne ses composants
parse_url($url);
#Récupère plusieurs variables et les filtre
filter_var_array($array, $filters);
#Cette fonction est utile pour récupérer plusieurs valeurs sans avoir à appeler plusieurs fois la fonction filter_input().
filter_input_array(INPUT_POST, $args);
#Récupère une variable externe et la filtre
$search_html = filter_input(INPUT_GET, 'search', FILTER_SANITIZE_SPECIAL_CHARS);En savoir plus sur filter_input, filter_input_array
Les formulaires exposées par un service web permettent à n’importe quel client de soumettre des données. Le client peut envoyer n’importe quelle donnée, autant de fois qu’il le désire et peut essayer, s’il est mal intentionné, de briser ou de pénétrer dans votre service web.
Chaque formulaire agrandit la surface d’attaque de votre service web.
Règle d’or : NE JAMAIS FAIRE CONFIANCE AU CLIENT. Toute donnée envoyée par un client doit être validée par le serveur.
Quid des contraintes mises sur les input dans votre page HTML ? D’un point de vue sécurité, elles ne valent rien. RIEN. Un client peut les modifier à sa guide, il peut même soumettre des données à votre serveur sans utiliser votre formulaire. Les contraintes sur les input servent simplement à guider votre client et l’informer sur ce que vous attendez (UX).
Par définition, tout ce qui se passe côté client (côté front) est
public et ne peut pas être sécurisé. C’est donc au serveur, ici à vos
scripts PHP, de vérifier toute donnée entrante provenant de l’extérieur.
Le serveur doit vérifier toute donnée soumise via une
requête GET (paramètres d’URL) ou POST (corps
de la requête) avant de les manipuler et de les
utiliser pour réaliser le service attendu.
Pour sécuriser les données entrantes dans le service web, il existe plusieurs stratégies :
Identifier clairement les données dans votre script PHP qui proviennent de l’extérieur, et sont potentiellement dangereuses pour votre service, et celles qui ont été validées.
//On déclare un tableau qui contient toutes les données validées
$clean = [];
//Si une donnée est validée, on l'ajoute dans le tableau
if(isset($_POST['color']) && in_array($_POST['color'], $acceptedColors)){
$clean['color'] = $_POST['color'];
}La validation est le cas idéal et doit être utilisée dès que possible. Cela consiste à comparer la valeur fournie à une liste de valeurs acceptées. Si la valeur fournie n’est pas dans la liste, elle est rejetée (liste blanche)
//Liste des valeurs acceptées
$acceptedColors = ['red', 'white','blue'];
//Si la donnée 'color' soumise est dans la liste, on l'accepte et on poursuit le traitement
if(isset($_POST['color']) && in_array($_POST['color'], $acceptedColors)){
$clean['color'] = $_POST['color'];
}else{
echo "Couleur invalide";
exit;
}Côté client, utiliser la validation se traduit par le fait de proposer le moins d’inputs “ouverts” possibles (input text) et de privilégier les inputs “fermés” : choix parmi une liste (select, checkbox, radio buttons). Fermer au maximum les questions que vous posez aux clients pour améliorer l’experience utilisateur et la sécurité de votre service.
Lorsqu’il n’est pas possible de valider des données (un nom, un prénom par exemple), i.e quand le nombre possible de réponses est impossible à déterminer à l’avance, on peut ajouter une couche de filtrage des données.
Filtrer (sanitize) les données consister à modifier
les données entrantes pour les rendre inoffensives, généralement en
supprimant ou remplaçant des caractères. Par exemple, en
retirant tous les caractères spéciaux qui pourraient être utilisés pour
former des balises HTML, à savoir <, >
et />.
Pour cela, en PHP, on peut utiliser les fonctions filter_var, filter_input, filter_input_array
Attention, une politique de filtration/sanitization n’apporte aucune garantie. À utiliser judicieusement, utiliser des filtres éprouvés (fournis par PHP).
L’échappement est, avec la validation, le meilleur mécanisme de défense pour votre application. Contrairement au filtrage (sanitization), l’échappement ne modifie pas les données entrantes mais s’assure que, dans un contexte donné, cette donnée soit utilisée de manière sécurisée en échappant (rendant inoffensif) les caractères jugés dangereux pour ce contexte.
Par exemple :
Dans un contexte de page HTML (ce qui nous intéresse ici), il
faut échapper tous les caractères permettant d’injecter des balises :
<, >, />, etc. Pour
cela, utiliser toujours la
fonction PHP htmlentities() quand vous générer une page
HTML avec des données provenant de l’extérieur du script
(données envoyées par le client, données récupérées depuis une base de
données).
Dans un contexte de requête SQL, il faut échapper les caractères
qui essayent de modifier la structure de la requête SQL: ',
--, ", _, etc. Cette attaque est
connue sous le nom d’injection
SQL. Pour échapper ces caractères, on utilise des
requêtes préparées.
Recommandation/Mantra : Don’t sanitize input. Escape output.
Par définition, PHP est un moteur de templates (imparfait) : à partir d’un unique script PHP, il est possible de générer un nombre indéterminé de pages web différentes en fournissant au script les données à présenter.
Pour créer des templates modulaires (avec des parties réutilisables), il est essentiel de diviser nos scripts PHP en plusieurs fichiers. Ainsi, un même script peut être réutilisé dans différents contextes.
Par exemple, le header et le footer d’un site web sont généralement
identiques d’une page à l’autre. On peut donc placer ces morceaux de
templates (template parts) ou composants dans des
fichiers header.php et footer.php et les
utiliser (importer) dans le script index.php qui génère la
page d’accueil.
Pour utiliser le code PHP placé dans un autre fichier, on peut
utiliser le
construct require_once:
require_once lève une
erreur fatale qui met fin a à l’execution du script.<?php
//Inclure tout le contenu (UNE FOIS) du fichier foo.php dans le script courant
require_once './foo.php';
//Inclure tout le contenu (UNE FOIS) du fichier foo.php dans le script courant
require_once './bar.php';
//N'aura aucun effet
require_once './bar.php';Il existe d’autres construct pour inclure des scripts : include, include_once, require, etc.. Pour le moment, privilégier
require_once.
Le require_once n’agit pas comme un simple
copier/coller textuel brut du contenu du script importé (comme
#include en C), mais comme une évaluation du
script. Conséquences :
require sont visibles
dans le fichier inclus ;Fichier component.php :
<?php $last_component = "p"; ?>
<p>Un composant</p>Fichier index.php :
//require_once ouvre, compile et execute le script component.php comme si on avait fait 'php component.php'
require_once 'component.php';
echo $last_component;Exécuter le script index.php produit la sortie suivante
:
<p>Un composant</p>
pLorsque l’on utilise PHP pour générer des pages HTML, on peut utiliser une syntaxe alternative afin d’améliorer la lisibilité du code source de la page.
Prenons un exemple :
//Tout le HTML est généré depuis un code PHP
$items = ['pomme', 'poire', 'raisin'];
echo "<ul>";
foreach($items as $item){
echo "<li>$item</li>";
}
echo "</ul>";Il existe de nombreuses façons de construire le code source d’une page HTML avec PHP.
Le script PHP est essentiellement constitué de texte (code HTML), le contenu est donc imprimé sur la sortie sans modification (voir fonctionnement de PHP dans le Module 1). Les balises PHP ne sont ouvertes que pour insérer des données dynamiques :
//Le HTML est écrit directement, PHP est seulement utilisé pour les données dynamiques
<?php $items = ['pomme', 'poire', 'raisin']; ?>
<ul>
<?php foreach ($items as $item) { ?>
<li><?php echo $item ?></li>
<?php } ?>
</ul>Idem que précédemment mais avec la syntaxe alternative pour le
foreach : les accolades ouvrantes et fermantes de la boucle
sont remplacées par le caractère : et par une instruction
endforeach;
//Le HTML est écrit directement, PHP est seulement utilisé pour les données dynamiques
<?php $items = ['pomme', 'poire', 'raisin']; ?>
<ul>
<?php foreach ($items as $item) :?>
<li><?php echo $item ?></li>
<?php endforeach; ?>
</ul>Cette syntaxe alternative est plus lisible, notamment lorsque les pages à générer deviennent plus complexes.
La structure de contrôle if/else (embranchement) dispose
également d’une syntaxe alternative.
<?php $items = ['pomme', 'poire', 'raisin']; ?>
//...
//Syntaxe alternative pour le if
<?php if(!empty($items)) : ?>
<!-- Cette balise sera écrite dans la page web si la liste d'items n'est pas vide -->
<p> Il y a des items dans la liste ! </p>
<?php else : ?>
<!-- Cette balise sera écrite dans la page web sinon -->
<p> La liste est vide ! </p>
<?php endif; ?>Exemple d’usage de la syntaxe alternative pour la Loop Wordpress, un pattern au coeur du fonctionnement du CMS :
//Script PHP en charge d'afficher la liste des articles, d'un article, etc.
//S'il y a des posts (articles), parcourir les posts (while)
//Affiche le contenu du post (the_content()) et passer au post suivant (the_post())
<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
<div class="entry">
<?php the_content(); ?>
</div>
<?php endwhile;?>Accéder à la démo sur le dépôt du cours.
Illustration avec le code source du template de la page principale d’un thème Wordpress
PHP offre tous les outils nécessaires pour manipuler des fichiers (créer, supprimer, lire, écrire).
Un fichier texte est un moyen simple, universel et efficace de persister des données sur le disque. En effet, “PHP n’a pas de mémoire”. Lorsqu’un script termine son execution, toutes les données manipulées sont perdues. Dans le contexte du web, chaque requête HTTP est traitée par un script PHP de manière indépendante. A chaque fin d’execution, toute la mémoire allouée au script (par exemple le contenu des variables) est libérée.
Nous pouvons nous servir de simples fichiers textes comme système de mémoire pour notre site web, et permettre aux scripts de maintenir des données d’un traitement à l’autre. C’est une base de données simple et minimale, mais qui peut s’avérer largement suffisante dans bien des cas d’utilisation.
//Ouverture d'un fichier en écriture (le crée s'il n'existe pas)
$file = fopen('data.txt', 'w');
//Écriture dans le fichier
fwrite($file, 'Cette ligne sera écrite dans le fichier.');
//Fermeture du fichier
fclose($file);
//Supprimer le fichier
unlink('data.txt');Voir la liste de toutes les fonctions relatives à la manipulation de fichiers.
//Ouverture du fichier en mode lecture ('r')
$file = fopen('data.txt', 'r');
//Vérifier que le fichier existe et a bien été ouvert
if(!$file){
echo "Une erreur s'est produite à l'ouverture du fichier";
exit;
}
//Lire un fichier ligne par ligne
$lines=[];
//Tant que ce n'est pas la fin du fichier, lire la prochaine ligne avec fgets
while(!feof($file)){
//Lire ligne par ligne avec fgets
$line = fgets($file);
$lines[] = $line;
}
var_dump($lines);Il est possible de lire byte par byte avec fgetc
//Lit tout le contenu d'un fichier et retourne le contenu sous forme de chaine de caractère
$content = file_get_contents('somefile');
//Lit tout le contenu d'une URL
$content = file_get_contents('https://google.com');Le format CSV est un format simple et efficace pour stocker des données au format texte. Chaque ligne du texte correspond à une ligne d’un tableau et les virgules correspondent aux séparations entre les colonnes de ce tableau. Par défaut, la séparation entre les colonnes est marquée par une virgule.
Exemple de fichier CSV : 3 enregistrements (la première ligne contient les noms des colonnes et est ignorée) sur 3 colonnes.
Sexe,Prénom,Année de naissance
M,Alphonse,1932
F,Béatrice,1964
F,Charlotte,1988
PHP fournit des fonctions natives pour faciliter le travail avec le format CSV. Ces fonctions se chargent de formater les données, en écriture et en lecture. Utilisez-les ! Elles permettent notamment de valider le format du fichier pour éviter des erreurs de lecture ou d’écriture.
fputcsv 3 #<?php
//Des données que l'on souhaite écrire sur le disque
$list = array (
array('aaa', 'bbb', 'ccc', 'dddd'),
array('123', '456', '789'),
array('"aaa"', '"bbb"')
);
//Ouverture d'un fichier avec l'extension .csv (convention) en mode écriture 'w'
$fp = fopen('file.csv', 'w');
//Ecriture
foreach ($list as $fields) {
fputcsv($fp, $fields);
}
//Fermeture du fichier
fclose($fp);fgetcsv 4 #<?php
$row = 1;
//Si le fichier existe et a pu être ouvert en lecture
if (($file = fopen("test.csv", "r")) !== FALSE) {
//Tant qu'on est pas à la fin du fichier, lire le fichier
//ligne par ligne
while (($line = fgetcsv($file, 1000, ",")) !== FALSE) {
$num = count($line);
echo "<p> $num champs à la ligne $row: <br /></p>\n";
$row++;
for ($c=0; $c < $num; $c++) {
echo $line[$c] . "<br />\n";
}
}
fclose($file);
}Il est facile de créer des URLs de manière dynamique (en fonction du contenu de la base de données par exemple) pour chaque ressource (post, article, étudiant, rendez-vous, etc.) avec les paramètres d’URL (query part).
Ainsi, votre site expose autant de ressources et donc de pages HTML que nécessaire de manière automatique.
C’est ce que fait YouTube par exemple. Voici une URL d’une vidéo Youtube :
https://www.youtube.com/watch?v=-I-VpPMzG7c
v qui a pour valeur -I-VpPMzG7c ;https://www.youtube.com;?) :
v=-I-VpPMzG7cDans nos templates on peut donc générer des URL dynamiquement (en fonction du contenu) en créant des URL de la forme :
//Chaque URL est unique et la ressource est identifiée par la valeur du paramètre d'URL 'id'
GET http://localhost:5001/student.php?id=1
GET http://localhost:5001/student.php?id=2
...
GET http://localhost:5001/student.php?id=200
Pour produire la page web présentant un·e étudiant·e il suffit :
id dans l’URL avec
$_GET['id'];student.php;Lorsqu’un·e nouvel·le étudiant·e est enregistrée (nouvel
id), iel a automatiquement sa page web !
Accéder à la démo sur la création d’URLs dynamiques à partir d’enregistrements dans un fichier CSV sur le dépôt du cours.
Les applications web dynamiques ont très souvent besoin de persister des données : utilisateurs, produits, commandes, messages, etc.
Pour cela, on utilise une base de données relationnelle, souvent MySQL, accessible depuis PHP.
PHP fournit une extension standard et recommandée pour interagir avec les bases de données : PDO (PHP Data Objects).
GET) en cliquant sur un
lien ;POST) ;Pour se connecter à une base MySQL, il faut :
pdo_mysql activée.Vérification rapide :
php -m | grep pdoUne connexion PDO nécessite :
host : adresse du serveur MySQLdbname : nom de la baseusername : utilisateur base de donnéespassword : password utilisateur base de donnéesExemple de DSN (Data Source Name) MySQL :
mysql:host=localhost;dbname=demoUne base de données est “comme” un petit système d’exploitation : elle définit des utilisateurs avec des droits
<?php
$dsn = 'mysql:host=localhost;dbname=demo;charset=utf8mb4';
$user = 'user';
$password = 'password';
$pdo = new PDO($dsn, $user, $password);<?php
$dsn = 'mysql:host=localhost;dbname=demo;charset=utf8mb4';
$user = 'user';
$password = 'password';
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
$pdo = new PDO($dsn, $user, $password, $options);<?php
try {
$pdo = new PDO($dsn, $user, $password, $options);
} catch (PDOException $e) {
echo "Erreur de connexion à la base de données";
exit;
}Isoler la connexion dans un fichier (un module !)
dédié db.php :
<?php
//Connexion ouverte avec la base de donnée stockée dans la variable $pdo
$pdo = new PDO($dsn, $user, $password, $options);Depuis le reste de votre application :
require_once 'db.php';
//Utiliser $pdo dans l'applicationUtiliser la méthode fetchAll() :
$sql = 'SELECT * FROM students';
$stmt = $pdo->query($sql);
$students = $stmt->fetchAll();<ul>
<?php foreach ($students as $student) : ?>
<li>
<?php echo htmlentities($student['firstname']) ?>
<?php echo htmlentities($student['lastname']) ?>
</li>
<?php endforeach; ?>
</ul>Une injection SQL consiste à injecter du code SQL via une donnée fournie par le client, afin de modifier la requête exécutée par le serveur.
Entrée malveillante typique : ' OR '1'='1
# NE JAMAIS FAIRE ÇA !
$id = $_POST['id'];
$sql = "SELECT * FROM students WHERE id = '$id'";
$result = $pdo->query($sql);Si le client envoie (via un input de formulaire par ex) :
' OR '1'='1
La requête devient :
-- 1=1 est toujours vrai !
SELECT * FROM students WHERE id = '' OR '1'='1''1'='1' est toujours vrai !Toute donnée externe DOIT passer par une requête préparée.
$sql = 'SELECT * FROM students WHERE id = :id';
$stmt = $pdo->prepare($sql);
$stmt->execute(['id' => $_POST['id']]);
$student = $stmt->fetch();Bénéfices immédiats :
Lire des données (SELECT) ne suffit pas. Une application
web doit aussi créer, modifier et
supprimer des données. On parle de CRUD
(Create Read Update Delete), opérations de base d’édition de
données.
Ces opérations correspondent aux instructions SQL suivantes :
INSERT : créer un nouvel
enregistrement dans la table ;SELECT : lire un ou plusieurs
enregistrements (vu);UPDATE : mettre à jour un ou plusieurs
enregistrements déjà présent dans la table ;DELETE : supprimer un ou plusieurs
enregistrement.Rappel : toutes ces opérations DOIVENT utiliser des requêtes préparées.
INSERT #$sql = '
INSERT INTO students (firstname, lastname, email)
VALUES (:firstname, :lastname, :email)
';
$stmt = $pdo->prepare($sql);
$stmt->execute([
'firstname' => $firstname,
'lastname' => $lastname,
'email' => $email,
]);
//Récupérer l’identifiant généré
$studentId = $pdo->lastInsertId();UPDATE #$sql = '
UPDATE students
SET firstname = :firstname,
lastname = :lastname,
email = :email
WHERE id = :id
';
$stmt = $pdo->prepare($sql);
$stmt->execute([
'id' => $id,
'firstname' => $firstname,
'lastname' => $lastname,
'email' => $email,
]);Toujours utiliser une clause
WHEREsinonUPDATEs’applique à tous les enregistrements de la table !
DELETE #$sql = 'DELETE FROM students WHERE id = :id';
$stmt = $pdo->prepare($sql);
$stmt->execute(['id' => $id]);
//Nombre de lignes affectées : vérifier qu’un DELETE (ou UPDATE) a bien eu un effet
$affectedRows = $stmt->rowCount();htmlentities() dans les templates PHP.WHERE pour les instructions SQL
UPDATE et DELETE pour cibler un enregistrement
;-- Créer une base de données pour notre site ("répertoire")
CREATE DATABASE blog;
use blog
-- Table qui contiendra nos articles
CREATE TABLE posts (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
author VARCHAR(100) NOT NULL,
image_path VARCHAR(255),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;-- Insérer quelques posts manuellement pour commencer
INSERT INTO posts (title, content, author, image_path) VALUES
('Mon premier post', 'Bienvenue sur mon blog !', 'Jane', 'uploads/post1.jpg'),
('PHP is simple', "PHP est un langage de programmation facile à prendre en main", 'John', 'uploads/post2.jpg');<?php
$host = 'localhost';
$db = 'mon_site';
$user = 'utilisateur';
$pass = 'motdepasse';
$charset = 'utf8mb4';
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
];
try {
$pdo = new PDO($dsn, $user, $pass, $options);
} catch (PDOException $e) {
die("Erreur de connexion : " . $e->getMessage());
}
$sql = "SELECT id, title, content, author, image_path, created_at, updated_at
FROM posts
ORDER BY created_at DESC";
$stmt = $pdo->query($sql);
$posts = $stmt->fetchAll();
foreach ($posts as $post) {
echo "<h2>" . htmlspecialchars($post['title']) . "</h2>";
echo "<p><em>Par " . htmlspecialchars($post['author']) . " le " . $post['created_at'] . "</em></p>";
if ($post['image_path']) {
echo "<img src='" . htmlspecialchars($post['image_path']) . "' alt='Image de l\'article' style='max-width:300px;'><br>";
}
echo "<p>" . nl2br(htmlspecialchars($post['content'])) . "</p><hr>";
}/admin-new-post.php du site web pour pouvoir
publier du contenu sans passer par la base de
données.GET), le
navigateur envoie automatiquement tous les cookies associés à ce
site avec la requête.Les cookies permettent de se souvenir d’un utilisateur entre différentes pages.
Un cookie est écrit sous forme de paire clé=valeur,
comme les paramètres d’URL ou les champs d’un formulaire HTML. Par
exemple :
sessionId=abc123xyzEn PHP, pour créer un cookie, utiliser la fonction setcookie() :
<?php
//clé, valeur, date d'expiration (timestamp)
setcookie("user", "Jone Doe", time() + 3600); // cookie valable 1 heure$_COOKIE permet d’accéder aux
cookies envoyés par le client. Par exemple,
$_COOKIE['user'] permet de récupérer la valeur du cookie
'user' côté serveur.En plus de cette clé/valeur, un cookie peut contenir des attributs optionnels séparés par des points-virgules, qui définissent son comportement dans le navigateur.
login.php.<?php
if ($_POST['username'] === 'admin' && $_POST['password'] === '1234') {
//Authentification réussie, déposer un cookie côté client avec des informations sur l'user
setcookie("auth_user", "admin", time() + 3600, "/");
echo "Connexion réussie !";
}<?php
if (isset($_COOKIE['auth_user'])) {
echo "Bienvenue " . htmlspecialchars($_COOKIE['auth_user']);
} else {
echo "Veuillez vous connecter.";
//Rediriger avec la fonction header()
header("Location: login.php");
}Si le cookie n’existe pas ou est expiré :
rediriger l’user vers la page de connexion
(login.php).
C’est un usage simplifié ici : n’importe quel client pourrait créer son propre cookie et se faire passer pour un user connecté !
<?php
setcookie("auth_user", "", time() - 3600, "/"); // supprime le cookie (valeur vide)
header("Location: login.php");
exit;<?php
session_start(); // toujours au début du script
?>session_start() crée ou reprend une session existante
;Utiliser la variable super globale $_SESSION pour lire/écrire des informations propres à la session client :
$_SESSION['user'] = 'john';
$_SESSION['role'] = 'admin';
$_SESSION['panier'] = ['produit1', 'produit2'];PHPSESSID).Sur une ressource qui nécessite authentification par exemple :
// PHP traite le cookie avec l'identifiant et regarde si une session avec cet identifiant existe.
// Si c'est le cas, les données sont chargées dans $_SESSION.
session_start();
if (isset($_SESSION['user'])) {
echo "Bienvenue " . $_SESSION['user'];
} else {
echo "Veuillez vous connecter.";
}Sur la ressource logout.php par exemple :
<?php
session_start();
session_destroy(); // supprime la session côté serveur, libère les données associées.time() + 3600 par exemple) ;httponly pour empêcher l’accès via JavaScript ://Un cookie sécurisé : valable 1h, qui ne peut pas être lu par du JavaScript, transmis via HTTPS uniquement si site le permet
setcookie('auth_user', $username, time() + 3600, '/', '', false, true);secure si le site est en HTTPS, pour que le
cookie soit transmis uniquement via HTTPS.