Skip to main content

Develop rock-solid code in PHP: Write reusable functions, Part 3

Getting the most out of your functions

Amol Hatwar (amolhatwar@consultant.com), Independent Consultant
Amol Hatwar has been into computers for as long as he can remember. Being a GNU/Linux bigot, he feels guilty for spending his past life programming on Microsoft platforms. He now works as an independent consultant, helping companies migrate to GNU/Linux. A specialist in developing Web applications, he spends his mythical free time dabbling with technologies no one has heard about. His current interests include open-source software, Web services, peer-to-peer computing, and high-availability clustering. You can contact Amol at amolhatwar@consultant.com.

Summary:  In Part 3 of this series on developing efficient PHP code in real-world situations, Amol Hatwar discusses how to build the most efficiently functional functions, functions you can use without sacrificing too much performance or manageability. The author focuses on writing reusable functions and covers how to avoid the most common problems associated with this task.

Date:  01 Nov 2002
Level:  Introductory
Activity:  578 views
Comments:  

Welcome back. In Part 1 of this series, I discussed some basic PHP design rules and covered how to write secure, simple, platform-independent, speedy code. In Part 2, I opened the world of variables and discussed their usage -- both good and bad practices -- in PHP coding.

In this article you'll see how to use functions in PHP wisely. In every high-level programming language, the programmer can define functions and PHP is no different. The only difference is that you don't have to worry about return types of functions.

Taking a closer look

You can use functions to:

  • Encapsulate several lines of code into a single statement.

  • Simplify code.

  • Most importantly, think of the application as a symphony of smaller applications in harmony.

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 expensive in terms of CPU and memory resources. This is mainly because PHP is interpreted and loosely typed.

To wrap or not to wrap

There are developers who wrap every function they use just because they don't like the name and there are others who don't prefer using wraps at all.

Wrapping existing PHP functions without adding or complementing existing functionality is a strict no-no. Besides increasing the size and the execution time, such renamed functions can sometimes prove to be management nightmares.

Inlining functions in code results in unintelligible code and even bigger management horrors. The only benefit you may gain by doing this is faster code.

The wiser way is to define functions only when you need to use the code multiple times and there are no PHP built-ins for the task you want to achieve. Renaming or limiting use to only when it's required are your choices.

