Submit Resources  Users' Login
img

Develop rock-solid code in PHP
Home » Tutorials » Php »



Author: Amol Hatwar
Added: October 17, 2006

Intended Audience

This tutorial is intended for both amateur and professional PHP programmers who want to design and write quality functions. To complete this tutorial, you must have a basic level of working knowledge with PHP, and know how to define functions.

Every high-level programming language allows the programmer to define functions. PHP is no different.

A closer look at writing functions

Functions are primarily used to:
  • Encapsulate several lines of code into a single statement
  • Simplify and reuse code
  • Treat the application as a collection of smaller applications working harmoniously.
For developers who come to PHP from compiled languages such as C/C++, PHP's level of performance comes as a shock. User-defined functions are more expensive in terms of CPU and memory resources. But if you design and code your functions properly, you’ll be in safe waters.

The name game

The advantage of using a common naming convention throughout can’t be overstressed. Use the following guidelines when naming functions:
  • Choose names that provide a good hint about what the function does
  • Use prefixes that indicate the package or module the function belongs to
  • Use verbs as function names, rather than nouns.
Suppose you have a module named “user”, which contains user-management functions. Then function names such as usr_is_online() and usrIsOnline() are good candidates for a function that handles checking if the user is currently online. Contrast this with a function name such as is_online_checker(). Using verbs as function names is more logical than using nouns, because functions always do something.

To wrap or not to wrap?

There are developers who wrap every function they use just because they don't like the name. Then there are others who don't like wrapping functions at all. While wrapping may add little overhead in compiled languages, the same is not true in PHP.

Wrapping existing PHP functions without adding or complementing existing functionality should be avoided. Besides increasing the size and the execution time, such renamed functions can turn code management into a nightmare.

How many arguments?

In order to maximize the reach of your code, both for yourself and for other developers, you must write functions that are easy to use. No one likes to use functions with cryptic and hard-to-follow arguments.

Choosing a name that explains the purpose of the function as well as and keeping to a minimum the number of arguments the function takes, are good ways to ensure ease-of-use (EOU). Many argue over the magic number for function arguments. In my opinion more than three or four arguments make a function less memorable. Complex functions that need more arguments should rather be broken down into several simpler functions.

Another trick is to glue smaller arguments to a single large one. One mentionable example here is the PEAR DB's connect() function. The function takes a string called the Data Source Name (DSN) plus a Boolean value that indicates whether or not to use persistent connections.

This DSN is actually many parameters cemented into a string. If you are connecting to a MySQL database named 'foobar' with the username: 'john' and password: 'doe' on a host called: 'spydor'; the DSN would be 'mysql://john:doe@spydor/foobar'. All done in one argument! Contrast this with the plethora of functions one has to call using PHP’s native MySQL support just to connect to a database.

However, to make sense of the DSN from any other string, DB::connect() has to be internally separated into individual arguments like the username, password, hostname, database type and the database name always an additional step. Since things like making connections to a database are generally done only once in a script, gluing strings together to make a single argument and separating them doesn't have a serious impact on performance. Steer clear of functions that parse arguments internally if you have to use them in a script regularly, for example in loops.

Write quality functions

Say you want to set the header-block of an HTML document before pushing it off to the browser. (The header-block is all the stuff between the <head>...</head> tags.) Assume that you want to set both a title and a meta tag. Instead of an odd setHeader({title/meta}, name, value="") function that does it all, using setTitle(title) and setMeta(name, value) separately is a better solution. This sets the title and the meta tags independently of each other.

A header can have only one title tag, but it can have multiple meta tags. When that happens the code will have to call setMeta() multiple times. In such cases, a better solution is to pass setMeta() a two-dimensional array with name-value pairs, and have the work done in one fell swoop.

In general, one-shot functions such as this one are preferable. It's always better to call a function once with all the data it needs to process rather than to invoke it multiple times and feed it arguments incrementally at each call. The idea here is to write a function to minimize calls to it from other code.

