Object-Oriented PHP Part 2: Relationships

21st August, 2006 @ 10:11pm UTC

Development, PHP, Web / 9 Comments

Following on from my posts “Object-Oriented Concepts” and “Object-Oriented Javascript“, I’m going to take a look at OOP in PHP.

In “Part 1: Definition” we took a look at defining objects and classes in PHP. In part 2 I’m going to look at the most important part of any object-oriented system - the relationships.

Relationships

The relationships you use dictate the way objects interact with each other. Here’s how to model them in PHP.

Inheritance

Inheritance allows us to define a class that inherits all the attributes and methods of another class. To do this in PHP4 and PHP5, we need to employ the extends keyword. Here’s an example based around HTML form elements:

class formElement
{
  var $str_id;
  var $str_name;
  var $str_class;

  function isRequired()
  {
    // Validation code here
  }
}

class dateInput extends formElement
{
  var $str_value;

  // Specific validation for dateInput
  function isValidDate()
  {
    // Validation code here
  }
}

In the above example, the class dateInput is inheriting all the attributes and methods of the class formElement. This means we could do all of the following:

// Instantiate our class as an object
$myDate = new dateInput();

$myDate->str_id = 'mydate';
$myDate->str_name = 'mydate';
$myDate->str_value = '2006/06/09';
$myDate->isRequired();
$myDate->isValidDate();

Some languages allow multiple inheritance; allowing you to inherit from more than one class. Unfortunately, this is not the case in PHP.

If your parent class has a constructor, PHP won’t evaluate it when you instantiate the child class. If you wish the constructor to be run, you’ll need to call in as if it were any other method within the class. Here’s an example of what I mean:

PHP4

class fruit
{
  // Constructor
  function fruit()
  {
  }
}

class apple extends fruit
{
  function apple()
  {
    // Call the constructor of our parent class
    $this->fruit();
  }
}

PHP5

class fruit
{
  // Constructor
  function __construct()
  {
  }
}

class apple extends fruit
{
  function __construct()
  {
    // Call the constructor of our parent class
    // using the scope resolution operator -
    // we'll talk about this later.
    parent::__construct();
  }
}

In the PHP5 example you’ll notice we don’t use the $this keyword. This is simply because $this could evaluate to either apple or fruit because the constructor method for each is called __construct. To get around this we use the scope resolution operator which I will talk about a little later on.

Composition - Association and Aggregation

Association and aggregation differ from inheritance because rather than passing the methods and properties of a class to another class, they involve instantiating whole objects and using them as properties, or passed-in parameters, in another class.

This might sound complicated but it’s really very simple in practice. I’m going to borrow some of the examples in my Object-Oriented Concepts post and adapt them for PHP:

Association

class brick
{
  var $sample_attribute;
}

class wall
{
  var $brick1;
  var $brick2;
  var $brick3;
  var $brick4;

  // Constructor
  function wall()
  {
    $this->brick1 = new brick();
    $this->brick2 = new brick();
    $this->brick3 = new brick();
    $this->brick4 = new brick();
  }
}

In the above example we have a class wall which has four brick attributes. To associate our brick class we instantiate it as the $brick attributes of the wall class.

We can now access the $sample_attribute of each brick within our wall class like so:

echo $this->brick1->sample_attribute;
echo $this->brick2->sample_attribute;
echo $this->brick3->sample_attribute;
echo $this->brick4->sample_attribute;

Or from outside an instantiated object like so:

$myWall = new wall();

echo $myWall->brick1->sample_attribute;
echo $myWall->brick2->sample_attribute;
echo $myWall->brick3->sample_attribute;
echo $myWall->brick4->sample_attribute;

As you can see, this type of relationship has the potential to be highly useful - however, if you wish to share an object between other classes (for instance, a database connection object) we run into a problem. In our example you can see that the brick objects are encapsulated within our wall class which means they are only accessible within our myWall object. To get around this problem we could use aggregation.

Aggregation basically involves passing an instantiated object into a class so that the class can “use” our object. Once again, I’ll poach one of my earlier examples to illustrate what I mean. To save on code snippets, I’m only going to use PHP4 syntax for the moment:

Aggregation

class person
{
}

class car
{
  var $driver;

  // Constructor
  function car(&$driver)
  {
    $this->driver = (object) $driver;
  }
}

// Instantiate our person object
$me = new person();

// Now pass it in as $driver
$myMotor = new car($me);

In this example we instantiate our class person as an object ($me). We then pass the $me object into the car class so that it is used as the $driver attribute in the $myMotor object. If we were to destroy the $myMotor object, we wouldn’t destroy the $me object; which means we could carry on using it for other things. In fact, even though we’re using the $me object within our $myMotor object, we could still use it in other objects like so:

class van
{
  var $driver;

  // Constructor
  function van(&$driver)
  {
    $this->driver = (object) $driver;
  }
}

// Our $me object already exists
// so pass it in as $driver
$myVan = new van($me);

If you look at our example you’ll notice the ampersand symbol (&) before the parameter declaration of our constructor method. This tells PHP that the parameter is passed by reference. This is the key to the aggregation relationship.

