• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • 对象继承

    继承已为大家所熟知的一个程序设计特性,PHP 的对象模型也使用了继承。继承将会影响到类与类,对象与对象之间的关系。

    比如,当扩展一个类,子类就会继承父类所有公有的和受保护的方法。除非子类覆盖了父类的方法,被继承的方法都会保留其原有功能。

    继承对于功能的设计和抽象是非常有用的,而且对于类似的对象增加新功能就无须重新再写这些公用的功能。

    Note:

    除非使用了自动加载,否则一个类必须在使用之前被定义。如果一个类扩展了另一个,则父类必须在子类之前被声明。此规则适用于类继承其它类与接口。

    Example #1 继承示例

    <?php
    class foo
    {
        public function printItem($string) 
        {
            echo 'Foo: ' . $string . PHP_EOL;
        }
        
        public function printPHP()
        {
            echo 'PHP is great.' . PHP_EOL;
        }
    }
    class bar extends foo
    {
        public function printItem($string)
        {
            echo 'Bar: ' . $string . PHP_EOL;
        }
    }
    $foo = new foo();
    $bar = new bar();
    $foo->printItem('baz'); // Output: 'Foo: baz'
    $foo->printPHP();       // Output: 'PHP is great' 
    $bar->printItem('baz'); // Output: 'Bar: baz'
    $bar->printPHP();       // Output: 'PHP is great'
    ?>
    
    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
     
    ?>
    
    I think the best way for beginners to understand inheritance is through a real example so here is a simple example I can gave to you 
    <?php
    class Person
    {
      public $name;
      protected $age;
      private $phone;
      public function talk(){
        //Do stuff here
      }
      protected function walk(){
        //Do stuff here
      }
      private function swim(){
        //Do stuff here
      }
    }
    class Tom extends Person
    {
      /*Since Tom class extends Person class this means 
        that class Tom is a child class and class person is 
        the parent class and child class will inherit all public 
        and protected members(properties and methods) from
        the parent class*/
       /*So class Tom will have these properties and methods*/
       //public $name;
       //protected $age;
       //public function talk(){}
       //protected function walk(){}
       //but it will not inherit the private members 
       //this is all what Object inheritance means
    }
    PHP7 gives you a warning if you redeclare a function in a child class with different parameters. For example:
    class foo {
       function print($text='') {
         print text;
       }
    }
    class bar extends foo {
       function print($text1='',$text2='') {
          print text1.text2
       }
    }
    will give a PHP Warning: Declaration of bar::print($text1 = '', $text2 = '') should be compatible with foo::print($text= '').
    The Idea that multiple inheritence is not supported is correct but with tratits this can be reviewed.
    for e.g.
     
    <?php
    trait custom
    {
       public function hello()
       {
         echo "hello";
       }
    }
    trait custom2
    {
        public function hello()
        {
          echo "hello2";
        }
    }
    class inheritsCustom
    {
        use custom, custom2
        {
           custom2::hello insteadof custom;
        }
    }
    $obj = new inheritsCustom();
    $obj->hello();
    ?>
    
    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]
    ?>
    
    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!
    ?>
    
    Inheritance works at create time, i.e. using the keyword 'new'. Static properties confused my understanding, so in order tho show the effect of visibility to inherintence I've created a simple demo script along with some set and get magic:
    <?php
    class A {
    private $a  = 'private';
    protected $b = 'protected';
    public $c  = 'public';
    static $d  = 'static';
    public function __construct()
    {
      $this->e = 'constructed';
    }
    public function __set($property, $value)
    {
      echo ' set ' . $property . '=' . $value;
      $this->$property=$value;
    }
    public function __get($property)
    {
      echo ' get ' . $property;
      $this->$property = 'dynamic'; // invokes __set() !!
      return $this->$property;
    }
    }
    class B extends A
    {
    public function constructMe()
    {
      $this->e = 'constructed2';
    }
    }
    class C extends B
    {
    public function __construct()
    {
      parent::constructMe();
    }
    }
    echo " \n";
    $a = new A();
    $b = new B();
    echo " \n";
    echo ' B:c='.$b->c;
    echo " \n";
    echo ' B:d=' .$b->d;
    echo " \n";
    $c = new C();
    echo " \n";
    print_r($a);
    print_r($b);
    print_r($c);
    print_r(A::$d);
    print_r(B::$d);
    print_r(C::$d);
    echo 'A class: ';
    $R = new reflectionclass('A');
    print_r($R->getdefaultproperties());
    print_r($R->getstaticproperties());
    echo 'B class: ';
    $R = new reflectionclass('B');
    print_r($R->getdefaultproperties());
    print_r($R->getstaticproperties());
    ?>
    This outputs:
     set e=constructed 
     B:c=public 
     get d set d=dynamic B:d=dynamic 
     set e=constructed2 
    A Object
    (
      [a:A:private] => private
      [b:protected] => protected
      [c] => public
      [e] => constructed
    )
    B Object
    (
      [a:A:private] => private
      [b:protected] => protected
      [c] => public
      [d] => dynamic
    )
    C Object
    (
      [a:A:private] => private
      [b:protected] => protected
      [c] => public
      [e] => constructed2
    )
    staticstaticstaticA class: Array
    (
      [d] => static
      [a] => private
      [b] => protected
      [c] => public
    )
    Array
    (
      [d] => static
    )
    B class: Array
    (
      [d] => static
      [b] => protected
      [c] => public
    )
    Array
    (
      [d] => static
    )
    This shows how private variables ($a) are inherited, how static variables ($d) are inherited (by the class, not by the object) and that changing or adding variables in the parent ($e, $d) are not inherited by the child.
    Even when autoloading (SPL) is used, class inheritance does not seem to work. Simply the PHP engine is unable to find parent (inherited) class. PHP 5.6 and 7.0 behave exactly same on this, which beats the purpose of autoloading.
    And IMHO it's easy to fix as the autoloader is able to find all first level classes w/o problems, it just needs to follow same path recursively on parents too.
    <?php
    //Using default SPL autoloader, with namespaces mapping 1:1 to directory structure, with file names being all lowercase. 
    //This works with first level classes only, for inheritance it does NOT work, it cannot find parent classes.
    spl_autoload_register();
    //This is ugly but working code if you want to be able to autoload parent classes too.
    spl_autoload_register(function ($class){
      require_once __DIR__ . '/' . strtolower(str_replace('\\', '/', $class) . '.php');
    });
    Similar to functions, unless they are in conditionals, it is possible to define classes anywhere within your script and they still generate instances.
    It is also possible to define child classes before the parent class, however, this seems to act the same way as a conditional, meaning that you need to define these classes before they can be used.
    Examples:
    - Standard usage (A and B are always accessible)
    <?php
    class A {}
    class B extends A {}
    var_dump( class_exists( 'A' ), class_exists( 'B' ) );
    // Outputs: bool(true) bool(true)
    ?>
    - End of file declaration (A and B are still always accessible, even if you have a return or exit before it)
    <?php
    var_dump( class_exists( 'A' ), class_exists( 'B' ) );
    // Outputs: bool(true) bool(true)
    die;
    class A {}
    class B extends A {}
    ?>
    - Child before parent declaration (B is not accessible until it has been defined)
    <?php
    var_dump( class_exists( 'A' ), class_exists( 'B' ) );
    // Outputs: bool(true) bool(false)
    class B extends A {}
    var_dump( class_exists( 'B' ) );
    // Outputs: bool(true)
    class A {}
    ?>
    - Conditional definition (Same as child before parent example above)
    <?php
    var_dump( class_exists( 'A' ), class_exists( 'B' ) );
    // Outputs: bool(true) bool(false)
    if ( ! class_exists( 'B' ) ) {
      class B extends A {}
    }
    var_dump( class_exists( 'B' ) );
    // Outputs: bool(true)
    class A {}
    A simple trait to create multi inheritance in php
    <?php
    trait tExtend {
      private $objectExtend = [];
      final public function extend() {
        foreach ($objects = func_get_args() as &$object) {
          if (is_object($object) && !$object instanceOf self) {
            $this->objectExtend[] = $object;
            
            if (is_callable([$object, 'syncExtend']))
              $object->syncExtend($this, $objects);
          }
        }
      }
      final public function syncExtend(&$object = null, array $objects) {
        if (is_object($object) && !$object instanceOf self && !in_array($object, $this->objectExtend))
          $this->objectExtend[] = $object;
        foreach ($objects as &$object)
          if (is_object($object) && !$object instanceOf self && !in_array($object, $this->objectExtend))
            $this->objectExtend[] = $object;
        return $this;
      }
      final public function __call($method, $args) {
        if (method_exists($this, $method))
          return $this->{$method}(... $args);
        else {
          foreach ($this->objectExtend as $i => &$object) {
            if (method_exists($object, $method))
              return $object->{$method}(... $args);
          }
        }
        throw new \Exception('Call to undefined method ' . get_called_class() . '::' . $method . '()');
      }
      final public function __get($key) {
        if (array_key_exists($key, $this))
          return $this->{$key};
        foreach ($this->objectExtend as &$object) {
          if (array_key_exists($key, $object))
            return $object->{$key};
        }
        trigger_error('Undefined property: ' . get_called_class() . '::$' . $key, E_USER_NOTICE);
      }
      final public function __isset($key) {
        if (array_key_exists($key, $this))
          return true;
        foreach ($this->objectExtend as &$object) {
          if (array_key_exists($key, $object))
            return true;
        }
        return false;
      }
    }
    /*************/
    class F {
      public $relationship = 'Sister';
    }
    class M {
      public $relationship = 'Brother';
    }
    class A extends M {
      use tExtend;
    }
    class B extends F {
      use tExtend;
      function getName() {
        return 'B';
      }
    }
    class C extends F {
      use tExtend;
      function getName() {
        return 'C';
      }
    }
    class D extends M {
      use tExtend;
    }
    class Z {
      use tExtend;
      function getParent() {
        return 'Z';
      }
      function __construct() {
        $this->extend(... func_get_args());
      }
    }
    $a = new A;
    $b = new B;
    $c = new C;
    $d = new D;
    $z = new Z($a, $b, $c, $d);
    $d->extend($a); // Change nothing
    $a->varA = 'A';
    $c->varA = 'C';
    echo $a->getParent(); // print Z
    echo $a->getName(); // print B
    echo $b->getName(); // print B
    echo $c->getName(); // print C
    echo $d->getName(); // print B
    echo $z->getName(); // print B
    echo $z->varA; // print A
    echo $c->varA; // print C
    ?>
    
    [Document:Main.php]
    <?php
    //
    include("person.php");    
    include("student.php");  
    include("teacher.php");    
    //-----Creating a object of the class "teacher"--------
    $t1=new teacher();
    $t1->name="Mustermann";
    $t1->forename="Max";
    $t1->email="max@mustermann.com";
    echo $t1->name.", ".$t1->forename.": ".$t1->email;
    $t1->ID();
    //-----Creating a object of the class "student"--------
    $s1=new student();
    $s1->name="Kevin";
    $s1->forename="Alpha";
    echo $s1->name."; ".$s1->forename;
    $s1->ID();
    ?>
    //-------------------------------------------------------------------------------------------
    [Document:person.php]
    <?php
    class person
    {
     //Attribute
     public $name;
     public $forename;
    }
    ?>
    //-------------------------------------------------------------------------------------------
    [Document:teacher.php]
    <?php
    class teacher extends person  /
    {
     //Attribute
     public $email;
     //Function
     public function ID()
     {
      echo "<p>I am a teacher</p>"; 
     }
    }
    ?>
    //-------------------------------------------------------------------------------------------
    [Document:student.php]
    <?php
    class student extends Person 
    {
     //Attribute 
     //Functionen
     public function ID()
     {
      echo"<p>I am a student</p>"; 
     }
    }
    ?>
    
    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 B {}
    $A = new A;
    ?>
    I have no explanation as to why this is the case though.
    This page is not clear about the nature and specifics of Object Inheritance especially that you can only make the visibility of an inherited method or property weaker and not stronger within the subclass that is inheriting these from its parent class.
    Example:
    <?php
    class Test1
    {
      protected $a_property = "This is a property";
      
      protected function printProperty()
      {
        echo $this->a_property;
      }
    }
    class Test2 extends Test1
    {
      private $b_property = "This is another property";
      
      private function printProperty()
      {
        echo $this->b_property;
      }
    }
    $test2 = new Test2();
    ?>
    This code produces a PHP Fatal error with message:
    PHP Fatal error: Access level to Test2::printProperty() must be protected (as in class Test1) or weaker
    So if you inherit a protected method and you want redeclare it in your subclass then you can either declare its visibility as protected or public. If public is inherited then it stays public.
    Here is the working version of the code snippet above:
    <?php
    class Test1
    {
      protected $a_property = "This is a property";
      
      protected function printProperty()
      {
        echo $this->a_property;
      }
    }
    class Test2 extends Test1
    {
      private $b_property = "This is another property";
      
      public function printProperty()
      {
        echo $this->b_property;
      }
    }
    $test2 = new Test2();
    $test2->printProperty();
    ?>
    To iterate what the description on the page says, subclasses inherit all protected and public properties and methods.
    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);
      }
    }
    ?>
    
    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;
      }
      
    }
    ?>
    
    I've noticed one thing concerning inheritance...
    When declaring an abstract class with a private method,
    which is overridden by a sub-class, private takes precedence over public for child class...
    (in the case you're redeclaring a method with a different signature in fact).
    Hope this helps
    Overriding a method which is called from base class works like this:
    <?php
    class Foo
    {
      public function printItem($string)
      {
        echo 'Foo: ' . $string;
      }
      
      public function printPHP($string)
      {
        $this->printItem($string);
      }
    }
    class Bar extends Foo
    {
      public function printItem($string)
      {
        echo 'Bar: ' . $string ;
      }
    }
    $foo = new Foo();
    $bar = new Bar();
    $foo->printPHP('baz'); //Foo: baz
    $bar->printPHP('baz'); //Bar: baz
    ?>
    
    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