Recent Weblogs

Links I like

PHP Magic Methods

PHP has introduced some awesome new functionality to classes in PHP 5. This functionality goes from abstract classes to class type hints on function declarations. These new features have direct and obvious uses, but the magic methods didn't seem to have a purpose for me, I thought they were very interesting but I couldn't come up with a valid use case, until now.

Design Patterns, Composite/Proxy

By using the __get/__set magic methods we can easily delegate these operations to an internal object reference. Since the __get method only fires when an attempt to access a property that doesn't exist, we can assume that the implementation is looking for our internal object and not a native property of the proxy object. This can be applied to functions as well using the __call magic method. By doing this we can completely delegate the 'unknown' operations to our reference(s) and the proxy object will behave just as its assumed object(s).

Use Cases

An example that I have is just a proxy to a reference of ole stdClass. The intent is to track what operations act on the object. In Drupal you pass around an object 'node' which gets modified and manipulated in a massive passive observer pattern. Knowing what method last acted or set a property is crucial for debugging, so lets look at some code.

Class debug_dto

class debug_dto{
	protected $debug;
	protected $hash;
	public function __construct(){
		$this->debug = new stdClass();	
		$this->hash = new stdClass();
	}
	public function __set($key, $value){
		$this-> hash -> $key = $value;
		$debug = debug_backtrace();
		array_shift($debug);//remove this method call.
		$this-> debug -> $key = array_shift($debug); //the immediate parent, the function that called/set this property
	}
	public function __get($key){
		return $this-> hash -> $key;
	}
	public function get_debug(){
		return $this->debug;
	}
}

debug_dto has two internal properties, one is hash, which actually stores the values that are being set. If I had set the property native to the proxy object ($this -> $key = $value) instead of hash, the next time that property is set, it wouldn't execute the __set method because it would no longer be unknown. The debug property is another object reference that uses the same keys as hash but instead of the value, its value is the second index in the result of debug_backtrace, which gives you an array of the stack trace of function calls. The first would be the __set method itself, the one after that is the method that is attempting to set the property.

Example Code of debug_dto in use.

function my_func_one($obj){
	$obj -> title = "Something";
	my_func_two($obj);
}
function my_func_two(&$obj){
	$obj -> id = 10;
	$obj -> title .= " else";
}


$obj = new debug_dto();

my_func_one($obj);


die(print_r($obj -> get_debug(), true));



And the output...

stdClass Object
(
    [title] => Array
        (
            [file] => C:\wampserver\www\dev\debug-node.php
            [line] => 25
            [function] => my_func_two
            [args] => Array
                (
                    [0] => debug_dto Object
                        (
                            [debug:protected] => stdClass Object
 *RECURSION*
                            [hash:protected] => stdClass Object
                                (
                                    [title] => Something else
                                    [id] => 10
                                )

                        )

                )

        )

    [id] => Array
        (
            [file] => C:\wampserver\www\dev\debug-node.php
            [line] => 25
            [function] => my_func_two
            [args] => Array
                (
                    [0] => debug_dto Object
                        (
                            [debug:protected] => stdClass Object
 *RECURSION*
                            [hash:protected] => stdClass Object
                                (
                                    [title] => Something else
                                    [id] => 10
                                )

                        )

                )

        )

)



Comments

July 08, 2009Michael

Nice, but I would include some calls to custom getters and setters like getMyVar(), setMyVar(val), if exists and throw an exception if the Member does not exist.

July 09, 2009Matt

Michael, I believe the method __get will throw an error when referencing a non-existent property of the internal hash variable.

May 24, 2014bella

I really appreciate this post. I've searched everywhere. Please keep us updated like this. Thanks for sharing!
http://www.frivjuegosnow.com

Name
Site
Comment
  CAPTCHA Image
Reload Captcha Image
Captcha