jump to navigation

Writing PHP Classes with “Callback-aware” functionality January 12, 2013

Posted by Tournas Dimitrios in PHP.
trackback

Many PHP Classes and core-functions have a “callback-aware” functionality , for instance : array_walk , usort , preg_replace_callback  , header_register_callback , SPL’s CallbackFilterIterator are a representative sample  .These functions (and Classes) allow you to plug custom functionality into a component at run-time that is not directly related to that component’s core task . This clever architectural design could also be implemented into our code (library , framework) and transform it into a flexible component . Couldn’t we directly customize the code ,  one may ask …  Well yes , but by separating (delegating) some parts of our code to external functions , we give others the power to extend our code in contexts we don’t yet know about . The “consumer” of our library wouldn’t need to care about our code , as their code is isolated  and future updates to our component will not overwrite their custom code .

Before “digging” into a practical example , a bit of theory will uncover the mystery of callback functions . It’s absolute necessary (especially for new PHP developers) , otherwise  the whole concept  won’t make any sense  .

The term “callback” is typically used to describe functions that are passed as parameters to other functions . Callbacks are frequently used in event handling architectures , where you designate code to be invoked at a later time . PHP makes using callbacks extremely simple using two helper functions (is_callable() and call_user_func() )  . There are three ways to use callbacks :

  1. By defining the custom code into a stand-alone function and assigning its name as a parameter into PHP’s callback helper function .
    function myCallback($msg) {
    echo 'Your message is  : ' . $msg ;
        }
    $callback = 'myCallback' ;
    if(is_callable($callback)) {
    call_user_func($callback , 'Bla-bla') ;
    }
    
    /*
    Defining mulitple arguments into the callback-function
    */
    function myCallback2($msg1 , $msg2 , $msg3) {
    echo "Your message is  :  $msg1  and  $msg2  and $msg3"  ;
        }
    $callback2 = 'myCallback2' ;
    if(is_callable($callback2)) {
    call_user_func($callback2 , 'Bla-bla' , 'xxxxx' , 'zzzzz' ) ;
    }
    
  2. By defining the custom code as a “static” method into a Class .
    class MyClass {
     public static function myCallback() {
     echo "hello from the Callback function " ;
            }
        }
    $classname = "MyClass"  ;
    $callback = array('MyClass', 'myCallback');
     if (is_callable($callback)) {
    call_user_func(array($classname, 'myCallback')) ;
    }
    
    /*
    Defining mulitple arguments into the callback-function
    */
    class MyClass2 {
     public static function myCallback($msg , $msg2 , $msg3 ) {
     echo "hello from the Callback function  : $msg  and $msg2  and $msg3" ;
            }
        }
    $classname2 = "MyClass2"  ;
    $callback2 = array('MyClass2', 'myCallback');
     if (is_callable($callback2)) {
    call_user_func(array($classname2 , 'myCallback') , 'xxxx' , 'yyy' , 'zzzz') ;
    }
    
  3. By defining the code as a publicly accessible method into a Class .
    class MyClass {
     public function  myCallback() {
    // Custom code goes here
     echo "hello from the Callback function " ;
    	}
    }
    $object = new MyClass()  ;
    $callback = array('MyClass', 'myCallback');
    if (is_callable($callback)) {
    call_user_func(array($object, 'myCallback')) ;
    }
    /*
    As in previous example (2) we could also define multiple arguments into the callback-function
    */
    

Notice the “is_callable” helper function , it’s optionally used , and verifies that the provided argument is a valid callback function . The real “magic” is done by the “call_user_func”  function , it handles the initiation of the function (or the Class) and pass the proper arguments to it . Once the concepts of callback are clarified , let’s demonstrate a practical example .

Writing a PHP Class with “Callback-aware” functionality :

Let’s pretend the following scenario , a Class is designed to produce “virtual toys” . Each toy should have a unique serial-number assigned to it . The developer should have the choice to record the aforementioned serial-number on a persistent storage medium (log-file , database , printer ) .
The implementation of the storage process is left to the developer (its done via a callback function ) . Let’s see  how the callback functionality “fits” into our scenario .

