PHP Australia Conference 2015

Héritage

L'héritage est un des grands principes de la programmation orientée objet, et PHP l'implémente dans son modèle objet. Ce principe va affecter la manière dont de nombreuses classes sont en relation les unes avec les autres.

Par exemple, lorsque vous étendez une classe, la classe fille hérite de toutes les méthodes publiques et protégées de la classe parente. Tant qu'une classe n'écrase pas ces méthodes, elles conservent leur fonctionnalité d'origine.

L'héritage est très utile pour définir et abstraire certaines fonctionnalités communes à plusieurs classes, tout en permettant la mise en place de fonctionnalités supplémentaires dans les classes enfants, sans avoir à réimplémenter en leur sein toutes les fonctionnalités communes.

Note:

A moins que l'autoload ne soit utilisé, les classes doivent être connues avant d'être utilisées. Les classes mères doivent être définies avant l'écriture d'un héritage. Cette règle générale s'applique aussi dans le cas d'héritage ou d'implémentation d'interfaces.

Exemple #1 Exemple d'héritage

<?php

class Foo
{
    public function 
printItem($string)
    {
        echo 
'Foo: ' $string PHP_EOL;
    }
    
    public function 
printPHP()
    {
        echo 
'PHP est super' PHP_EOL;
    }
}

class 
Bar extends Foo
{
    public function 
printItem($string)
    {
        echo 
'Bar: ' $string PHP_EOL;
    }
}

$foo = new Foo();
$bar = new Bar();
$foo->printItem('baz'); // Affiche : 'Foo: baz'
$foo->printPHP();       // Affiche : 'PHP est super'
$bar->printItem('baz'); // Affiche : 'Bar: baz'
$bar->printPHP();       // Affiche : 'PHP est super'

?>
add a note add a note

User Contributed Notes 7 notes

up
28
jackdracona at msn dot com
4 years ago
Here is some clarification about PHP inheritance – there is a lot of bad information on the net.  PHP does support Multi-level inheritance.  (I tested it using version 5.2.9).  It does not support multiple inheritance.

This means that you cannot have one class extend 2 other classes (see the extends keyword).  However, you can have one class extend another, which extends another, and so on.

Example:

<?php
class A {
       
// more code here
}

class
B extends A {
       
// more code here
}

class
C extends B {
       
// more code here
}


$someObj = new A();  // no problems
$someOtherObj = new B(); // no problems
$lastObj = new C(); // still no problems

?>
up
24
strata_ranger at hotmail dot com
4 years ago
I was recently extending a PEAR class when I encountered a situation where I wanted to call a constructor two levels up the class hierarchy, ignoring the immediate parent.  In such a case, you need to explicitly reference the class name using the :: operator.

Fortunately, just like using the 'parent' keyword PHP correctly recognizes that you are calling the function from a protected context inside the object's class hierarchy.

E.g:

<?php
class foo
{
  public function
something()
  {
    echo
__CLASS__; // foo
   
var_dump($this);
  }
}

class
foo_bar extends foo
{
  public function
something()
  {
    echo
__CLASS__; // foo_bar
   
var_dump($this);
  }
}

class
foo_bar_baz extends foo_bar
{
  public function
something()
  {
    echo
__CLASS__; // foo_bar_baz
   
var_dump($this);
  }

  public function
call()
  {
    echo
self::something(); // self
   
echo parent::something(); // parent
   
echo foo::something(); // grandparent
 
}
}

error_reporting(-1);

$obj = new foo_bar_baz();
$obj->call();

// Output similar to:
// foo_bar_baz
// object(foo_bar_baz)[1]
// foo_bar
// object(foo_bar_baz)[1]
// foo
// object(foo_bar_baz)[1]

?>
up
12
jarrod at squarecrow dot com
4 years ago
You can force a class to be strictly an inheritable class by using the "abstract" keyword. When you define a class with abstract, any attempt to instantiate a separate instance of it will result in a fatal error. This is useful for situations like a base class where it would be inherited by multiple child classes yet you want to restrict the ability to instantiate it by itself.

