Programmation objet de base en PHP

Le but de ce tp est d'illustrer en PHP l'héritage sur un exemple classique de géometrie; les polygones et leurs spécialisation en rectangles, en triangles etc....

Un polygones sera caractériser par ses sommets qui sont des points.

Exercice 1

Ecrire, dans un fichier Point.php, une classe pour representer des points en 2 dimensions. Elle comprendra au moins 1 constructeur ainsi qu'une méthode pour calculer la distance d'un point à un autre et une méthode __toString pour pouvoir afficher les coordonnées d'un point sous la forme (x,y)

Corrigé

Dans le fichier Point.php:
<?php

class Point{
  public $x;
  public $y;

  //un constructeur de point
  function __construct($x,$y){
    $this->x = $x;
    $this->y = $y;
  }
  
  //distance du point courant au point p
  public function distance($p){
   //sqrt donne la racine carré
   return sqrt(
      ($this->x-$p->x)*($this->x-$p->x) +
      ($this->y-$p->y)*($this->y-$p->y));
  }
  
  /* pour l'affichage:
  dans tout les contextes où une chaine est attendue et où on  fourni
   un point, il sera automatiquement converti en chaine par la fonction
   __toString
  */
  public function __toString(){
      return "(".$this->x.",".$this->y.")";
  }
}

?>
Pour le tester, on peut écrire un fichier de test testPoint.php
<?php
require "Point.php";
$p1=new Point(3,4);
$p2=new Point(1,3);
echo "la distance entre $p1 et $p2 est :".$p1->distance($p2)."\n";
//comme $x et $y sont public vous pouvez ecrire
$p2->x=42;
echo '$p2->x vaut '.$p2->x ."\n";
?>


qui devrait afficher
la distance entre (3,4) et (1,3) est :2.2360679774998
$p2->x vaut 42

Si vous mettez les coordonnées x et y en private

         ...
         private $x;
         private $y;
	
vous aurez alors le message d'erreur
Fatal error: Cannot access private property Point::$x
	
Car x et y ne sont plus visible à l'exterieur de la classe Point. On peut definir des fonction pour y acceder en lecture et en ecriture. Cela permet de d'affranchir l'utilisateur de la classe de la fonçon dont les donnés sont implementées.


N'hesitez pas à introduire des erreurs pour voir ce que cela donne. Par exemple si vous creer un point sans donner les coordonnées:

$p3=new Point();
vous devriez avoir un message du genre
HP Warning:  Missing argument 1 for Point::__construct(),
Il est toujours important de s'entrainer a comprendre les messages d'erreurs et les avertissements

Ceux qui auraient des difficultés avec le calcul de la distance peuvent consulter wikipedia


Exercice 2

Ecrire, dans un fichier Polygone.php, une classe polygone où un polygome est un ensemble (un tableau) de points. Elle aura au moins un constructeur prenant en arguments les points qui composent le polygone.

Corrigé

<?php
// Un Polygone étant composé de Point, on charge la classe qui les represente. 
// on poura le supprimer si on opte pour l'autoload (http://php.net/manual/fr/function.autoload.php)
require_once("Point.php"); 

class Polygone {
  // declaration d'un tableau de points
  //Protected signifit que cette donnée n'est visible  que  dans la classe polygone et celle qui en héritent
  protected  $sommets; 

 //Constructeur prenant un tableau de point en argument
  public function __construct($tableauDePoint){
    $this->sommets=array();  //initialisation a vide du tableau 
    $nb=count($tableauDePoint);
       for($i= 0;$i < $nb;$i++){
	 $p=$this->sommets[$i]=$tableauDePoint[$i];
        
    }
}
}
?>

On ne sait pas à l'avance de combien de point est composé le polygone, c'est au momment de la creation qu'on va lui passer les points qui le compose. Pour gerer ce nombre d'argument variable on a opté pour les passer dans un tableau. On peut le tester par exemple avec un fichier PolygoneTabTest.php

<?php
     require_once("Polygone.php");
     $p=new Polygone(array(new Point(2,3),new Point(4,5)));
     var_dump($p)
?>
	

qui affichera:
object(Polygone)#1 (1) {
  ["sommets":protected]=>
  array(2) {
    [0]=>
    object(Point)#2 (2) {
      ["x"]=>
      int(2)
      ["y"]=>
      int(3)
    }
    [1]=>
    object(Point)#3 (2) {
      ["x"]=>
      int(4)
      ["y"]=>
      int(5)
    }
  }
}
	

Une autre solution dans l'ecriture du constructeur est d'utiliser, les fonctions func_num_args et func_get_arg permettant de gerer les arguments passés au construteur.

