Zend PHP 5 Certification Study Guide (2014)

Functions

The heart of PHP programming is, arguably, the function. The ability to encapsulate any piece of code in such a way that it can be called again and again is invaluable—it is the cornerstone of structured procedural and object-oriented programming.

In this chapter, we focus on various aspects of creating and managing functions from within PHP scripts—therefore, this chapter is about writing functions, rather than using them.

Basic Syntax

Function syntax is—at its most basic—very simple. To create a new function, we simply use the keyword function, followed by an identifier, a pair of parentheses and braces (otherwise known as a code block):

function name() {

    // ... code

}

PHP function names are not case-sensitive. As with all identifiers in PHP, the name must consist only of letters (a-z), numbers and the underscore character, and must not start with a number.

To make your function do something, simply place the code to be executed between the braces, and then call it.

function hello() {

    echo "Hello World!";

}

hello(); // Displays "Hello World!"

Returning Values

All functions in PHP return a value—even if you don’t explicitly cause them to. Thus, the concept of “void” functions does not really apply to PHP. You can specify the return value of your function by using the return keyword:

Listing 2.1: Returning a value

function hello() {

    return "Hello World"; // No output is shown

}

// Assigns the return value "Hello World" to $txt

$txt = hello();

echo hello(); // Displays "Hello World"

Naturally, return also allows you to interrupt the execution of a function and exit it even if you don’t want to return a value:

Listing 2.2: Returning and exiting early

function hello($who) {

    echo "Hello $who";

    if ($who == "World") {

        return; // Nothing else in the

                // function will be processed

    }

    echo ", how are you";

}

hello("World"); // Displays "Hello World"

hello("Reader") // Displays "Hello Reader, how are you?"

Note, however, that even if you don’t return a value, PHP will still cause your function to return NULL.

Functions can also be declared so that they return by reference; this allows you to return a variable as the result of the function, instead of as a copy (returning a copy is the default for every data type except objects). Typically, this is used for things like resources (such as database connections) and when implementing the Factory pattern. However, there is one caveat: you must return a variable—you cannot return an expression by reference, or use an empty return statement to force a NULL return value:

Listing 2.3: Returning by reference

function &query($sql) {

    $result = mysql_query($sql);

    return $result;

}

// The following is incorrect and will cause PHP

// to emit a notice when called.

function &getHello() {

    return "Hello World";

}

// This will also cause the warning to be

// issued when called

function &test() {

    echo 'This is a test';

}

Variable Scope

PHP has three variable scopes: global scope, function scope, and class scope. The global scope is, as its name implies, available to all parts of the script; if you declare or assign a value to a variable outside of a function or class, that variable is created in the global scope.

Class scope is discussed in the Object-Oriented Programming in PHP chapter.

However, any time you enter a function, PHP creates a new scope—a “clean slate” that, by default, contains no variable and is completely isolated from the global scope. Any variable defined within a function is no longer available after the function has finished executing. This allows the use of names which may be in use elsewhere without having to worry about conflicts.

Listing 2.4: Variable scope

$a = "Hello World";

function hello() {

    $a = "Hello Reader";

    $b = "How are you";

}

hello();

echo $a; // Will output Hello World

echo $b; // Will emit a notice

There are two ways to access variables in the global scope from inside a function; the first consists of “importing” the variable inside the function’s scope by using the global statement:

Listing 2.5: Accessing with the global statement

$a = "Hello";

$b = "World";

function hello() {

    global $a, $b;

    echo "$a $b";

}

hello(); // Displays "Hello World"

You will notice that global takes a comma-separated list of variables to import—naturally, you can have multiple global statements inside the same function.

Many developers feel that the use of global introduces an element of confusion into their code, and that “connecting” a function’s scope with the global scope can easily become a source of problems. They prefer, instead, to use the $GLOBALS superglobal array, which contains all the variables in the global scope:

Listing 2.6: Accessing $GLOBALS array

$a = "Hello";

$b = "World";

function hello() {

    echo $GLOBALS['a'] .' '. $GLOBALS['b'];

}

hello(); // Displays "Hello World"

Passing Arguments

Arguments allow you to inject an arbitrary number of values into a function in order to influence its behaviour:

Listing 2.7: Passing arguments

function hello($who) {

    echo "Hello $who";

}

hello("World");

/* Here we pass in the value, "World", and the function

   displays "Hello World" */

You can define any number of arguments and, in fact, you can pass an arbitrary number of arguments to a function, regardless of how many you specified in its declaration. PHP will not complain unless you provide fewer arguments than you declared.

Additionally, you can make arguments optional by giving them a default value. Optional arguments must be rightmost in the list and can only take simple values—expressions are not allowed:

Listing 2.8: Setting argument defaults

function hello($who = "World") {

    echo "Hello $who";

}

hello();

/* This time we pass in no argument and $who is assigned

   "World" by default. */

hello("Reader");

/* This time we override the default argument */

Type-hinting

Type-hinting occurs when a function (or method) specifies what type of data must be passed in. For example, specifying that an argument must be an integer, in which case passing a string or even a float will cause an error.

Because PHP is loosely typed, and because almost all input data for PHP is string-based, it does not support type-hinting in the traditional sense, specifically for scalar values.

With PHP you may specify either one of:

·        Any class or interface name - the value must be of that class, or a sub-class of it, or implement that interface.

·        Array - the value must be an array. Introduced in PHP 5.1.

·        Callable - the value must be a valid callback (see the section on Callbacks for more details). Added in PHP 5.4.

