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
    Create a CSS Cube

    CSS cubes really showcase what CSS has become over the years, evolving from simple color and dimension directives to a language capable of creating deep, creative visuals.  Add animation and you've got something really neat.  Unfortunately each CSS cube tutorial I've read is a bit...

  • By
    Camera and Video Control with HTML5

    Client-side APIs on mobile and desktop devices are quickly providing the same APIs.  Of course our mobile devices got access to some of these APIs first, but those APIs are slowly making their way to the desktop.  One of those APIs is the getUserMedia API...

Incredible Demos

  • By
    Create Digg URLs Using PHP

    Digg recently came out with a sweet new feature that allows users to create Tiny Digg URLs which show a Digg banner at the top allowing easy access to vote for the article from the page. While I love visiting Digg every once in a...

  • By
    CSS Custom Cursors

    Remember the Web 1.0 days where you had to customize your site in every way possible?  You abused the scrollbars in Internet Explorer, of course, but the most popular external service I can remember was CometCursor.  CometCursor let you create and use loads of custom cursors for...

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!