Functions

Despite the fact that PHP comes with such a large selection of functions to perform all sorts of tasks, you will want to create your own functions when the need arises. If you find yourself doing the same thing repeatedly, or you want to share code across projects, user functions are for you.

Writing monolithic programs—code that starts at the beginning and runs straight through to the end—is considered very bad for program maintainability, as you are not able to reuse code. By writing functions, you make your code shorter, easier to control and maintain, and less prone to bugs.

A Simple User Function

You can give your functions whatever name you like; they follow the same guidelines (without the $) as PHP's variables. You may not redefine PHP's built-in functions, and care should be taken to ensure that your function names do not collide with existing PHP functions—just because you don't have the imagepng() function available, it doesn't mean others also won't.

The simplest user function in PHP looks something like this:

    function foo() {
            return 
 1;
    }

    print foo();

You define your functions with the function keyword, followed by the name of the function and two parentheses. The actual code your function will execute lies between braces—in our example function $foo, our sole line of code is return 1; we will get to that in a moment.

After the function definition, we can treat foo() like any other function, as seen in line four where we print out the value it returns (known as its return value).

Return Values

You're allowed to return one (and only one) value back from functions, and you do this by using the return statement. In our example, we could have used "return 'foo';" or "return 10 + 10;" to pass other values back, but return 1; is easiest and usually the most common, as it is the same as return true;.

You can return any variable you want, as long as it is just one variable—it can be an integer, a string, a database connection, etc. The return keyword sets up the function return value to be whatever variable you use with it, then exits the function immediately. You can also just use return;, which means "exit without sending a value back." If you try to assign to a variable the return value of a function that has no return value (e.g., it uses return; rather than return $someval;), your variable will be set to NULL.

Consider this script:

    function foo() {
            print "In function";
            return 1;
            print "Leaving function...";
    }

            print foo();
    ?>

That will output In function, followed by 1, and then the script will terminate. The reason we never see Leaving function ... is because the line return 1 passes one back then immediately exits—the second print statement in foo() is never reached.

If you want to pass more than one value back, you need to use an array—this is covered in Chapter 5.

A popular thing to do is to return the value of a conditional statement, for example:

    return $i > 10;

If $i is indeed greater than 10, the > operator will return 1, so it is the same as having return 1, but if $i is less than or equal to 10, it is the same as being return 0.

Parameters

You can design your functions to accept parameters by modifying the definition to include as many as you want. You need to give each parameter the name you will be using to refer to it inside the function—when you later call that function, PHP will copy the values it receives into these parameters, like this:

    function multiply($num1, $num2) {
            $total = $num1 * $num2;
            return $total;
    }

            $mynum = multiply(5, 10);
    ?>

After running that script, $mynum will be set to 50. The multiply() function could have been rewritten so that it was just one line: return $num1 * $num2, but it is good to show that you can make your functions as long as you want.

Passing By Reference

When it comes to references, things get more complicated because you need to be able to accept parameters by reference and also return values by reference. This is done with the reference operator, &.