<?php
  ....

  //Constructeur vide ou prenrant des point en aguments
  public function __construct(){
    $this->sommets=array();
    $nb=func_num_args();  //nombre d'arg passes au constructeur
       for($i= 0;$i < $nb;$i++){
	 $p=func_get_arg($i); //argument d'indice i

      $this->sommets[$i]=new Point($p->x,$p->y);
        
    }
  }
?>

Exercice 3

La munir d'une méthode pour calculer le nombre de sommets ainsi que d'une méthode pour calculer le périmètre.

Corrigé

....
  //Pour retrouver le nombre de sommet
  public function nbrSommet(){
    return count($sommets);
  }
  
  //Pour calculer le perimetre
  //on calcule la distance de chaque sommet au suivant
  //puis du dernier au premier (indice 0)
  //et on fait la somme de ces distances
 
  public function perimetre(){
    $result=0;
    $nbs;
      for ($i=0;$i < count($this->sommets)-1;$i++){
      $result = $result + $this->sommets[$i]->distance($this->sommets[$i+1]);
    }
    
    if(count($this->sommets)>0)
      return $result +($this->sommets[0]->distance($this->sommets[count($this->sommets) -1])) ;
    else return 0; 
  }
 ...

Exercice 4

Ajouter une méthode __toString pour l'affichage d'un polygone par l'affichage de la liste des sommets (les points).

Corrigé

 //Pour l'affichage
    public function __toString(){
      $result ="";
      
      for ($i=0;$i < count($this->sommets);$i++){
	$result = $result ." , ".$this->sommets[$i];
     }
      return $result."
"; } }

Exercice 5

Ecrire un fichier testPolynome.php pour tester vos polygones et leur methodes.

Par exemple

$P= new Polygone(new Point(1,1), new Point(2,2),new Point(1,2));
print("je suis un polygone de sommet(s)".$P);
print("et de perimetre ".$P->perimetre());
echo "
";
affichera:
je suis un polygone de sommet(s) , (1,1) , (2,2) , (1,2)
et de perimetre 3.41421356237

Corrigé

<?php
//pour l'autoload
function __autoload($class)
{
    require_once($class . '.php');
}

//creation d'un polygone (seconde version du constructeur)
$P= new Polygone(new Point(1,1), new Point(2,2),new Point(1,2));
print("je suis un polygone de sommet(s)".$P);
print("et de perimetre ".$P->perimetre());
echo "
"; ?>

Exercice 6

Ecrire dans un fichier Rectangle.php une classe rectangle qui spécialise les polygones. Le constructeurs prend deux points en argument correspondants aux coins supérieur gauche et inférieur droit)

Corrigé

<?php

//La classe rectangle qui herite des polynomes

 class Rectangle extends Polygone {

   //Constructeur prenant les coins superieur gauche et inferieur droit
   public function __construct($x,$y){
     $this->sommets=array($x,new Point($y->x,$x->y),$y,new Point($x->x,$y->y));
        }
?>

Exercice 7

Ajouter des méthodes pour calculer la longueur et la largeur.

Corrigé

...
/////////  Ajout de fonctions specifiques aux rectangles
   
   //Calcul de la longueur
   public function longueur(){
    return abs($this->sommets[0]->x - $this->sommets[2]->x);
  }

   //calcul de la largeur
   public function  largeur(){
    return abs($this->sommets[0]->y - $this->sommets[2]->y);
  }
...   


Exercice 8

Testez les ! puis remarquez que pour un rectangle, il existe une façon plus rapide de calculer le périmètre donc redéfinissez la méthode périmètre.

Corrigé

...
//Redefinition de la fonction de calcul du perimetre
   
   public function perimetre(){
       return 2*($this->longueur()+$this->largeur());
   } 
   
}
...

Exercice 9

Ecrire une classe triangle qui hérite de polygone.

Corrigé

<?php

//La classe triangle qui herite des polynomes
 class Triangle extends Polygone {

   //Constructeur prenant les coins superieur gauche et inferieur droit
   public function __construct($x,$y,$z){
     parent::__construct($x,$y,$z);
   }

 }


Exercice 10

Trouvez des opérations spécifiques aux triangles et implémentez-les. testez le tout !

Corrigé

public function equilateral(){    
     $s=$this->sommets;
     // attention s'agissant de float, les erreurs de precision
     // dans le calcul de la distance empeche de comparer les distance
     // avec une egalite. Nous  utilisons donc des differences et 
     // on fixe ici la precision à 1E-15
     return (
	     abs($s[0]->distance($s[1])- $s[1]->distance($s[2]))<1E-15
	     && abs($s[1]->distance($s[2]) -$s[2]->distance($s[0]))<1E-15);
   }
 }

N'oublier pas d'utiliser l'autoloading dans le fichier de test.


Last modified: Mon May 22 15:49:47 CEST 2017