Object-Oriented PHP Part 3: Taking Relationships Further

Posted Wednesday 23rd August, 2006

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: Relationships” we looked at linking our objects and classes together. In part 3 I’m going to add to those relationships by looking at visibility, polymorphism and the scope resolution operator.

Access Modifiers (aka Visibility)

Access modifiers allow you to alter the visibility of any class member. They have long been an important part of OOP and have finally been implemented in PHP as of version 5 - in fact they replace the deprecated var declaration. However, if you’re still a member of the PHP4 posse, don’t fret; there’s a little known suggestion in the PHP manual that can help us to imply private. I’ll cover that in a second.

To define the visibility of a class member we need to prefix it with one of the keywords public, private or protected. Public declared members can be accessed everywhere. Protected limits access to inherited and parent classes (and to the class that defines the item). Private limits visibility only to the class that defines the item.

To illustrate this in action, I’m going to poach a couple of examples directly from the PHP manual. Firstly, let’s take a look at access modifiers applied to class attributes:

class myClass
{
  public $public = 'Public';
  protected $protected = 'Protected';
  private $private = 'Private';

  function printHello()
  {
    echo $this->public;
    echo $this->protected;
    echo $this->private;
  }
}

$obj = new myClass();
echo $obj->public; // Works
echo $obj->protected; // Fatal Error
echo $obj->private; // Fatal Error

// Shows Public, Protected and Private
$obj->printHello();

class myClass2 extends myClass
{
  // We can redeclare the public and
  // protected method, but not private
  protected $protected = 'Protected2';

  function printHello()
  {
    echo $this->public;
    echo $this->protected;
    echo $this->private;
  }
}

$obj2 = new myClass2();
echo $obj->public; // Works
echo $obj2->private; // Undefined
echo $obj2->protected; // Fatal Error

// Shows Public, Protected2, not Private
$obj2->printHello();

Now let’s take a look at access modifiers applied to methods:

class myClass
{
  // Contructors must be public
  public function __construct() { }

  // Declare a public method
  public function myPublic() { }

  // Declare a protected method
  protected function myProtected() { }

  // Declare a private method
  private function myPrivate() { }

  // This is public
  function foo()
  {
    $this->myPublic();
    $this->myProtected();
    $this->myPrivate();
  }
}

$myclass = new myClass;
$myclass->myPublic(); // Works
$myclass->myProtected(); // Fatal Error
$myclass->myPrivate(); // Fatal Error

// Public, Protected and Private work
$myclass->foo();

class myClass2 extends myClass
{
  // This is public
  function foo2()
  {
    $this->myPublic();
    $this->myProtected();
    $this->myPrivate(); // Fatal Error
  }
}

$myclass2 = new myClass2;
$myclass2->myPublic(); // Works

// Public and Protected work, not Private
$myclass2->foo2();

For users of PHP4 there are, unfortunately, no access modifiers available. However, under ”Object Aggregation/Composition Functions” in the PHP manual the following is noted:

Note that for the purposes of object aggregation, private elements of a class/object begin with an underscore character (“_”), even though there is not real distinction between public and private class/object elements in PHP.

With this in mind, I usually declare private members with an underscore character regardless of whether it’s for aggregation or not.

Polymorphism

An object-oriented programming language must support polymorphism; meaning different classes can have different behaviours for the same attribute or method. This can best be illustrated with the same example I used in my ”Object-Oriented Concepts” post:

class formElement
{
  var $id;
  var $name;
  var $class;

  function getHtml()
  {
    // returns generic form element HTML
    // using heredoc to save on escapes
    return <<<EOD

<input id="{$this->id}" name="{$this->name}" class="{$this->class}"/>

EOD;
  }
}

class textarea extends formElement
{
  var $cols;
  var $rows;

  function getHtml()
  {
    // returns generic form element HTML
    // using heredoc to save on escapes
    return <<<EOD

<textarea id="{$this->id}" name="{$this->name}" class="{$this->class}" cols="{$this->cols}" rows="{$this->rows}"></textarea>

EOD;
  }
}

As you can see in the example, both classes have the method “getHtml” but the “textarea” class is a subclass of the “formElement” class. This results in the “getHtml” method being overloaded. Overloading is a good example of polymorphism in action - an object-oriented language must support polymorphism in order to know which “getHtml” method applies to which object.

Polymorphism, at its most basic, describes the fact that a given function may have different specifications, depending on the object to which it is applied.

Scope Resolution Operator

The Scope Resolution Operator, “::” (double-colon or Paamayim Nekudotayim - that’s double-colon in Hebrew - don’t ask me; ask the Zend team!), allows us to access a method or an attribute from an uninstantiated class. This is incredibally useful if you wish to create a class of functions but don’t want to instantiate an object of that class. Here’s an example class:

class validation
{
  function isValidEmail($str_data)
  {
    return (!eregi("^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$", $Email)) ? false : true;
  }
  
