Class Object to create objects of type Object, in a dynamic way, with magic methods I have a project in my mind for quite some time, and it requires me to create dynamic objects.

Usually, casting an array to object does the trick (e.g.: (object)array('test'=&gt;5)). But sometimes, we still want the magic methods to fire up. For that, I have developed a class.

It supports the magic methods __construct, __get, __set, __isset and __unset. This also has a method to check the keys in your object.

class Object {
    private $props = array();
    private $methods = array(
        '__construct'=&gt;null,
        '__get'=&gt;null,
        '__set'=&gt;null,
        '__isset'=&gt;null,
        '__unset'=&gt;null
    );

    private function __run_magic_method( $method, $args=null )
    {
        $obj = (object)$this-&gt;props;
        $return = call_user_func_array( $this-&gt;methods[$method], array_merge( array(&amp;$obj), (array)$args ) );
        $this-&gt;props = (array)$obj;
        return $return;
    }

    private function __has_magic_method( $method )
    {
        return isset( $this-&gt;methods[$method] ) &amp;&amp; is_callable( $this-&gt;methods[$method] );
    }

    function __construct( array $props )
    {
        foreach( array_keys($this-&gt;methods) as $method )
        {
            if( isset( $props[$method] ) )
            {
                $this-&gt;methods[$method] = $props[$method];
                unset( $props[$method] );
            }
        }

        $this-&gt;props = $props;

        if( $this-&gt;__has_magic_method( '__construct' ) )
        {
            $this-&gt;__run_magic_method( '__construct' );
        }
    }

    function __get( $key )
    {
        if($this-&gt;__has_magic_method('__get' ) )
        {
            return $this-&gt;__run_magic_method( '__get', array($key) );
        }
        else
        {
            return $this-&gt;props[$key];
        }
    }

    function __set( $key, $value )
    {
        if( $this-&gt;__has_magic_method( '__set' ) )
        {
            $this-&gt;__run_magic_method( '__set', array( $key, $value ) );
        }
        else
        {
            $this-&gt;props[$key] = $value;
        }
    }

    function __isset( $key )
    {
        if( $this-&gt;__has_magic_method( '__isset' ) )
        {
            return $this-&gt;__run_magic_method( '__isset', array($key) );
        }
        else
        {
            return isset( $this-&gt;props[$key] );
        }
    }

    function __unset( $key )
    {
        if( $this-&gt;__has_magic_method( '__unset' ) )
        {
            return $this-&gt;__run_magic_method( '__unset', array($key) );
        }
        else
        {
            unset( $this-&gt;props[$key] );
        }
    }

    function __call( $key, $args )
    {
        if( isset( $this-&gt;props[$key] ) &amp;&amp; is_callable( $this-&gt;props[$key] ) )
        {
            return call_user_func_array( $this-&gt;props[$key], $args );
        }
        else
        {
            throw new Exception( 'The key "'.addslashes($key).'" is not a callable' );
        }
    }

    private function __keys()
    {
        return array_keys( $this-&gt;props );
    }

    static function keys( Object $obj=null )
    {
        if( $obj === null )
        {
            return array();
        }
        else
        {
            return $obj-&gt;__keys();
        }
    }
}


Example of usage:

$obj = new Object(array(
    '__construct'=&gt;function(){echo 'constructor executed', PHP_EOL;},
    '__isset'=&gt;function($t,$k){
        echo 'checking key: ', $k, PHP_EOL;
        return isset($t-&gt;{$k});
    },
    'test'=&gt;5
));

if( isset($obj-&gt;test) )
{
    echo $obj-&gt;test;
}

/*
Should output:

constructor executed
checking key: test
5
*/


What can I improve in this code, in terms of readability? What crimes am I committing with this code?
As pointed out before, in a comment, non-magic methods in a class shouldn't be prefixed with __.

Since they are private, they are prefixed with _.



Closures are a really nice feature that came with PHP5.3, and they weren't being used to their full potential.

Closures have the method bindTo, which generates a new Closure binded to that object.
This allows to use the variable $this inside the Closure.
It helps the fellow programmers that decide to use this to write in a more natural way.
The best part is that there are no kludges needed!