In this light, our setHeader() solution was really a kludge. We can obviously refractor setHeader() to setHeader(title, array="") - where the array contains all the name-value pairs for the meta tag. But we must also consider the lost ability to set the title and meta tags independently of each other.

Also, in real-world environments a header can contain more tags than just title and meta. If suddenly, more tags need to be added after an application is ready, one will have to change setHeader() and alter all other code that depends on it. On the other hand, if the setTitle() and setMeta() approach was, used the change would only require writing one more function and then using it.

Optimizing functions

Many developers make the mistake of trying to optimize functions prematurely. The moment they find that they have a useful function at hand, they’ll get down to optimizing it. Remember that optimization is a relatively late step, and is best done when your Web application is ready. With the increasing time-to-deploy expectations that clients have these days, it makes perfect sense to complete the Web application, wait and see which functions get hit most, and then optimize them.

The following equation is a good recipe for writing quality functions: Memorable name + Unambiguous arguments + Speed and efficiency = Quality function

The layered approach

Functions seldom exist in a vacuum. They work with other functions, exchanging and processing data to get tasks done. Writing functions that work well with other functions in the same group or module is important because it's precisely these groups of functions or modules that one would like to be able to reuse.

Starting with built-in PHP functions, one can build abstraction functions, and then go on and use these abstraction functions to make functions that deal with basic necessities. These basic functions must then be used to build functions specific to the application.

Continuing with the hypothetical page-building example, let’s consider how this module may depend and inter-operate with other modules to do its job. The goal of the example is to illustrate simply how functions and groups of functions can be made to mesh easily, increasing their reusability factor.

Page-building modules are typically divided into functions that:
  • Draw the masthead
  • Draw navigation bars
  • Display content
  • Add the footer
There may be additional functions to:
  • Cache the page
  • Check to see if the page has already been cached
  • Display the page if it has been cached
Let's call these the pageBuilder and the cache module respectively.

The pageBuilder module must build a page by querying a database. Because this database is external to PHP, it must depend on a database abstraction module. It’s the DB abstraction module’s responsibility to provide a homogeneous interface with PHP's different vendor-specific database functions. Important functions in this module are: connecting to a database, querying the database, and giving out the results of the query. Packages like PEAR DB, AdoDB and Metabase fit this role.

One will now proceed to make functions that can build components of the HTML page. In a real-world example these components will be dynamic, and the information they contain will depend on things like authentication, and the authority of the application user. For example, admin pages and links may not be displayed to unprivileged users. Here, the pageBuilder module must be able to talk to with the user management module. In turn, the user management module will rely on querying the database, and thus will rely on the database abstraction module.

Another module that runs on top of the page building and user management modules may weave the components of a page together, caching it if required. Also, if a page component fails, this module must show or redirect the user to an appropriate error page.

By now you probably get the gist. Most core functionality must be separated into well-scoped logical modules. To do their tasks, your applications must use the functions that these modules provide.

Notice the hierarchy between core modules and modules that handle the application. Core modules can call as well as declare functions encapsulating functions from the abstraction modules or layers below it. But the application code must not encapsulate or wrap underlying functions. It must only use functions from the layers below. Your application code is like glue that binds all the core modules, and like an orchestra conductor that makes them work together in harmony. Just like the conductor, it must direct and use core modules and functions to get work done, but must not play its own music.

Functional techniques

Now that we have an idea on how functions should be used and written, let's look at some techniques you can employ frequently.


Using references

Simply put, PHP references are logically just like pointers in C (though conceptually and fundamentally different). One major difference is that you don't need to de-reference them in PHP like you do with the * operator in C. References are more like an alias to a variable, array, or object. Whatever operations you carry on, the alias affects the real variable.

Listing 1. References at work

<?php     $name = 'Amol';     $nom = &$name; // $nom is now a reference to $name     $nom .= ' Hatwar';     print("Are you $name?\n"); // Jimmy Ray parody? ?>

