Question about Object Aggregation

Hi there long time procedural programmer making the switch too OOP. I’ve been reading the sitepoint PHP Anthology book and have a question about Object Aggregation, mostly I’m unclear on scope it seems.

So the book is creating objects that generate html entities, namely an unordered list and list items therein. They both use a general htmelement class as their base. Here are the scripts in question.

First the htmlelement class that both use as a basis.


<?php
class HTMLElement
{
  protected $content;
  protected $tagname;
  protected $attributes;
  
  public function __construct($content, $attributes = array())
  {
    $this->content = $content;
    $this->attributes = $attributes;
  }
   
  public function getSource()
  {
    return '<' . $this->tagname . $this->getAttributeSource() . '>' .
        $this->content . 
        '</' . $this->tagname . '>';
  }
  
  public function getAttributeSource()
  {
    $attributes = '';  
    if (count($this->attributes)) {
      foreach ($this->attributes as $attrnme => $attrval)
      {
        $attributes .= ' ' . $attrnme . '="' . $attrval . '"';
      }
    }
    return $attributes;
  }
  
  public function __toString()
  {
    return $this->getSource();
  }
}

?>

And next the class that creates a list item:


<?php
require_once 'HTMLElement.class.php';

class HTMLListItem extends HTMLElement
{
  protected $tagname = 'li';
  public function __construct($content, $attributes = array())
  {
    parent::__construct($content, $attributes);
  }
}
?>

And finally here is the unordered list class:


<?php
require_once 'HTMLListItem.class.php';

class HTMLUnorderdList extends HTMLElement
{
  protected $tagname = 'ul';
  private $items = array();
  
  public function __construct($content, $attributes = array())
  {
    parent::__construct($content, $attributes);
  }
  
  public function addListItem(HTMLListItem $item)
  {
    $this->items[] = $item;
  }
  
  public function getSource()
  {
    if (count($this->items)) {
      $this->content = '';    
      foreach ($this->items as $item)
      {
        $this->content .= $item->getSource();
      }
    }
    return parent::getSource();
  } 
}

?>

Ok so here’s my confusion, I get the part in the unorderedlistclass that reads:


public function addListItem(HTMLListItem $item)
  {
    $this->items[] = $item;
  }

It’s basically creating a object from the htmllistitem class and adding a number of LI elements to the local HTMLunordered object array.

However I get lost in the get source method. Mostly I have a hard time figuring out scope and object reference since they all share methods and properties of the same name.

Here’s the code that looses me:


public function getSource()
  {
    if (count($this->items)) {
      $this->content = '';    
      foreach ($this->items as $item)
      {
        $this->content .= $item->getSource();
      }
    }
    return parent::getSource();
  } 
}


Ok so it’s overwriting the getSource from the htmlElement I get that. It determines if there are items in the array, if so it resets the unorderdedlist property content to a blank string. It then loops through each item received from the htmllistitem object, saving the content of each item as “item”.

So far so good. Next it places into the content item but processing it via getSource… whoa, what? which getSource? The getSource of the HTMLUnorderedList class? Or the get Source of the parent?

And to add to my confusion, what’s the remainder of parent::getSource below return? I thought we were overriding the parent’s getSource method?

I hope I made that clear enough if anything is missing let me know.

Thanks! :slight_smile:

Neither. It’s calling the getSource of $item. $item may or may not be of the same class - You can’t know until at runtime.

When you override a method, you shadow for the original method, but you can still access it from within the overriding method. That’s the parent keyword.

Just to clarify so I understand (which I think I am).

That’s because $item is an object itself correct? I’d forgotten that part and was thinking too procedurally. It makes sense since $item is an object I can reference it’s methods and properties.

Got it. I think I’ll have to instantiate the classes and see what the output is to properly review but I understand it now in principle.

Thanks!

Yep, exactly. Objects add a level of indirection that isn’t there with procedural code. When you call a function (aka a method) on an object, it may resolve to different actual implementations, depending on which class the object belongs to.

It’s a good idea to play around with it. Don’t worry that you don’t get it right away - It is indeed complicated.

A piece of advise on the subject of inheritance: In general, it’s a common rookie mistake to use inheritance too much. You can almost always get away with using delegation instead of inheritance, and it is usually easier to reason about and leads to more reusable code. So learn about inheritance because other people are going to use it and you have to work with their code, but try to avoid using it your self, as much as possible.

Wow, I’ve been doing something similar which I developed independently for a while, now. I always thought I was crazy, but I’ve never liked the tediousness of writing HTML. Is it common to wrap HTML output like this?

The book seems to disagree with your statement.

Type hinting can also be applied to object interfaces, which we discuss in the section called “Object Interfaces”. The convention in PHP programming is, in fact, to only type hint interfaces, not concrete class implementations. However, we can be forgiven for doing so in our simple OOP introduction.

In my opinion if you’re going to go down this route you might as well extend as you won’t have to worry about type hinting an object (wish we had all more available apart from array).

I’m not sure. Do you mean that the quoted part is in disagreement with my statement, or was it two separate remarks?

I didn’t read the book, but I pretty much stand by my statement. At least as a broad generalisation.

I didn’t read the book, but I pretty much stand by my statement. At least as a broad generalisation.

I’m with kyber avoid inhertence whenever you can, unless it absolutely makes sense or makes life easier.

Cheers,
Alex

+1 on that. Inheritance can be a good thing, if you use it for the right reasons and limit the inheritance chain. If you can easily avoid using it, you should. We’re not the only ones in the industry that see it that way, either.