The method _run_magic_method() will look like this:

    private function _run_magic_method( $method, $args=null )
    {
        $obj = (object)$this-&gt;props;

        if( is_a( $this-&gt;methods[$method], 'Closure' ) )
        {
            $return = call_user_func_array( $this-&gt;methods[$method]-&gt;bindTo( $obj ), (array)$args );
        }
        else
        {
            $return = call_user_func_array( $this-&gt;methods[$method], array_merge( array(&amp;$obj), (array)$args ) );
        }
        $this-&gt;props = (array)$obj;
        return $return;
    }


Since objects are passed as references in PHP5, all changes are reflected on the properties.



The final code:

&lt;?php

    class Object {
        private $props = array();
        private $methods = array(
            '__construct'=&gt;null,
            '__get'=&gt;null,
            '__set'=&gt;null,
            '__isset'=&gt;null,
            '__unset'=&gt;null,
            '__toString'=&gt;null
        );
        private function _run_magic_method( $method, $args=null )
        {
            $obj = (object)$this-&gt;props;

            if( is_a( $this-&gt;methods[$method], 'Closure' ) )
            {
                //Inside the closure, the $this will be changing the parent $this-&gt;props
                $return = call_user_func_array( $this-&gt;methods[$method]-&gt;bindTo( $obj ), (array)$args );
            }
            else
            {
                $return = call_user_func_array( $this-&gt;methods[$method], array_merge( array( &amp;$obj ), (array)$args ) );
            }

            $this-&gt;props = (array)$obj;
            return $return;
        }
        private function _has_magic_method( $method )
        {
            return isset( $this-&gt;methods[$method] ) &amp;&amp; is_callable( $this-&gt;methods[$method] );
        }
        function __construct( array $props )
        {
            foreach( array_keys( $this-&gt;methods ) as $method )
            {
                if( isset( $props[$method] ) )
                {
                    $this-&gt;methods[$method] = $props[$method];
                    unset( $props[$method] );
                }
            }
            $this-&gt;props = $props;
            if( $this-&gt;_has_magic_method( '__construct' ) )
            {
                $this-&gt;_run_magic_method( '__construct' );
            }
        }
        function __get( $key )
        {
            if($this-&gt;_has_magic_method('__get' ) )
            {
                return $this-&gt;_run_magic_method( '__get', array($key) );
            }
            else
            {
                return $this-&gt;props[$key];
            }
        }
        function __set( $key, $value )
        {
            if( $this-&gt;_has_magic_method( '__set' ) )
            {
                $this-&gt;_run_magic_method( '__set', array( $key, $value ) );
            }
            else
            {
                $this-&gt;props[$key] = $value;
            }
        }
        function __isset( $key )
        {
            if( $this-&gt;_has_magic_method( '__isset' ) )
            {
                return $this-&gt;_run_magic_method( '__isset', array($key) );
            }
            else
            {
                return isset( $this-&gt;props[$key] );
            }
        }
        function __unset( $key )
        {
            if( $this-&gt;_has_magic_method( '__unset' ) )
            {
                return $this-&gt;_run_magic_method( '__unset', array($key) );
            }
            else
            {
                unset( $this-&gt;props[$key] );
            }
        }
        function __call( $key, $args )
        {
            if( isset( $this-&gt;props[$key] ) &amp;&amp; is_callable( $this-&gt;props[$key] ) )
            {
                return call_user_func_array( $this-&gt;props[$key], $args );
            }
            else
            {
                trigger_error( 'The key "'.addslashes($key).'" is not a callable', E_USER_ERROR );
            }
        }
        function __toString( )
        {
            if( $this-&gt;_has_magic_method( '__toString' ) )
            {
                return $this-&gt;_run_magic_method( '__toString' ) . '';
            }
            else
            {
                trigger_error( 'There is no defined __toString method', E_USER_ERROR );
            }
        }
        private function _keys()
        {
            return array_keys( $this-&gt;props );
        }
        static function keys( Object $obj=null )
        {
            if( $obj === null )
            {
                return array();
            }
            else
            {
                return $obj-&gt;_keys();
            }
        }
    }


Example of execution:

$obj = new Object(array(
    'a'=&gt;5,
    '__get'=&gt;function($k){
        echo 'Getting the value from the key \'',$k,'\'', PHP_EOL;
        return $this-&gt;{$k};
    },
    '__set'=&gt;function($k,$v){
        echo 'Setting a value on the key \'',$k,'\'', PHP_EOL;
        $this-&gt;{$k}=$v;
    }
));

/*
 *Should output:
 *
 * Getting the value from the key 'a'
 * 5
 * Setting a value on the key 'b'
 * Getting the value from the key 'b'
 * 6
 *
 */