Create PHP Classes with Dynamic Functions

By  on  

As you have probably found out, when I see something interesting I don't know how to do, my only goal is to figure out how to do it. Thus is the case with PHP classes featuring dynamic methods based on database records (or arrays). I took some time to figure out how it was done.

The Sample MySQL Record in a PHP Array

$record = array(
		'id' => 12,
		'title' => 'Greatest Hits',
		'description' => 'The greatest hits from the best band in the world!'
	);

Pretend we created the above array based on a record retrieved via a MySQL query.

The PHP Class with Dynamic Functions

/* create class */
class Record {
	
	/* record information will be held in here */
	private $info;
	
	/* constructor */
	function Record($record_array) {
		$this->info = $record_array;
	}
	
	/* dynamic function server */
	function __call($method,$arguments) {
		$meth = $this->from_camel_case(substr($method,3,strlen($method)-3));
		return array_key_exists($meth,$this->info) ? $this->info[$meth] : false;
	}
	
	/* uncamelcaser: via http://www.paulferrett.com/2009/php-camel-case-functions/ */
	function from_camel_case($str) {
		$str[0] = strtolower($str[0]);
		$func = create_function('$c', 'return "_" . strtolower($c[1]);');
		return preg_replace_callback('/([A-Z])/', $func, $str);
	}	
}

The first step is to create a very primitive class with a constructor which receives the record array and a __call magic method which gets executed any time a class method is called. When a method is called we parse the method name to remove "get" and check to see if the corresponding array key exists. If the key exists, we return its value -- if the key does not exist, we simple return false.

The PHP Example Usage

/* usage */
$Record = new Record(
	array(
		'id' => 12,
		'title' => 'Greatest Hits',
		'description' => 'The greatest hits from the best band in the world!'
	)
);

/* proof it works! */
echo 'The ID is:  '.$Record->getId(); // returns 12
echo 'The Title is:  '.$Record->getTitle(); // returns "Greatest Hits"
echo 'The Description is:  '.$Record->getDescription(); //returns "The greatest hits from the best band in the world!"

Above we pass the array to the class and are then able to retrieve values via the "get" functions. Awesome!

The above example is not the most dynamic or realistic usage of dynamic function creation but this example will get you crawling before walking. Let me know if you have better examples or real uses.

Recent Features

Incredible Demos

  • By
    Adding Events to Adding Events in MooTools

    Note: This post has been updated. One of my huge web peeves is when an element has click events attached to it but the element doesn't sport the "pointer" cursor. I mean how the hell is the user supposed to know they can/should click on...

  • By
    Xbox Live Gamer API

    My sharpshooter status aside, I've always been surprised upset that Microsoft has never provided an API for the vast amount of information about users, the games they play, and statistics within the games. Namely, I'd like to publicly shame every n00b I've baptized with my...

Discussion

  1. olivier

    this is very good but can be improved to handle ‘get’ and ‘set’

  2. Lorenzo

    Nice but… Why?

  3. I’m getting more into OO PHP, so it’s always nice to see clear examples like this. I like the __call magic method – hadn’t come across that one yet.

    Thanks David.

  4. Ok, but the reason of the unCamelCase function isn’t really explained in your example. You should add a more accurate property in your Record like :

    $Record = new Record(
    	array(
    		'my_id' => 12
    	)
    );
    
    echo $Record->getMyId();
  5. EmEhRKay

    or you simply have a get method (like mootools) that looks up the field in the info hash and save a few method calls :)

    I just got into this argument at work about getters and setters. I was fighting for a chainable set() method

    set('field', 'value')->set()->otherMethod()
    

    they didnt understand or wasnt trying to hear me out. I did it anyway.

    Btw, I do think that this is an example of how most people use the __call magic method

    Here is mine

        public function __call($name, $args){
            $return = false;
            
            if(isset($name) && trim($name) !== ''){
                $value = $args[0];
                
                if(substr($name,0,4) == 'set_'){
                    $var = substr($name,4);
                    
                    if(!empty($var) && isset($value) && property_exists($this, $var)){
                        $return = $this->_setDirty($name, $value);
                    }
                }else{
                    $return = $this->_getVar($name);
                }
            }
            
            return $return;
        }
    
  6. ace

    If you wanna use array as an object use ArrayObject. No need for inventing warm water. :)

  7. Err, why would you use this instead of mysql_fetch_object?

    http://nl3.php.net/mysql_fetch_object

  8. Rob

    I did the same some time ago, but instead using the __call function I used __set and __get, which lead to the same result but one can access the fields directly as class attributes.

  9. FYI, $info should be $_info since it’s private. See Zend Framework coding standards: http://framework.zend.com/manual/en/coding-standard.naming-conventions.html#coding-standard.naming-conventions.variables

  10. @Joe Devon: Zend Framework is certainly not law. And many people (myself included) find underscores annoying in a language that can handle private properties.

    I only use _prop in things like Javascript or Python.

  11. Yeah, I hate prefixing variables with underscore.

  12. Robin

    Here is another tutorial which explains the same, but more complete and a better implementation (almost 4 years old already):
    http://www.ibm.com/developerworks/xml/library/os-php-flexobj/

  13. @Robin: Thank you for the link — makes sense and provides great detail.

  14. Arian

    Why do you use the PHP4 class syntax (no public/private/protected, no __construct)

    besides, it is faster to use a set() and get() method. php will look first at all methods of your class if the method exists and then call __call(), so it’s slower than just set()/get()

  15. $record = array(
    		'id' => 12,
    		'title' => 'title',
    		'description' => 'description'
    	);
    

    php is frameworks…

  16. Very useful, thanks for the advice

  17. /**
     * This class creates a dynamic shell to
     * define and set any Setter or Getter
     *
     * Example:
     *
     * $property = new DynamicPropertiesUtility();
     * $property->setFax("123-123-1234"); // set[anything here first letter upper case]("value here")
     * echo $property->getFax()."\n"; // get[anything here first letter upper case]()
     */
    
    class DynamicPropertiesUtility {
        private $properties;
        
        public function __call($name, $args) {
            if (preg_match('!(get|set)(\w+)!', $name, $match)) {
                $prop = $match[2];
                if ($match[1] == 'get') {
                    if (count($args) != 0) {
                    	throw new Exception("Method '$name' expected 0 arguments, got " . count($args)."\n");
                    }
                    return $this->properties[$prop];
                } else {
                    if (count($args) != 1) {
                    	throw new Exception("Method '$name' expected 1 argument, got " . count($args)."\n");
                    }
                    $this->properties[$prop] = $args[0];
                }
            } else {
            	throw new Exception("Unknown method $name");
            }
        }
    }
    
  18. Nice tips !

    I followed the links in comments and it is real one revelations.

    I am great satisfied, thank you =)

  19. kailash malav

    Good for beginners…………..

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!