The workflow of the Class is as follows :

  • Creating an abstract Class , it’s used as a template from which all toys (chickens , ducks , rabbits) are derived 
  • The “Production” Class implements all the logic . Its constructor takes one argument , an instance of a toy (Duck , Chicken) .
  • All callback functions are stored into a private array via the “registerCallBack” function
  • Each newly produced toy is assigned an unique serial number (generated by “generateSerialNum” function)
  • The production starts via a “produce” function , which takes one argument (quantity of toys to produce) . If there are registered callback-functions , a foreach-loop will call each callback-function in a sequential order .
abstract class Product {
private $color ;
private $price ;
private $serialNumber ;
function __construct( $color , $price = 25 ) {
$this->color = $color ;
$this->price = $price ;
			} // End of constructor
public function setSerial($serial) {
$this->serialNumber = $serial ;
		} // End of setSerial  funciton
	} // End of Product class

class Chicken extends Product {}
class Duck  extends Product {}

class Production {
private $callbacks  ;
private $product  ;
function __construct( Product $product ) {
 $this->product = $product ;
	} // End of construct
public function registerCallback( $callback ) {
if ( is_callable( $callback ) )   $this->callbacks[] = $callback  ;
	} // End of registerCallBack
public function produce($pieces )  {
for ($i = 1; $i <= $pieces ; $i++) {
$serial = $this->generateSerialNum() ;
$product = $this->product ;
$product->setSerial($serial) ;
echo  "A new Product  was produced  with serial : $serial <br>"  ;
if(!empty($this->callbacks) ){
foreach ( $this->callbacks as $callback ) {
call_user_func( $callback , $i , get_class($product) , $serial) ;
				} // End of foreach
			}//End of if-Callbacks
unset($product)  ;
		} // End for-loop
	}  // End function produce
private function generateSerialNum() {
    $template   = 'XX99-XX99-99XX-99XX-XXXX-99XX';
    $k = strlen($template);
    $sernum = '' ;
    for ($i=0; $i<$k; $i++)
    {
        switch($template[$i])
        {
            case 'X': $sernum .= chr(rand(65,90)); break;
            case '9': $sernum .= rand(0,9); break;
            case '-': $sernum .= '-';  break;
			}// End of switch
		}// End of for-loop
    return $sernum ;
	} // End generateSerialNum

} // End Class Production

How to use the Class : 

The process of implementing the “Produce” Class is simple , just instantiate a new Product Class  , its constructor takes on object of type “Duck or Chiken” . Next step is to define as many callback-functions as needed (for instance , Db , log , email ….. ) . Of course , each callback function will implement its own custom code (functionality) . The last step is to call the “produce” method and passing a number to it (quantity of toys to produce) . Here follows an example .


function logFile($times , $type , $serial) {
// Here goes the custom code
echo "--------------$times $type(s) created with serial : $serial -----------------<br>" ; 
}
function dataBase($times , $type , $serial) {
// Here goes the custom code
echo "--------------DataBase Records updated-----------------<br>" ; 
}

$toy = new Production(new Duck('red'))   ;
$toy->registerCallBack('logFile') ; 
$toy->registerCallBack('dataBase') ; 
$toy->produce(5)  ; 
$toy2 = new Production(new Chicken('blue')) ; 
$toy2->registerCallBack('logFile') ;
$toy2->produce(1) ; 

Comments»

1. Valencia - January 17, 2013

Peculiar article, exactly what I wanted to find.

tournasdimitrios1 - January 17, 2013

@Valencia
Keep in mind , this article outlined the basic concepts . On production (and large projects) there is a more robust implementation , the “Observer Design Pattern” .
That’s a subject for a future article though .
Thanks for commenting on this Blog .

2. Celli - February 6, 2013

your blog has some interesting stuff.

3. Owen Knower - February 7, 2013

I just want to tell you that I’m newbie to weblog and really loved this blog. Probably I’m likely to bookmark your blog . You really come with superb articles. Thanks a lot for revealing your web-site.

4. Clarice - February 9, 2013

Many thanks for sharing excellent informations. Your web page is very cool.
I am stunned at details that you might have employing this internet
site. It reveals how nicely you perceive this subject.

Bookmarked this website page, is for more articles.

You, my friend, ROCK! I recently came across simply the
info I already searched everywhere and just couldn’t discover. What a perfect site.

5. rosyjski - February 13, 2013

Keep on working, great job!

6. Daniel - June 8, 2013

I just like the valuable information you supply
in your articles. I’ll bookmark your blog and check once more here frequently. I am moderately sure I will learn a lot of new stuff proper here! Best of luck for the following!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s