Marking a parameter as "passed by reference" is done in the function definition, not in the function call. That is:

    function multiply(&$num1, &$num2) {

is correct, whereas

    $mynum = multiply(&5, &10);

is wrong. This means that if you have a function being used multiple times across your project, you only need edit the function definition to make it take variables by reference. Passing by reference is often a good way to make your script shorter and easier to read—the choice is rarely driven by performance considerations. Consider this code:

    function square1($number) {
            return $number * $number;
    }

    $val = square1($val);

    function square2(&$number) {
            $number = $number * $number;
    }

    square2($val);

The first example passes a copy of $val in, multiplies the copy, then returns the result, which is then copied back into $val. The second example passes $val in by reference, and it is modified directly inside the function—hence square2($val) is all that is required, instead of the first example's copying.

A reference is a reference to a variable. If you define a function as accepting a reference to a variable, you cannot pass a constant into it. That is, given our definition of square2(), you cannot call the function using square2(10); 10 is not a variable, so it cannot be treated as a reference.

Returning by Reference

Unlike passing values by reference, where you specify the referenced nature of the parameter in the function definition, to return references you need to specify such in the definition and at call time. To specify that a function should return a reference, you place the ampersand reference operator before the function name, and to specify that you wish to reference the result of the function as opposed to copying it, you use the normal reference assign that you learned earlier.

Here's how that looks:

    function &return_fish() {
            $fish = "Wanda";
            return $fish;
    }

    $fish_ref =& return_fish();

Default Parameters

When designing your functions, it is often helpful to assign default values for parameters that aren't passed. PHP does this for most of its functions, and it saves you having to pass in parameters most of the time, if they are usually the same.

To define your own default parameters for a function, add the constant value you would like them to be set to after the variables, like this:

    function doHello($Name = "Paul") {
            return "Hello $Name!\n";
    }

    doHello();
    doHello("Paul");
    doHello("Andrew");

That script will output the following:

    Hello Paul!
    Hello Paul!
    Hello Andrew!

Now, consider this function:

    function doHello($FirstName, $LastName = "Smith") { }

That does not mean that both $FirstName and $LastName should be set to Smith. Instead, only $LastName gets that value—PHP treats the two variables as functionally independent of each other, which means you can use code like this:

    function doHello($FirstName = "John", $LastName = "Smith") {
            return "Hello, $FirstName $LastName!\n";
    }

So, to greet three people named John Smith, Tom Davies, and Tom Smith, you would use this code:

    doHello();
    doHello("Tom", "Davies");
    doHello("Tom");

If you wanted to greet someone named John Wilson, ideally you would let PHP fill in the first parameter for you, as John is the default for the function, and you would provide the Wilson part. But if you try code like this, you will see it does not work:

    doHello("Wilson");

Instead of John Wilson, you will get Wilson Smith—PHP will assume the parameter you provided was for the first name, as it fills its parameters from left to right. The same logic dictates that you cannot put a default value before a non-default value, like this:

    function doHello($FirstName = "Joe", $LastName) { }

If someone used doHello("Peter"), would they be trying to provide a value for $FirstName to use instead of the default, or do they want the default value in there and Peter for $LastName? Hopefully you can see why PHP will flag up an error if you attempt this!

Variable Parameter Counts

The printf() function (see Chapter 7) is able to take an arbitrary number of parameters—it could take just one parameter, or five, or fifty, or five hundred. It can take as many as are passed into it by the user. This is known as a variable-length parameter list, and it is automatically implemented in user functions. For example:

    function some_func($a, $b) {
            $j = 1;
    }

    some_func(1,2,3,4,5,6,7,8);

Here the function some_func() is defined to take only two parameters, $a and $b, but we call it with eight parameters and the script should run without a problem. This is one aspect in which PHP varies greatly from C: in C, your functions must be used precisely as declared in their prototypes. In the example above, 1 will be placed into $a, and 2 will be placed into $b, but what happens to the other parameters?

Coming to your rescue are three functions: func_num_args(), func_get_arg(), and func_get_args(), of which the first and last take no parameters. To get the number of arguments that were passed into your function, call func_num_args() and read its return value. To get the value of an individual parameter, use func_get_arg() and pass in the parameter number you want to retrieve to have its value returned back to you. Finally, func_get_args() returns an array of the parameters that were passed in. Here's an example:

    function some_func($a, $b) {
            for ($i = 0; $i < func_num_args(); ++$i) {
                    $param = func_get_arg($i);
                    echo "Received parameter $param.\n";
            }
    }

    function some_other_func($a, $b) {
            $param = func_get_args();
            $param = join(", ", $param);
            echo "Received parameters: $param.\n";
    }

    some_func(1,2,3,4,5,6,7,8);
    some_other_func(1,2,3,4,5,6,7,8);

Using func_num_args(), you can easily implement function error checking. You can, for example, start off each of your functions by checking to make sure func_num_args() is what you are expecting, and, if not, exit. Once you add func_get_arg() into the mix, however, you should be able to easily create your own functions that work with any number of parameters.

Variable Scope in Functions

Variables declared outside of functions and classes are considered global, which means they are generally available elsewhere in the script. However, as functions are independent blocks, their variables are self-contained and do not affect variables in the main script. In the same way, variables from the main script are not implicitly made available inside functions. Take a look at this example:

    function foo() {
            $bar = "wombat";
    }


    $bar = "baz";
    foo();
    print $bar;

Execution of the script starts at the $bar = "baz" line, and then calls the foo() function. Now, as you can see, foo() sets $bar to wombat, then returns control to the main script where $bar is printed out. Function foo() is called, and, having no knowledge that a $bar variable exists in the global scope, creates a $bar variable in its local scope. Once the function ends, all local scopes are tossed away, leaving the original $bar variable intact.

Overriding Scope with the GLOBALS Array

The $GLOBALS superglobal array allows you to access global variables even from within functions. All variables declared in the global scope are in the $GLOBALS array, which you can access anywhere in the script. Here is a demonstration:

    function foo() {
            $GLOBALS['bar'] = "wombat";
    }

    $bar = "baz";
    foo();
    print $bar;

That would print wombat to the screen because the foo() function literally alters a variable outside of its scope. Even after it returns control back to the main script, its effect is still felt. You can read variables in the same way:

    $localbar = $GLOBALS['bar'];

However, that is quite hard on the eyes. PHP allows you to use a special keyword, GLOBAL, to allow a variable to be accessed locally:

    function myfunc() {
            GLOBAL $foo, $bar, $baz;
            ++$baz;
    }

That would allow a function to read the global variables $foo, $bar, and $baz. The ++$baz line will increment $baz by 1, and this will be reflected in the global scope also.

Recursive Functions

Sometimes the easiest way to model a problem is to make a function call itself—a technique known as recursive function calling. Calculating factorials is a commonly cited example. The factorial of 6 is 6 * 5 * 4 * 3 * 2 * 1, or 720, and is usually represented as "6!". So, given that factorial 6 (6!) is 720, and "7!" is "7 * 6!", you need only calculate "6!" then multiply the result by 7 to get "7!".

This equation can be represented like this: "n! = n * ((n—1)!)". That is, the factorial for any given number is equal to that number multiplied by the factorial of the number one lower—clearly a case for recursive functions . What we need is a function that will accept an integer and, if that integer is not 0, call the function again—this time passing in the same number it accepted, minus 1—then multiply that result by itself. Here is a working script to calculate factorials:

    function factorial($number) {
            if ($number =  = 0) return 1;
            return $number * factorial($number--1);
    }

    print factorial(6);

That will output 720, although you can easily edit the factorial() function call to pass in 20 rather than 6, for example. Factorials increase in value very quickly ("7!" is 5040, "8!" is 40320, etc.), so you will eventually hit a processing limit—not time, but merely recursive complexity; PHP will only allow you to have a certain level of recursion ("18!" is about the max you are likely to be able to calculate using the above code).

As you can see, recursive functions make programming certain tasks particularly easy, and it is not all math, either—consider how easy it is to write a function showchildren() for a forum, which automatically shows all replies to a message, and all replies to those replies, and all replies to the replies to the replies, and so on.

Get PHP in a Nutshell now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.