November 10th, 2009
The Ultimate PHP5 OOP Class - 1
After reading up on PHP5 object orientation I decided to build a little test class that implemented as many of the OOP features offered by PHP5 as I could find.
Think magic functions, constants, cloning, getters and setters, abstract, final, inheritance, serialization and of course var_export support.
If I missed any concepts feel free to drop a note.
<?php
/* The Ultimate PHP5 OOP Class
*
* Implements most of PHP5's OOP functionality in a single demonstration
*/
/* PHP_VERSION_ID is available as of PHP 5.2.7, if our
* version is lower than that, then emulate it. Some of the
* functionality we test here is only available in 5.3.0 and
* higher.
*/
if(!defined('PHP_VERSION_ID'))
{
$version = explode('.',PHP_VERSION);
define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2]));
}
/* Interfaces specify a set of functions that a class MUST
* implement.
*/
interface DemoInterface
{
public function InterfaceTest();
}
/* Abstract classes cannot be instantiated. They define concepts that
* their derived classes must implement.
*/
abstract class DemoAbstract
{
/* Constructor
*
*/
public function __construct()
{
$this->LogEntry(__CLASS__,__METHOD__);
}
/* LogEntry
*
* Reports on each methods entry. Defined in the parent class so that
* we can call it from this constructor as well.
*
* @param string Methodname (from __METHOD__)
*/
public function LogEntry($methodName)
{
echo "{$methodName} Called" . PHP_EOL;
}
abstract public function AbstractTest();
}
/* The DemoPHP5Class is marked as "final". Because of this selfish act
* nobody is able to derive from this work and create their own sub class.
*/
final class DemoPHP5Class extends DemoAbstract implements DemoInterface
{
/* Class Constants cannot be changed */
const VERSION = '1.0';
/* Private members are only available inside this class */
private $_className;
private $_constructorName;
private $_fileName;
private $_instanceCount;
/* Location for overloaded data. */
private $_data = array();
/* As the class is marked final there really is not point in declaring
* any protected (accessible by derived classes) variables but
* for good measure we include one.
*/
protected $_meaningOfLife;
/* The constructor is labeled as final. Because of this derived
* classes are unable to implement their own constructor.
*/
final function __construct()
{
parent::__construct(); // Calling the parent constructor
$this->_className = __CLASS__; // Magic constants
$this->_constructorName = __METHOD__;
$this->_fileName = __FILE__;
$this->_instanceCount = 0; // Original, not a clone
$this->_meaningOfLife = 42; // No need to say more
$this->LogEntry(__CLASS__,__METHOD__);
}
final function __destruct()
{
$this->LogEntry(__METHOD__);
}
/* Implements the Abstract members from the DemoAbstract
* class.
*
* @returns string Notification message
*/
public function AbstractTest()
{
$this->LogEntry(__METHOD__);
}
/* Implements the DemoInteface
*
* @returns string Notification message
*/
public function InterfaceTest()
{
$this->LogEntry(__METHOD__);
}
/* Static functions are not part of an instance
* and can only be called through "classname::staticmethod"
*/
public static function StaticTest($number)
{
/* Note how LogEntry is NOT static, but we can still call it */
/* A warning is generated in STRICT mode */
parent::LogEntry(__CLASS__,__METHOD__);
echo "StaticTest call with ({$number}) succesfull" . PHP_EOL;
}
/* Returns the name of the class
*
* @returns string Name of the class as discovered by the constructor
*/
public function GetClassName()
{
$this->LogEntry(__METHOD__);
return $this->_className;
}
/* Set the meaning of life
*
* @param int Meaning of life
* @thows Exception On invalid input
* @returns this Allow for chaining of calls
*/
public function SetMeaningOfLife($meaningOfLife)
{
$this->LogEntry(__METHOD__);
if (is_int($meaningOfLife))
{
$this->_meaningOfLife = $meaningOfLife;
}
else
{
throw new Exception('Meaning of Life doubted');
}
return $this;
}
/* Returns the meaning of life
*
* @returns int Meaning of life as a core value
*/
public function GetMeaningOfLife()
{
$this->LogEntry(__METHOD__);
return $this->_meaningOfLife;
}
/* Returns the instance count
*
* @returns int Instance count
*/
public function GetInstanceCount()
{
return $this->_instanceCount;
}
/* Magic Methods
*
* These methods have special meaning inside of PHP5.
*/
/* Return a string description of the class
*
* @returns string Whatever the class fancies. Usually a description
*/
public function __toString()
{
$this->LogEntry(__METHOD__);
return '__toString called ; the meaning of life is our domain' . PHP_EOL;
}
/* From PHP 5.3.0 its possible to "invoke" a class, using it like
* a function. Eg. 'DemoPHP5Class(50)'.
*/
public function __invoke($x)
{
$this->LogEntry(__METHOD__);
echo "__invoke was called with {$x}" . PHP_EOL;
}
/* PHP 5 allows the simulation of properties. If a property does not
* exist when attempting to write the __set method is called giving
* you an opportunity to handle the variable yourself.
*/
/* Called when a not-class defined property is set by the user. For
* example "DemoPHP5Class->FooBar = 500". We add an entry to the
* $this->_data table.
*
* @param string A valid name
* @param undefined Any valid PHP value
* @throws Exception Only strings are considered valid names
*/
public function __set($name,$value)
{
$this->LogEntry(__METHOD__);
if (is_string($name)) {
$this->_data[$name] = $value;
return;
}
throw new Exception('Invalid Property name');
}
/* Called when a not class defined property is retrieved. We
* attempt to satisfy the called by checking $this->_array.
*
* @param string A valid name
* @throws Exception When the property is not defined
* @returns undefined Value of the named property
*/
public function __get($name)
{
$this->LogEntry(__METHOD__);
if (is_string($name) &amp;amp;amp;amp;&amp;amp;amp;amp; array_key_exists($name, $this->_data))
return $this->_data[$name];
throw new Exception("Attempted to read undefined proporty");
}
/* This method is called whenever we try to access an inaccessible
* property. We check $this->_data to see if a matching key exists.
*
* @param string A valid string property name
* @returns bool True on finding the property in the array
*/
public function __isset($name)
{
$this->LogEntry(__METHOD__);
if (is_string($name) &amp;amp;amp;amp;&amp;amp;amp;amp; array_key_exists($name, $this->_data))
return true;
}
/* This method is called when unset is called on an inaccessible
* class property. We check $this->_data and try to unset it.
*
* @param string A valid string property name
* @throws Exception The property must exist, mmmm, ok?
*/
public function __unset($name)
{
$this->LogEntry(__METHOD__);
if (is_string($name) &amp;amp;amp;amp;&amp;amp;amp;amp; array_key_exists($name, $this->_data))
unset($this->_data[$name]);
else
throw new Exception("Unsetting non-existant property");
}
/* When a class is serialized (eg. put away into cold storage)
* it is checked for a __sleep method. If it exists it is called
* to allow the class to prepare a list with names of its most important
* variables for safe storage.
*
* @returns array Critical variables that need to be stored
*/
public function __sleep()
{
$this->LogEntry(__METHOD__);
return array("_meaningOfLife","_className","_constructorName",
"_fileName","_data");
}
/* On deserialization the class needs to re-initialise.
* This is a good point to re-establish the database connection and
* any other critical (but not storable) resources.
*/
public function __wakeup()
{
$this->LogEntry(__METHOD__);
}
/* When an object is "cloned" an exact copy is created. This is not
* always desirable (think resource handles that are now shared by two
* objects). The __clone magic function is called on the COPY and
* allows it to initialise any variables it sees fit.
*/
public function __clone()
{
$this->LogEntry(__METHOD__);
// Clones know they are copies because only the original
// object has an _instanceCount value of 0.
$this->_instanceCount++;
}
/* Used by var_export which allows the creation of a PHP evaluable
* string that would re-create this object.
*
* See also: http://www.thoughtlabs.com/2008/02/02/phps-mystical-__set_state-method/
*/
public static function __set_state($an_array)
{
echo "static::__set_state called" . PHP_EOL;
// Create a new class, set its parameters and
// exit.
$tmp = new DemoPHP5Class();
foreach($an_array as $name => $value)
$tmp->$name = $value;
return $tmp;
}
}
/* Enable strict warnings */
error_reporting(E_ALL | E_STRICT);
/* Test Code */
$demo = new DemoPHP5Class();
/* Example of a class constant */
echo "Class version: " . DemoPHP5Class::VERSION . PHP_EOL;
/* Test #1: Abstract method in parent class, simply call it*/
$demo->AbstractTest();
/* Test #2: Call the Interface member */
$demo->InterfaceTest();
/* Test #3: Call of a static member */
DemoPHP5Class::StaticTest(20);
/* Test #4: Call a regular get method */
$demo->GetClassName();
/* Test #5: Call a regular set method with valid param */
$demo->SetMeaningOfLife(43);
/* Test #6: Call a regular set method with INVALID param */
try {
$demo->SetMeaningOfLife(42.5);
}
catch (Exception $e) {
echo "Exception Caught OK" . PHP_EOL;
}
/* Test #7: Try to print the object , calling __tostring() */
echo $demo;
/* Test #8: Invoke the object , bit like magic really */
/* only available from PHP 5.3.0 up */
if (PHP_VERSION_ID > 50300)
$demo('abacradaba');
/* Test #9: Set an non-existant member of the object, causing
* a call to __set, which will emulate/store the value
*/
$demo->non_existing_member = 256;
/* Test #10: Confirm that the new member has been set */
if (isset($demo->non_existing_member))
echo 'Confirmed existance of \$demo->non_existing_member' . PHP_EOL;
else
echo 'Failed to verify setting of \$demo->non_existing_member' . PHP_EOL;
/* Test #11: Retrieve the non-existing member, causing a call
* to __get, which will attempt to retrieve it from the internal
* storage array.
*/
if ($demo->non_existing_member == 256)
echo "Retrieval of \$demo->non_existing_member succesfull" . PHP_EOL;
else
echo "Retrieval of \$demo->non_existing_member FAILED" . PHP_EOL;
/* Test #12: Remove the member, and test if this was succesfull */
unset($demo->non_existing_member);
if (!isset($demo->non_existing_member))
echo 'Confirmed removal of \$demo->non_existing_member' . PHP_EOL;
else
echo 'Failed to remove \$demo->non_existing_member' . PHP_EOL;
/* Test #13: Serialization , put the object to sleep and turn it
* into a form that we can store away savely.
*/
$serialized_data = serialize( $demo );
$demo2 = unserialize($serialized_data);
/* Test #14: Cloning. After cloning the object the clone should
* have a higher instance value than the original object because
* of the additional changes made by __clone
*/
$demo3 = clone $demo;
if ($demo3->GetInstanceCount() > $demo->GetInstanceCount())
echo "Succesfull cloning; instance count has been increased. " . PHP_EOL;
else
echo "Failed to clone; instance count is the same. " . PHP_EOL;
/* Test 15: Var_Export must produce valid PHP code that can be
* run through 'eval'. It has trouble with objects , so it needs
* a little help from the __set_state magic function
*/
eval('$demo4 = ' . var_export($demo, true) . ';');
/* Remember, we modified the meaning of life to 43 in $demo */
if ($demo4->GetMeaningOfLife()==$demo->GetMeaningOfLife())
echo "Succesfull re-creation of the \$demo class" . PHP_EOL;
else
echo "Failed to recreate. The meaning of life was lost. " . PHP_EOL;
/* EOF */
?>



July 22nd, 2010 at 12:57 am
Buy:Viagra Super Active+.Levitra.Cialis.Viagra Soft Tabs.VPXL.Propecia.Cialis Professional.Tramadol.Maxaman.Viagra.Viagra Professional.Super Active ED Pack.Soma.Cialis Super Active+.Cialis Soft Tabs.Viagra Super Force.Zithromax….