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

  • By
    7 Essential JavaScript Functions

    I remember the early days of JavaScript where you needed a simple function for just about everything because the browser vendors implemented features differently, and not just edge features, basic features, like addEventListener and attachEvent.  Times have changed but there are still a few functions each developer should...

  • By
    Vibration API

    Many of the new APIs provided to us by browser vendors are more targeted toward the mobile user than the desktop user.  One of those simple APIs the Vibration API.  The Vibration API allows developers to direct the device, using JavaScript, to vibrate in...

Incredible Demos

  • By
    Display Images as Grayscale with CSS Filters

    CSS filters aren't yet widely supported but they are indeed impressive and a modern need for web imagery.  CSS filters allow you to modify the display of images in a variety of ways, one of those ways being displaying images as grayscale. Doing so requires the...

  • By
    “Top” Watermark Using MooTools

    Whenever you have a long page worth of content, you generally want to add a "top" anchor link at the bottom of the page so that your user doesn't have to scroll forever to get to the top. The only problem with this method is...

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!