http://www.developer.com/

Back to article

The Object Oriented Improvements of PHP 5


January 22, 2004

While PHP 4 was the first version to offer Object-Oriented Programming (OOP) capabilities, many considered the feature to be poorly designed and more an afterthought than anything else. PHP 5 resolves many of the version 4 OOP inconsistencies, not to mention greatly enhances the language's object-oriented capabilities. While you're still free to program using a procedural methodology, those of you with object oriented backgrounds will certainly feel much more at home creating fully object-driven PHP applications. In this article I'll introduce many of these new and improved features. In addition, for those of you somewhat new to the subject, this article will also offer a bit of instruction regarding those concepts key to truly understanding OOP.

Private and Protected Members

Object-oriented programming is largely about the ability to hide what's not important to the user and to highlight what is. One of the primary means for doing so is through defining scope for class properties. PHP 4 offered no standardized means for specifying the variety of property scopes typically offered by full-featured OO languages. PHP 5 remedies this in part through support for two common scopes:

Private: Private members are accessible within the class in which they are created. Note that they are not accessible by any subclass!

Protected: Protected members are accessible within the class in which they are created, as well as by any subclass.

In the following example, we'll create a class which contains both a private and a protected member.

<?php
   class boxer {
      // Not available to streetfighter class
      private $age;

      // Available to streetfighter class
      protected $wins;

      function __construct() {
         $this->age = 15;
      }

      // Return boxer's wins
      function getWins() {
         return $this->wins;
      }

      // Return boxer's age
      function getAge() {
         return $this->age;
      }
 
   }

   // Extend the boxer class
   class streetfighter extends boxer {

      protected $teeth;

      function __construct() {
         // This works
         $this->wins = 12;
         // This does not
         $this->age = 30;
      }

   }

   $sf = new streetfighter();
   echo $sf->getWins();

?>

One oddity regarding misuse of private members is the lack of any error message. Rather, attempts to set or get them will simply be ignored.

Note that any member not assigned a scope of private or protected defaults to the public scope, meaning that it's directly accessible from the caller! Because such practice generally goes against the goals of OOP, its advised that you designate such members as private and manage them through getters and setters (otherwise known as properties). Unfortunately, a unified getter/setter methodology has yet to be worked into the language, leaving you to your own devices to come up with a method-based solution.

Final, Private, Protected and Static Methods

Like members, it's often useful to set various levels of scope for methods. PHP 5 defines four previously unavailable levels: final, private, protected and static. I'll discuss each in this section.

Final Methods

While methods which have been declared as final can be used within subclasses, but cannot be overridden. Let's consider an example:

<?php
class logger {

   final function getCredits() {
      return "Copyright 2004 you know who.";
   }

}

class myLogger extends logger {

   function getCredits() {
      return "Give credit where credit is due!";
   }

}

$mylog = new myLogger();
$mylog->getCredits();

?>

Executing this example results in the following fatal error:

Fatal error: Cannot override final method logger::getCredits() in /www/htdocs/developer/finalmethod.php

Private Methods

Like private members, private methods are only accessible within the class in which they are declared. An example follows:

<?php
   class boxer {

      protected $bouts;
      protected $wins;
      protected $losses;

      private function winPercentage() {
         return ($wins / $bouts) * 100;
      }

   }

   class streetfighter extends boxer {

      function __construct() {
         $this->bouts = 10;
         $this->wins = 5;
      }

      function stats() {
         return $this->winPercentage();
      }

   }
?>

Like private members, attempts to access private methods from outside of the originating class will simply result in no action, rather than any error message.

Protected Methods

Like protected members, protected methods are accessible both within the class in which they are declared, as well as by all subclasses.

Static Methods

Most applications make use of various functions of use throughout the entire application. Because such functions aren't necessarily related to any particular object, they're often placed in a general utility class. However, such a strategy is followed because it's good OO programming practice, and not because we want to invoke a "utility" object (although you could). Rather, we just want to call the method as necessary, while still managing to encapsulate it in some sort of class. Class methods that can be called without first instantiating an object are known as static. Let's consider an example:

<?php
   class logger {

      public static function getCredits() {
         return "Copyright 2004 you know who.";
      }

   }

   echo logger::getCredits();
?>

Note that you cannot reference the $this keyword within static methods, because there was no prior object instantiation.

Abstract Methods

An abstract method is intended as a cue for any developer interested in implementing a specific class. While an abstract method doesn't define the method's implementation, it acts as a guide to the developer, informing him that he will have correctly implemented the class provided that it and all other abstract methods found in the class are implemented. An example is provided in the next section.

Abstract Classes

The object-oriented development strategy is particularly appealing because we easily relate to it. Humans are quite adept at thinking in terms of hierarchical structures. For example, its obvious to all of us that compact discs, MP3s and MPEGs all fall under the category of media. However, media is solely an abstract concept which we use to generalize a group of items that share a set of common characteristics which we collectively define as media.

Its often convenient for programmers to embody these shared characteristics and behaviors in an abstract class. Class abstraction affords the programmer the convenience of wielding control over a core set of properties and behaviors that he knows will be shared by a number of similar, yet unique classes. While an abstract class is never directly instantiated, any class which inherits it will also possess its properties and behaviors.

