<?php
class PropertyHandler
{
const GET_REGEX = "^get([aA-zZ0-9_]+)$";
const SET_REGEX = "^set([aA-zZ0-9_]+)$";
private $props;
function __construct()
{
$props = array();
}
public function __call($method, $arguments)
{
if($this->isGetMethod($method))
{
return $this->execGetMethod($method);
}elseif($this->isSetMethod($method))
{
return $this->execSetMethod($method, $arguments);
}else
{
throw new Exception("Unhandled method $method");
}
}
private function isGetMethod($method)
{
return $this->isTypeMethod($method, self::GET_REGEX);
}
private function isSetMethod($method)
{
return $this->isTypeMethod($method, self::SET_REGEX);
}
private function isTypeMethod($method, $regex)
{
return (preg_match("/$regex/", $method) > 0);
}
private function execGetMethod($method)
{
$matches = array();
preg_match("/" . self::GET_REGEX . "/", $method, $matches);
if(count($matches) > 0)
{
return $this->getProp($matches[1]);
}
}
private function execSetMethod($method, $arguments)
{
if(count($arguments) > 1)
{
throw new Exception("Set methods are not allowed to take more than one argument");
}
$matches = array();
preg_match("/" . self::SET_REGEX . "/", $method, $matches);
if(count($matches) > 0)
{
$this->setProp($matches[1], $arguments[0]);
}
}
public function setProp($propName, $propVal)
{
if(!$propName)
{
throw new Exception("Invalid empty property name");
}
$this->props[$propName] = $propVal;
}
public function getProp($propName)
{
if(isset($this->props[$propName]))
{
return $this->props[$propName];
}
}
}
/**
* Very simple example
*/
class PropTest extends PropertyHandler
{
public function __construct()
{
parent::__construct();
}
}
$props = new PropTest();
//Notice that neither PropTest nor PropertyHandler actually have setFirstName or getFirstNames
//These are setting and retrieving associative the associative array value with key "FirstName"
$props->setFirstName('Mark');
echo $props->getFirstName();
?>
Refactorings
No refactoring yet !
exil.myopenid.com
September 15, 2008, September 15, 2008 22:13, permalink
Great code, but it has a lot of unnecessary overhead, since it forms the base/core class that all other classes take functionality from, this overhead will be exponential - pretty code must take a backseat here, stability and speed must be the overall implementation goal.
First I would strongly advise against such usage of regex's, and I would also re-consider breaking it down so much.
Here is my take on it, I bashed this up and tested it with your above example, using a loop to execute it 100,000 times, it took half the time - (I know its missing the getProp() function etc, but they're easily added)
<?php
/**
* This class implements auto-getter and setters by making using of PHP magic __call() method..
*/
class PropertyHandler
{
private $props;
function __construct()
{
$props = array();
}
public function __call($method, $arguments)
{
// Handle Setters....
if(strpos($method, "set") === 0)
{
$param = substr($method, 3, strlen($method));
if(!strlen($param))
{
throw new Exception("Invalid or empty property name");
}
if(!count($arguments))
{
throw new Exception("Not allowed more than 1 argument..");
}
$this->props[$param] = $arguments[0];
}
// Handle Getters....
elseif(strpos($method, "get") === 0)
{
$param = substr($method, 3, strlen($method));
if(strlen($param) == 0)
{
throw new Exception("Invalid or empty property name");
}
return $this->props[$param];
}
// No manageable type found? thats fatal..
else
{
throw new Exception("Unhandled method $method");
}
}
}
/**
* Very simple example
*/
class PropTest extends PropertyHandler
{
public function __construct()
{
parent::__construct();
}
}
for($i = 0; $i < 100000; $i++)
{
$props = new PropTest();
$props->setFirstName('Mark');
$props->getFirstName();
}
?>
fain182.myopenid.com
November 13, 2008, November 13, 2008 11:16, permalink
it isn't better to use __get and __set?
auxide
December 19, 2008, December 19, 2008 04:39, permalink
Dont know how quick this is, but its small!:)
<?php
class PropertyHandler
{
private $props = array();
public function __call($method, $arguments)
{
if (strlen($method) >= 4){
$action = substr($method, 0, 3);
$function = substr($method, 3);
if ($action == 'get'){
return $this->props[$function];
} elseif ($action == 'set') {
$this->props[$function] = !isset($arguments[1]) ? $arguments[0] : $arguments;
}
}
}
}
?>
theman
January 9, 2009, January 09, 2009 20:59, permalink
you guys are nubs
<?php
class my_class {
private $someVariable;
public function my_class() {
// some logic
}
// override the php garbage method
private function __set() { }
private function __get() { }
public function setSomeVariable($val) {
// i <3 php type casting..
}
public function getSomeVariable() {
return $this->variable;
}
}
?>
SeanJA
April 17, 2010, April 17, 2010 15:44, permalink
Personally I do it this way, I find that I prefer to have my code completed by the ide so I know what there is that I can do with the class... I tend to avoid using __call and __callStatic. The speed is comparable to @exil.myopenid.com's version.
<?php
/**
* @property-read string $class_name
*/
abstract class rootClass{
/**
* Contains the data for this class
* @var array
*/
protected $data = array();
/**
*
*/
public function __construct(){
$this->addProperty('class_name', '');
$this->setProperties();
}
/**
* Add a property to this class
* @param string $var the property being added
* @param mixed $default the default value
*/
protected function addProperty($var, $default = ''){
$this->data[$var] = $default;
}
/**
* Set the properties of this subclass
*/
abstract protected function setProperties();
/**
* Get the name of this class
* @return string
*/
public function __toString(){
return get_class($this);
}
/**
*
* @param mixed $var
* @return mixed
*/
public function __get($var){
if(array_key_exists($var, $this->data)){
$value = $this->data[$var];
} else {
throw new Exception($var . ' does not exist in ' . $this->class_name);
}
$function = '_get'.strtolower(str_replace('_', '', $var));
if(method_exists($this, $function)){
$value = $this->$function($value);
}
return $value;
}
/**
* Set a variable in the class
* @param string $var
* @param mixed $value
* @return mixed
*/
public function __set($var, $value){
$function = '_set'.strtolower(str_replace('_', '', $var));
if(method_exists($this, $function)){
$this->data[$var] = $value = $this->$function($value);
} elseif(array_key_exists($var, $this->data)) {
$this->data[$var] = $value;
}
return $value;
}
/**
* Get the class name
*/
protected function _getClassName(){
return $this->__toString();
}
/**
* Stop the setting of the classname
*/
protected function _setClassName(){
throw new Exception('What\'re you trying to pull?');
}
}
/**
* @property string $var_one
* @property number $var_two
* @property string $first_name
*/
class childClass extends rootClass{
/**
* Set the default properties for this class
* @see rootClass::setProperties
*/
protected function setProperties(){
$this->addProperty('var_one', '');
$this->addProperty('var_two', '');
$this->addProperty('first_name');
}
/**
* Make sure that var_one is not 'test' and is a string
* @param $value
* @return string
*/
protected function _setVarOne($value){
if(!is_string($value)){
throw new Exception('var_one needs to be a string');
}
if($value == 'test'){
throw new Exception('var_one cannot be "test".');
}
return $value;
}
/**
* Make sure that var_two is numeric, if it is not set it to 0
* @param number $value
* @return number
*/
protected function _setVarTwo($value){
if(!is_numeric($value)){
$value = 0;
}
return $value;
}
}
$start = microtime(true);
for ($i = 0; $i < 100000; $i ++){
$c = new childClass();
$c->first_name = 'test';
$c->first_name;
}
$end = microtime(true);
echo $end - $start;
This class lets you handle basic getting & setting of class properties without having to declare methods for each property.
All methods that start with "get" or "set" (that aren't already declared) are routed through to __call magic method and then values are set & retrieved from an associative array.