The chart in Figure 1 shows you a rough idea of the give-and-take relationship between manageability and speed versus the number of functions you use. (I won't indicate the units here because the numbers depend on individual and team capabilities; the relationship is the important visual data.)


Figure 1. Manageability / speed vs. number of functions
Figure 1. Manageability / speed vs. number of functions

Naming functions

Just as I mentioned in Part 2 of this series (see Resources), using a common naming convention throughout is imperative for efficient code management.

Two other practices to consider:

  • The name you choose should provide a good hint about what the function does.
  • Use prefixes that indicate the package or module.

Suppose you have a module named user that contains user-management functions, then the 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(). The catch is to use verbs rather than nouns because functions always do something.

How many arguments?

There is a good chance that you'll use functions you've constructed. Even if this is not the case, you'll probably want to maximize the reach of your code. To do this, you and other developers will want to stay with functions that are easy to use. No one likes to use functions with cryptic and hard-to-follow arguments, so write easy-to-use functions.

Choosing a name that explains the purpose of a function (as well as reducing the number of arguments the function takes) is a good way to ensure ease of use. What's the magic number for the number of arguments? In my opinion, more than three arguments makes a function less memorable. Complex functions that take many arguments can almost always be broken down to simpler ones.

Nobody likes to use a kludge.

Write quality functions

Say you want to set the header of an HTML document before pushing it off to the browser. The header is all the stuff between the <head>...</head> tags.

Assume that you want to set a title and a meta tag. Instead of a setHeader(title, 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.

On further thought, a header can have only one title tag, but it can have multiple meta tags. If you need to set multiple meta tags, the code will have to call setMeta() multiple times. In that case, a better solution is to pass setMeta() a two-dimensional array with name-value pairs and have the function loop through it -- performing all the activities simultaneously.

In general, simultaneous 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 call it multiple times and to feed it data incrementally. The idea 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 refactor setHeader() to setHeader(title, array), but then we must also consider the lost ability to set the title and meta tags independently of each other.

Also, a header can contain more tags than just title and meta in real-world environments. If more tags need to be added, you'll have to change setHeader() and alter all other code that depends on it: In the latter case, you'll just have to write one more function.

The following equation holds true in all programming languages:

memorable name + unambiguous arguments + speed and efficiency = quality function that holds good in all programming languages

Orchestrating functions with 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 you must be able to reuse.

Let's continue with your hypothetical page-building example. The responsibility of the module here is to build a page by using HTML. (Let's not dive into specifics and code right now because the object of the example is to simply illustrate how functions and groups of functions can be made to mesh easily while increasing the reusability factor.)

Starting with built-in PHP functions, you can build abstraction functions, use them for making more functions that deal with basic neccessities, and then use them in turn to build functions specific to the application. Figure 2 gives you an idea of how this works.


Figure 2. Layered functions
Figure 2. Layered functions

Now, you'll build the page first in memory and then dish out the completed page to the browser.

Building the page in memory offers two major benefits:

  • You can cache completed pages using your own scripts.

  • If a page can't be built successfully, you can discard the half-done page and point the browser to an error page.

Now your users won't see pages screaming with error messages in between.

According to the structure of most pages, you'll divide your page-building module into functions that:

  • Draw the top bar
  • Draw navigation bars
  • Display content
  • Add the footer

You'll also have 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 this the pagebuilder module.

The pagebuilder module does its job by querying a database. Because this database is external to PHP, you'll use a database-abstraction module with the responsibility of providing a homogeneous interface to PHP's different vendor-specific database functions. Important functions in this module are connecting to a database, querying the database, and giving out results of the query.

Pretend you also want to implement a site-wide search engine. This module will be responsible for searching documents on the site related to a keyword or a group of keywords and displaying results based on the relevance or maximum occurrence of the search string. If you also want to log the searches for audit, this module will work with the database-abstraction module.

Remember, you are accepting input from the user. You need to screen it and discard things that seem malicious. This calls for another module, one that validates the data that users submit through forms.

By now you must have a rough idea about the concept I am hinting at. Most core functionality must be separated into logical modules and to do their tasks, your applications must use the functions that these modules provide.

Functional techniques

Figure 3. Our layered page-building application

Using references

Simply put, references are just like pointers in C. The only difference is that you don't need to de-reference them in PHP like you do with the * operator in C. You can think of them as an alias to a variable, array, or object. Whatever operations you carry on, the alias affects the real variable.

Listing 1 offers a look at an example.


Listing 1. Variable references

<?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 arguments directly. Listing 2 shows an example that demonstrates the problem.

<?php

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

$myNum  = 15;
$result = half($myNum);

print("The half of $myNum is: $result\n");
print("\$myNum contains: $myNum\n");

?>

We want $myNum to be altered directly; this can be easily done by passing the reference of $myNum to the half() function. But remember, this is not a good practice. Developers who use your code will have to keep track of the references used. This can be one way bugs creep in unintentionally. It also affects how easy your functions are to use .

A better practice is to use references directly in the function declaration -- in our case, half(&$num) instead of half($num). This way you can intentionally forget to pass arguments to functions by remembering the reference.

PHP takes care things behind the scenes. Newer PHP versions (from 4.0 forward) deprecate call-time pass-by-reference and issue a warning regardless. (Some advice here: If you are using code made for older PHP versions, it's better to update it rather than changing PHP's behavior by altering the php.ini file.

Retaining variables between function calls

You often need to maintain the value of variables between function calls. Global variables can be used, but variables are vulnerable and may 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 when I want to count how many user-defined functions are executed on sites in which debugers are not available. I simply alter all functions (using an automated script, of course) and add a call to function that does the job of counting on the first line of the function body. Listing 3 describes the function.

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

Collecting the return value in a variable by calling funcCount() just before the script finishes does the trick. Surprisingly, $count is not reset to zero; the line that initializes the static variable is only executed once.

If you must access a global variable inside a function, you need to use the global keyword before using the variable.

One more thing about starting from PHP 4 -- it's OK to use a function first and define it afterwards, provided you don't attempt to declare it twice.

Doing dynamic 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 or when you want to call a particular function when an event external to your system has been triggered. Scripts that communicate over a network illustrate this situation.

The method is similar to using variable names. You simply use the external event to set a variable and use it as a function (provided you have declared corresponding functions). Confused? Listing 4 make things clear.

<?php

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';
$my_func();

// Greet the user
$my_func = 'say_greeting';
$my_func();

// Call it a day
$my_func = 'say_bye';
$my_func();

?>

When you feel lazy, you can also use this method for writing several switch-case statements to evaluate which function to use. Simply set the variable and use it as a function. However, though we are deliberately setting variables here, remember that it can be done dynamically and that is what gives this technique its power.


Summary

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

In the next article we'll illuminate classes and objects in PHP, build on our current skills, and hammer out some code that does caching and database abstraction.


Resources

  • In "The Art Of Software Development: Understanding Need" at Developer Shed, icarus focuses on the first part of the application development cycle, explaining some of the things you'll have to do before you sit down to write your first line of code.

  • Read Harish Kamath's tutorial, "Using PHP with Java" at Developer Shed; it's full of illustrative code examples, tested on Linux/i586 with JDK 1.3.0, Apache 1.3.20, and PHP 4.1.1.

  • Mike Britton's article, "Scratching the Surface: Getting Started with PHP Fusebox," delivers a thorough introduction to the latest version of Fusebox -- a scalable and effective Web-box style architecting tool.

  • The DevArticles Web site offers a tutorial, "10 PHP Functions I Bet You Didn't Know About!," that delivers 10 uncommon functions you can use to save time when coding with PHP.

  • Also from the DevArticles Web site: "Making Sense Of PHP Errors" illuminates several types of PHP errors and how to make sense of them during development time.

  • The PHP Debugger is available (at no cost) for profiling and debugging PHP code.

  • Part 1 of this series, "Lay the foundation" (developerWorks, August 2002), discusses how to lay a rock-solid foundation in PHP.

  • Part 2 of this series, "Use variables effectively" (developerWorks, September 2002), shows how to make script configuration easy by constructing a configuration file parser using variable names that differ.

  • Visit the dW Web architecture zone for more Web architecture resources.

About the author

Amol Hatwar has been into computers for as long as he can remember. Being a GNU/Linux bigot, he feels guilty for spending his past life programming on Microsoft platforms. He now works as an independent consultant, helping companies migrate to GNU/Linux. A specialist in developing Web applications, he spends his mythical free time dabbling with technologies no one has heard about. His current interests include open-source software, Web services, peer-to-peer computing, and high-availability clustering. You can contact Amol at amolhatwar@consultant.com.

Comments



Trademarks

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development, Linux, Open source
ArticleID=11729
ArticleTitle=Develop rock-solid code in PHP: Write reusable functions, Part 3
publish-date=11012002
author1-email=amolhatwar@consultant.com
author1-email-cc=