References

PHP references allow you to define two variables that refer to the same content. To do this we use the & operator. Here’s an example:

$a =& $b;

In this example $a and $b point to the same content.

The same syntax can be used with functions, that return references, and with the new operator:

$bar =& new fooclass();
$foo =& find_var($bar);

If we don’t use the & operator when we’re instantiating an object, PHP creates a copy of said object. If you use $this in the class it will operate on the current instance of the class. The assignment without & will copy the instance (i.e. the object) and $this will operate on the copy, which is not always what is desired. Usually you want to have a single instance to work with, due to performance and memory consumption issues.

Basically, it’s a good idea to always assign a reference when instantiating an object unless you have a specific case not to.

Note: Since PHP5, in fact, new returns references automatically so using =& in this context is deprecated and produces E_STRICT level messages.

There is another way that references help us when developing object-oriented solutions in PHP; by allowing method parameters to be passed by reference as we did in our aggregation example above.

Passing by reference basically allows us to use or modify a variable within a method (function). Here’s a good example from the PHP documentation:

function foo(&$var)
{
   $var++;
}

$a=5;
foo($a);
// $a is 6 here

In the aggregation example we’re passing an entire object as a parameter. If we passed the parameter without the reference we would be creating a copy of the object that was only available within the function. By adding the reference we’re simply telling the function that the parameter is another name for our original object.

Summary

Although these relationships can appear simple to begin with, it’s all too easy to overlook their hidden power. Used in the right way, relationships can help to build highly powerful, easily extendable applications with relatively little fuss.

Deciding which relationship to use can be tricky; and it’s certainly not something you’ll get right everytime. However, rest assured, if you experiment and use them regularly, you’ll find it easier to solve problems that are presented to you.

In the next post, I’ll be taking a look at extending our relationships a little further using visibility and polymorphism.

Click here to continue on to “Part 3: Taking Relationships Further”…

Like this post? Digg it or Del.icio.us it!

Comments (9)

Skip to the comment form…

  1. Gravatar Image Dylan August 22, 2006 @ 12:23 pm

    Once again mate, thanks. This is coming with perfect timing for me.

  2. Gravatar Image Gruntfuttuck June 19, 2007 @ 7:43 pm

    Very clear and helpful. It’s good to see association, aggregation and inheritance explained so well. I thinks to many people focus to much on inheritance and not enough on aggregation and inheritance.
    Good post.

  3. Gravatar Image Dave February 1, 2008 @ 9:42 am

    At first, I thought you had completely missed the obligatory car analogy when explaining OOP but then I thought: “Maybe he gets to it in a later chapter…”.

    So I reached this page and I saw the fruit/apple analogy and the wall analogy and I thought: “He’s forgotten the car analogy. He’s being all clever and stuff with his fancy new analogies but he’s forgotten his duty to the car analogy.”

    … and then I saw it. You didn’t disappoint us… and it was all the sweeter for being made to wait.

  4. Gravatar Image Tim February 1, 2008 @ 10:51 am

    @Dave: It’s just not an OOP tutorial without the car analogy! ;)

    To be fair though, I originally wrote more… uh… adult-themed code examples. However, since the blog is a more public arena, I decided to make it family friendly — for the kids, y’see.

  5. Gravatar Image Monkeytail September 28, 2008 @ 2:25 am

    Good tut!

    I don’t get the ‘by reference’ part.
    What would happen if you let the & out in car(&$driver) ?

  6. Gravatar Image Monkeytail September 28, 2008 @ 2:29 am

    ^^ in the car case, is it a thing about benefits.. or THE way to do it, otherwise aggregation won’t work?

  7. Gravatar Image Godmode0110 July 13, 2009 @ 3:14 pm

    PHP 5 is giving me an issue about my ‘passed’ object parameter not containing the method for which I send it.

    I find this frustrating, in that I’m attempting to make a sort of ‘linked list’ object. however, I’ve been running into a brick wall over PHP5 not wanting to allow the passed object to run a function. You see, it won’t even compile the class. And this vexes me.

    While your artical was highly illuminating, it did not provide enough information to solve my problem.

  8. Gravatar Image sohdubom August 7, 2009 @ 7:43 pm

    hi, great tutorials … what confuses me sometimes is that in theory we classify the relationships between objects like: association, aggregation and composition … but they are all some kind of association … and considering that in composition the parent class is responsible for creating the children, isn’t your example of association more like a composition?

  9. Gravatar Image tomt610 February 22, 2010 @ 7:00 pm

    Few years have passed and there is still error in example code:

    class apple extends fruit
    {
      function apple()
      {

    in PHP5 example. And then it is wrotten that:

    In the PHP5 example you’ll notice we don’t use the $this keyword. This is simply because $this could evaluate to either apple or fruit because the constructor method for each is called __construct.

    I wonder if this will be still corrected.

Leave a comment





Categories

Syndication

Technorati

© 2010 Tim Huegdon, All Rights Reserved / Website design and development by Nefarious Designs

Powered by Wordpress / Log in

Due to the dodgy manufacturing process, this website may contain traces of nuts!