PHP 5 supports class abstraction. As an example, let's create the media class. This class holds one property, $copyrightInfo, one public function, setCopyrightInfo(), and one abstract function, play(). While this class in useless by itself, it can readily be inherited by any of our specific media types.

<?php
   abstract class media {

      private $copyrightInfo;

      abstract function play();

      function setCopyrightInfo($info) {
         $this->copyrightInfo = $info;
      }

   }

   class cd inherits media {
      function play() {
          echo "The CD has started playing!";
      }
   }

   $cd = new cd();
   $cd->setCopyrightInfo("Copyrighted by who else? Me!");
   $cd->play();

?>

Interfaces

Interfaces bring an additional level of both convenience and discipline to object-oriented programming by providing a means for blueprinting an application component, defining the methods which together make that component what it is. A class is said to implement an interface provided that it satisfies all of its defining characteristics.

To the great pleasure of many developers, PHP 5 offers interfaces! Let's consider an example:

<?php

   interface fighter {
      function power();
      function weight();
   }

   class boxer implements fighter {

      private $power;
      private $weight;

      function power() {
         return $this->power;
      }

      function weight() {
         return $this->weight;
      }

}
?>

A class can also simultaneously implement multiple interfaces. Let's consider another example:

<?php

   interface fighter {
      function power();
      function weight();
   }

   interface kickboxer {
      function kick();
   }

   class warrior implements fighter, kickboxer {

      private $power;
      private $weight;

      function power() {
         return $this->power;
      }

      function weight() {
         return $this->weight;
      }

      function kick() {
         return "kick!";
      }

}

?>

Failing to implement all of the functions as defined within an interface results in an error like so:

Fatal error: Class warrior contains 1 abstract methods and must therefore be declared abstract (kickboxer::headbutt) in /www/htdocs/developer/interface.php on line 30

Unified Constructors

While PHP 4 offered constructors, the syntax required that the constructor method be the same name as the class. PHP 5 resolves this inconvenience by providing a standardized constructor syntax, using the name __construct(). For example:

<?php

   class blog {

      private $title;

      function __construct() {
         $this->title = "Just another rant";
         echo "New blog object created!;
      }
   }

   $blogobj = new blog();
?>

Executing this code results in the assignment of the $title member, and output of the following message:

New blog object created!

Unified Destructors

Good programming practice dictates that you properly clean up all resources once you're done with them. This may involve closing a database connection, writing information to a log file, or anything else you deem appropriate. Prior to PHP 5, you were forced to devise your own methodology for dealing with such matters once work with an object is complete, resulting in inconsistent coding practice. PHP 5 resolves this with the introduction of unified destructors.

Like the new constructor syntax, destructors are called like so: __destruct(). Consider this example:

<?php
   class blog {

      private $title;

      function __construct() {
         $this->title = Just another rant...;
      }

      function __destruct() {
         $fh = fopen(bloglog.txt, a+);
         $date = date("Y-m-d at h:i:s");
         fwrite($fh, "New blog entry: $this->title. Created on $date\n");
         fclose($fh);
      }
   }

   $blogobj = new blog();
?>

Executing this script will result in a new object of type blog being created, and its $title property assigned the value "Just another rant...". Next, a handle to the file "bloglog.txt" will be opened, and an entry similar to the following will be added to it:

New blog entry: Just another rant.... Created on 2004-01-20.

Once appended, the filehandle is closed.

Class Constants

It's often useful to define constants on a per-class basis. This is now possible in PHP 5. For example, suppose that you wanted to define a standard thumbnail height and width for an image gallery class:

<?php

   class imagegallery {

      const THUMB_HEIGHT = 120;
      const THUMB_WIDTH = 120;

      private $height;
      private $width;

      function create_thumbnail($height="",$width="") {
         if (!isset($height)) $this->height = THUMB_HEIGHT;
         if (!isset($width)) $this->width = THUMB_WIDTH;
      }
   }

   echo "Default thumbnail height: ".imagegallery::THUMB_HEIGHT;
?>

These constants are only relevant to the imagegallery class, allowing you to reuse the terms "HEIGHT" and "WIDTH" within other classes or scripts comprising the application. Furthermore, as I demonstrate at the conclusion of the above example, you can reference the class constants from outside of the class by prefacing their name with the class name (not a class instance!) and double-colons.

Conclusion

PHP 5 offers major enhancements to its object oriented feature set, which certainly only adds to its allure as a powerful Web scripting language. However, these improvements are only the tip of the iceberg in regards to what PHP 5 has to offer. In the next installment of this two-article series, I'll round out the many features that users can expect from the forthcoming release.

I welcome questions and comments! E-mail me at jason@wjgilmore.com. I'd also like to hear more about your experiences experimenting with PHP 5!

About the Author

W. Jason Gilmore (http://www.wjgilmore.com/ ) is an Internet application developer for the Fisher College of Business. He's the author of the upcoming book, PHP 5 and MySQL: Novice to Pro, due out by Apress in 2004. His work has been featured within many of the computing industry's leading publications, including Linux Magazine, O'Reillynet, Devshed, Zend.com, and Webreview. Jason is also the author of A Programmer's Introduction to PHP 4.0 (453pp., Apress). Along with colleague Jon Shoberg, he's co-author of "Out in the Open," a monthly column published within Linux magazine.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date