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
    6 Things You Didn’t Know About Firefox OS

    Firefox OS is all over the tech news and for good reason:  Mozilla's finally given web developers the platform that they need to create apps the way they've been creating them for years -- with CSS, HTML, and JavaScript.  Firefox OS has been rapidly improving...

  • By
    Serving Fonts from CDN

    For maximum performance, we all know we must put our assets on CDN (another domain).  Along with those assets are custom web fonts.  Unfortunately custom web fonts via CDN (or any cross-domain font request) don't work in Firefox or Internet Explorer (correctly so, by spec) though...

Incredible Demos

  • By
    Image Reflections with CSS

    Image reflection is a great way to subtly spice up an image.  The first method of creating these reflections was baking them right into the images themselves.  Within the past few years, we've introduced JavaScript strategies and CANVAS alternatives to achieve image reflections without...

  • By
    Drag & Drop Elements to the Trash with MooTools 1.2

    Everyone loves dragging garbage files from their desktop into their trash can. There's a certain amount of irony in doing something on your computer that you also do in real life. It's also a quick way to get rid of things. That's...

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!