CHAPTER 5 Working with Functions and Classes IN THIS CHAPTER: 5.1 Defining Custom Functions 5.2 Avoiding Function Duplication 5.3 Accessing External Variables from Within a Function 5.4 Setting Default Values for Function Arguments 5.5 Processing Variable-Length Argument Lists 5.6 Returning Multiple Values from a Function 5.7 Manipulating Function Inputs and Outputs by Reference 5.8 Dynamically Generating Function Invocations 5.9 Dynamically Defining Functions 5.10 Creating Recursive Functions 5.11 Defining Custom Classes 5.12 Automatically Executing Class Initialization and Deinitialization Commands 5.13 Deriving New Classes from Existing Ones 5.14 Checking If Classes and Methods Have Been Defined 5.15 Retrieving Information on Class Members 5.16 Printing Instance Properties 5.17 Checking Class Antecedents 5.18 Loading Class Definitions on Demand 5.19 Comparing Objects for Similarity 5.20 Copying Object Instances 5.21 Creating Statically-Accessible Class Members 5.22 Altering Visibility of Class Members 5.23 Restricting Class Extensibility 5.24 Overloading Class Methods 5.25 Creating Catch-All Class Methods 5.26 Auto-Generating Class API Documentation 131
132 PHP Programming s As with any programming language worth its salt, PHP supports functions and classes, which you can use to make your code more modular, maintainable, and reusable. Functions, in particular, have been wellsupported in PHP for a long time, and so most of the new developments in PHP have focused on the object model, which has been completely redesigned to bring PHP in closer compliance with OOP standards. The solutions in this chapter are therefore a mix of old and new techniques. Among the golden oldies: dealing with variable scope; using variable-length argument lists and default arguments; extending classes; using class constructors; and checking class ancestry. Among the brash newcomers: overloading methods; cloning and comparing objects; using abstract classes; and protecting class members from outside access. Together, they add up to a fairly interesting collection. See for yourself! 5.1 Defining Custom Functions You want to define your own functions. Use PHP s function keyword to name and define custom functions, and invoke them as required: // define function // to calculate circle area function getcirclearea($radius) { return pi() * $radius * $radius; // invoke function // for circle of radius 10 // result: "The area of a circle with radius 10 is 314.159265359" echo "The area of a circle with radius 10 is ". getcirclearea(10);
Chapter 5: Working with Functions and Classes 133 A function is an independent block of code that performs a specific task, and can be use more than once at different points within the main program. Every programming language comes with built-in functions and typically also allows developers to define their own custom functions. PHP is no exception to this rule. Function definitions in PHP begin with the function keyword, followed by the function name (this can be any string that conforms to PHP s naming rules), a list of arguments in parentheses, and the function s code within curly braces. Function arguments make it possible to supply variable input to the function at run time. Within the function itself, the return keyword is used to return the result of the function s operations to the calling program. In the previous example the function is named getcirclearea(), accepts a single argument (the circle radius, represented by $radius), and uses this argument to calculate the area of the circle. Once a named function has been defined in the manner described previously, using it is as simple as calling (or invoking) it by its name, in much the same way one would call built-in functions such as implode() or exists(). An example of such function invocation can be seen in the previous listing, which demonstrates the newly-minted getcirclearea() function being invoked with an argument of 10 units (the circle radius) and returning a value of 314.16 units (the corresponding circle area). 5.2 Avoiding Function Duplication You want to test if a function has already been defined. Use PHP s function_exists() function: // sample function function dothis() { return false;
134 PHP Programming s // test if function exists // result: "Function exists" echo function_exists("dothis")? "Function exists" : "Function does not exist"; // result: "Function does not exist" echo function_exists("dothat")? "Function exists" : "Function does not exist"; It s a good idea to check for the prior existence of a function before declaring it, because PHP generates an error on any attempt to re-declare a previously defined function. The function_exists() function provides an easy solution to the problem, as illustrated in the previous example. 5.3 Accessing External Variables from Within a Function You want to access a variable from the main program within a function definition. Use the global keyword within the function definition to import the variable from the global scope: // define variable outside function $name = "Susan"; // access variable from within function function whoami() { global $name; return $name;
Chapter 5: Working with Functions and Classes 135 // call function // result: "Susan" echo whoami(); By default, variables within a function are isolated from variables outside it in PHP. The values assigned to them, and the changes made to them, are thus local and restricted to the function space alone. Often, however, there arises a need to share a variable from the main program with the code inside a function definition. You can accomplish this by making the variable global, so that it can be manipulated both inside and outside the function. PHP s global keyword, when prefixed to a variable name within a function definition, takes care of making the variable global. This is illustrated in the previous listing. An alternative way of accomplishing the same thing is to access the variable by name from the special $_GLOBALS associative array, as demonstrated in the next listing: // define variable outside function $name = "Susan"; // access variable from within function function whoami() { return $GLOBALS['name']; // call function // result: "Susan" echo whoami(); For a more detailed demonstration of variable scope inside and outside a function, consider the following listing: // define a variable outside the function $name = "Joe";
136 PHP Programming s // define a function to alter the variable function whoami() { // access the variable from inside the function global $name; // check the variable // result: "I am Joe at the beginning of the function." echo "I am $name at the beginning of the function.\n"; // redefine the variable inside the function $name = "Jane"; // check the variable // result: "I am Jane at the end of the function." echo "I am $name at the end of the function.\n"; // check the variable // result: "I am Joe before running the function." print "I am $name before running the function.\n"; // call the function echo whoami(); // check the variable // result: "I am Jane after running the function." print "I am $name after running the function.\n"; NOTE PHP also comes with so-called superglobal variables (or superglobals) variables that are always available, regardless of whether you re inside a function or outside it. The $_SERVER, $_ POST, and $_GET variables are examples of superglobals, which is why you can access things like the currently executing script s name or form values even inside a function. The good news about superglobals is that they re always there when you need them, and you don t need to jump through any hoops to use the data stored inside them. The bad news is that the superglobal club is a very exclusive one, and you can t turn any of your own variables into superglobals. Read more about superglobals and variable scope at http://www.php.net/variables.predefined and http://www.php.net/variables.scope.
Chapter 5: Working with Functions and Classes 137 5.4 Setting Default Values for Function Arguments You want to set default values for one or more function arguments, thereby making them optional. Assign default values to those arguments in the function signature: // define function // with default arguments function orderpizza($crust, $toppings, $size="12") { return "You asked for a $size-inch pizza with a $crust crust and these toppings: ". implode(', ', $toppings); // call function without optional third argument // result: "You asked for a 12-inch pizza with a // thin crust and these toppings: cheese, anchovies" echo orderpizza("thin", array("cheese", "anchovies")); Normally, PHP expects the number of arguments in a function invocation to match that in the corresponding function definition, and it will generate an error in case of a smaller argument list. However, you might want to make some arguments optional, using default values if no data is provided by the user. You can do this by assigning values to the appropriate arguments in the function definition. TIP Place optional arguments after mandatory arguments in the function s argument list.
138 PHP Programming s 5.5 Processing Variable-Length Argument Lists You want your function to support a variable number of arguments. Use PHP s func_get_args() function to read a variable-length argument list: // define a function function somefunc() { // get the number of arguments passed $numargs = func_num_args(); // get the arguments $args = func_get_args(); // print the arguments print "You sent me the following arguments: "; for ($x=0; $x<sizeof($args); $x++) { print "\nargument $x: "; // check if an array was passed // iterate and print contents if so if (is_array($args[$x])) { print " ARRAY "; foreach ($args[$x] as $index=>$element) { print " $index => $element "; else { print " $args[$x] "; // call the function with different arguments // returns: "You sent me the following arguments: // Argument 0: red // Argument 1: green // Argument 2: blue
Chapter 5: Working with Functions and Classes 139 // Argument 3: ARRAY 0 => 4 1 => 5 // Argument 4: yellow" somefunc("red", "green", "blue", array(4,5), "yellow"); PHP s func_get_args() function is designed specifically for functions that receive argument lists of varying length. When used inside a function definition, func_get_args() returns an array of all the arguments passed to the function; the individual arguments can then be extracted and processed with a for() loop. Remember that the argument list can contain a mixture of scalar variables and arrays, so if you re unsure what input to expect, make it a point to check the type of each argument before deciding how to process it. Here s another example of this in action: // define function that accepts // a dynamic number of arguments function calcsum() { $sum = 0; // get argument list as array $args = func_get_args(); // process argument list // add each argument to previous total // if any of the arguments is an array // use a loop to process it for ($x=0; $x<sizeof($args); $x++) { if (is_array($args[$x])) { foreach ($args[$x] as $a) { $sum += $a; else { $sum += $args[$x]; return $sum;
140 PHP Programming s // call function with 2 scalar arguments // result: "The sum of 1 and 10 is 11." echo "The sum of 1 and 10 is ". calcsum(1,10). ".\n"; // call function with mixture // of 3 scalar and array arguments // result: " The sum of 1, 2, 5 and 1 is 9." echo "The sum of 1, 2, 5 and 1 is ". calcsum(1, 2, array(5,1)). ".\n"; 5.6 Returning Multiple Values from a Function You want to return more than one value from a function. Place the set of desired return values in an array, and return that instead: // define function // that returns more than one value function getuserinfo() { return array("simon Doe", "London", "simon@some.domain.edu"); // extract returned list into separate variables // result: "My name is Simon Doe from London. // Get in touch at simon@some.domain.edu" list ($name, $place, $email) = getuserinfo(); echo "My name is $name from $place. Get in touch at $email"; A PHP function can only return a single value to the caller; however, this value may be of any supported type (scalar, array, object, and so on). So, if you d like a function to return multiple values, the simplest way to do this is to add them all to an array and return that instead.
Chapter 5: Working with Functions and Classes 141 5.7 Manipulating Function Inputs and Outputs by Reference You want to pass input arguments to, or receive return values from, a function by reference (instead of by value). To pass an argument by reference, prefix the argument with the & symbol in the function definition: // define a function // that changes a variable by reference function changeday(&$day) { $day = "Thursday"; return $day; // define a variable outside the function $day = "Sunday"; // check variable // result: "Before running changeday(), it is Sunday." echo "Before running changeday(), it is $day.\n"; // pass variable by reference changeday($day); // check variable // result: "After running changeday(), it is Thursday." echo "After running changeday(), it is $day.";
142 PHP Programming s To return a value by reference, prefix both the function name and the function invocation with the & symbol: // define a function // that returns a value by reference function &incrementnum() { global $num; $num++; return $num; // define a variable outside the function $num = 0; // invoke function // get return value of function as reference // result: "Number is 1." $retval =& incrementnum(); echo "Number is $retval.\n"; // invoke function again incrementnum(); // check reference // result: "Number is 2." echo "Number is $retval.\n"; By default, arguments to PHP functions are passed by value that is, a copy of the variable is passed to the function, with the original variable remaining untouched. However, PHP also allows you to pass by reference that is, instead of passing a value to a function, you pass a reference to the original variable and have the function act on that instead of a copy. In the first example, because the argument to changeday() is passed by reference, the change occurs in the original variable rather than in a copy. That s the reason why, when you re-access the variable $day after running the function on it, it returns the modified value Thursday instead of the original value Sunday. It s also possible to get a reference to a function s return value, as in the second example. There, $retval is a reference to (not a copy of) a global variable.
Chapter 5: Working with Functions and Classes 143 So, every time the global variable changes, the value of $retval changes as well. If, instead, you set things up to get a copy of (not a reference to) the function s return value, the value of $retval would remain 1. NOTE References make it possible to manipulate variables outside the scope of a function, in much the same way as the global keyword did in the listing in 5.3: Accessing External Variables From Within a Function. The PHP manual makes the relationship clear when it says when you declare [a] variable as global $var, you are in fact creating reference to a global variable. Read more about references at http://www.php.net/references. 5.8 Dynamically Generating Function Invocations You want to dynamically generate a function invocation from a PHP variable. Use parentheses to interpolate the variable name with the function invocation: // sample function function playtrack($id) { echo "Playing track $id"; // define variable for operation $op = "play"; // build function name from variable $func = $op. "Track"; // call function // result: "Playing track 45" $func(45);
144 PHP Programming s PHP supports the use of variable functions, wherein a function name is dynamically generated by combining one or more variables. When PHP encounters such a variable function, it first evaluates the variable(s) and then looks for a function matching the result of the evaluation. The previous listing illustrates this, creating a function invocation from a variable. 5.9 Dynamically Defining Functions You want to define a function dynamically when another function is invoked. Nest one function inside the other: // define function function findoil() { // define nested function // this function only becomes available // once the outer function has been invoked function startdrilling() { echo "Started drilling. We're gonna be rich!\n"; echo "Found an oil well. Thar she blows!\n"; // run functions // returns: "Found an oil well. Thar she blows!" findoil(); // returns: "Started drilling. We're gonna be rich!" startdrilling(); PHP supports nested functions, wherein the inner function is defined only when the outer one is invoked. In the previous listing, the startdrilling() function
Chapter 5: Working with Functions and Classes 145 does not exist until after the findoil() function is called. You can verify this by invoking startdrilling() before and after invoking findoil(); PHP will return an undefined function fatal error in the first instance, but not in the second. 5.10 Creating Recursive Functions You want to recursively perform a task. Write a recursive function that runs repeatedly until a particular condition is met: // recursive function // to calculate factorial function calcfactorial($num) { // define variable to hold product static $product = 1; // recurse until $num becomes 1 if ($num > 1) { $product = $product * $num; $num--; calcfactorial($num); return $product; // result: "Factorial of 5 is 120" echo "Factorial of 5 is ". calcfactorial(5); PHP supports recursive functions, which are essentially functions that call themselves repeatedly until a particular condition is met. Recursive functions are commonly used to process nested data collections for example, multidimensional arrays, nested file collections, XML trees, and so on. The previous listing illustrates
146 PHP Programming s a simple example of recursion a function that calculates the factorial of a number by repeatedly calling itself with the number, reducing it by 1 on each invocation. Recursion stops only once the number becomes equal to 1. Here s another example of recursion; this one processes a directory collection and prints a list of the files found: // define recursive function // to display directory contents function recursedir($dir) { // check for valid argument if (!is_dir($dir)) { die("argument '$dir' is not a directory!"); // open directory handle $dh = opendir($dir) or die ("Cannot open directory '$dir'!"); // iterate over files while (($file = readdir($dh))!== false) { // ignore. and.. items if ($file!= "." && $file!= "..") { if (is_dir("$dir/$file")) { // if this is a subdirectory // recursively process it recursedir("$dir/$file"); else { // if this is a file // print file name and path echo "$dir/$file \n"; // recursively process directory recursedir('/tmp'); Here, every time the function encounters a directory entry, it first checks to see if that value is a file or a directory. If it s a directory, the function calls itself again to process the directory; if it s a file, the file name is printed. The process continues until the end of the directory tree is reached.