[ Team LiB ] Previous Section Next Section

Inheritance

To create a class that inherits functionality from a parent class, we need to alter our class declaration slightly. Listing 9.6 simplifies the Item class and creates an inheriting class called PriceItem.

Listing 9.6 Creating a Class That Inherits from Another
 1:<?php
 2:class Item {
 3:  var $name;
 4:
 5:  function Item( $name="item", $code=0) {
 6:    $this->name = $name;
 7:    $this->code = $code;
 8:  }
 9:
10:  function getName() {
11:   return $this->name;
12:  }
13: }
14:
15: class PriceItem extends Item {
16:
17: }
18:
19: $item = new PriceItem( "widget", 5442 );
20: print $item->getName ();
21: // outputs "widget"
22:
23: ?>

In addition to the simple Item class defined on line 2, we have created an even more basic PriceItem class on line 15. Notice the extends clause in the class declaration. This means that a PriceItem object inherits all the functionality laid down in the Item class. Any PriceItem object will have access to a getName() method and a name property just as any Item object would (depending upon privacy settings).

If that's not enough, there's even more magic in Listing 9.6. Notice that we didn't define a constructor method for the PriceItem class. So how was the $name property changed from the default, "item", to the value ("widget") passed to the PriceItem class? Because we didn't provide a constructor in PriceItem, the Item class's constructor was automatically called.

graphics/bytheway_icon.gif

If a class extending another doesn't contain a constructor method, the parent class's constructor method will be called automatically when a child object is created. This feature was introduced in PHP 4.


Overriding the Method of a Parent Class

The PriceItem class currently creates objects that behave in exactly the same way as Item objects. In object-oriented code, child classes can override the methods of their parents, allowing objects instantiated from them to behave differently, while otherwise retaining much of the same functionality. Listing 9.7 gives the PriceItem class its own getName() method.

Listing 9.7 The Method of a Child Class Overriding That of Its Parent (PHP 4 Syntax)
 1:<?php
 2: class Item {
 3:   var $name;
 4:
 5:   function Item( $name="item", $code=0) {
 6:     $this->name = $name;
 7:     $this->code = $code;
 8:   }
 9:
10:  function getName() {
11:    return $this->name;
12:  }
13: }
14:
15: class PriceItem extends Item {
16:   function getName() {
17:     return "(price)."$this->name;
18:   }
19: }
20:
21: $item = new PriceItem( "widget", 5442 );
22: print $item->getName();
23: // outputs "(price) widget"
24:
25: ?>

The getName() method in the PriceItem class (line 16) is called in preference to that in the parent class. At this point, we can pause for a moment and consider the effect of making the $name property in the Item class private:


class Item {
  private $name;
  // ...

Making this change to Listing 9.7 would cause the output to change from the following:


(price) widget

The new output is


(price)

The PriceItem class does not have access to the $name property. If your child classes need to access methods or properties of their ancestor classes, then you should use the protected keyword in preference to private.

Calling an Overridden Method

Occasionally, you want the functionality of a parent class's method as well as the benefit of your own additions. Object-oriented programming allows you to have your cake and eat it, too. You can refer to a parent class using the parent keyword. In Listing 9.8, the PriceItem's getName() method calls the method in the Item class that it has overridden.

Listing 9.8 Calling an Overridden Method (PHP 5 Syntax)
 1: <?php
 2: class Item {
 3:   private $name;
 4:
 5:   function __construct( $name="item", $code=0 ) {
 6:     $this->name = $name;
 7:     $this->code = $code;
 8:   }
 9:
10:  function getName() {
11:    return $this->name;
12:  }
13:}
14:
15:class PriceItem extends Item {
16:  function getName() {
17:    return "(price) ".parent::getName ();
18:  }
19:}
20:
21:$item = new PriceItem ("widget", 5442);
22:print $item->getName();
23:// outputs "(price) widget"
24:
25:?>

By using the following syntax, we can call any method that we have overridden:


parent::methodname ()

We do so in the PriceItem class's getName() method on line 17. Because the PriceItem class no longer works directly with the Item class's $name property, we could at this point declare the $name property private, with no effect on output. If you are working exclusively with PHP 5, it is good practice to lock down your methods and properties as far as you can.

Working with Constructors

We have seen that a parent class's constructor is automatically called if the child class does not define its own constructor method. If the child class does define a constructor, it becomes responsible for calling the constructor of its parent. In Listing 9.9, we add a constructor to our PriceItem class.

Listing 9.9 Adding a Constructor to PriceItem
 1:<?php
 2:class Item {
 3:  private $name;
 4:
 5:  function __construct( $name="item", $code=0 ) {
 6:    $this->name = $name;
 7:    $this->code = $code;
 8:  }
 9:
10:  function getName () {
11:    return $this->name;
12:  }
13:}
14:
15:class PriceItem extends Item {
16:  private $price;
17:
18:  function __construct( $name, $code, $price ) {
19:    parent::__construct( $name, $code );
20:    $this->price = $price;
21:  }
22:
23:  function getName() {
24:    return "(price) ".parent::getName ();
25:  }
26:}
27:
28:$item = new PriceItem ("widget", 5442, 5.20);
29:print $item->getName ();
30:// outputs "(price) widget"
31:
32:?>

We create a constructor method on line 18, accepting arguments for name and code and adding a new one for price. We use the parent keyword to invoke the Item class's constructor (line 19) before setting the $price property ourselves. It is here that we can see one reason for PHP 5's new syntax for constructors. The following line is nicely generic:


parent::__construct( $name, $code );

If we insert a new class into the inheritance hierarchy between Item and PriceItem, the constructor of the new class would be invoked according to the altered extends clause of PriceItem. Prior to PHP 5, however, it was necessary to refer to a parent class's constructor, which was the name of the parent class itself:


parent:Item( $name, $code );

It was a common bug in object-oriented code that a class's extends clause would be modified to point to an intermediate parent class, and the constructor call would be forgotten, causing unexpected and hard-to-analyze errors. The new syntax addresses this bug to some extent.

We will return to the Item and PriceItem classes in Hour 17, "Advanced Objects."

    [ Team LiB ] Previous Section Next Section