<?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 tracetrytry doit être suivi d'un bloc catchtry
try {
// code à exécuter
}
catch (Exception $e) {
echo "Erreur, exception renvoyée : " . $e->getMessage();
}
Exceptioncatchcatch 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
interfaceclass maClasse implements monInterfacearray 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)