PHP 5 - Zend Engine v 2.0

Nouveau modèle objet

La gestion des objets a été complètement réécrite permettant d'obtenir des meilleures performances et d'utiliser plus de fonctionnalités objet.

Dans les versions précédentes de PHP, les objets étaient gérés comme des primitives (integers, strings). Avec cette méthode, l'objet était copié dans son intégralité. Dans la nouvelle approche, les objets sont gérés par référence et non copiés par valeur lors de l'assignation à une variable.

Variables privées et protégées

PHP 5 introduit les variables privées et protégées, ce qui permet de définir la visibilité des propriétés d'une classe.

Les variables protégées peuvent être accédées uniquement par les classes descendantes de la classe dans laquelle ces dernières sont déclarées alors que les variables privées ne sont accessibles que par la classe même dans laquelle elles sont déclarées.

<?php

  class MyClass {
    
    private $Hello = "Hello, World!\n";
    protected $Bar = "Hello, Foo!\n";
    protected $Foo = "Hello, Bar!\n";
    
    function printHello() {
      print "MyClass::printHello() " . $this->Hello;
      print "MyClass::printHello() " . $this->Bar;
      print "MyClass::printHello() " . $this->Foo;
    }
    
  }

  class MyClass2 extends MyClass {
    protected $Foo;
           
    function printHello() {
      MyClass::printHello();                        /* Affichage OK */
      print "MyClass2::printHello() ".$this->Hello; /* Aucun affichage */
      print "MyClass2::printHello() ".$this->Bar;   /* Aucun affichage */
      print "MyClass2::printHello() ".$this->Foo;   /* Aucun affichage */
    }
  }

  $obj = new MyClass();
  print $obj->Hello;  /* Aucun affichage */
  print $obj->Bar;    /* Aucun affichage */
  print $obj->Foo;    /* Aucun affichage */
  $obj->printHello(); /* Affichage OK */

  $obj = new MyClass2();
  print $obj->Hello;  /* Aucun affichage */
  print $obj->Bar;    /* Aucun affichage */
  print $obj->Foo;    /* Aucun affichage */
  $obj->printHello();

?>

Méthodes privées et protégées

Avec PHP 5, les méthodes privées et protégées sont également introduites :

<?php

  class Foo {
  
    private function aPrivateMethod() {
        echo "Foo::aPrivateMethod() called.\n";
    }
    
    protected function aProtectedMethod() {
        echo "Foo::aProtectedMethod() called.\n";
        $this->aPrivateMethod();
    }
    
  }

  class Bar extends Foo {
  
    public function aPublicMethod() {
        echo "Bar::aPublicMethod() called.\n";
        $this->aProtectedMethod();
    }
    
  }

  $o = new Bar;
  $o->aPublicMethod();

?>

L'ancien code qui ne possède pas de classes ou fonctions nomenclaturées public, protected ou private ne doit poser aucun problème de migration.

Classes abstraites et méthodes

PHP 5 introduit également les méthodes et classes abstraites. Une méthode abstraite déclare uniquement la signature de la méthode et ne fournit pas d'implémentation. Une classe qui contient des méthodes abstraites doit être déclarée avec le mot clé abstract.

<?php

  abstract class AbstractClass {
    abstract public function test();
  }

  class ImplementedClass extends AbstractClass {
    public function test() {
        echo "ImplementedClass::test() called.\n";
    }
  }

  $o = new ImplementedClass;
  $o->test();

?>

Les classes abstraites ne peuvent être instanciées. L'ancien code ne possédant pas le mot clé abstract ne doit poser aucun problème au cours de la migration.

Interfaces

Zend Engine 2.0 introduit les interfaces. Une classe peut implémenter une liste arbitraire d'interfaces.

<?php

  interface Throwable {
    public function getMessage();
  }

  class Exception implements Throwable {
    public function getMessage() {
      // ...
    }
  }

?>

L'ancien code ne possédant pas le mot clé interface ou implements ne doit poser aucun problème au cours de la migration.

Final

PHP 5 introduit le mot clé final pour déclarer des méthodes et variables finales. Les méthodes et variables déclarées final ne peuvent pas être overridés par des classes descendantes.

<?php

class Foo {
  final function bar() {
    // ...
  }
}

?>

L'ancien code ne possédant pas le mot clé final ne doit poser aucun problème au cours de la migration.

Clônage d'objets

PHP 4 (Zend Engine 1.0) n'offrait aucun contrôle sur la copie d'un objet. Durant la duplication, PHP4 effectuait une copie bit par bit pour construire un réplicat identique (propriétés, méthodes etc...).

La création d'une copie d'un objet avec toutes les propriétés répliquées n'est pas toujours le comportement souhaité, c'est pourquoi la méthode __clone() est offerte avec PHP 5.

Une copie d'objet est réalisée avec la méthode __clone() :

<?php
  
  $copy_of_object = $object->__clone();

?>

Lors de l'appel de cette méthode, Zend Engine v 2.0 vérifie si la méthode __clone() a été définie ou pas. Dans le cas contraire, la méthode __clone() par défaut est appelée, laquelle effectue une copie de toutes les propriétés de l'objet. Lorsque la méthode __clone() est définie, cette dernière permet de définir explicitement la copie des propriétés dans l'objet créé : ceci permet d'overrider uniquement les propriétés qui nécessitent d'être changées.