When you pass arguments to functions, the function receives a copy of them. Any changes you make to the arguments get lost as soon as the function returns. This is a problem if you want to alter the contents of the arguments directly inside the function. Listing 2 shows an example that demonstrates the problem.

Listing 2. The problem with passing arguments to functions

<?php     function half($num) {         $num = $num / 2;         return $num;     }

    
$myNum = 15;     $result = half($myNum);     print("Half of $myNum is: $result\n");     print("\$myNum contains: $myNum\n"); ?>

We want $myNum to be altered directly. This could be done easily by passing the reference of $myNum to the half() function by calling it like this: half(&$myNum) but this would not be good practice. Developers who used your code would have to keep track of the references used. This is an example of how bugs creep into code unintentionally. It also affects the EOU of your functions.

A better practice is to use references directly in the function declaration - in our case, the function half() must be declared as function half(&$num) instead of function half($num). Also note that since the function half() directly modifies the argument passed to it, there is no need to return. Our new half() function that takes references as arguments must something like this:

Listing 3. Our new half() function

<?php     function half(&$num) {         $num /= 2;         // return $num;     }

    
$myNum = 30;     half($myNum);     print("\$myNum contains: $myNum\n"); ?>

When making functions that take references as arguments, it's a good idea not to return the result. For complex functions that take references, returning a simple Boolean TRUE or FALSE to indicate whether or not the operation succeeded is also a good idea. That way there is a clearer distinction between functions that take arguments as references, and those that don't. In case of confusion, if a function call doesn't return a result, or returns TRUE or FALSE, it uses a referenced argument.

Retaining variables between function calls

You often need to maintain the value of variables between function calls. Global variables can be used, but variables with global scope are more vulnerable to be corrupted by other functions. We want our variable to be local to the function and still retain its value.

Employing the static keyword is an elegant solution. I often use this in-situ when I want to count how many user-defined functions are executed, as debuggers are not generally available on live sites. I simply alter all functions (using an automated script, of course) and add a call to function that counts a function call on the first line of the function body. Listing 4 details the function.

Listing 4. Counting user-defined functions

    function funcCount() {         static $count = 0;         return $count++;     }

Collecting the return value of funcCount() in a variable by calling it just before the script finishes does the trick. New users may find this surprising as $count isn’t set to zero at every function call; the line that initializes the static variable is only executed once.

Dynamic function calls

In many situations you'll find that you really don't know which function has to be called next. Such situations arise when you are doing event-driven programming; that is when you want to call a particular function when an event external to your system has been triggered. One often comes across situations like these while writing scripts that communicate over a network, or when scripting with PHP-GTK.

The method is similar to using variable names. First, you must declare and define functions that may need to be called during execution. Next, you simply use the external event to set a variable with the name of the function that has to be called. Finally, you use the variable as though it was a function. Still confused? Listing 5 will make things clearer.

Listing 5. Dynamic function calls

<?php

    
// Lets define some simple functions     function say_hi() {         print("Hi! ");     }

    function
say_greeting() {         print("How are you today?\n");     }

    function
say_bye() {         print("Enough functions for the day, I hope to see             you again next month.\n");         print("Till then, have a good time\n");     }

    
// Lets pretend someone just logged in     $my_func = 'say_hi';

    
// NOTE: $my_func is a variable and not a function. So this is the say_hi() function executing, as $my_func contains the string 'say_hi'.     $my_func();

    
// Greet the user     $my_func = 'say_greeting';     // $my_func now contains the string 'say_greeting', so the say_greeting() function will execute     $my_func();

    
// Lets call it a day     $my_func = 'say_bye';

    
// You guessed it! Now it's the say_bye() function     $my_func(); ?>

Though here we are deliberately setting variables, remember that this can be done at run-time, and in fact this is what gives this technique its power.

One can also use this technique instead of writing lengthy code inside switch-case statements. Tuck all the code pertinent to a case inside a function. After evaluating which function to use, set a variable and use it. An example that illustrates this is in order.

ACME Widget Co. has many types of users, from first-timers who have just discovered how powerful their widgets can be for hunting roadrunners to veteran coyotes who constantly scour the online store for newer ways to earn their lunch in style. Right now, the system handles three types of users: first-timers, regulars and veterans.

Our requirement here is for each type of user to get an appropriate greeting. First-timers are given a warm welcome and introduced to simple widgets, while the more experienced ones are welcomed and given a choice of more complex contraptions to choose from.

Listing 6. Greeting using traditional switch-case

<?php     // Get what type of user we're dealing with from the DB     $uType = getUserType($uName);

    switch (
$uType) {         case 'newbie':             $greeting = 'Welcome to the ACME widget store. It seems to be your first time here. We\'ve selected some widgets that you may find useful and simple to use from our vast collection. Happy hunting and Bon Appetit!';         break;

        case
'regular':             $greeting = 'Welcome back to the ACME widget store. We\'re sure that the widgets you purchased last worked out for you. May we suggest our new cliff-hanging widget that will help you catch your prey on tight heights?';         break;

        case
'veteran':             $greeting = 'Thank you for choosing ACME widget store. We are now running an exchange scheme where you can exchange your old unused widgets for new ones. In our pursuit of constant innovation, we\'ve just come out with ACME fault-free parachutes - they\'ll work no matter how much you bungle up. Like all ACME Pro products, they come with a lifetime guarantee. You can always exchange a defective ACME parachute for a new one should you survive the experience';         break;

        default:
// assume newbie             $greeting = 'Welcome to the ACME widget store. It seems to be your first time here. We\'ve selected some widgets that you may find useful and simple to use from our vast collection. Happy hunting and Bon Appetit!'; }

print(
$greeting); ?>

If management wants more user types, you’d have to edit the existing switch-case structure to provision for them. In an ideal scenario, one would only add to the code for extending its capabilities. Editing must be minimal.

Let us see what we can achieve using dynamic function calls.

Listing 7. Greeting with dynamic function calls

<?php     // Define some functions that will print greeting messages to users according to user types

    
function newbie() {     print('Welcome to the ACME widget store. It seems to be your first time here. We\'ve selected some widgets that you may find useful and simple to use from our vast collection. Happy hunting and Bon Appetit!');     }

    function
regular() {     print('Welcome back to the ACME widget store. We\'re sure that the widgets you purchased last worked out for you. May we suggest our new cliff-hanging widget that will help you catch your prey on tight heights?');     }

    function
veteran() {     print('Thank you for choosing ACME widget store. We are now running an exchange scheme where you can exchange your old unused widgets for new ones. In our pursuit of constant innovation, we\'ve just come out with ACME fault-free parachutes &#8211; they\'ll work no matter how much you bungle up. Like all ACME Pro products, they come with a lifetime guarantee. You can always exchange a defective ACME parachute for a new one should you survive the experience');     }

    
// Get the user type from the DB and greet him/her     $func = getUserType($uName);     if ('' == $func) {         $func = newbie; // Assume newbie if utype is null     }

    
$func(); ?>

Notice that Listing 7 does the same job as listing 6 with several added advantages. Firstly, we've done away with the painful switch case structure. Secondly, if management decides to add user types you simply add more functions.

Summary

In this article we found out how to design and write quality functions. We also saw how to make a collection of modules and scripts mesh together to craft larger applications. We also examined techniques that reduce coding effort, and produce more elegant code.

This article was first published by IBM developerWorks at http://ibm.com/developerworks/web/library/wa-phprock3/

About the author

Amol Hatwar is a GNU/Linux bigot. He's the Founder and Principal Consultant at Pixelmunge. When not busy with consulting, programming or writing; he likes giving advanced programmers sermons on simple and effective programming techniques. You can reach him at: amol@pixelmunge.com

Trusted Websites
Create Htm.com - offers Html t...


 
                                                                                   -- Site Pro News      ComputerScripts.com - Free & Commercial Web Scripts!      Ex-design.net