<?php
class Rectangle {
private $longueur;
private $largeur;
public function __construct($L, $l) {
$this->longueur = $L;
$this->largeur = $l;
}
public function getLongueur() {
return $this->longueur;
}
public function getLargeur() {
return $this->largeur;
}
}
$c = new Rectangle(5, 2);
echo "Dimensions : {$c->getLongueur()}x{$c->getLargeur()}\n";
echo "Erreur fatale :\n";
echo "Dimensions : {$c->longueur}x{$c->largeur}";
$this
dans une classe pour
faire référence aux propriétés et méthodes
<?php
class Toto {
public $x = 3;
function afficherX() {
echo $x; // (au lieu de $this->x)
}
}
$toto = new Toto();
$toto->afficherX(); // lève une Notice: undefined variable $x
$
,
mais pas ailleurs. Cependant, mettre un $
n'est pas une erreur de syntaxe, ça fait juste
autre chose que ce qu'on voudrait…
<?php
class Toto {
public $x = 3;
}
$toto = new Toto();
echo $toto->x; // affiche 3
echo $toto->$x; // lève une Notice: undefined variable $x
$variable = "x";
echo $toto->$variable; // affiche 3 : $toto->$variable est interprété comme $toto->x !
<?php
class Toto {
public $x = 3;
public $y = 'coucou';
}
$toto = new Toto();
foreach ($toto as $k => $v) {
echo "clef $k, valeur $v\n";
}
// résultat :
// clef x, valeur 3
// clef y, valeur coucou
__construct()
__destruct()
: appelée lorsque l'instance est détruite
(utilisation de unset
, ou fin de l'exécution du script)__toString()
: appelée lorsque l'objet doit être converti
en chaîne de caractères
<?php
class Point {
public $x;
public $y;
public function ___construct($x, $y) {
$this->x = $x;
$this->y = $y;
}
public function __toString() {
return "({$this->x}, {$this->y})";
}
}
$p = new Point(3, 2);
echo $p;
(, )
Que s'est-il passé ?
static
$this
, mais avec le mot-clef self
,
qui représente « la classe de l'instance courante »
<?php
// Exemple de classe avec des membres statiques
class Toto {
private static $compteur = 0;
private $dummy;
public function __construct($contenu) {
$this->dummy = $contenu;
self::$compteur++;
}
public function printInstanceStats() {
// $this->compteur n'est pas défini => donne une chaine vide
return $this->dummy . " et " . $this->compteur . "\n";
}
public static function printClassStats() {
//$this->dummy = "toto"; // Fatal error
return "Il y a actuellement " . self::$compteur . " instances de la classe Toto\n";
}
public function __destruct() {
echo "Destruction d'une instance\n";
self::$compteur--;
}
}
echo Toto::printClassStats();
$A = new Toto("instance A");
echo "\$this->compteur n'est pas défini => donne une chaine vide et une Notice\n";
echo "A : " . $A->printInstanceStats();
echo Toto::printClassStats();
$B = new Toto("instance B");
echo "\$this->compteur n'est pas défini => donne une chaine vide et une Notice\n";
echo "B : " . $B->printInstanceStats();
echo Toto::printClassStats();
echo "unset(\$A)\n";
unset($A);
echo Toto::printClassStats();
unset($B);
Il y a actuellement 0 instances de la classe Toto $this->compteur n'est pas défini => donne une chaine vide et une Notice A : instance A et Il y a actuellement 1 instances de la classe Toto $this->compteur n'est pas défini => donne une chaine vide et une Notice B : instance B et Il y a actuellement 2 instances de la classe Toto unset($A) Destruction d'une instance Il y a actuellement 1 instances de la classe Toto Destruction d'une instance
const
::
<?php
class MyClass {
const ma_constante = 'toto';
public function showConstant() {
echo "Voici ma constante: " .self::ma_constante . "\n";
}
public function chgeConstant() {
// Fatal error
// self::ma_constante = "test d'erreur";
}
}
$obj = new MyClass();
$obj->showConstant();
echo "MyClass::ma_constante : " . MyClass::ma_constante . "\n"; // Fonctionne
echo "\$obj->ma_constante : " . $obj->ma_constante . "\n"; // Ne fonctionne pas
echo "\$obj::ma_constante : " . $obj::ma_constante . "\n"; // Marche avec PHP > 5.3.0
extends
<?php
class Rectangle {
protected $longueur;
protected $largeur;
public function __construct($longueur, $largeur) {
$this->longueur = $longueur;
$this->largeur = $largeur;
}
}
class Carre extends Rectangle {
public function __construct($largeur) {
parent::__construct($largeur, $largeur);
}
parent::__construct()
self
: static
static::toto
n'est pas forcément le toto
de la classe courante,
mais celui de la classe qui a effectivement été instanciée à l'exécution
<?php
class MyClass
{
public $public = 'Public';
protected $protected = 'Protected';
private $private = 'Private';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj = new MyClass();
echo $obj->public; // Works
//echo $obj->protected; // Fatal Error
//echo $obj->private; // Fatal Error
$obj->printHello(); // Shows Public, Protected and Private
// classe dérivée
class MyClass2 extends MyClass
{
// We can redeclare the public and protected method, but not private
protected $protected = 'Protected2';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj2 = new MyClass2();
echo $obj2->public; // Works
echo $obj2->private; // Undefined
//echo $obj2->protected; // Fatal Error
$obj2->printHello(); // Shows Public, Protected2, not Private
PublicPublicProtectedPrivatePublicPublicProtected2
<?php
class Rectangle {
protected $longueur;
protected $largeur;
public function __construct($longueur, $largeur) {
$this->longueur = $longueur;
$this->largeur = $largeur;
}
// Accesseurs
// Getters
public function getLongueur() { return $this->longueur; }
public function getLargeur() { return $this->largeur; }
// Setters
public function setLongueur($longueur) { $this->longueur = $longueur; }
public function setLargeur($largeur) { $this->largeur = $largeur; }
}
class Carre extends Rectangle {
public function __construct($largeur) {
parent::__construct($largeur, $largeur);
}
public function setLargeur($largeur) {
parent::setLongueur($largeur);
parent::setLargeur($largeur);
}
}
echo "Définition d'un rectangle 5x2 :\n";
$R = new Rectangle(5, 2);
// echo $R->longueur; produit Fatal error
// Utiliser les accesseurs :
echo "Longueur : " . $R->getLongueur() . "\n";
echo "Largeur : " . $R->getLargeur() . "\n";
echo "Définition d'un carré de largeur 4\n";
$C = new Carre(4);
echo "Largeur : " . $C->getLargeur() . "\n";
echo "Changement de la largeur : \n";
$C->setLargeur(8);
echo "Largeur : " . $C->getLargeur() . "\n";
echo "Longueur : " . $C->getLongueur() . "\n";
Définition d'un rectangle 5x2 : Longueur : 5 Largeur : 2 Définition d'un carré de largeur 4 Largeur : 4 Changement de la largeur : Largeur : 8 Longueur : 8
Au passage, noter le mot-clef parent
pour appeler une méthode (ou le constructeur) de la superclasse
throw new Exception("Houston, we have a problem");
getMessage()
: message d'erreur de l'exceptiongetCode()
: code d'erreur de l'exceptiongetFile()
: fichier dans lequel l'exception a été renvoyéegetLine()
: n° de ligne où l'exception a été renvoyéegetTrace()
: tableau contenant la tracegetTraceAsString()
: chaîne de caractères avec la tracetry
try
doit être suivi d'un bloc catch
try
try { // code à exécuter } catch (Exception $e) { echo "Erreur, exception renvoyée : " . $e->getMessage(); }
Exception
catch
catch
est passée au bloc suivant
<?php
class Titi {
private $x = false;
protected $y = "";
public $z = null;
function methode() { }
}
echo "---- var_export ----\n";
var_export(new Titi());
echo "\n";
echo "---- print_r ----\n";
print_r(new Titi());
echo "\n";
echo "---- var_dump ----\n";
var_dump(new Titi());
echo "\n";
---- var_export ---- \Titi::__set_state(array( 'x' => false, 'y' => '', 'z' => NULL, )) ---- print_r ---- Titi Object ( [x:Titi:private] => [y:protected] => [z] => ) ---- var_dump ---- /users/ensweb/www-prod/TW4b/pres/oophp/demo/exPrintDebug.php:19: class Titi#4 (3) { private $x => bool(false) protected $y => string(0) "" public $z => NULL }
final
pour une méthode : la méthode ne peut pas être redéfinie dans une classe dérivéefinal
pour une classe : la classe ne peut pas être dérivée
<?php
class finalEx {
private $donnee = "exemple";
final function maj() {
return strtoupper($this->donnee);
}
}
final class finalExtend extends finalEx {
/* Fatal error : impossible de redéfinir maj()
public function maj() {
return $this->donnee;
} */
}
/* Fatal error : impossible de dériver finalExtend
class erreur extends finalExtend {
// impossible
}
*/
$class = new finalEx();
echo $class->maj();
echo "\n";
$classExtend = new finalExtend();
echo $classExtend->maj();
?>
EXEMPLE EXEMPLE
abstract
<?php
abstract class AbstractClass
{
// Force la classe étendue à définir cette méthode
abstract protected function getValue();
// méthode commune
public function printOut() {
print $this->getValue();
}
}
class ConcreteClass1 extends AbstractClass
{
protected function getValue() {
return "ConcreteClass1";
}
}
class ConcreteClass2 extends AbstractClass
{
protected function getValue() {
return "ConcreteClass2";
}
}
$class1 = new ConcreteClass1;
$class1->printOut();
$class2 = new ConcreteClass2;
$class2->printOut();
?>
ConcreteClass1ConcreteClass2
interface
class maClasse implements monInterface
array
et callable
)
function MaFonction(MaClasse $param)
: $param doit être une instance de la classe MaClasseint
ou string
:
voir la liste
TypeError
est envoyée si le type n'est pas respecté, sauf
si la conversion est possible !
declare(strict_types=1);
pour bénéficier du « typage strict »,
qui renverra bien une TypeError
dans tous les cas où le paramètre donné ne respecte pas
la déclaration. Sauf pour les int
, qui pourront toujours être convertis en float
.
function toto(): float { return 3.14; }
void
en plus
null
pour certains cas particuliers (par exemple des erreurs)
function titi(?int $x)
Un principe important en POO est de favoriser la composition plutôt que l'héritage pour mutualiser du code
En effet l'héritage a des conséquences pénibles, notamment le fait qu'à partir du moment où on a sous-classé une classe, il devient difficile de la faire évoluer (tout changement a des conséquences sur les classes filles)
cela contrevient à la modularisation du code — les classes sont trop dépendantes les unes des autres, alors que le but de la POO est plutôt d'essayer de créer des « boîtes noires » dont le comportement interne n'a pas d'influence à l'extérieur
En utilisant la composition, on garde l'indépendance entre les classes
le prix à payer est une syntaxe un peu plus lourde dans la classe, et l'obligation parfois d'écrire des méthodes de « délégation »
ex autom cell: comportement en fonction de la règle, en fonction de l'affichage voulu
Les traits de PHP sont (principalement) un moyen de faire de la composition, sans les inconvénients
trait MonTrait { function uneMethodeUtile() { } class Toto { use MonTrait; }
Toto
contient la méthode uneMethodeUtile
, comme si elle avait hérité de MonTrait
, sauf qu'il n'y a pas eu d'héritage :
Toto
__sleep
et __wakeup
: code à exécuter lors de la sérialisation et désérialisation d'un objet__get __set __call
pour gérer la surcharge objet (voir le manuel)