More information on classes and interfaces can be found in the Object-Oriented Programming chapter.

To type-hint an argument, just prepend the argument with the required type:

Listing 2.9: Type-hinting arguments

function hello(array $people = null) {

    // $people must be an array or if nothing is

    // passed, it will be false

}

function f(SomeObject $obj, Callable $callback) {

    // $obj must be compatible with SomeObject

    // $callback must be a valid callback

}

When combining type-hinting with a default value (as in our first function above), the default value must be null. Also, PHP only uses the default value if no value is passed. If an invalid value is passed it will instead cause an error.

Passing Arguments by Reference

Function arguments can also be passed by reference, as opposed to the traditional by-value method, by prefixing them with the by-reference operator &. This allows your function to affect external variables.

Listing 2.10: Passing by reference

function countAll(&$count) {

    if (func_num_args() == 0) {

        die("You need to specify at least one argument");

    } else {

        // Returns an array of arguments

        $args = func_get_args();

        // Remove the defined argument from the beginning

        array_shift($args);

        foreach ($args as $arg) {

            $count += strlen($arg);

        }

    }

}

$count = 0;

countAll($count, "foo", "bar", "baz"); // $count equals 9

Note—and this is very important—that only variables can be passed as by-reference arguments; you cannot pass an expression as a by-reference parameter.

Unlike PHP 4, PHP 5 allows default values to be specified for parameters even when they are declared as by-reference:

Listing 2.11: Passing by reference with defaults

function cmdExists($cmd, &$output = null) {

    $output = `whereis $cmd`;

    if (strpos($output, DIRECTORY_SEPARATOR) !== false) {

        return true;

    } else {

        return false;

    }

}

In the example above, the $output parameter is completely optional—if a variable is not passed in, a new one will be created within the context of cmdExists() and, of course, destroyed when the function returns.

Variable-Length Argument Lists

A common mistake when declaring a function is to write the following:

function foo($optional = "null", $required) {

}

This does not cause any errors to be emitted, but it also makes no sense—because you will never be able to omit the first parameter ($optional) if you want to specify the second, and you can’t omit the second because PHP will emit a warning.

In this case, what you really want is variable-length argument lists—that is, the ability to create a function that accepts a variable number of arguments, depending on the circumstances. A typical example of this behaviour is exhibited by the var_dump() family of functions.

PHP provides three built-in functions to handle variable-length argument lists: func_num_args(), func_get_arg() and func_get_args(). Here’s an example of how they’re used:

Listing 2.12: Handling variable-length argument lists

function hello() {

    if (func_num_args() > 0) {

        // The first argument is at position 0

        $arg = func_get_arg(0);

        echo "Hello $arg";

    } else {

        echo "Hello World";

    }

}

hello("Reader"); // Displays "Hello Reader"

hello(); // Displays "Hello World"

You can use variable-length argument lists even if you do specify arguments in the function header. However, this won’t affect the way the variable-length argument list functions behave—for example, func_num_args() will still return the total number of arguments passed to your function, both declared and anonymous.

Listing 2.13: Counting variable-length argument lists

function countAll($arg1) {

   $args = func_get_args(); // Returns an array of arguments

   // Remove the defined argument from the beginning

   array_shift($args);

   $count = strlen($arg1);

   foreach ($args as $arg) {

      $count += strlen($arg);

   }

   return $count;

}

echo countAll("foo", "bar", "baz"); // Displays '9'

Our countAll function requires at least one value, so we define one explicit (named) argument, and then use func_get_args() to retrieve the rest.

Variadics

PHP 5.6 introduced a new feature called variadics. Variadics allow you to explicitly denote a function as accepting a variable length argument list.

With this new feature, we no longer have to manually retrieve our variable arguments, nor separate them from named arguments.

The syntax for variadics is an argument variable prefixed with three periods: ... $args. The variadic argument must be the last argument in the argument list.

If we wanted to rewrite our function above, we can do so like this:

Listing 2.14: Using variadics

function f($arg1, ...$args) {

    $count = strlen ($arg1);

    foreach ($args as $arg) {

        $count += strlen($arg);

    }

    return $count;

}

Additionally, variadics allows for passing by reference:

function foo(&...$args) {

    // each argument is passed by reference

}

As well as allowing for type-hinting:

function foo(array ...$args) {

    // each argument is an array

}

Despite the improvements that variadics brings, it is still important to keep in mind that variable-length argument lists are full of potential pitfalls; while they are very powerful, they do tend to make your code confusing, because it’s nearly impossible to provide comprehensive test cases if a function that accepts a variable number of parameters is not constructed properly.

Argument Unpacking

In addition to the new variadics functionality in PHP 5.6, the same syntax is used for another new feature known as argument unpacking or “splat.”

Argument unpacking works the opposite of the way variadics does, allowing you to unpack an array-like data structure into an argument list when calling a function or method.

$args = ["World", "Universe", "Hello World"];

echo str_replace(...$args); // Returns "Hello Universe"

Argument unpacking works with any function or method, but similar to variadics, must be the last argument passed in.

Summary

Functions are one of the most often used components of the PHP language (or, for that matter, of any language). Without them, it would be virtually impossible to write reusable code—or even use object-oriented programming techniques.

For this reason, you should be well versed not only in the basics of function declaration, but also in the slightly less obvious implications of elements like passing arguments by reference and handling variable-length argument lists. The exam features a number of questions centered around a solid understanding of how functions work—luckily, these concepts are relatively simple and easy to grasp, as illustrated in this chapter.