Check It Yeah, It's On

8Mar/110

PHP: The Generic Standard Class

I've been using a generic class as the foundation for simple objects as I find it makes sense for me to overload object properties (PHP5), so that I don't have to update object class variables and add them as parameters to constructors. This works for me particularly well when representing database records as objects. Perhaps it will make sense for you too.

Let's take a look at a simple classic object class definition:

<?php
class Apple {
  
  public $type;
  public $color;
  public $sweet;
  
  public function Apple($type, $color, $sweet) {
    $this->type = $type;
    $this->color = $color;
    $this->sweet = $sweet;
  }

}
?>

As you can see, this approach will likely create a lot of tedious code and is subject to needing to update the class member variables and constructor when a new property is added or an existing one is modified (or deleted). Developers would have to know the parameter order of the constructor so that they will pass in the data correctly. This is error prone and is not a best practice.

Now let's look at the generic class. Basically, I have a generic object class upon which other classes inherit from. This generic object exhibits an infoArray protected variable that serves as the storage mechanism for the properties of objects that inherit from it. Then, the use of the magic __get() and __set() are used to access the properties. Let's take a look...

The Generic Object

<?php
/**
 * The Generic Object
 * @source http://checkit.alexng.net/2011/03/08/php-the-generic-standard-class/
 **/
class GenericObject {

  protected $_infoArray = array();
  
  /**
   * Constructor takes in an optional array containing initial key-value data
   *   for the object.
   * The information is stored with the keys lower-cased.
   **/
  public function __construct($infoArray = array()) {
    foreach ($infoArray as $key => $value) {
      $this->_infoArray[strtolower($key)] = $value;
    }
  }
  
  /**
   * Magic getter looks up the property and if it exists in the infoArray, 
   *   returns it
   **/
  public function __get($property) {
    if (isset($this->_infoArray[strtolower($property)])) {
      return $this->_infoArray[strtolower($property)];
    } else {
      return null;
    }
  }
  
  /**
   * Magic setter sets (or updates) a property into the the object
   **/
  public function __set($property, $value) {
    $this->_infoArray[strtolower($property)] = $value;
  }

  /**
   * Magic isset to check the _infoArray as well as the member properties
   */
  public function __isset($property) {
      if (isset($this->_infoArray[strtolower($property)])) {
          return true;
      } else if (isset($this->$property)) {
          return true;
      } else {
          return false;
      }
  }

}
?>

First, let's see how it is easily extended and used.

<?php
class Apple extends GenericObject {
  
  public function __construct($infoArray = array()) {
    // call parent constructor
    parent::__construct($infoArray);
  }

}

// instantiate new Apple object and set some properties
$myApple = new Apple();
$myApple->type = 'Red Delicious';
$myApple->color = 'Red';
$myApple->sweet = true;

// perhaps you queried a bunch of Apple records from the database 
// (using an associative fetch method)
$appleObjs = array();
$appleRecords = $dbHandler->query("Select * From Apples");

foreach ($appleRecords as $appleRecord) {
  // create new Apple objects by passing the associative data array 
  // to the constructor
  $appleObj = new Apple($appleRecord);
  
  // we can start access the object's properties using object notation
  $appleObjs[$appleObj->Id] = $appleObj;
}
?>

Inheriting from such a GenericObject class doesn't limit you from customizing your classes to have its own member variables and functionality.

<?php
class Apple extends GenericObject {
  
  public function __construct($infoArray = array()) {
    // call parent constructor
    parent::__construct($infoArray);
  }
  
  public function eat() {
    // if this fruit is sweet, then proceed to eat it
    // here, the "sweet" property can be accessed as if it were a 
    // local member variable
    if ($this->sweet) {
      // before you eat an apple, wash it!
      $this->wash();
    }
  }

  public function wash() {
    // wash the fruit
  }

}

class Banana extends GenericObject {

  public static $ripeColor = 'yellow';

  // this class member variable is specific for the Banana class
  public $monkeyObj;
  
  public function __construct($infoArray = array()) {
    // call parent constructor
    parent::__construct($infoArray);
  }
  
  public function eat() {
    // if this fruit is of the ripe color, then proceed to eat it
    if (strtolower($this->color) == self::$ripeColor) {
      // before you eat a banana, peel it!
      $this->peel();
      
      // if a monkey is set, feed it to the monkey!
      if (isset($this->monkeyObj)) {
        $this->monkeyObj->feed($this);
    }
  }

  public function peel() {
    // peel the fruit
  }

}
?>

Considerations:

  • This may not be suitable for storing more complex or large data types.
  • You can not assign references to properties to the overloaded GenericObject, therefore references should be assigned to class member variables instead. (for example, the $monkeyObj in the Banana class above)

Hope this helps!

Filed under: Code Leave a comment
Comments (0) Trackbacks (0)

No comments yet.


Leave a comment

No trackbacks yet.