Programmation PHP
Transcription
Programmation PHP
Programmation PHP François Rioult 7 septembre 2012 Table des matières 1 Notion de programme, instructions 1.1 1.2 1.3 4 Premier exemple : lire un message et l'acher . . . . . . . . . . . 4 1.1.1 Entrée / sortie 4 1.1.2 Suite de l'exemple 1.1.3 Séquencement . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.1.4 Variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Structures de contrôle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1 Instruction conditionnelle 1.2.2 Itérations . . . . . . . . . . . . . . . . . . 5 8 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 Bilan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2 TP1 13 2.1 Découvrir 2.2 Programmes et exercices (TD) 2.2.1 2.2.2 2.2.3 2.2.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 Acher le double d'un nombre choisi par l'utilisateur . . 13 Acher le nombre de lettres et le nombre de "a" que contient un mot choisi par l'utilisateur . . . . . . . . . . . 13 Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Comparer deux nombres saisis par l'utilisateur - diérentes écritures . . . . . . . . . . . . . . . . . . . . . . . . 2.2.5 13 . . . . . . . . . . . . . . . . . . . 14 Répondre des messages diérents en fonction du nom de . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.2.6 l'utilisateur Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.2.7 Répeter un traitement jusqu'à ce que l'utilisateur veuille 2.2.8 s'arrêter . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Amélioration : compter le nombre de traitements faits 16 . . 2.3 Jeu du nombre mystérieux . . . . . . . . . . . . . . . . . . . . . . 17 2.4 Vote . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.4.1 Version 1 : saisie des noms au clavier . . . . . . . . . . . . 18 2.4.2 Version 2 : les noms sont stockés dans un chier 18 . . . . . 3 Fichier texte et algorithmes sur les chiers 3.1 3.2 19 Notion de chier texte . . . . . . . . . . . . . . . . . . . . . . . . 19 3.1.1 Dénition . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.1.2 Principe d'utilisation des chiers textes . . . . . . . . . . 20 . . . . . . . . . . . . . . 22 Algorithmes élémentaires sur les chiers 3.2.1 Lecture d'un chier . . . . . . . . . . . . . . . . . . . . . . 22 3.2.2 Création et écriture d'un chier . . . . . . . . . . . . . . . 25 1 4 TP 2 - Calcul de moyenne de notes 26 4.1 Exercice 1 : utilisation de boucles . . . . . . . . . . . . . . . . . . 4.2 Exercice 2 : compter les rouges et les verts . . . . . . . . . . . . . 26 4.3 Exercice 3 : moyenne de notes par personne . . . . . . . . . . . . 27 5 Les tableaux 26 29 5.1 Les tableaux simples . . . . . . . . . . . . . . . . . . . . . . . . . 5.2 Les chaînes de caractères . . . . . . . . . . . . . . . . . . . . . . . 31 5.3 Les tableaux associatifs 32 5.4 Fonctions pour les tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 TD 3 6.1 6.2 34 35 Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.1.1 29 Manipulations 35 . . . . . . . . . . . . . . . . . . . . . . . . 35 Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 7 TP 3 38 7.1 Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 7.2 Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 7.3 Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 7.3.1 Version élémentaire . . . . . . . . . . . . . . . . . . . . . . 38 7.3.2 Vérication des balises . . . . . . . . . . . . . . . . . . . . 38 7.3.3 Vérication avancée 39 . . . . . . . . . . . . . . . . . . . . . 8 Les fonctions 40 8.1 Exemples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.2 Portée des variables 8.3 Génie logiciel 8.4 Récursivité 8.5 . . . . . . . . . . . . . . . . . . . . . . . . . 40 42 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Passage par référence . . . . . . . . . . . . . . . . . . . . . . . . . 46 9 TD 4 49 9.1 Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 9.2 Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 10 TP 4 50 10.1 Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 10.1.1 Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 10.2 Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 10.3 Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 11 Programmation objet 51 11.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2 Conception orientée objet 51 . . . . . . . . . . . . . . . . . . . . . . 52 11.3 Passage d'objet . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 11.4 Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 12 TD 5 56 12.1 Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.1.1 enregistre 12.1.2 lit 56 . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 2 13 Héritage 57 14 TD - Héritage 62 14.1 Mise en place des formulaires . . . . . . . . . . . . . . . . . . . . 14.2 Ajout de classes pour gérer les enfants . . . . . . . . . . . . . . . 15 Exceptions 63 65 15.1 Principe des exceptions 15.2 La classe PHP 62 . . . . . . . . . . . . . . . . . . . . . . . 65 . . . . . . . . . . . . . . . . . . . . . . 65 Exception 15.3 Héritage d'exception . . . . . . . . . . . . . . . . . . . . . . . . . 15.4 Utilisation des exceptions . . . . . . . . . . . . . . . . . . . . . . 3 66 67 Chapitre 1 Notion de programme, instructions 1.1 Premier exemple : lire un message et l'acher Programme : description précise d'actions à exécuter sur un ordinateur pour réaliser une tâche. Les programmes ne se rencontrent pas seulement en informatique. Exemples : partition musicale, recette de cuisine, méthode pour calculer la longueur moyenne et le poids moyen d'un ensemble de personnes. Traitement automatique : signie que le traitement, une fois déni, ne dépend que des données fournies en entrée et non de choses extérieures. L'ordinateur n'est pas l'unique type de machine à eectuer des traitements automatiques (ou programmés) : un orgue de barbarie, un métier à tisser automatique, une machine à laver sont aussi des machines programmées. Exemple de programme PHP : acher un message à l'écran Voici ce premier programme : 1 <?php ?> echo "Bonjour à tous\n" ; Remarquer les balises <?php et ?> pour indiquer au système qu'il s'agit d'un script PHP. Ces balises doivent obligatoirement être présentes. Le texte entre " " est une chaine de caractères. \n est le saut de ligne dans un contexte de chaîne. Pour exécuter le programme (stocké dans le chier de nom test1.php), taper la ligne de commande : bruno@pc159:~$ php test1.php 1.1.1 Entrée / sortie On souhaite maintenant modier le programme pour que celui-ci demande à l'utilisateur son nom et l'ache. De quoi avons-nous besoin ? 4 entrée / sortie : saisie du variable : pour stocker et nom (entrée) et achage (sortie), retrouver le nom. Les notions d'entrée / sortie (encore appelées lecture / écriture) sont à considérer du point de vue de l'ordinateur (cf. gure 1.1). écrire humain lire Figure 1.1 lecture / écriture entrée : l'utilisateur fournit des informations à l'ordinateur (i.e. lecture lire() <RETOUR>). d'information de la part de l'ordinateur) : l'instruction une ligne de saisie (n de la saisie terminée par sortie collecte : l'ordinateur donne des informations à l'utilisateur (i.e. écriture (achage) d'information de la part de l'ordinateur) : la fonction echo envoie ses arguments sur le périphérique de sortie standard. Remarque : La fonction lire utilisée dans les programmes qui suivent pour lire une chaine de caractères tapée au clavier par l'utilisateur, n'est pas une fonction prédénie dans PHP. La suite d'instructions permettant de réaliser ce travail n'est pas détaillée ici cer elle est un peu complexe pour démarrer. Le code de cette fonction est rangé dans le chier fonctions1.inc que vous devez recopier dans votre répertoire de travail. Pour pouvoir l'utiliser dans un programme rajouter en tête du programme l'instruction : require("fonctions1.inc") 1.1.2 Suite de l'exemple Voici le programme qui permet de réaliser cette version améliorée de l'exemple. 1 5 <?php require("fonctions1.inc"); echo "Bonjour à tous\n"; echo "Quel est ton nom : "; $nompersonne = lire(); echo "Bonjour $nompersonne !\n"; ?> Voici maintenant un exemple de résultat d'exécution de ce programme. Bonjour à tous Quel est ton nom : fanny Bonjour fanny 5 En fait dans un premier temps il y a eu seulement achage de : Bonjour à tous Quel est ton nom : Le programme s'arrête, et reprend son exécution lorsque l'utilisateur a ni de taper une chaine de caractères terminée par un retour-chariot. Le reste du texte est alors aché. 1.1.3 Séquencement Séquencement. Les instructions d'un programme sont exécutées séquen- tiellement. Cela signie : les instructions sont traitées les unes après les autres, chaque instruction est traitée une fois, aucune n'est répétée ni omise, les instructions sont traitées de la première à la dernière, le programme est alors ni, l'état nal (par exemple, les variables et leurs valeurs) d' une instruction devient l'état initial de l'instruction qui la suit. Marque de séquencement séparant deux instructions : ";". 1.1.4 Variable $nompersonne est ce que l'on appelle une variable. En informatique, une variable est un objet permettant de stocker et de retrouver une information en mémoire (cf. gure 1.2). <nom> <valeur> $utilisateur Pierre Figure 1.2 schématisation d'une variable Une variable est caractérisée par : nom : choisi par le programmeur, désigne la variable sans ambiguïté (ici : $nompersonne). valeur : contenu de la variable (ici : fanny après que l'utilisateur ait ni de taper ce texte au clavier. Remarque : une variable peut être "accessible" de certains points d'un pro- gramme et pas d'autres. C'est ce que l'on appelle la portée d'une variable. Cette question sera vue avec la modularité. • Pas de véritable notion de type en PHP (contrairement à d'autres langages). Un nom de variable commence par • $ Choisir des noms signicatifs pour les variables. PHP distingue majuscules / minuscules. Convention possible : faire ressortir les diérentes parties d'un nom par une majuscule. 6 • Initialisation de variables par défaut par PHP. Cette initialisation dépend du contexte (la notion de contexte sera ultérieurement détaillée). Si la variable considérée est : nombre : valeur 0, chaîne : chaîne vide, valeur booléenne attendue : valeur "faux" (codée FALSE) Attention : une chaîne ayant la valeur 0 est considérée dans un contexte booléen comme ayant la valeur faux. Exemple (cet exemple sera détaillé à la section 1.2.1). <?php $a = "toto"; if ($a) { echo "vrai \n" ; } else { echo "faux \n" ; } ?> 1 5 ache faux si $a n'est pas initialisé. aché si $a faux est aussi aché si a (par exemple) la valeur 1 $a a la valeur 0 mais vrai est Autre exemple : echo "Somme = ", 30 + $a + 10 ; echo "\n" ; echo "Chaine = ", "30" . $a . "10" ache : Somme = 40 Chaine = 3010 Aectation. Assignation d'une valeur à une variable : symbole = (à ne pas confondre avec la notion d'égalité !). $n1 = $n2 ; $n1 est le receveur, $n2 le donneur Le donneur peut être n'importe quelle expression, le receveur doit être un emplacement valide de stockage (variable, élément de tableau). $n1 = $n1 + $n2 ; $n1 est ici d'abord donneur puis receveur. lire() est ce que l'on appelle une fonction. Quand $nompersonne = lire(), est rencontrée, il y a successivement Dans le texte ci-dessus l'instruction 7 • exécution de la fonction lire() : le programme s'arrète et ne reprend qu'après que l'utilisateur ait tapé un retour chariot. • la chaine de caractère qu'a tapé l'utilisateur est considérée comme le résultat de la fonction lire(). On dit que lire() retourne caractères. • cette valeur retournée est aectée à la variable Quand le programme passe à l'instruction suivante cette chaine de $nompersonne echo '"Bonjour", $nompersonne , c'est alors la valeur de cette variable qui est utilisée pour exécuter l'instruction et produire l'achage. 1.2 Structures de contrôle 1.2.1 Instruction conditionnelle Imaginons qu'on veuille personnaliser la réponse : si l'utilisateur est Pierre, on ache bonjour mon cher Pierre, sinon bonjour <Nom de l'utilisateur>. Pour cela, il faut utiliser une instruction conditionnelle. if (expression) { instruction1 ; instruction2 ; } else { instruction3 ; instruction4 ; } Dans le contexte d'une instruction conditionnelle, <expression>, (aussi apFALSE). Ne pas oublier pelée test), est évaluée à une valeur booléenne (TRUE ou les () autour de <expression>. Les délimiteurs de blocs sont impératifs dès que else et les il y au moins deux instructions à exécuter dans une alternative. Le instructions qui le suivent peuvent être absents. 1 <?php require("fonctions1.inc"); echo "Bonjour à tous\n" ; echo "Quel est ton nom : " ; $nompersonne = lire() ; 5 10 ?> if ($nompersonne == "Pierre") { echo "Bonjour mon cher Pierre\n" ; } else { echo "Bonjour ", $nompersonne ,"\n" ; } Remarques : • Ici les { } auraient pu être omises puisque dans chaque alternative il n'y a qu'une seule instruction à exécuter. 8 • Observer l'indentation du texte du programme. Elle n'est pas signicative pour PHP. Le texte pourait être écrit entièrement sur une seule ligne. Mais une bonne disposition facilite la lecture et la compréhension de la structuration du programme pour le programmeur. • Une partie entre accolades s'appelle un bloc. Le ; qui suit un bloc peut-être omis. S'il y a plus de deux alternatives on peut utiliser une "cascade" de elseif : <?php require("fonctions1.inc") ; echo "Bonjour à tous\n" ; echo "Quel est ton nom : " ; $nompersonne = lire() ; 1 5 10 15 ?> if ($nompersonne == "Pierre") { echo "Bonjour mon cher Pierre \n" ; } elseif ($nompersonne == "Marie") { echo "Bonjour ma chère Marie\n" ; } elseif ($nompersonne == "Clara") { echo "Bonjour ma très chère Clara\n" ; } else { echo "Bonjour ", $nompersonne ,"\n" ; } Remarquez que les diérentes alternatives sont disjointes : si on passe dans une, les suivantes sont ignorées même si une ou plusieurs conditions sont vériées. Comme dit précédemment, l'évaluation de l'expression qui suit le if doit produire vrai ou faux. Par extension on considèrera qu'une chaine de carac- TRUE. FALSE. tères qui n'est pas vide ou un nombre non nul à la valeur et 0 sont considérés comme ayant la valeur booléenne La chaine vide Exemple : <?php if ($a) { print "vrai \n" ; } else { print "faux \n" ; } ?> Dans le programme ci-dessus $a n'a jamais reçu d'aectation. Il est considéré comme étant la chaine vide ou le nombre 0 et dans le test l'évaluation booléenne sera donc faux. Ce programme achera donc le mot 9 faux. 1.2.2 Itérations On souhaite répéter l'achage du message tant que la chaîne fin n'est pas tapée. Pour cela, on utilise une itération : while (expression) { instruction1 ; instruction 2 ; } L'itération est répétée tant que expression a la valeur vrai. Dans le contexte <expression>, (i.e. test), est évaluée à une valeur booléenne. () autour <expression>. Le bloc entre accolades qui suit while s'appelle le corps de la boucle. (expression) exprime la "condition d'une itération, Ne pas oublier les le d'arrêt". <?php require("fonctions1.inc"); echo "Bonjour à tous\n"; echo "Quel est ton nom : "; $nompersonne = lire(); 1 5 10 15 20 ?> } while ($nompersonne != "fin") { if ($nompersonne == "Pierre") { echo "Bonjour mon cher Pierre\n"; } elseif ($nompersonne == "Marie") { echo "Bonjour ma chère Marie\n"; } else { echo "Bonjour ", $nompersonne ,"\n"; } echo "Quel est le nom suivant : "; $nompersonne = lire(); Remarques : • ($nompersonne != "fin") est un test qui prend la valeur vrai si la valeur de $nompersonne est diérente de fin • Observer le == pour tester l'égalité entre la valeur reçue par $nompersonne et les chaines Pierre ou Marie suivant le cas. • Observer que l'instruction $nompersonne = lire() ; apparaît deux fois. Une première fois avant le while pour saisir le nom de la première personne. Une deuxième fois en n de boucle, an d'avoir une nouvelle valeur de $nompersonne avant de refaire le test. Il existe plusieurs structures de contrôle permettant de réaliser des boucles, et souvent plusieurs manières diérentes d'écrire des traitement identiques ou voisins. Nous y reviendrons ultérieurement. Reprenons le programme précédent. On veut en outre compter le nombre de personnes à qui le programme dit bonjour. 10 Quel va être le principe ? L'expliciter en langage courant. Quelles variables et traitement sont à introduire ? Voici une solution : <?php /*commentaire Saluer des personnes. Ce programme permet de montrer les briques élémentaires d'un programme. */ require("fonctions1.inc"); 1 5 echo "Bonjour à tous\n" ; $nbpersonne = 0 ; // initialisation facultative echo "Quel est ton nom : " ; $nompersonne = lire() ; 10 while ($nompersonne != "fin") { $nbpersonne = $nbpersonne + 1 ; if ($nompersonne == "Pierre") { echo "Bonjour mon cher Pierre\n"; } elseif ($nompersonne == "Marie") { echo "Bonjour ma chère Marie\n"; } else { echo "Bonjour ", $nompersonne ,"\n"; } ; echo "Quel est le nom suivant : " ; $nompersonne = lire() ; } ; 15 20 25 30 ?> echo "J'ai dit bonjour à ", $nbpersonne, " personne (s).\n" ; /* ... */ encadrent un texte que l'on appelle commentaire, qui peut tenir sur plusieurs lignes. // précède également un commentaire mais qui occuppe uniquement la n de la ligne. Les commentaires sont ignorés par PHP. Ils sont très utiles au programmeur et autres lecteurs du programme pour comprendre celui-ci. Remarquer ici l'instruction $nbpersonne = $nbpersonne + 1 ; L'exécution de cette instruction se déroule selon un mécanisme analogue à celui décrit un peu plus haut pour l'aectation $nbpersonne + 1 $nompersonne = lire() ; est exécuté ou plus exactement calculé. le résultat de ce calcul est rangé dans la variable à gauche du signe = cette instruction a donc pour eet d'augmenter de 1, la valeur connue par le système pour la variable $nbpersonne 11 1.3 Bilan Eectuons le bilan de chapitre : • Un programme se compose d'instructions, celles-ci sont ordonnées et exécutées séquentiellement. • Deux types d'instructions : instruction élémentaire : instruction directement comprise par PHP : aectation, lecture, fonctions (echo, log,. . .). instruction non élémentaire et écrite par le programmeur : (lire(). . . Une telle instruction est écrite pour réaliser une tâche et nit toujours par se décomposer en termes d'instructions élémentaires (cf. fonctions et cours sur la modularité). • On distingue : mots réservés : mots et symboles appartenant à PHP : if, $, ; ,. . . echo, print, mots ou instructions librement choisis par le programmeur en respectant les conventions du langage : • Commentaire $nbpersonne = 0 ;. . . : texte facilitant la lecture du programme par un humain mais non exploité par la machine. Remarquez la notation diérente entre commentaire uniligne et multiligne. • Présentation : PHP est un langage de format libre : en général, les espaces entre éléments sont optionnels. Remarquez l'indentation, elle est importante pour la lisibilité du programme par le programmeur. Principe de déroulement d'un programme (cf. gure 1.3). état initial état final programme données résultats Figure 1.3 déroulement d'un programme Les instructions manipulent des objets portant l'information : les variables. Les variables sont consultées ou modiées. Analogie avec les langues naturelles : nom : variable, comme endroit pour conserver quelque chose, un endroit qui a un nom. verbe : commande. Une instruction commençant par un verbe est en gé- néral impérative et est évaluée pour ses eets secondaires. Ces verbes sont souvent appelés procédures (la commande echo est un tel verbe, la sortie est l'eet secondaire désirée). D'autres verbes traduisent leurs paramètres d'entrée en valeurs de retour (fonctions). 12 Chapitre 2 TP1 2.1 Découvrir • gérez vos répertoires et vos chiers : créez un répertoire pour le module d'analyse / programmation en PHP, des sous-répertoires par séquence ou thème. .php • suxez vos scripts PHP par • travaillez par analogie : sachez récupérer un programme et le transformer pour vos propres besoins. • récupérez les programmes vus en cours, copiez-les dans votre répertoire de travail, exécutez-les, modiez-les à votre guise pour observer les diérences, jusqu'à ce que vous ayez bien compris le rôle de chaque instruction. 2.2 Programmes et exercices (TD) 2.2.1 Acher le double d'un nombre choisi par l'utilisateur Programme doublelog.php 1 5 <?php require("fonctions1.inc"); echo "Veuillez rentrer un nombre :\n"; $nombre = lire(); echo "Le double de $nombre est : "; echo 2*$nombre; echo "\n"; ?> 2.2.2 Acher le nombre de lettres et le nombre de "a" que contient un mot choisi par l'utilisateur Programme nblettres.php 13 <?php require("fonctions1.inc"); 1 echo "Veuillez entrer un mot\n"; $chaine = lire(); $nblettres = strlen($chaine); 5 10 15 $nba = 0; for($i=0; $i<$nblettres; $i++) { if($chaine[$i] == 'a') { $nba++; } } echo "Le mot '$chaine' contient $nblettres lettres et $nba 'a'.\n"; ?> 2.2.3 Exercice En vous inspirant des deux exemples qui précèdent écrire les deux programmes ci-dessous : 1. On demande deux nombres à l'utilisateur, acher leur somme et leur produit. 2. On demande un mot à l'utilisateur, acher le nombre de voyelles de chaque type et le nombre total de voyelles. 2.2.4 Comparer deux nombres saisis par l'utilisateur diérentes écritures Programme pour comparer deux nombres : Programme comparenombres2.php <?php // Comparaison de deux nombres avec test d'égalité require("fonctions1.inc"); // Saisie des nombres ============================= echo "Donnez-moi un nombre \n "; $nombre1 = lire(); echo "Donnez-moi un autre nombre \n"; $nombre2 = lire(); // Comparaison ==================================== if ($nombre1 < $nombre2) { echo "le premier nombre est plus petit que le deuxième \n" ;} elseif ($nombre2 < $nombre1) {echo "le deuxième nombre est inférieur au premier \n" ;} else 14 { echo "Les deux nombres sont egaux \n";} ?> Ou encore Programme comparenombres3.php <?php // Comparaison de deux nombres avec test d'égalité require("fonctions1.inc"); // Saisie des nombres ============================= echo "Donnez-moi un nombre \n "; $nombre1 = lire(); echo "Donnez-moi un autre nombre \n"; $nombre2 = lire(); ?> // Comparaison ==================================== if ($nombre1 == $nombre2) { echo "Les deux nombres sont égaux \n" ;} elseif ($nombre2 < $nombre1) {echo "le deuxième nombre est inférieur au premier \n" ;} else { echo "Le premier nombre est inférieur au second \n";} 2.2.5 Répondre des messages diérents en fonction du nom de l'utilisateur Programme nom.php <?php require("fonctions1.inc"); ?> echo "quel est votre nom \n"; $nompersonne = lire(); if ($nompersonne == "fanny") { print "Bonjour ma chère fanny";} else {print "Bonjour $nompersonne";} 2.2.6 Exercice Ecrire un programme qui compare le nombre de voyelles de deux mots donnés par l'utilisateur. 2.2.7 Répeter un traitement jusqu'à ce que l'utilisateur veuille s'arrêter On reprend le programme qui répond des messages diérents à l'utilisateurs Programme nomIter1.php 15 <?php require("fonctions1.inc"); echo "quel est votre nom \n"; $nompersonne = lire(); ?> while ($nompersonne != "fin") { if ($nompersonne == "fanny") print "Bonjour ma chère fanny\n"; else print "Bonjour $nompersonne\n"; echo "quel est votre nom \n"; $nompersonne = lire(); } 2.2.8 Amélioration : compter le nombre de traitements faits Le même en comptant le nombre de personnes saluées. Programme nomIter2.php <?php require("fonctions1.inc"); echo "quel est votre nom \n"; $nompersonne = lire(); $nbpersonne = 0; ?> while ($nompersonne != "fin") {$nbpersonne = $nbpersonne +1 ; if ($nompersonne == "fanny") print "Bonjour ma chère fanny\n"; else print "Bonjour $nompersonne \n"; echo "quel est votre nom \n"; $nompersonne = lire(); } echo "j'ai salué $nbpersonnes \n"; 16 2.3 Jeu du nombre mystérieux Dans le jeu du nombre mystérieux, l'ordinateur tire au hasard un nombre que l'utilisateur doit deviner. Pour cela, l'utilisateur propose un nombre : s'il est trop petit, l'ordinateur répond trop petit, s'il est trop grand, l'ordinateur répond trop grand, sinon, c'est que le nombre proposé par l'utilisateur est celui choisi par l'ordinateur : l'utilisateur a alors gagné. Le but de cet exercice est d'écrire en procédant par étapes un programme de jeu du nombre mystérieux. Dans cet exercice, les étapes sont proposés et chaque étape complète la précédente. 1. Dans un premier temps, on triche : le nombre mystérieux n'est pas tiré au hasard : il est xé (par le programmeur) dans une variable. Cela permettra de faire plus facilement les tests ! Ecrivez le programme qui initialise cette variable et demande un nombre à l'utilisateur. 2. Indiquez au joueur si le nombre qu'il a proposé est plus petit, plus grand ou s'il a gagné. 3. Répétez les tours de jeu jusqu'à ce que le joueur ait deviné. 4. Indiquez en combien de tours le jour a deviné. On pourra adresser un message de félicitations si le joueur a deviné (par exemple) en moins de 8 tours. 5. Maintenant c'est l'ordinateur qui choisit le nombre. En phase de test, vous pouvez en début de partie faire acher le nombre choisi. Evidemment en version nale cet achage disparaitra. Indication : Le chier fonctions1.inc contient une fonction choix() qui retourne un nombre compris entre 0 et 100. 6. Bien sûr, d'autres améliorations sont possibles : Enchaîner les parties : à la n d'une partie, le programme demande à l'utilisateur s'il souhaite enchaîner une nouvelle partie. Indroduisez une condition d'arrêt supplémentaire : si au bout d'un certain nombre d'essais (xé par l'utilisateur ou le programme) l'utilisateur n'a pas trouvé le nombre mystérieux, le programme s'arrête automatiquement. Ajoutez un message de d'accueil pour souhaiter la bienvenue à l'utilisateur, un message d'adieu. Le programme peut demander au début le nom de l'utilisateur et le message d'adieu peut ainsi être personnalisé à l'utilisateur. Modiez le programme pour qu'il compte le nombre d'essais de l'utilisateur et qu'il ache, à chaque proposition de l'utilisateur, le nombre d'essais eectués. 17 2.4 Vote 2.4.1 Version 1 : saisie des noms au clavier Dans une petite commune, les électeurs, lors d'une élection partielle, ont le choix entre deux candidats, mais peuvent aussi voter pour une personne de leur choix. On souhaite connaître le nombre et le pourcentage de votes pour les deux candidats ociels. Scénario : les électeurs saisissent au clavier (l'anonymat et la validité du vote sont garantis !), chacun leur tour le nom de la personne pour laquelle ils désirent voter. A la clôture du bureau, le responsable tape fin pour terminer la saisie et acher le résultat (partiel) de l'élection. 1. Ecrivez d'abord un programme qui accepte des noms désignant les élus potentiels (par exemple, pierre, marie,. . .) saisis XXX. La après chaque saisie : Vous avez voté pour n de saisie. fin au clavier et ache, chaîne fin indique la ne doit pas être aché comme un vote. 2. Modiez le programme pour que celui-ci connaisse les noms des deux candidats déclarés saisis au clavier et indique : le nombre de voix obtenu par chacun de ces candidats lequel de ces deux candidats a obtenu le plus de le plus de voix. 3. Indiquez le pourcentage de voix de chacun des candidats par rapport au nombre total de votants. Avant de vous lancer dans les modications du programme, rééchissez aux questions suivantes : comment s'obtient (se calcule) un pourcentage ? quelle variable faut-il introduire ? Que se passe-t-il si personne ne vient voter ? 2.4.2 Version 2 : les noms sont stockés dans un chier On suppose maintenant que les noms ne sont plus saisis au clavier mais qu'ils ont été rangés dans un chier au fur et à mesure du vote et qu'un traitement de ce chier est ensuite fait pour calculer les résultats. Ecrire un chier texte contenant les votes (1 nom par ligne). Modiez le programme précédent, pour qu'il lise ce chier et ache les résultats du vote. Modier le programme pour que le nom du chier texte contenant les votes soit saisi au clavier et qu'un message d'erreur s'ache si le chier n'est pas trouvé. 18 Chapitre 3 Fichier texte et algorithmes sur les chiers 3.1 Notion de chier texte 3.1.1 Dénition Dénition d'un chier. Un chier est un ensemble d'informations identié par un nom et enregistré sur un support magnétique. Un chier texte est une suite de caractères ASCII. Ces caractères sont organisés en lignes. Chaque ligne est terminée par le caractère <RETOUR> (facultatif pour la dernière ligne). Les diérentes lignes ne sont pas nécessairement de même longueur. Historiquement, une ligne (ou che) d'un chier correspondait à un élément (par exemple, les ches signalétiques des livres d'une bibliothèque, des caractéristiques d'individus d'une même population,...). La gure 3.1 représente un chier texte. fin de ligne fin de fichier Figure 3.1 schématisation d'un chier Intérêts des chiers : stockage sur un support permanent (disque dur, disquette) autorisant l'archivage et non pas en mémoire vive, 19 stockage d'informations nécessaires à plusieurs programmes (cela évite de devoir maintenir une information commune à plusieurs programmes, cette idée débouchant sur les systèmes de gestion de bases de données). La contiguïté est l'ordre implicite des éléments du chier : la correspondance entre une position et un élément est implicite (on est sur le 3 ème élément uni- quement parce que celui-ci a deux prédécesseurs). Les chiers textes standards ne permettent pas d'accéder directement à une ligne : pour accéder à la i ème ème ligne, il faut parcourir les (i-1) premières. 3.1.2 Principe d'utilisation des chiers textes Un chier texte peut être édité et manipulé avec un éditeur de texte. Dans certains cas, c'est le moyen le plus aisé pour le modier (par exemple, remplacer une ligne). Dans d'autres cas, un programme eectuera plus aisément la tâche (par exemple remplacer une chaîne de caractères par une autre pour les lignes impaires du programme). Il peut être aussi nécessaire d'accéder à un chier par le biais d'un programme pour, par exemple, accéder à des données ou enregistrer des résultats. La manipulation d'un chier texte par un programme nécessite les 3 étapes suivantes : 1. Ouverture du chier. Cette étape comprend 2 opérations : (a) eectuer le lien entre le nom du chier dans le programme) et le nom physique du chier (nom du chier en mémoire secondaire) : canal d'entrée / sortie. (b) indiquer le mode d'utilisation du chier : lecture, écriture ou ajout de données (en fait, d'autres modes sont possibles). 2. Traitement des éléments du chier. Exemple : lecture des lignes, écriture d'information. Pour un chier séquentiel, l'accès se fait élément par élément sans possibilité de retour en arrière (si on souhaite revenir en arrière, il est nécessaire de fermer le chier, puis de le ré-ouvrir et d'avancer jusqu'à atteindre l'élément désiré). Le caractère de n de chier permet d'arrêter l'itération. En général, les algorithmes ont la forme indiquée à la gure 3.2. ouverture en lecture : ouverture en écriture lire fichier : <FICHIER> traiter ligne traiter ligne écrire fichier : print FICHIER...; Figure 3.2 principe des algorithmes usuels sur les chiers 3. Fermeture du chier. En PHP, l'ouverture du chier est eectuée par la fonction deux arguments. Par exemple : 20 fopen qui possède $FichPers = fopen("fpers1.txt","r") ; fopen handle ) de chier (sauf en cas false est retourné). Le chier fpers1.txt (qui est dans le répertoire renvoie un pointeur (ou descripteur ou d'échec ou courant) est ouvert en lecture. Le mode d'utilisation du handle de chier est désigné par le deuxième argu- fopen : r (comme dans ment de l'exemple ci-dessus) : le chier est ouvert en lecture seule. Le programme est prêt à lire le premier élément, mais celui-ci n'est pas encore lu. w : ouverture en écriture seule. Si le chier n'existe pas, il est créé (et son contenu est vide), s'il existe déjà, cette opération le vide de son contenu (cela permet de réinitialiser un chier). a : ouverture en ajout. Si le chier n'existe pas, il est créé. Dans tous les cas, on est positionné à la n du chier, prêt à écrire un nouvel élément après le dernier élément. Exemples d'échec : tentative d'ouverture en lecture d'un chier qui n'existe pas ou pour lequel on ne dispose pas des autorisations, tentative d'ouverture en écriture sur un chier protégé en écriture, à un répertoire inaccessible. Aussi, il est judicieux de vérier les valeurs de retour des fonctions fclose (cf. exemple ci-dessous). Un appel à fclose fopen et peut échouer en cas de disque plein, si le serveur contenant le chier devient inaccessible, etc. • la fermeture du chier s'eectue avec fclose avec comme argument un pointeur de chier. Par exemple : fclose($FichPers) ; feof • test de n de chier : • lecture dans un chier. Il existe plusieurs fonctions (pf désigne un pointeur avec comme argument un pointeur de chier. de chier) : fgetc(pf) : lecture d'un caractère fgets(pf,nbmax) : lecture d'une chaîne (terminée par \n ou la n de chier) ayant au plus nbmax - 1 caractères. fgetss(pf,nbmax) : comme fgets mais exclut les balises HTML et PHP lors de l'achage (mais celles-ci sont comptées pour nbmax). fread(pf,nbmax) fscanf(pf,chaine) file(nomphysiquedufichier) : chaque ligne du chier est placée dans un élément d'un tableau. • écriture dans un chier : pointé par pf. fwrite(pf,chaîne) : écrit chaîne dans le chier Un troisième argument optionnel indique le nombre de caractères de la chaîne à écrire. fputs est un alias de • file_exists(nomphysiquedufichier) true si il existe, false sinon). • filesize(nomphysiquedufichier) ment. 21 fwrite teste si le chier existe (renvoie donne la taille du chier en argu- 3.2 Algorithmes élémentaires sur les chiers 3.2.1 Lecture d'un chier Achage des lignes d'un chier On suppose que le chier existe. Exemple : chier de personnes, chaque ligne du chier contient le nom d'une personne, son âge et sa taille, ces caractéristiques étant séparées deux à deux par un espace. Le but du programme est d'acher chaque ligne, telle qu'elle se présente dans le chier. Attention : si le nom de la variable indiquant le nombre de caractères lus dans le $NbCarMaxLigne) a la valeur 0 phiée et a donc par défaut la valeur 0), alors aucun caractère n'est lu à chaque itération et on boucle. <?php $nbcarmaxligne = 1000 ; $fichpers = fopen("fpers1.txt","r") ; $num = 0 ; while (! feof($fichpers)) { $ligne = fgets($fichpers, $nbcarmaxligne) ; $num = $num + 1 ; echo "Ligne numéro $num : $ligne" ; } fclose($fichpers) ; ?> Remarque : Il est possible d'utiliser une constante : define (NBCARMAXLIGNE, 1000) ; ... $ligne = fgets($fichpers, NBCARMAXLIGNE) ; Pour le chier fpers1.txt suivant : Pierre 1.70 17 Noémie 1.61 25 Julie 1.66 18 Barthélemy 1.79 21 le programme ache : Ligne Ligne Ligne Ligne numéro numéro numéro numéro 1 2 3 4 : : : : fgets (ici, (par exemple, parce qu'elle a mal été orthogra- Pierre 1.70 17 Noémie 1.61 25 Julie 1.66 18 Barthélemy 1.79 21 22 \n) est $ligne. d'open et close : Chaque ligne du chier (y compris le caractère lue une à une lors de l'itération et est successivement stockée dans En contrôlant les valeurs de retour <?php $nbcarmaxLigne = 1000 ; $fichpers = fopen("fpers1.txt","r") ; if (! $fichpers) { echo "Impossible d'ouvrir fpers1.txt\n" ; exit ; } $num = 0 ; while (! feof($fichpers)) { $ligne = fgets($fichpers, $nbcarmaxligne) ; $num = $num + 1 ; echo "Ligne numéro $num : $ligne" ; } $res = fclose($fichpers) ; if (! $res) { echo "Impossible de fermer fpers1.txt\n" ; } ?> Achage des caractéristiques des lignes d'un chier Idem que précédemment, mais on souhaite un achage séparé des caractéristiques de chaque ligne. Pour cela, il est nécessaire de connaître la structure d'une ligne : quelles sont les caractéristiques d'une ligne, comment ces caractéristiques sont-elles séparées La structure d'une ligne de cet exemple est précisée au paragraphe 3.2.1. Les explode. list est utilisé pour aecter en rafale une liste de variables, chaelles correspondant à un élément du tableau renvoyé par explode diérentes caractéristiques d'une ligne s'obtiennent grâce à la fonction Le constructeur cune d'entre <?php $nbcarmaxligne = 1000 ; $fichpers = fopen("fpers1.txt","r") ; if (! $fichpers) { echo "Impossible d'ouvrir fpers1.txt\n" ; exit ; } $num = 0 ; while (! feof($fichpers)) { $ligne = fgets($fichpers, $nbcarmaxligne) ; list($nom, $taille, $age) = explode(" ", $ligne) ; $num = $num + 1 ; 23 } echo "Ligne numéro $num - Nom : $nom Taille : $taille Age : $age" ; $res = fclose($fichpers) ; if (! $res) { echo "Impossible de fermer fpers1.txt\n" ; } ?> ache : Ligne Ligne Ligne Ligne numéro numéro numéro numéro 1 2 3 4 - Nom Nom Nom Nom : : : : Pierre Taille : 1.70 Age : 17 Noémie Taille : 1.61 Age : 25 Julie Taille : 1.66 Age : 18 Barthélemy Taille : 1.79 Age : 21 echo \n. Remarquez que le passage à la ligne ne s'eectue pas par le biais du du programme, mais à cause de la caractéristique $age qui contient un Moyenne des caractéristiques lues dans un chier Calcul et achage de l'âge moyen et de la taille moyenne des personnes du chier. <?php $nbcarmaxligne = 1000 ; $fichpers = fopen("fpers1.txt","r") ; if (! $fichpers) { echo "Impossible d'ouvrir fpers1.txt\n" ; exit ; } $nbpers = 0 ; $somtaille = 0 ; $somage = 0 ; while (! feof($fichpers)) { $ligne = fgets($fichpers, $nbcarmaxligne) ; $ligne = rtrim($ligne) ; list($nom, $taille, $age) = explode(" ", $ligne) ; $nbpers = $nbpers + 1 ; $somtaille = $somtaille +$taille ; $somage = $somage + $age ; } $res = fclose($fichpers) ; if (! $res) { echo "Impossible de fermer fpers1.txt\n" ; } $agemoy = $somage / $nbpers ; $agemoyannees = intval($agemoy) ; $nbmois = round(($agemoy - $agemoyannees) * 12) ; 24 echo "taille moyenne : ", number_format($somtaille/$nbpers, 2), " age moyen $agemoyannees et $nbmois mois\n" ; ?> ache, toujours pour notre chier usuel : Taille moyenne : 1.69 Age moyen : 20 ans et 3 mois $age contient la valeur numérique de l'âge, \n. La somme des âges est cependant correctement calculée car la chaîne age\n est convertie dans le contexte arithmétique de $somage += $age ; en valeur numérique de age. Remarquez qu'à chaque itération suivi du caractère 3.2.2 Création et écriture d'un chier Lecture d'un chier de personnes et création d'un chier contenant les noms et les tailles des personnes supérieures à une certaine taille. <?php require("fonctions1.inc") ; $nbcarmaxligne = 1000 ; $fichpers = fopen("fpers1.txt","r") ; if (! $fichpers) { echo "Impossible d'ouvrir fpers1.txt\n" ; exit ; } $fichpersout = fopen("fpers2.txt","w") ; if (! $fichpersout) { echo "Impossible d'ouvrir fpers2.txt\n" ; exit ; } echo "Taille ? : " ; $seuiltaille = lire() ; while (! feof($fichpers)) { $ligne = fgets($fichpers, $nbcarmaxligne) ; list($nom, $taille, $age) = explode(" ", $ligne) ; if ($taille >= $seuiltaille) { fwrite($fichpersout, "$nom $taille\n") ; } } $res = fclose($fichpers) ; if (! $res) { echo "Impossible de fermer fpers1.txt\n" ; } $res = fclose($fichpersout) ; if (! $res) { echo "Impossible de fermer fpers2.txt\n" ; } ?> 25 Chapitre 4 TP 2 - Calcul de moyenne de notes 4.1 Exercice 1 : utilisation de boucles En utilisant la structure for ($i = 1; $i <= 1000; $i ++){ ... } écrire un programme qui ache 1000 fois "Mais non, ce n'est pas une chanson monotone". écrire un programme qui ache 1000 fois "X kilomètres à pied, ça use les souliers" pour X variant de 1 à 1000. 4.2 Exercice 2 : compter les rouges et les verts On considère disposer d'un chier contenant, pour chaque ligne, le mot rouge ou vert. rouge vert rouge rouge vert rouge rouge rouge vert vert vert rouge Écrire un programme qui lit ce chier et compte le nombre de rouges et de verts. Pour cela, on pourra utiliser deux variables structure switch suivante : 26 $rouge et $vert puis la switch ($motludanslefichier}{ case "rouge": $rouge ++; break; case "vert": $vert ++; break; } default: echo "couleur non autorisée\n"; break; 4.3 Exercice 3 : moyenne de notes par personne On considère des chiers structurés comme le chier fnotes1.txt (_ est le caractère souligné) : sport_math_français_histoire_anglais paul:12_7_8_3_4 marie:7_2_5_19_11 albert:0_0_0_0_0 jean:15_9_10_7_20 julie:7_18_20_0_20 clara:13_7_15_10_9 La première ligne du chier contient une liste de matières, chaque matière est séparée par le caractère puis le caractère : _. Chaque ligne contient le nom d'une personne, puis une série de notes, les notes étant séparées par l_. 1. Écrivez une procédure qui reçoit en paramètre d'entrée une chaîne de caractères contenant des nombres séparés par _ et qui retourne la moyenne de ces nombres. Écrivez le script php minimal qui teste cette fonction (indication : le test peut être eectué en utilisant une chaîne comme par exemple 9_11_8_14). 2. Écrivez un script php achant le nom de chaque personne contenue dans un chier structuré comme fnotes1.txt La première ligne du chier est donc à ignorer (i.e. à sauter). 3. Écrivez un script php achant pour chaque personne sa moyenne. Pour fnotes1.txt, le résultat est : paul : 6.80 marie : 8.80 albert : 0.00 jean : 12.20 julie : 13.00 clara : 10.80 4. Écrivez une procédure qui ache les éléments (clé et valeur) d'un tableau dont les clés sont des chaînes de caractères. Écrivez le script php mini- 27 mal qui teste cette fonction. En utilisant la fonction éléments de Tableau asort(Tableau), les sont triés suivant l'ordre de leurs valeurs. 5. Modiez le script php écrit à la question 3 pour que celui-ci range la moyenne de chaque personne dans un tableau dont les clés sont les noms des personnes. 6. Modiez le script php écrit à la question précédente pour que la première ligne du chier (i.e. les matières) soit traitée (c'est-à-dire, la liste des matières est achée avant les noms et les moyennes). Plusieurs techniques sont possibles pour traiter la première ligne. Sur fnotes1.txt, ce script produit : Les Cle Cle Cle Cle Cle Cle matières sont : sport math français histoire anglais : albert - valeur : 0.00 : paul - valeur : 6.80 : marie - valeur : 8.80 : clara - valeur : 10.80 : jean - valeur : 12.20 : julie - valeur : 13.00 Remarques : On rappelle que pour écrire l'ensemble d'un programme deux démarches sont possibles : (a) démarche ascendante : soit commencer par écrire et tester les fonc- tions les plus élémentaires. Ensuite, remonter jusqu'au programme principal. (b) démarche descendante : soit commencer par écrire le programme principal avec une version simpliée des fonctions nécessaires et tester l'ensemble. Ensuite, descendre jusqu'à l'écriture complète des fonctions élémentaires. En fait, ces deux démarches sont concomitantes et complémentaires et, dans la pratique, on passe souvent de l'une à l'autre. Dans les questions précédentes, vous pouvez distinguer celles relevant d'une démarche ascendante et celle d'une démarche descendante. 28 Chapitre 5 Les tableaux 5.1 Les tableaux simples Les tableaux permettent un premier niveau de structuration des données en PHP. Il s'agit de collectionner des éléments qui ont un rapport entre eux, en utilisant un nom de variable générique. Par exemple, dans le chier ci-dessous, on décide de stocker les noms des mois de l'année dans un tableau $annee[1] sera février, $annee. Ainsi $annee[0] sera janvier, etc. 1 <?php // -------------------- mois.php -------------------// manipulation d'un tableau de mois 5 // création d'un tableau $annee = array( "janvier", "fevrier", "mars", "avril"); 10 15 20 25 // affichage formaté print_r($annee); /*Array ( [0] => janvier [1] => fevrier [2] => mars [3] => avril )*/ // affichage avec un compteur for($i = 0; $i < sizeof($annee); $i ++) echo "le mois " . ($i + 1) . " de l'annee est $annee[$i].\n"; /*le mois 1 de l'annee est janvier. le mois 2 de l'annee est fevrier. le mois 3 de l'annee est mars. le mois 4 de l'annee est avril. */ 29 30 35 40 45 50 55 60 65 70 75 // affichage avec foreach foreach($annee as $mois) // pas d'indice echo "$mois\n"; /*janvier fevrier mars avril */ // ajout d'un mois supplémentaire à la fin array_push($annee, "mai"); $annee[] = "juin"; $annee[sizeof($annee)] = "juillet"; print_r($annee); /*Array ( [0] => janvier [1] => fevrier [2] => mars [3] => avril [4] => mai [5] => juin [6] => juillet )*/ // retrait de la fin echo "dernière valeur : " . array_pop($annee) . "\n"; /*dernière valeur : juillet*/ print_r($annee); /*Array ( [0] => janvier [1] => fevrier [2] => mars [3] => avril [4] => mai [5] => juin )*/ // ajout d'un mois supplémentaire au début array_unshift($annee, "decembre"); print_r($annee); /*Array ( [0] => decembre [1] => janvier [2] => fevrier [3] => mars [4] => avril 30 80 85 90 95 )*/ [5] => mai [6] => juin // retrait du début echo "première valeur : " . array_shift($annee) . "\n"; // première valeur : decembre print_r($annee); /*Array ( [0] => janvier [1] => fevrier [2] => mars [3] => avril [4] => mai [5] => juin )*/ ?> 5.2 Les chaînes de caractères Les chaînes de caractère peuvent être considérées comme des tableaux de caractères. Dans l'exemple suivant, $chaine[$i] est le caractère de rang dans la chaîne. 1 <?php $chaine = "Bonjour à tous !"; 5 10 15 20 for($i = 0; $i < strlen($chaine); $i ++) echo "caractère $i : $chaine[$i]\n"; /* caractère caractère caractère caractère caractère caractère caractère caractère caractère caractère caractère caractère caractère 0 : B 1 : o 2 : n 3 : j 4 : o 5 : u 6 : r 7 : 8 : à 9 : 10 : t 11 : o 12 : u 31 $i 25 caractère 13 : s caractère 14 : caractère 15 : ! */ ?> On peut créer un tableau en découpant une chaîne de caractère suivant un délimiteur grâce à l'instruction explode : <?php 1 $mots = explode(" ","Bonjour à tous !"); 5 10 15 print_r($mots); /* Array ( [0] => Bonjour [1] => à [2] => tous [3] => ! ) */ ?> 5.3 Les tableaux associatifs Jusqu'ici, nous avons vu des tableaux où les valeurs correspondent à des indices entiers, appelés également des clés. Les tableaux associatifs permettent d'associer une clé de type chaîne de caractère à une valeur. Les usages sont multiples : associer sa traduction en Anglais à un mot français, liste de caractéristiques d'un ensemble de personnes, indiquer pour chaque immatriculation le nom du propriétaire, . . . Dans l'exemple ci-dessous, on associe à un nom d'artiste son score de vente. 1 <?php 5 // définition du tableau de classement, qui associe un artiste à son score de vente $classement = array("georges" => 1000, "jacques" => 500, "lucienne" => 750, "edith" => 800); 10 // affichage du tableau print_r($classement); 15 /*-------------------------------------------Résultat : Array ( 32 20 ) */ [georges] => 1000 [jacques] => 500 [lucienne] => 750 [edith] => 800 ?> 1 <?php // ----------------------------------- exo2.php -------------------------------------// parcours de tableau 5 require("definition.inc"); 10 15 // affichage du tableau foreach($classement as $artiste => $score) echo "l'artiste $artiste a un score de $score\n"; echo "\n"; // affichage avec un while reset($classement); while(list($artiste, $score) = each($classement)) echo "l'artiste $artiste a un score de $score\n"; echo "\n"; 20 25 30 // affichage avec un itérateur if(sizeof($classement)){ end($classement); $fin = key($classement); reset($classement); while(true){ echo "l'artiste " . key($classement); echo " a un score de " . current($classement) . "\n"; if(key($classement) == $fin) break; next($classement); } } exit(0); 35 40 /*-------------------------------------------Résultat : l'artiste georges a un score de 1000 l'artiste jacques a un score de 500 l'artiste lucienne a un score de 750 l'artiste edith a un score de 800 */ 33 ?> 5.4 Fonctions pour les tableaux Nom Signication array_pop($variable) ; supprime le dernier élément d'un tableau tout array_push($variable, $val) ; ajoute une valeur à la n un tableau. array_unshift($variable, $val) ; ajoute une valeur au début d'un tableau. arsort($tableau) trie en le retournant. un tableau dans un ordre inverse en conservant le couple clé/valeur. asort($variable) trie les valeurs d'un tableau en conservant le valeur = current($variable) retourne l'élément courant désigné par le poin- couple clé/valeur. teur d'un tableau. $tableau = each($variable) retourne chacune des paires clé/valeur dans un tableau à quatre éléments (0 => clé, 1 => 'valeur', key => clé, value => 'valeur') et avance le pointeur de tableau. end($variable) ; place le pointeur sur le dernier élément du tableau. true | false = in_array($valeur, recherche la valeur spéciée dans un tableau. $tableau) clé | false = ar- retourne la clé associée en cas de réussite de ray_search($valeur, $tableau) la recherche de la valeur spéciée. clé = key($variable) retourne la clé en cours dans le tableau. nombre = krsort($variable) trie les clés d'un tableau dans l'ordre inverse en conservant les paires clé/valeur. nombre = ksort($tableau) trie les clés d'un tableau en conservant les paires clé/valeur. list($variable , ..., $variableN) aecte une série de variables en une seule opération. valeur | false = next($variable) avance le pointeur au prochain élément et retourne la valeur correspondante. valeur | false = prev($variable) déplace le pointeur sur l'élément précédent et valeur = reset($variable) déplace le pointeur sur le premier élément d'un nombre = sizeof($variable) retourne le nombre d'éléments d'un tableau. retourne la valeur. tableau et retourne sa valeur. 34 Chapitre 6 TD 3 6.1 Exercice Il s'agit de lire un chier contenant, sur chaque ligne, une association <artiste> <score>. Ecrire un programme qui charge le contenu d'un tel chier dans un tableau associatif. Indications : pour découper la ligne en artiste et score, utilisez la fonction explode(" ", $ligne) informations à l'aide de qui renvoie un tableau dont vous pouvez récupérer les list($cle, $valeur). 6.1.1 Manipulations En le parcourant, calculer puis acher la taille du tableau. Chercher dans le tableau la personne qui a le meilleur score. Chercher la personne qui a un score de 750. 6.2 Exercice Réaliser un programme qui compte les occurrences des lettres de l'alphabet contenues dans ce chier. Utiliser un tableau de 26 valeurs, indexées de 0 à 25. ord($chaine) renvoie le code ASCII de la première ord($char) - ord(A) est l'index de $char. od -c <fichier> ache le contenu d'un chier en mode Indications : la fonction lettre (voir table 6.1). Ainsi, La commande Linux caractère. 35 code 0 1 2 3 4 5 6 7 8 9 0 NUL SOH STX ETX EOT ENQ ACK BEL BS HT 10 LF VT NP CR SO SI DLE DC1 DC2 DC3 20 DC4 NAK SYN ETB CAN EM SUB ESC FS GS 30 RS US SP ! " # $ % & ' 40 ( ) * + , - . / 0 1 50 2 3 4 5 6 7 8 9 : ; 60 < = > ? @ A B C D E 70 F G H I J K L M N O 80 P Q R S T U V W X Y 90 Z [ ] ∧ _ ` a b c 100 d e f g h i j k l m 110 n o p q r s t u v w 120 x y z { | } DEL Table 6.1 Table de caractères ASCII - 7 bits 36 nom décimal clavier terme anglais NULL 0 Ctrl+@ Null terme français Nul SOH 1 Ctrl+A Start of heading Début d'entête Début de texte STX 2 Ctrl+B Start of text ETX 3 Ctrl+C End of text Fin de texte EOT 4 Ctrl+D End of transmit Fin de communication ENQ 5 Ctrl+E Enquiry Demande ACK 6 Ctrl+F Acknowledge Accusé de réception BELL 7 Ctrl+G Bell Sonnerie BS 8 Ctrl+H Backspace Retour arrière Tabulation horizontale HT 9 Ctrl+I Horizontal tab LF 10 Ctrl+J Line feed Interligne VT 11 Ctrl+K Vertical tab Tabulation verticale FF 12 Ctrl+L Form feed Page suivante CR 13 Ctrl+M Carriage return Retour en début de ligne SO 14 Ctrl+N Shitf out Hors code SI 15 Ctrl+O Shift in En code DLE 16 Ctrl+P Data line escape Echappement en transmission DC1 17 Ctrl+Q Device control 1 Commande auxiliaire 1 DC2 18 Ctrl+R Device control 2 Commande auxiliaire 2 DC3 19 Ctrl+S Device control 3 Commande auxiliaire 3 DC4 20 Ctrl+T Device control 4 Commande auxiliaire 4 NAK 21 Ctrl+U Negative acknowledge Accusé de réception négatif SYN 22 Ctrl+V Synchronous idle Synchronisation ETB 23 Ctrl+W End of transmit block Fin de bloc transmis CAN 24 Ctrl+X Cancel Annulation EM 25 Ctrl+Y End of medium Fin de support SUB 26 Ctrl+Z Substitute Remplacement ESC 27 Ctrl+[ Escape Echappement FS 28 Ctrl+ File separator Séparateur de chier GS 29 Ctrl+] Group separator Séparateur de groupe RS 30 Ctrl+∧ Record separator Séparateur d'enregistrement US 31 Ctrl+_ Unit separator Séparateur d'unité SP 32 Barre Space Espacement DEL 127 Del Delete Eacement Table 6.2 Caractères ASCII non imprimables 37 Chapitre 7 TP 3 7.1 Exercice Lire dans un chier une liste d'association <artiste> <score>. Il peut y avoir plusieurs lignes pour un même artiste, auquel cas il faut additionner le score lu au score total. Acher le classement par ordre d'artiste puis par meilleur score. Indications : la partie relative au chargement a été traitée en TD. Quand on $artiste => $score, il faut tester si elle est déjà répertoisset(). connaît l'assocation riée, à l'aide de 7.2 Exercice Pour expérimenter l'emploi d'un tableau pour gérer une pile (resp. une le), écrire un programme qui charge un chier dans un tableau à l'aide de array_push ou de la notation $tableau[] = $ligne puis ache les lignes du array_pop (resp. array_unshift pour ob- tableau à l'aide d'une succession de tenir le comportement d'une le. 7.3 Exercice 7.3.1 Version élémentaire Réaliser un programme qui lit un chier HTML (utiliser le chier fourni, test.html, qui reprend une structure simple de tableau) et stocke toutes les balises lues dans un tableau. On supposera qu'il n'y a qu'une seule balise par ligne. Indication : utiliser une expression régulière avec parenthèse capturante pour preg_match("/<(.*)>/", $ligne, $match) teste $ligne et met dans $match[1] le contenu matché par ($match[0] contient l'expression régulière complète). lire la balise. Par exemple s'il y a une balise dans les parenthèses 7.3.2 Vérication des balises Réaliser un programme qui vérie que chaque balise fermante correspond bien à la dernière balise ouvrante et indique le numéro de ligne et le type d'erreur. 38 Indication : désormais, il ne faut pas stocker toutes les balises dans le tableau, seulement les ouvrantes. Quand une fermante est lue, on la compare avec le résultat d'un array_pop(). 7.3.3 Vérication avancée Améliorer le programme précédent pour qu'il fonctionne avec plusieurs balises par ligne. preg_match_all("/<(.*)>/U", $ligne, $match[1] la liste de toutes les balises de la ligne. Indication : utiliser la fonction $match) qui aecte dans L'option U de l'expression régulière permet de neutraliser le mode glouton qui cherche à faire correspondre la plus grande chaîne possible. Ici, ce sera la plus petite. 39 Chapitre 8 Les fonctions Les fonctions permettent de regrouper un ensemble d'instructions complexes. L'appel de la fonction remplacera donc cette suite de commandes. Elles répondent à des besoins de structuration du programme, an que sa réalisation procède par étapes, et proposent au programmeur des possibilités de généricité. 8.1 Exemples Nous détaillons la réalisation de la fonction lire, que nous utilisons pour récupérer une chaîne saisie au clavier par l'utilisateur. Le programme ci-dessous lit les prénom et nom puis les ache. 1 <?php //---------------------------- cours1.php ------------------define(MAXLIGNE, 1000); 5 // lecture du prénom 10 echo "saisissez votre prénom : "; $fich = fopen("/dev/tty","r"); // ouverture entrée standard if (!$fich) die("erreur d'ouverture de l'entrée standard"); $prenom = rtrim(fgets($fich, MAXLIGNE)); // lecture d'une ligne fclose($fich) or die("erreur de fermeture du fichier"); // fermeture // lecture du nom 15 20 echo "saisissez votre nom : "; $fich = fopen("/dev/tty","r"); // ouverture entrée standard if (!$fich) die("erreur d'ouverture de l'entrée standard"); $nom = rtrim(fgets($fich, MAXLIGNE)); // lecture d'une ligne fclose($fich) or die("erreur de fermeture du fichier"); // fermeture // affichage echo "Bonjour, $prenom $nom\n"; 40 25 ?> Une fonction est dénie par son nom, introduit à l'aide du mot clé function, et par une liste d'arguments. Elle dénit un bloc d'instruction, à la n duquel une valeur est eventuellement retournée, qui peut être utilisée par l'instruction qui a appelé la fonction. Une fonction peut retourner tout type de valeur géré par PHP : booléen (true ou false), numérique, chaîne de caractères, tableau, identiant de chier, etc. Dans l'exemple précédent, on remarque qu'on refait quasiment deux fois la même chose. La version ci-dessous est mieux structurée, et sa forme générale (section principale et fonction principale) sera réutilisée dans les programmes futurs. 1 <?php //---------------------------- cours2.php ------------------define(MAXLIGNE, 1000); 5 // lecture d'une chaîne function lirechaine(){ $fich = fopen("/dev/tty","r"); // ouverture entrée standard if (!$fich) die("erreur d'ouverture de l'entrée standard"); $chaine = rtrim(fgets($fich, MAXLIGNE)); // lecture d'une ligne fclose($fich) or die("erreur de fermeture du fichier"); // fermeture return $chaine; } 10 15 20 25 // fonction principale function main(){ echo "saisissez votre prénom : "; $prenom = lirechaine(); echo "saisissez votre nom : "; $nom = lirechaine(); echo "Bonjour, $prenom $nom\n"; } // section principale main(); ?> Les fonctions utilisées ici ne prennent pas d'argument. La version ci-dessous est plus générique car elle permet de lire dans n'importe quel chier. 1 5 <?php //---------------------------- cours3.php ------------------define(MAXLIGNE, 1000); define(CLAVIER, "/dev/tty"); // lecture d'une chaîne dans le fichier de nom $nomfich function lirechaine($nomfich){ $fich = fopen($nomfich ,"r"); // ouverture entrée standard 41 10 } 15 20 25 if (!$fich) die("erreur d'ouverture de $nomfich"); $chaine = rtrim(fgets($fich, MAXLIGNE)); // lecture d'une ligne fclose($fich) or die("erreur de fermeture de $nomfich"); // fermeture return $chaine; // fonction principale function main($nomfich){ echo "saisissez votre prénom : "; $prenom = lirechaine($nomfich); echo "saisissez votre nom : "; $nom = lirechaine($nomfich); echo "Bonjour, $prenom $nom\n"; } // section principale main(CLAVIER); main("test.txt"); ?> 8.2 Portée des variables Les variables utilisées n'ont d'existence que dans la fonction. Cela semble naturel pour les paramètres, mais c'est valable pour toute variable qui n'est pas déclarée à l'aide du mot clé $chaine global. Dans l'exemple ci-dessous, la variable lirechaine ce lirechaine2, il est déclarée globale, ce qui n'empêche pas que dans soit une autre variable du même nom qui soit utilisée. Dans est rappelé que $chaine est globale, son aectation est donc conservée dans la fonction appelante. 1 5 10 15 <?php //---------------------------- cours4.php ------------------define(MAXLIGNE, 1000); define(CLAVIER, "/dev/tty"); // lecture d'une chaîne dans le fichier de nom $nomfich function lirechaine($nomfich){ $fich = fopen($nomfich ,"r"); // ouverture entrée standard if (!$fich) die("erreur d'ouverture de $nomfich"); $chaine = rtrim(fgets($fich, MAXLIGNE)); // lecture d'une ligne fclose($fich) or die("erreur de fermeture de $nomfich"); // fermeture return $chaine; } // lecture d'une chaîne avec affectation de variable globale function lirechaine2($nomfich){ $fich = fopen($nomfich ,"r"); // ouverture entrée standard if (!$fich) die("erreur d'ouverture de $nomfich"); global $chaine; 42 20 } 25 30 35 $chaine = rtrim(fgets($fich, MAXLIGNE)); // lecture d'une ligne fclose($fich) or die("erreur de fermeture de $nomfich"); // fermeture return $chaine; // fonction principale function main(){ global $chaine; $chaine = "toto"; echo "$chaine\n"; echo lirechaine(CLAVIER) . " $chaine\n"; echo lirechaine2(CLAVIER). " $chaine\n"; } // section principale main(); 40 /* toto titi titi toto tata tata tata */ 45 ?> // écho saisie clavier // écho saisie clavier 43 8.3 Génie logiciel Un programme est nalement une suite d'instructions élémentaires qu'il est illusoire de vouloir écrire directement. Il faut procéder par analyse descendante, qui dissèque progressivement toutes les étapes à réaliser, en allant des plus complexes aux plus simples. Une fois cette analyse réalisée à la main, éventuellement à l'aide d'UML, on peut passer à la phase d'écriture du code PHP, en utilisant cette fois-ci une analyse montante : du plus élémentaire au plus complexe, en écrivant une fonction pour chaque étape. Lors de la conception de programmes, on veillera à respecter quelques règles : utiliser un minimum de variables globales. On se retreindra pour cela aux variables fournies par le navigateur (utiliser phpinfo()), en considérant que le script entier est en fait un appel de fonction avec ses paramètres ; une fonction doit faire un minimum d'eet de bord. Par exemple, on évitera systématiquement les achages, en choisissant plutôt de renvoyer une chaîne de caractères qui sera achée par l'instruction appelante. Cette technique permet de séparer le fond (les calculs menés par les fonctions PHP) et la forme, gérée par le HTML, dans lequel on fera appel aux fonctions à l'aide d'échappements locaux de la forme <?php echo fonction(...);?>. En eet, lors de la production d'un site complexe, ce ne seront pas forcément les mêmes personnes qui écriront le HTML et le PHP. Le chier HTML doit donc contenir un minimum de PHP et vice-versa. Le script général page.php sera chargé par le navigateur, et sa section prin- cipale contiendra uniquement une inclusion du chier HTML. Il doit donc être écrit de façon à être parfaitement inactif, le HTML se chargeant de déclencher tous les achages. <?php //------------------- page.php ------------------//------------------- fonctions utilitaires ------function fonction1(...){ ... return ... } function fonction2(...){ ... return ... } //------------------- section principale ---------include(``page.html''); ?> <!------------------- page.html -------------------> <html> 44 <head> <title>....</title> </head> <body> <table> <tr> <td> <?php echo fonction1(...);?> </td> <td> <?php echo fonction2(...);?> </td> </tr> </table> </body> </html> 8.4 Récursivité Lorsque cela est plus naturel, on pourra écrire une fonction qui s'appelle elle-même. Par exemple, pour calculer la somme des éléments d'un tableau, on pourra avoir les deux visions possibles : itérative : parcourir tout le tableau à l'aide d'un variable $somme foreach qui ajoute à une $somme ; chaque élément du tableau et renvoyer récursive : considérer que la somme d'un tableau, c'est celle de son premier élément et du reste du tableau auquel on a retiré ce premier élément. 1 <?php //---------------------------- cours5.php ------------------- 5 10 15 20 // calcul de la somme des éléments d'un tableau // version itérative function sommeiterative($tab){ $somme = 0; foreach($tab as $val) $somme += $val; return $somme; } // calcul de la somme des éléments d'un tableau // version récursive function sommerecursive($tab){ if(!sizeof($tab)) return 0; $premier = array_shift($tab); return $premier + sommerecursive($tab); } 45 25 30 // fonction principale function main(){ $tab = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); echo "somme iterative = " . sommeiterative($tab) . "\n"; echo "somme récursive = " . sommerecursive($tab) . "\n"; } // section principale main(); ?> Si la forme récursive est plus séduisante intellectuellement, elle n'est pas très ecace d'un point de vue informatique. Dans l'exemple précédent, PHP devra conserver autant de copies diérentes du tableau qu'il contient d'éléments. Malgré cela, cette forme sera utilisée lorsque la forme itérative est dicile à concevoir. 8.5 Passage par référence Nous avons vu que les fonctions que nous écrivons ne doivent pas faire d'eet de bord. Cela dans le but de mieux contrôler l'endroit où ces eets sont réalisés, en général dans le code HTML. Nous avons cité le cas des achages, pour lesquels on conseille de renvoyer le résultat à acher. C'est la fonction appelante qui se charge de l'eet de bord. Un eet de bord important consiste en l'aection d'une valeur à une variable globale. Si de telles instructions sont écrites, il devient dicile de retrouver les causes d'une erreur qui mettent en cause cette variable. Un simple renommage de la variable devient impossible. D'une façon générale, on s'eorcera de ne travailler que sur les variables locales de la fonction. Comment alors modier ces variables pour que les fonctions ne se contentent pas de renvoyer un résultat, mais puissent également modier leurs paramètres ? Si on ne le précise pas, la variable qui représente le paramètre est une copie de l'argument de la fonction. La fonction modie cette copie autant qu'elle le veut, mais une fois son action terminée, ces modications ne sont pas répercutées sur l'original. Il faut préxer le nom d'un paramètre de fonction avec le symbole & (éperluette ou et-commercial). Dans ce cas, PHP comprend qu'il ne doit pas recopier l'argument correspondant, mais utiliser le paramètre comme une référence de l'argument, c'est à dire un autre nom de variable que celui utilisé lors de l'appel. Il ne s'agit plus d'une recopie du contenu de la variable argument, mais d'une copie de son mode d'accès, sa référence. Ainsi, toute modication de ce paramètre dans la fonction aecte nécessairement la variable argument, car PHP utilise deux noms diérents pour accéder à la même chose. 1 <?php //------------------------------ cours1.php ------------- 46 5 10 15 20 25 30 35 40 45 50 // ajoute $element dans $tableau, passage par valeur function ajoute($tableau, $element){ $tableau[] = $element; echo "dans ajoute : tableau = "; print_r($tableau); } // ajoute $element dans $tableau, passage par référence function ajoute_reference(&$tableau, $element){ $tableau[] = $element; echo "dans ajoute_reference : tableau = "; print_r($tableau); } function main(){ $annee = array("janvier", "fevrier", "mars", "avril"); ajoute($annee, "mai"); echo "dans main : annee = "; print_r($annee); ajoute_reference($annee, "mai"); echo "dans main : annee = "; print_r($annee); } main(); /* dans ajoute : tableau = Array ( [0] => janvier [1] => fevrier [2] => mars [3] => avril [4] => mai ) dans main : annee = Array ( [0] => janvier [1] => fevrier [2] => mars [3] => avril ) dans ajoute_reference : tableau = Array ( [0] => janvier [1] => fevrier [2] => mars [3] => avril [4] => mai ) 47 55 60 65 dans main : annee = Array ( [0] => janvier [1] => fevrier [2] => mars [3] => avril [4] => mai ) */ ?> 48 Chapitre 9 TD 4 9.1 Exercice Ecrire des fonctions élémentaires qui chargent un chier d'associations : ouverture d'un chier et retour de son descripteur ; lecture d'une ligne et retour de la chaîne correspondante ; fermeture d'un chier ; chargement d'un chier et retour du tableau correspondant ; 9.2 Exercice Ecrire une série de fonctions qui achent les cellules d'un tableau : constitution de la chaîne d'achage d'une section constitution de la chaîne d'achage d'une section td ; tr ; constitution de la chaîne complète d'achage d'un tableau, à l'aide d'un parcours séquenciel ; idem avec un parcours récursif du tableau. 49 Chapitre 10 TP 4 10.1 Exercice Un chier musiciens.txt contient une liste de musiciens, caractérisés par leur identiant et nom. Dans un chier exo1.php, écrire une fonction qui af- che un tableau HTML avec deux colonnes. Cette fonction sera appelée par la fonction principale main. Indications : on pourra procéder de deux manières diérentes. Soit charger entièrement le chier dans un tableau PHP puis créer le tableau HTML, soit lire le chier ligne par ligne et écrire le tableau HTML en une fois. La première solution a l'intérêt d'utiliser des fonctions génériques mais le défaut de requérir l'utilisation d'un tableau intermédiaire. La seconde est donc plus ecace. 10.1.1 Exercice Créer un chier biblio.php qui regroupe les fonctions de la bibliothèque chiers et tableaux. 10.2 Exercice Aménager le programme précédent pour séparer la forme et le fond et le visualiser dans un navigateur. Pour cela, copier un chier exo2.html exo1.php en exo2.php et écrire qui sera inclus dans le chier PHP et remplace l'appel principal. Le chier de bibliothèque sera également utilisé. 10.3 Exercice instruments.txt contient une liste de d'instruments. affectations.txt contient une liste d'associations musi- Un deuxième chier Un dernier chier cien/instrument. Acher un tableau HTML récapitulatif de l'aectation de chaque personne. Indications : réaliser une fonction jointure, qui prend en argument un ta- bleau gauche, un tableau milieu et un tableau droit puis construit un tableau correspondant à la jointure entre ces trois tables. 50 Chapitre 11 Programmation objet 11.1 Introduction Les objets permettent de manipuler des modèles informatiques composés, représentatifs d'une entité imaginaire. Il s'agit d'une extension du principe de variable, qui permet de regrouper dans une structure autonome des variables et leur traitement. Jusqu'ici nous avons utilisé des variables de diérents types : numériques, mySQL. echo ou chaînes de caractères, tableaux, identiants de chiers ou de connection Cela nous a semblé parfaitement naturel d'utiliser une même méthode print_r pour acher ces variables, indépendamment de leur type. Pourtant, à l'aide de chaque variable, nous avons représenté notre conception d'un objet du monde. Par exemple, une seule variable entière $idclient nous permettait de qualier un client, et d'accéder à ses attributs (prénom, nom), selon que ces valeurs étaient issues d'un formulaire ou d'une base de données. Nous pourrions utiliser des variables de type client, et des méthodes sur ces clients, comme l'achage dans une page HTML ou l'accès à une base de données. La conception objet permet de dénir de nouveaux types complexes, voire de les composer entre eux. Pour représenter la classe des clients, dont chaque entité est dénie par un identiant, un prénom et un nom, on utilise la syntaxe suivante : /// définition de la classe des clients class Client{ /// identifiant du client dans la base de données public $id = 0; /// M. ou Mme public $qualite = ""; /// prénom public $prenom = ""; /// nom public $nom = ""; /// constructeur de l'objet public function __construct($id, $qualite, $prenom, $nom){ $this->id = $id; 51 $this->qualite = $qualite; $this->prenom = $prenom; $this->nom = $nom; } /// fonction d'affichage public function affiche(){ return "$this->qualite $this->prenom $this->nom"; } } // fin de la classe Les attributs (ou champs) de la classe sont déclarés avec les mot clés private, protected. public, Il est conseillé d'indiquer la valeur par défaut du champ, ce qui permet d'en identier le type. Le constructeur (la fonction __construct)) crée un object (il instancie une variable du type de cet objet) et aecte la valeur de chaque attribut à l'aide de la variable $this qui représente l'objet courant. La èche permet d'accéder aux champs ou aux méthodes de l'objet. Il sera alors possible de créer un nouvel objet à l'aide de l'opérateur new, puis de l'acher : $client1 = new Client(1, "M.", "Jean", "Dupont"); $client2 = new Client(2, "Mme", "Jeanne", "Duponne"); echo $client1->affiche() . " est marie a " . $client2->affiche() . "\n"; On peut également accéder à tout composant de l'objet, mais c'est sâle. $client1->prenom = "Paule"; // accès en écriture du prénom // accès en lecture echo $client1->qualite . " " . $client1->prenom . " " . $client1->nom . " est marie a " . $client2->affiche() . "\n"; 11.2 Conception orientée objet Les avantages de la conception orientée objet sont multiples, dont le premier est la modularité. Une classe est une entité autonome qui contient à la fois des données et l'interface d'accès à ces données : c'est le concept d'encapsulation. Cette méthode permet de bénécier des avantages liés à l'utilisation des fonctions, mais également de les regrouper dans une structure unique, qui contient toute la sémantique de l'objet à représenter. Plus précisément, chaque élément de classe (attribut, méthode) possède un mode d'accès : public : l'accès à l'élément est libre ; private : l'accès ne peut se faire que par une méthode de la classe. Il faut donc concevoir une interface si l'on veut accéder d'autre part. protected : intermédiaire entre public et privé, accès autorisé aux classes dérivées (cf. cours suivant). 52 On déclarera en général les attributs selon ce mode et on créera une interface pour y accéder, c'est-à-dire des méthodes publiques de lecture et d'écriture. Cela permet de séparer le stockage des données dans l'objet de son mode d'accès pour l'extérieur. Si le mode de stockage est amené à évoluer, seules les méthodes de la classe qui font un accès direct aux attributs auront besoin d'être mises à jour. Les autres morceaux de code utilisant la classe se seront pas modiés. Reprenons l'exemple précédent de notre classe client : Chaque attribut a été doté d'un accesseur en lecture et d'un accesseur en écriture qui constituent l'interface de la classe. Si cela semble fastidieux, on peut utiliser le canevas générique suivant : /// accesseur lecture public function get(){ return $this->; } /// accesseur écriture public function set($){ $this-> = ; } Modions maintenant la structure de la classe, en remplaçant le prénom par un tableau de chaînes de caractères, pour gérer des prénoms multiples : class Client{ /// prénom private $prenom = array(); /// constructeur de l'objet public function __construct($id, $qualite, $prenom, $nom){ $this->prenom = array(0 => $prenom); } /// accesseur lecture public function getPrenom(){ $return = ""; foreach ($this->prenom as $prenom) $return .= "$prenom "; return $return; } /// accesseur écriture public function setPrenom($prenom){ $this->prenom = array($prenom); } /// ajouter un prénom public function pushPrenom($prenom){ 53 } $this->prenom[] = $prenom; /// retirer un prénom public function popPrenom(){ return array_pop($this->prenom); } } // fin de la classe $client1 = new Client(1, $client2 = new Client(2, echo $client1->affiche() /* M. Jean Dupont est marie */ "M.", "Jean", "Dupont"); "Mme", "Jeanne", "Duponne"); . " est marie a " . $client2->affiche() . "\n"; a Mme Jeanne Duponne $client1->setPrenom("Paule"); // accès en écriture du prénom $client1->pushPrenom("Jean"); $client1->pushPrenom("Marie"); // accès en lecture echo $client1->affiche() . " est marie a " . $client2->affiche() . "\n"; /* M. Paule Jean Marie Dupont est marie a Mme Jeanne Duponne */ $client1->popPrenom(); echo $client1->affiche() . " est marie a " . $client2->affiche() . "\n"; /* M. Paule Jean Dupont est marie a Mme Jeanne Duponne */ ?> On constate l'intérêt de l'encapsulation : le code qui utilisait la classe n'a pas à être modié, elle continue de fonctionner de façon autonome, sous la responsabilité de son programmeur. La conception objet permet également de mettre en ÷uvre la généricité des traitements. Pour reprendre l'exemple d'echo ou print_r, il est possible d'uti- liser un même nom de méthode dans diérentes classes pour désigner une opération équivalente, comme l'achage ou l'écriture de la structure dans une base de données. Seule l'implémentation dans la classe sera particulière. Par exemple, outre le constructeur, on trouvera souvent dans les classes des méthodes pour acher l'objet, l'enregistrer dans la base de données, y ajouter ou retirer un élément par push et pop. Enn, le génie logiciel fournit de nombreuses méthodes automatiques de conception et de modélisation objet, comme l'utilisation de diagrammes UML. Pour débuter, nous essayerons simplement de respecter les conseils suivants : chaque concept manipulé par un programme doit être traduit par une classe. Cela ne concerne pas les variables simples qui stockent un résultat 54 temporaire, eectuent un petit calcul, servent d'itération dans les boucles, etc. commencer la rédaction de la classe par la méthode qui permet d'acher l'objet. Cela permet de faire le point sur les attributs ; rédiger ensuite le constructeur, qui permet d'initialiser l'objet. rédiger l'interface : accesseurs en lecture get_ et écriture set_ 11.3 Passage d'objet Il est possible de passer un objet d'un script à l'autre à l'aide de la fonction serialize qui transforme l'objet en une chaîne de caractère qui contient tous ses attributs. unserialize eectue l'opération inverse. Il faut néanmoins traiter la chaîne de caractère pour dé-quoter les guillemets : $chclient = $methode["client"]; $chclient = str_replace('\\"', '"', $chclient); $client = unserialize($chclient); On pourra alors appeler une page PHP avec par exemple header("location:index.php?client=" . serialize($client)) ou mettre cette chaîne dans un champ caché, même si ce n'est pas le procédé le plus discret. 11.4 Documentation Il existe plusieurs solutions de documentation automatique de code. Nous présentons ici Doxygen (http://www.doxygen.org/), qui s'installe avec les paquets doxygen et doxygen-gui. L'utilitaire à lancer est doxywizard. Doxygen est très simple d'emploi : 1. conguration : l'assistant est susant pour indiquer le nom du projet, sa version et le répertoire de destination pour la documentation ; 2. en conguration expert, choisir le Français pour la langue et dans l'onglet input, choisir les chiers à documenter avec le premier bouton de sélection ; 3. choisir le répertoire de travail, c'est-à-dire où devront être stockés les chiers de conguration ; 4. sauvegarder la conguration ; 5. lancer le traitement ; 6. visualiser le HTML ; 7. compiler le latex avec pdflatex refman.tex (plusieurs compilations sont nécessaires pour résoudre les problèmes de lien). 55 Chapitre 12 TD 5 12.1 Exercice Munissez la classe des clients de deux méthodes : 12.1.1 enregistre Cette méthode enregistre le contenu de l'objet dans un chier ''client-$this->id.txt" sous la forme : id=1 qualite=M. prenom=Jean nom=Dupont 12.1.2 lit Cette méthode lit les champs de l'objet dans le chier 56 ''client-$this->id.txt". Chapitre 13 Héritage En programmation objet, l'héritage répond au besoin de gérer des objets ayant des propriétés similaires. Comparons les solutions suivantes pour diérencier les hommes des femmes : class People{ private $id; private $nom; private $prenom; private $type; public __construct($id, $nom, $prenom, $type){ $this->id = id$; $this->nom = $nom; $this->prenom = $prenom; $this->type = $type; } public getType(){ return $this->type; } } public viewHTML(){ switch($this->type){ case PEOPLE_WOMAN : return "Mme $this->prenom $this->nom"; case PEOPLE_MAN : return "M. $this->prenom $this->nom"; } } La solution suivante utilise l'héritage, en dénissant une classe ne peut être instanciée) People et deux classes dérivées abstraite (qui PeopleWoman et Peo- pleMan. Dans la pratique, c'est PHP qui connaît le type des objets. Lorsqu'une méthode est appelée, il exécute en priorité la méthode de la classe de l'objet, sinon 57 il remonte dans la hiérarchie à la recherche d'une méthode implémentée dans une classe parente. 1 5 <?php define('PEOPLE_WOMAN', 1); define('PEOPLE_MAN', 2); define('PEOPLE_DAUGHTER', 3); define('PEOPLE_SON', 4); abstract class People { private $id; private $prenom; private $nom; 10 public function __construct($id, $prenom, $nom) { $this->id = $id; $this->prenom = $prenom; $this->nom = $nom; } 15 public function getId() { return $this->id; } 20 public function getType() { 25 } public function getTitle() { } 30 public function edit() { return "<td><a href=\"?action=edit&id=$this->id\">edit</a></td>"; } 35 public function viewHTML() { return "<tr><td>$this->id</td><td>" . $this->getTitle() . "</td><td>$this->prenom</td><td>$this->nom</td>" . $this->edit() . "</tr>"; } 40 } 45 //-----------------------------------------------------------------------------class PeopleWoman extends People { public function __construct($id, $prenom, $nom) { 58 } 50 public function getType() { return PEOPLE_WOMAN; } public function getTitle() { return "Mme"; } 55 60 } //-----------------------------------------------------------------------------class PeopleMan extends People { public function __construct($id, $prenom, $nom) { parent::__construct($id, $prenom, $nom); } 65 public function getType() { return PEOPLE_MAN; } 70 75 80 parent::__construct($id, $prenom, $nom); public function getTitle() { return "M."; } } //-----------------------------------------------------------------------------class PeopleDaughter extends People { public function __construct($id, $prenom, $nom) { parent::__construct($id, $prenom, $nom); } public function getType() { return PEOPLE_DAUGHTER; } 85 public function getTitle() { return "Mlle"; } 90 } 95 //-----------------------------------------------------------------------------class PeopleSon extends People { 59 public function __construct($id, $prenom, $nom) { parent::__construct($id, $prenom, $nom); } 100 public function getType() { return PEOPLE_SON; } 105 110 public function getTitle() { return "Jr."; } } ?> Nous gérons maintenant une liste de personnes, stockée dans une base de données : 1 5 <?php require_once 'people.factory.php'; require_once 'outils_bd.class.php'; class PeopleList { private $peoples = array(); 10 15 20 25 30 public function __construct() { $this->peoples = array(); foreach (Outils_Bd::getInstance()->getConnexion()->query( "SELECT * FROM peoples") as $row) switch ($row['type']) { case PEOPLE_WOMAN: $this->peoples[] = new PeopleWoman($row['id'], $row['prenom'], $row['nom']); case PEOPLE_MAN: $this->peoples[] = new PeopleMan($row['id'], $row['prenom'], $row['nom']); case PEOPLE_DAUGHTER: $this->peoples[] = new PeopleDaughter($row['id'], $row['prenom'], $row['nom']); case PEOPLE_SON: $this->peoples[] = new PeopleSon($row['id'], $row['prenom'], $row['nom']); default: break; } } 60 public function viewHTML() { $return = '<table>'; foreach ($this->peoples as $people) $return .= $people->viewHTML(); return $return . '</table>'; } 35 40 } ?> Le contrôleur est alors simplement : 1 <?php 5 /// merci de reporter toutes les erreurs ini_set('display_errors', 1); error_reporting(E_ALL | E_STRICT); require_once("people.list.php"); 10 15 20 25 30 print_r($_GET); $list = new PeopleList(); $view = ''; if (isset($_GET['action'])) { switch ($_GET['action']) { case 'edit': $view = $list->edit($_GET); break; case 'save': $list->save($_GET); $view = $list->viewHTML(); break; default: $view = $list->viewHTML(); break; } }else $view = $list->viewHTML(); echo $view; ?> La base de données : 61 Chapitre 14 TD - Héritage 14.1 Mise en place des formulaires 1. comme indiqué par le contrôleur, mettre en place les méthodes permettant d'éditer une personne. Le code généré peut par exemple être <table><tr><td>1</td> <td>M.</td> <td>Jean</td> <td>Dupont</td> <td><a href="?action=edit&id=1">edit</a></td></tr> <form method="GET" action="index.php"> <input type="hidden" name="action" value="save"/> <input type="hidden" name="id" value="2"/> <tr><td>2</td> <td>Mme</td> <td><input type="text" name="prenom" value="Jeanne"/></td> <td><input type="text" name="nom" value="Dupont"/></td> <td><input type="submit"/></td> </form></tr> <tr><td>3</td> <td>Jr.</td> <td>Joe</td> <td>Dupont</td> <td><a href="?action=edit&id=3">edit</a></td></tr> <tr><td>4</td> <td>Mlle</td> <td>Averelle</td> <td>Dupon</td> <td><a href="?action=edit&id=4">edit</a></td></tr> </table> 2. ajouter les méthodes permettant de sauvegarder le résultat de l'édition. 62 14.2 Ajout de classes pour gérer les enfants On ajoute maintenant une classe abstraite des enfants, qui possède une information supplémentaire : un adulte responsable. //-----------------------------------------------------------------------------abstract class PeopleChildren extends People { private $responsable = -1; public function __construct($id, $prenom, $nom) { parent::__construct($id, $prenom, $nom); foreach (Outils_Bd::getInstance()->getConnexion()->query( "SELECT * FROM children WHERE id = $this->id") as $row) $this->responsable = $row['responsable']; } public function viewHTML() { return "<tr><td>$this->id</td><td>" . $this->getTitle() . "</td><td>$this->prenom</td><td>$this->nom</td>" . "<td>$this->responsable</td>" . $this->edit() . "</tr>"; } public function save($get) { parent::save($get); $this->responsable = $get['responsable']; Outils_Bd::getInstance()->getConnexion()->query("UPDATE children " . "SET responsable = $this->responsable WHERE id = $this->id"); } public function formulaire() { ... } } Pour cela, on ajoute une table children au système d'information : --- Structure de la table `children` -CREATE TABLE IF NOT EXISTS `children` ( `id` int(11) NOT NULL, `responsable` int(11) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin; -63 -- Contenu de la table `children` -INSERT INTO `children` (`id`, `responsable`) VALUES (4, 2), (3, 1); Implémenter le code HTML nécessaire pour que l'édition d'un enfant permette de préciser son responsable. Le code généré pourra être du type : <table border="1"> <tr><th width="50">Id</th> <th width="50">Title</th> <th width="180">Prénom</th> <th width="180">Nom</th> <th width="150">Responsable</th> <th width="100">Action</th> </tr> <tr><td>1</td><td>M.</td><td>Jean</td><td>Dupont</td><td> </td> <td><a href="?action=edit&id=1">edit</a></td></tr> <tr><td>2</td><td>Mme</td><td>Jeanne</td><td>Dupont</td><td> </td> <td><a href="?action=edit&id=2">edit</a></td></tr> <form method="GET" action="/~frioult/svn/m1dnr-dev1/sequence7/td/index.php"> <input type="hidden" name="action" value="save"/> <input type="hidden" name="id" value="3"/> <td>3</td> <td>Jr.</td> <td><input type="text" name="prenom" value="Joe"/></td> <td><input type="text" name="nom" value="Dupont"/></td> <td> <select name="responsable"> <option value="1"selected="selected">Jean Dupont</option> <option value="2">Jeanne Dupont</option></select> </td> <td><input type="submit"/></td> </form></tr> <tr><td>4</td><td>Mlle</td><td>Averelle</td><td>Dupont</td><td>2</td> <td><a href="?action=edit&id=4">edit</a></td> </tr> </table> 64 Chapitre 15 Exceptions 15.1 Principe des exceptions Dans le cadre de la programmation objet, les exceptions sont utilisées pour gérer les erreurs. En eet, la construction des objets n'est pas une fonction et ne peut donc pas renvoyer de code d'erreur. On pourrait éventuellement tester la qualité de l'objet pour détecter une erreur, mais cela surchargerait la conception. On utilise alors la structure suivante : try{ ...... le code pouvant générer une erreur } catch (Exception $e){ die('exception reçue ' . $e->getMessage()); } Pour lever une exception, il sut d'insérer dans le code l'instruction throw new Exception('<message>', $code, $previous); Le code est un entier, previous est l'exception précédente. 15.2 La classe PHP Exception Exception { /* Properties */ protected string $Exception->message ; protected int $code ; protected string $file ; protected int $line ; 65 } /* Methods */ public Exception::__construct ([ string $message = "" [, int $code = 0 [, Exception $previous = NULL ]]] ) final public string Exception::getMessage ( void ) final public Exception Exception::getPrevious ( void ) final public mixed Exception::getCode ( void ) final public string Exception::getFile ( void ) final public int Exception::getLine ( void ) final public array Exception::getTrace ( void ) final public string Exception::getTraceAsString ( void ) public string Exception::__toString ( void ) final private void Exception::__clone ( void ) Lorsqu'une exception est levée, l'objet correspondant est crée avec les numéro de ligne et le nom du chier où l'exception est envoyée. De plus, une exception conserve la trace des exceptions précédentes, ce qui permet d'acher une pile des envois. 15.3 Héritage d'exception La classe Exception peut être dérivée, pour particulariser l'exception levée. __toString() peut être surchargée. On constate que seule la méthode define('EXCEPTION_PEOPLE_LIST_CONSTRUCT', 1); define('EXCEPTION_PEOPLE_CONSTRUCT', 2); class ExceptionPeopleList extends Exception{ public function __construct($message, $previous) { parent::__construct($message, EXCEPTION_PEOPLE_LIST_CONSTRUCT, $previous); } } public function __toString() { return 'exception while creation some PeopleList ' . parent::getMessage(); } class ExceptionPeople extends Exception{ public function __construct($message, $previous) { parent::__construct($message, EXCEPTION_PEOPLE_LIST_CONSTRUCT, $previous); } } public function __toString() { return 'exception while saving some People ' . parent::getMessage(); } 66 Ce qui permet, par exemple pendant la création d'une liste de clients, de gérer les problèmes de connexion à la base de données. try { foreach (Outils_Bd::getInstance()->getConnexion()->query("SELECT * FROM peoples") as $row) $this->peoples[] = PeopleFactory::createPeople($row); } catch (Exception $e){ throw new ExceptionPeopleList( "probleme de connection a la base de donnees..." . $e->getMessage(), $e); } 15.4 Utilisation des exceptions Il faut toujours respecter les règles suivantes : 1. les exceptions doivent être exceptionnnelles, ce n'est pas le mode de fonctionnement normal du programme ; 2. ne jamais utiliser les exceptions pour contrôler le déroulement du programme ; 3. ne jamais utiliser les exceptions pour passer des paramètres. 67