Example........

<?php

abstract class Cheese
{
     
//can ONLY be inherited by another class
}

class
Cheddar extends Cheese
{
}

$dinner = new Cheese; //fatal error
$lunch = new Cheddar; //works!

?>
up
1
ghoucine at gmail dot com
1 month ago
For multiple single inheretance to work the order of class definition is very important if you're going up more than two levels in inheretence.

This won't work:

<?php

class A extends B {}

class
B extends C {}

class
C {}

$A = new A;
?>
Running the code above will spit a fatal error (Fatal Error:Class 'B' not found)

A change in class definition order will fix this:

<?php

class A extends B {}

class
C {}

class
B extends C {}

$A = new A;
?>

If you're goin up just one level in inhertance though there shouldn't be any problem. So this code works too:

<?php

class A extends B {}

class
{}

$A = new A;

?>

I have no explanation as to why this is the case though.
up
1
php at sleep is the enemy dot co dot uk
4 years ago
Here's fun, an attempt to make some degree of multiple inheritance work in PHP using mixins. It's not particularly pretty, doesn't support method visibility modifiers and, if put to any meaningful purpose, could well make your call stack balloon to Ruby-on-Rails-esque proportions, but it does work.

<?php
abstract class Mix {
   
    protected
$_mixMap = array();
   
    public function
__construct(){
       
        
$this->_mixMap = $this->collectMixins($this);
    }
   
    public function
__call($method, $args){
       
       
// doesn't pass scope
        //return call_user_func_array(array($className, $method), $args);
       
        // Error: Given object is not an instance of the class this method was declared in
        //$method = new ReflectionMethod($className, $method);
        //return $method->invokeArgs($this, $args);
       
       
$payload = $this->buildMixinPayload($this->_mixMap, $method, $args);
        if(!
$payload) throw new Exception('Method ' . $method . ' not found');
       
        list(
$mixinMethod, list($method, $args)) = $payload;
       
        return
$this->$mixinMethod($method, $args);
       
    }
   
    protected function
collectMixins($class){
       
        static
$found = array();
        static
$branch = array();
       
        if(empty(
$branch)) $branch[] = get_class($this);
       
$mixins = array();
       
        foreach(
array_reverse(get_class_methods($class)) as $method){
            if(
preg_match('/^mixin(\w+)$/', $method, $matches)){
               
               
$className = $matches[1];
               
                if(
in_array($className, $branch))
                    throw new
Exception('Circular reference detected ' . implode(' > ', $branch) . ' > ' . $className);
                   
                if(!
in_array($className, $found)){
               
                    if(!
class_exists($className)) throw new Exception('Class ' . $className . ' not found');
           
                   
// populate props from mixin class
                   
foreach(get_class_vars($className) as $key => $value){       
                        if(!
property_exists($this, $key)) $this->$key = $value;
                    }
                   
                   
$found[] = $branch[] = $className;               
                   
$mixins[$className] = $this->collectMixins($className);
                }
               
               
$branch = array(get_class($this));
            }
        }
       
        return
$mixins;
    }
   
    protected function
buildMixinPayload($mixins, $method, $args){
       
        foreach(
$mixins as $className => $parents){
           
           
$mixinMethod = 'mixin' . $className;
           
            if(
method_exists($className, $method)) return array($mixinMethod, array($method, $args));
           
            if(!empty(
$parents) && $return = $this->buildMixinPayload($parents, $method, $args)){
                return array(
$mixinMethod, $return);
            }
        }
       
        return
false;
    }
   
}
?>
up
-1
OZ
3 years ago
Model for Mixins pattern:

<?php
interface IMixinsCaller
{
    public function
__mixin_get_property($property);

    public function
__mixin_set_property($property, $value);

    public function
__mixin_call($method, $value);
}

