040ffc7f3327b05f01c54a8bef3ba60a

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.

<?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 !

55502f40dc8b7c769880b10874abc9d0

exil.myopenid.com

September 15, 2008, September 15, 2008 22:13, permalink

1 rating. Login to rate!

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();
	}

?>
1bf59120892e34eb60836725566c6e55

fain182.myopenid.com

November 13, 2008, November 13, 2008 11:16, permalink

No rating. Login to rate!

it isn't better to use __get and __set?

95a865245ca8347f4c80828ef9bb465b

auxide

December 19, 2008, December 19, 2008 04:39, permalink

1 rating. Login to rate!

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;
            }
        }
    }
}
?>
40ae6a389169b134c62fc18970be740c

theman

January 9, 2009, January 09, 2009 20:59, permalink

1 rating. Login to rate!

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;
           }
       }
?>
74cc83022ea14b42b49ea487b95d5f1e

SeanJA

April 17, 2010, April 17, 2010 15:44, permalink

No rating. Login to rate!

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;

Your refactoring





Format Copy from initial code

or Cancel