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
    Regular Expressions for the Rest of Us

    Sooner or later you'll run across a regular expression. With their cryptic syntax, confusing documentation and massive learning curve, most developers settle for copying and pasting them from StackOverflow and hoping they work. But what if you could decode regular expressions and harness their power? In...

  • By
    Creating Scrolling Parallax Effects with CSS

    Introduction For quite a long time now websites with the so called "parallax" effect have been really popular. In case you have not heard of this effect, it basically includes different layers of images that are moving in different directions or with different speed. This leads to a...

Incredible Demos

  • By
    Create a Trailing Mouse Cursor Effect Using MooTools

    Remember the old days of DHTML and effects that were an achievement to create but had absolutely no value? Well, a trailing mouse cursor script is sorta like that. And I'm sorta the type of guy that creates effects just because I can.

  • By
    Cross Browser CSS Box Shadows

    Box shadows have been used on the web for quite a while, but they weren't created with CSS -- we needed to utilize some Photoshop game to create them.  For someone with no design talent, a.k.a me, the need to use Photoshop sucked.  Just because we...

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!