  function isLessThan($str_data, $str_compare)
  {
    return ($str_data < $str_compare) ? true : false;
  }
  
  function isGreaterThan($str_data, $str_compare)
  {
    return ($str_data > $str_compare) ? true : false;
  }
}

Now if we want to check an email address, we can access the isValidEmail method, without instantiating the class, like so:

$bln_valid = validation::isValidEmail('big.cheese@dairy.com');

The PHP documentation takes this one step further by illustrating how the scope resolution operator can benefit polymorphism:

class A {
   function example() {
       echo "I am the original function A::example().
\n"; } } class B extends A { function example() { echo "I am the redefined function B::example().
\n"; A::example(); } } // there is no object of class A. // this will print // I am the original function A::example().
A::example(); // create an object of class B. $b = new B; // this will print // I am the redefined function B::example().
// I am the original function A::example().
$b->example();

In this example you can see that class B overloads the example() method. Since this is the case, the original example() method is “shadowed” and is therefore no longer available - that is, however, unless we use the scope resolution operator to access it. This basically allows us to extend the functionality of an overloaded method in a child class. Isn’t that just incredibally useful?

parent

PHP takes this particular use of the scope resolution operator one step further by allowing us to reference our parent class without having to use its literal name. To do this, we use the parent special name like so:

class A {
   function example() {
       echo "I am A::example() and provide basic functionality.
\n"; } } class B extends A { function example() { echo "I am B::example() and provide additional functionality.
\n"; parent::example(); } } $b = new B; // This will call B::example(), which will in turn call A::example(). $b->example();

By using this method you are avoiding using the name of your parent class in too many places - thus you will only need to change the class name in the extends clause.

PHP5 and the Scope Resolution Operator

In PHP5 any methods or attributes you want to access with the scope resolution operator must be declared as static. The static declaration must appear after the visibility declaration. However, if no visibility declaration is included (PHP4 compatibility), the member will be treated as public:

class monkey
{
  public static $int_bananas = 6;

  public static function addBananas($int_bananas)
  {
    // Add the new bananas to our
    // int_bananas attribute. I'll explain the self
    // keyword a little further below...
    self::$int_bananas += (int) $int_bananas;
  }
}

Any attributes you declare as static are subsequently unavailable within instantiated objects. Static methods, however, are available but, since they are callable without an object instance being created, cannot use the $this pseudo variable.

To reference a static member from within the same class, we can use the self special name; as seen in the previous example.

Static method calls are resolved at compile time. This means that, when we’re using an explicit class name, the method is already identified completely and, subsequently, no inheritance rules apply. This is also the case when using the self special name.

Static properties cannot be accessed through the object using the -> namespace separator.

Calling non-static methods statically generates an E_STRICT level warning.

Summary

As you can see from the above examples, polymorphism and visibility extend the relationships between your objects and classes ten-fold. In fact, you’ll find them invaluable once you start taking them into account during the code planning phase of your next big project. The scope resolution operator will allow you to take polymorphism once step further or to simplify relationships that you may be over-complicating (I’ve done that more than once, believe me).

Well that’s pretty much it for all you PHP4 developers out there - unfortunately the complex functionality I’m going to look at in the next post is limited to the updated object model in PHP5. However, I’d recommend you read it simply to improve your understanding of object-oriented systems. Of course, it’ll also help you make the decision to upgrade (or to keep pestering your boss to upgrade).

Click here to continue on to “Part 4: PHP5’s Improved Features”…

Included in: Development, PHP, Web

Categories:

  1. Accessibility
  2. Agile
  3. Ajax
  4. Apache
  5. API
  6. Architecture
  7. Books
  8. Browsers
  9. CMS
  10. CouchDB
  11. CSS
  12. Design
  13. Development
  14. Django
  15. Email
  16. Events
  17. Gaming
  18. Grammar
  19. Hardware
  20. HTML
  21. HTTP
  22. Humour
  23. Idea
  24. Information Architecture
  25. JavaScript
  26. jQuery
  27. Lean
  28. Life
  29. Linux
  30. Literature
  31. Mac OS X
  32. Management
  33. Meme
  34. Microformats
  35. Monday
  36. MySQL
  37. Networking
  38. News
  39. Personal
  40. Photoshop
  41. PHP
  42. Process
  43. Python
  44. Reference
  45. REST
  46. Science
  47. SEO
  48. Server
  49. Site
  50. Sitepimp
  51. Social
  52. Spelling
  53. Syndication
  54. Testing
  55. The Future
  56. Thoughts
  57. Tools
  58. Tutorial
  59. Tutorials
  60. Typography
  61. UI
  62. UNIX
  63. Virtualisation
  64. Web
  65. Web Standards
  66. Widgets
  67. Wii
  68. Writing
  69. Xbox
  70. XHTML