Extending PHP method/property modifiers
Posted: Thu Oct 16, 2008 2:57 am
The idea comes from viewtopic.php?f=19&t=89300 discussion.
It's an object "wrapper" class ("decorator"?).
Any comments on its design, implementation and usability are welcome 
EDIT: Some bugs fixed
It's an object "wrapper" class ("decorator"?).
Code: Select all
define('PROPERTY_WRITABLE', true);
define('PROPERTY_NOT_WRITABLE', false);
define('PROPERTY_READABLE', true);
define('PROPERTY_NOT_READABLE', false);
define('PROPERTY_NULLABLE', true);
define('PROPERTY_NOT_NULLABLE', false);
define('METHOD_EXECUTABLE', true);
define('METHOD_NOT_EXECUTABLE', false);
define('MODE_STRICT', true);
define('MODE_NOT_STRICT', false);
interface IACL
{
function isPropertyWritable($class, $property);
function isPropertyReadable($class, $property);
function isPropertyNullable($class, $property);
function isMethodExecutable($class, $method);
}
class NullObject
{
public function __call($method, $args)
{
return new NullObject();
}
public function __get($var)
{
return new NullObject();
}
public function __set($var, $value)
{
}
public function __toString()
{
return '';
}
}
class ProtectedPropertyException extends Exception
{
private $_objectName = null;
private $_propertyName = null;
public function __construct($message, $objectName, $propertyName)
{
parent::__construct($message, 0);
$this->_objectName = $objectName;
$this->_propertyName = $propertyName;
}
public function __toString()
{
return ": [{$this->_objectName}::{$this->_propertyName}]: {$this->message}\n";
}
}
class ProtectedMethodException extends Exception
{
private $_objectName = null;
private $_methodName = null;
public function __construct($message, $objectName, $methodName)
{
parent::__construct($message, 0);
$this->_objectName = $objectName;
$this->_methodName = $methodName;
}
public function __toString()
{
return ": [{$this->_objectName}::{$this->_methodName}]: {$this->message}\n";
}
}
class ProtectedProperty
{
private $_isReadable = false;
private $_isWritable = false;
private $_isNullable = false;
private $_strictMode = false;
private $_object = null;
private $_propertyName = null;
public function __toString()
{
return $this->get()->__toString();
}
public function __construct(&$object,
$propertyName,
$value = null,
$isReadable = PROPERTY_READABLE,
$isWritable = PROPERTY_WRITABLE,
$isNullable = PROPERTY_NULLABLE,
$strictMode = MODE_NOT_STRICT)
{
$this->_object =& $object;
$this->_propertyName = $propertyName;
$this->_isReadable = $isReadable;
$this->_isWritable = $isWritable;
$this->_isNullable = $isNullable;
$this->_strictMode = $strictMode;
if ($this->_isNullable !== PROPERTY_NULLABLE && $value === null)
{
if ($this->_strictMode == MODE_STRICT)
throw new ProtectedPropertyException('Property is not nullable.', get_class($this->_object), $this->_propertyName);
else
$this->_object->{$this->_propertyName} = $value;
}
else
$this->_object->{$this->_propertyName} = $value;
}
public function get()
{
if ($this->_isReadable !== PROPERTY_READABLE)
{
if ($this->_strictMode === MODE_STRICT)
throw new ProtectedPropertyException('Property is not readable.', get_class($this->_object), $this->_propertyName);
else
return new NullObject();
}
return $this->_object->{$this->_propertyName};
}
public function set($object)
{
if ($this->_isWritable !== PROPERTY_WRITABLE)
{
if ($this->_strictMode === MODE_STRICT)
throw new ProtectedPropertyException('Property is not writable.', get_class($this->_object), $this->_propertyName);
else
return false;
}
if ($this->_isNullable !== PROPERTY_NULLABLE && $object == null)
{
if ($this->_strictMode === MODE_STRICT)
throw new ProtectedPropertyException('Property is not nullable.', get_class($this->_object), $this->_propertyName);
else
return false;
}
$this->_object->{$this->_propertyName} = $object;
return true;
}
public function isReadable()
{
return $this->_isReadable === PROPERTY_READABLE;
}
public function isWritable()
{
return $this->_isWritable === PROPERTY_WRITABLE;
}
public function isNullable()
{
return $this->_isWritable === PROPERTY_NULLABLE;
}
}
class ProtectedMethod
{
private $_isExecutable = false;
private $_strictMode = false;
private $_object = null;
private $_methodName = null;
public function __construct(&$object,
$methodName,
$isExecutable = METHOD_EXECUTABLE,
$strictMode = MODE_NOT_STRICT)
{
$this->_object =& $object;
$this->_methodName = $methodName;
$this->_isExecutable = $isExecutable;
$this->_strictMode = $strictMode;
}
public function execute($args)
{
if ($this->_isExecutable !== METHOD_EXECUTABLE)
if ($this->_strictMode === MODE_STRICT)
throw new ProtectedMethodException(' Method is not executable.', get_class($this->_object), $this->_methodName);
else
return new NullObject();
$method = new ReflectionMethod(get_class($this->_object), $this->_methodName);
return $method->invoke($this->_object, $args);
}
public function isExecutable()
{
return $this->_isExecutable;
}
}
class ProtectedObject
{
public $_object = null;
private $_class = null;
private $_strictMode = MODE_NOT_STRICT;
private $_properties = Array();
private $_methods = Array();
public function __call($method, $args)
{
return $this->_methods[$method]->execute($args);
}
public function __get($var)
{
return $this->_properties[$var]->get();
}
public function __set($var, $value)
{
$this->_properties[$var]->set($value);
}
public function __construct($class_name, $ACL, $strictMode = MODE_NOT_STRICT)
{
$this->_class = new ReflectionClass($class_name);
$this->_object = $this->_class->newInstance();
$this->_strictMode = $strictMode;
foreach ($this->_class->getProperties() as $property)
{
if ($property->isPublic())
{
$this->_properties[$property->getName()] = new ProtectedProperty($this->_object, $property->getName(), $property->getValue($this->_object),
$ACL->isPropertyReadable($this->_class->getName(), $property->getName()),
$ACL->isPropertyWritable($this->_class->getName(), $property->getName()),
$ACL->isPropertyNullable($this->_class->getName(), $property->getName()),
$this->_strictMode);
}
}
foreach ($this->_class->getMethods() as $method)
{
if ($method->isPublic())
{
$this->_methods[$method->getName()] = new ProtectedMethod($this->_object, $method->getName(),
$ACL->isMethodExecutable($this->_class->getName(), $method->getName()),
$this->_strictMode
);
}
}
}
}
class ProtectedObjectProvider
{
private static $_instance = null;
private $_strictMode = MODE_NOT_STRICT;
private $_ACL = null;
public function setACL($ACL)
{
if ($ACL == null)
throw new Exception('ACL object can not be NULL.');
$this->_ACL = $ACL;
$class = new ReflectionClass(get_class($this->_ACL));
if (!$class->implementsInterface('IACL'))
{
$this->_ACL = null;
throw new Exception('ACL object must implement IACL interface.');
}
}
public function setMode($strictMode)
{
$this->_strictMode = $strictMode;
}
public static function getInstance()
{
if(!isset(self::$instance))
{
$object = __CLASS__;
self::$_instance = new $object;
}
return self::$_instance;
}
public function construct($class, $strictMode = null)
{
if (is_string($class))
return new ProtectedObject($class, $this->_ACL, $strictMode == null ? $this->_strictMode : $strictMode);
if (is_object($class))
return new ProtectedObject(get_class($class), $this->_ACL, $strictMode == null ? $this->_strictMode : $strictMode);
throw new Exception('Could not construct protected object from data <'.var_export($class, true).'>');
}
}EDIT: Some bugs fixed