abstract class
MixinsCaller implements IMixinsCaller
{
    protected
$mixins = array();

    public function
__call($name, $arguments)
    {
        if (!empty(
$this->mixins))
        {
            foreach (
$this->mixins as $mixin)
            {
                if (
method_exists($mixin, $name))
                {
                    return
call_user_func_array(array($mixin, $name), $arguments);
                }
            }
        }
       
trigger_error('Non-existent method was called in class '.__CLASS__.': '.$name, E_USER_WARNING);
    }

    public function
__mixin_get_property($property)
    {
        if (
property_exists($this, $property))
        {
            return
$this->$property;
        }
       
trigger_error('Non-existent property was get in class '.__CLASS__.': '.$property, E_USER_WARNING);
    }

    public function
__mixin_set_property($property, $value)
    {
        if (
property_exists($this, $property))
        {
            return
$this->$property = $value;
        }
       
trigger_error('Non-existent property was set in class '.__CLASS__.': '.$property, E_USER_WARNING);
    }

    public function
__mixin_call($method, $value)
    {
        if (
method_exists($this, $method))
        {
            return
call_user_func_array(array($this, $method), $value);
        }
       
trigger_error('Non-existent method was called in class '.__CLASS__.': '.$method, E_USER_WARNING);
    }

    public function
AddMixin($mixin)
    {
       
$this->mixins[] = $mixin;
    }
}

abstract class
Mixin
{
   
/** @var IMixinsCaller $parent_object */
   
private $parent_object;

    public function
__construct(IMixinsCaller $parent_object)
    {
       
$this->parent_object = $parent_object;
    }

    public function
__get($property)
    {
        return
$this->parent_object->__mixin_get_property($property);
    }

    public function
__set($property, $value)
    {
        return
$this->parent_object->__mixin_set_property($property, $value);
    }

    public function
__call($method, $value)
    {
        return
$this->parent_object->__mixin_call($method, $value);
    }
}
?>
up
-15
msg2maciej at aol dot com
3 years ago
PHP supports single class inheritance. My bare idea on accessing protected methods with power of abstracts and sort of "multi-class inheritance SIMULATION":

<?php
error_reporting
(E_ALL);

abstract class
Base {
    abstract protected function
__construct ();
    abstract protected function
hello_left ();
    abstract protected function
hello_right ();
}

abstract class
NotImplemented_Left extends Base {
protected function
hello_right () {
echo
'well, wont see that'; }}

abstract class
NotImplemented_Right extends Base {
protected function
hello_left () {
echo
'well, wont see that'; }}

class
Left extends NotImplemented_Left {
protected function
__construct () {        # limited visibility, no access from "outside"
echo __CLASS__.'::protected __construct'. "\n"; }
protected function
hello_left () {        # limited visibility, no access from "outside"
echo 'protected hello_left in ' . __CLASS__ . "\n"; }}

class
Right extends NotImplemented_Right {
protected function
__construct () {        # limited visibility, no access from "outside"
echo __CLASS__.'::protected __construct'. "\n"; }
protected function
hello_right () {        # limited visibility, no access from "outside"
echo 'protected hello_right in ' . __CLASS__ . "\n"; }
protected function
hello_left () {
echo
"wont see that, and easy to get rid of it from here\n"; }}

class
Center extends Base {
private
$left;
private
$right;
public function
__construct () {
echo
'welcome in ' . __CLASS__ . "\n";
echo
'Center::'; $this->left = new Left;
echo
'Center::'; $this->right = new Right;
echo
" oh and\n";
$this->hello_left();
$this->hello_right();
}
public function
hello_left () {            # calling class Left
echo __CLASS__.'::'; $this->left->hello_left(); }
public function
hello_right () {        # calling class Right
echo __CLASS__.'::'; $this->right->hello_right(); }
}

$c = new Center;
?>

Produces:

welcome in Center
Center::Left::protected __construct
Center::Right::protected __construct
oh and
Center::protected hello_left in Left
Center::protected hello_right in Right
To Top