<?php
  
  class MyCloneable {
    static $id = 0;
    
    function MyCloneable() {
      $this->id = self::$id++;
    }
           
    function __clone() {
      $this->name = $that->name;
      $this->address = "New York";
      $this->id = self::$id++;
    }
  }
        
  $obj = new MyCloneable();
  $obj->name = "Hello";
  $obj->address = "Tel-Aviv";
  print $obj->id . "\n";
  
  $obj = $obj->__clone();
  print $obj->id . "\n";
  print $obj->name . "\n";
  print $obj->address . "\n";

?>

Constructeurs unifiés

Avec PHP 4, les méthodes constructor étaient des méthodes qui avaient le même nom que la class, ceci posait des problèmes pour appeler les constructeurs du parent.

PHP 5 introduit une nouvelle voie pour déclarer des constructeurs en appelant la méthode __construct().

<?php
  
  class BaseClass {
    function __construct() {
      print "In BaseClass constructor\n";
    }
  }
  
  class SubClass extends BaseClass {
    function __construct() {
      parent::__construct();
      print "In SubClass constructor\n";
    }
  }
  
  $obj = new BaseClass();
  $obj = new SubClass();
  
?>

Pour des raisons de compatibilité, si PHP 5 ne trouve pas de méthode __construct() pour une classe donnée, l'ancienne méthode constructeur PHP 4 est cherchée (méthode du même nom que la classe).

Destructeurs

Avoir la possibilité de définir des destructeurs pour des objets peut être très utile. Les destructeurs permet de logger des messages, fermer des connexions aux bases de données ou tout autre nettoyage nécessaire.

PHP 5 introduit le concept de destructeur comme les autres langages de programmation. La méthode __destruct() n'acceptant aucun paramètre est appelé avant que tout objet ne soit détruit en mémoire.

<?php
  class MyDestructableClass {
    function __construct() {
      print "In constructor\n";
      $this->name = "MyDestructableClass";
    }
    
    function __destruct() {
      print "Destroying " . $this->name . "\n";
    }
    
  }

  $obj = new MyDestructableClass();
?>

Comme les constructeurs, les destructeurs parent ne sont pas appelés implicitement par le moteur PHP. Pour exécuter la méthode __destruct(), il faut appeler explicitement parent ::__destruct().

Constantes

PHP 5 introduit les constantes par classe :

<?php
  class Foo {
    const constant = "constant";
  }

  echo "Foo::constant = " . Foo::constant . "\n";
?>

PHP 5 autorise les expressions entre constantes, toutefois les constantes sont évaluées à la compilation, aussi ces dernières ne peuvent être évaluées sur des variables en runtime :

<?php

  class Bar {
    const a = 1<<0;
    const b = 1<<1;
    const c = a | b;
  }

?>

L'ancien code ne possédant pas le mot clé constant ne doit poser aucun problème au cours de la migration.

Exceptions

PHP 4 ne permettait pas la gestion des exceptions, PHP 5 introduit un modèle d'exception similaire aux autres langages de programmation.

<?php

  class MyExceptionFoo extends Exception {

    function __construct($exception) {
        parent::__construct($exception);
    }
    
  }

  try {
    throw new MyExceptionFoo("Hello");
  }
  
  catch (MyException $exception) {
    print $exception->getMessage();
  }
  
?>

L'ancien code ne possédant pas le mot clé exception ne doit poser aucun problème au cours de la migration.

Variables statiques et classes statiques

Les variables statiques et les classes statiques peuvent être maintenant initialisées :

<?php

  class foo {
    static $my_static = 5;
  }

  print foo::$my_static;

?>

Méthodes statiques

PHP 5 introduit le mot clé static pour déclarer une méthode statique, cette dernière pouvant être appelée en dehors du contexte de l'objet :

<?php

  class Foo {
    public static function aStaticMethod() {
        // ...
    }

}
Foo::aStaticMethod();
?>

La pseudo variable $this n'est pas disponible dans une méthode déclarée en statique.

__autoload( )

La fonction __autoload() est utilisée automatiquement lorsqu'une classe non déclarée doit être instanciée. Le nom de la classe est automatiquement passée à la fonction __autoload().

<?php

  function __autoload($className) {
    include_once $className . ".php";
  }
  
  $object = new ClassName;

?>

Appels de méthodes surchargeables et accès aux propriétés surchargeables

Les appels de méthodes et les accès aux propriétés peuvent être surchargés avec les méthodes __call(),__get() et __set().

Exemples : __get() et __set()

<?php

  class Setter {
    public $n;
    public $x = array("a" => 1, "b" => 2, "c" => 3);
    
    function __get($nm) {
      print "Getting [$nm]\n";
               
      if (isset($this->x[$nm])) {
        $r = $this->x[$nm];
        print "Returning: $r\n";
        return $r;
      } else {
        print "Nothing!\n";
      }
    }
           
    function __set($nm, $val) {
      print "Setting [$nm]to $val\n";
               
      if (isset($this->x[$nm])) {
        $this->x[$nm] = $val;
        print "OK!\n";
      } else {
        print "Not OK!\n";
      }
    }
  }
        
        
  $foo = new Setter();
  $foo->n = 1;
  $foo->a = 100;
  $foo->a++;
  $foo->z++;
  var_dump($foo);

?>

Exemple : __call()

<?php
  
  class Caller {
    
    var $x = array(1, 2, 3);
    
    function __call($m, $a) {
      print "Method $m called:\n";
      var_dump($a);
      return $this->x;
    }
  }
  
  $foo = new Caller();
  $a = $foo->test(1, "2", 3.4, true);
  var_dump($a);
  
?>