Functions from zero to hero

Last updated Mar 23, 2024 Published Mar 20, 2018

The content here is under the Attribution 4.0 International (CC BY 4.0) license

In PHP, functions were always widely used when Object Orientation was not so strong, as this was the only way to structure the code well and divide responsibilities. In this chapter, we will see in detail what we can do with functions, the main differences between a function used as a reference and one by value, the use of closures, and the PHP functions that we can use to make our lives easier.

Declaring functions and passing variables by value The syntax for creating a function is very simple. See in the code below that it does not receive parameters and does not return any value:

function treatData()
{
    if ($_POST['name'] == '')
    {
        print 'The name is blank';
    }
}

treatData();

This is a very basic function that just checks if the name parameter passed by POST is blank. If so, a message is displayed.

And we can also create functions by passing parameters:

function add($a, $b)
{
    print $a + $b;
}

add(10, 20); //30

Functions can also return values, like the following code, which transforms any text passed to uppercase:

function transformText($text)
{
    return strtoupper($text);
}

print transformText('hello'); // HELLO

With functions, we can also assign the return value to a variable:

function StringSize($text)
{
    return strlen($text);
}

$size = StringSize('hello');

print $size; //3

Setting default values

Of course, we can define default values ​​for function parameters, making their use very flexible:

function swapCurrency($currency = 2, $value = 10)
{
    return $value * $currency;
}

print swapCurrency(); //20

Defining default values ​​for a function’s parameters helps us because, depending on the context we are in, it will not be necessary to create validation just to define the right value for our function. In the previous example, this is very clear, since even without passing any parameters we have a return value.

One thing to pay close attention to when using default values ​​is that we must always place variables with the default value at the end of the function declaration, and not at the beginning.

function ligateTelevisao($televisao = 'LG', $controle) {} // Wrong

Let’s suppose I call the function callTelevisao('Remote control'). PHP will assume that I am passing the argument to the variable $televisao in the method signature, and not to the variable $controle.

When trying to execute this way, PHP will display a WARNING saying that the $control parameter was not passed to the function:

PHP Warning: Missing argument 2 for ligarTelevisao(), called in /my_dir/functions/funcao.php on line 6 and defined in /my_dir/functions/funcao.php on line 3
PHP stack trace:
PHP 1. {main}() /my_dir/functions/funcao.php:0
PHP 2. ligateTelevisao() /my_dir/functions/funcao.php:6

Warning: Missing argument 2 for ligarTelevisao(), called in /my_dir/functions/funcao.php on line 6 and defined in /my_dir/functions/funcao.php on line 3

Call Stack:
    0.0003 231432 1. {main}() /mnt/c/wamp/www/github/my_dir/functions/funcao.php:0
    0.0003 231480 2. ligateTelevisao() /mnt/c/wamp/www/github/my_dir/functions/funcao.php:6

This makes a lot of sense, as PHP cannot “guess” which parameter we are trying to pass the value to. In our example, we only used two parameters, but imagine the following situation:

function ligateTelevision($television = 'LG', $control, $mesa = 'Room') {
    print 'TV: ' . $television;
    print 'Control: ' . $control;
    print 'Table: ' . $table;
} // Wrong

How would PHP guess which parameter we are trying to pass values ​​to? In this case, are we trying to pass the values ​​Remote Control and Room to the variables $control and $mesa?

callTelevision('Remote Control', 'Room');

There is no way for this to happen, because, when we run this script, it will be assumed that we are passing the value Remote Control to the variable $televisao, and the value Quarto to the variable $controle.

TV: Remote Control
Control: Room
Table: Living room

Next, let’s see what the correct way to create this function would be:

function ligateTelevisao($controle, $televisao = 'LG', $mesa = 'Room') {} // Correct

See the difference. Now all variables have the default value at the end of the function declaration, and we can only pass the ones we really want.

callTelevision('Control 1'); // Correct and the function will assume the LG value for the television variable and the Room value for the $mesa variable

callTelevision('Controle 1', 'Sony'); // Correct and the function will assume the value Room for the variable $mesa

callTelevision('Control 1', 'Sony', 'Office'); // Correct and no default values ​​will be assumed

Although it is possible to omit some parameters in the middle of the function signature, as shown in the previous examples, This type of manipulation is not considered good practice. Some IDEs like netbeans display an alert showing us that there is a possible error in the order of the parameters.

Alert being displayed by netbeans about the order of parameters {w=100%}

Passing values ​​by reference

We can use passing values ​​by reference, just as PHP does with some internal functions, such as the sort function. Functions that receive parameters as references make it easier to change the parameters, without the need to return their value.

For our first example, we will use a function to add the value 2 to the passed parameter:

function addValue($a)
{
    return $a + 2;
}

$b = 2;
print addValue(); // 4

To achieve our objective with this function, which is to return the sum of the sent parameter plus 2, we need to return the new value through the reserved word return. This type of manipulation is known as passing a parameter by value, as we are passing a copy of the variable $b into the somarValor function. And if we change the value of variable $a, it will not be reflected to variable $b.

The scenario changes when we start using the passing of values ​​by reference, as any change that is made to the variable within the function is reflected outside of it. Let’s use our example again to add the value 2:

function addValue(&$a)
{
    $a += 2;
}

$b = 2;

addValue($b);

print $b;

Can you guess what result we will get when running this script?

This time, what changes is that we need to add an & sign, so that the sent parameter is a reference to the original parameter. In other words, a copy of the value will no longer be sent to the function, and any modification made to the parameter sent to the function will be reflected outside of it. Note that, this time, we do not use return to return the new value after adding 2 more to the variable $b, which leaves us with the following result:

4

Let’s look at some examples that are a little more realistic and plausible for use in our daily lives. This time, we’ll create a function that adds elements to a collection:

function addElement(array &$collection, $element = null)
{
    if ($element !== null)
    {
        $collection[] = $element;
    }
}

$fruits = [];

addElement($fruits);
addElement($fruits, 'pineapple');

As the first call to the addElemento function did not have any element passed as a parameter, the $colecao array was not modified and remained empty. The change happens in the second call, where we have the pineapple element passed as a parameter, and it is added to $colecao. Once again, notice that it was not necessary to use return to obtain the collection with the added elements.

Array
(
    [0] => pineapple
)

As we saw in passing by value, we can pass values ​​directly to functions, such as 10, which is an integer, or a string, such as Hello, this is my string.

// passing value directly to the function
myFunction(10, 'Hello, this is my string') {}

However, when using pass by reference, we cannot use this type of manipulation.

function addRecord(&$history, $record) {
    $historico[] = 'new record: ' . $record;
}

addRegistry([], 'Code House'); // Invalid

When we run this script, we get a FATAL ERROR, telling us that it is only possible to pass variables by reference.

PHP Fatal error: Only variables can be passed by reference in /my_dir/functions/referencia.php on line 7

The correct way to use this function would be to create a variable, assign an array to it, and then pass it as a parameter to the function.

$EventHistory = [];
addRegistry($historicoDeEventos, 'Casa do Code');
    // Valid

When displaying the variable $historicoDeEventos, we have the following result:

Array
(
    [0] => new record: Casa do Code
)

Returning values ​​by reference

In PHP, it is also possible to return a value from a given function by reference. To do this, simply insert a & before the function name, and another & for whoever is calling it. To make the example easier to understand, we will manipulate a method of a class that plays the role of the function.

class Home {
    private $light = 'on';

    public function &retornoPorReferencia() {
        return $this->light;
    }
}

Note that the only difference is that we need to put the & in front of the retornoPorReferencia method to use the value by reference:

$casa = new Casa();

$luz = &$casa->retornoPorReferencia();

print $light; // on

But what changes if we are accessing it as any method?

The answer is very simple: what happens is that we are creating the variable $luz that points to the class property Casa. In other words, any value that is changed in the $luz variable will be changed in the Casa class property as well, even if this property is private.

Can you figure out what the output of the following code will be?

$casa = new Casa();

$luz = &$casa->retornoPorReferencia();

print $light;

$light = 'off';

print $home->returnByReference();

As we return the value as a reference when we assign the value of the Casa class property to the $luz variable, any changes made to this variable will be reflected in subsequent calls to the retornoPorReferencia method. When executing the previous code, we have the following result:

on off

Using native PHP functions

PHP provides us with some functions to use when manipulating functions, such as func_num_args, func_get_arg and func_get_args. Next, we will see how each function works and their differences.

The first function we will see here is func_num_args, which returns the total number of arguments passed to the function.

function sum()
{
    $arguments = func_num_args();

    if ($arguments > 2) {
        \throw new \InvalidArgumentException();
    }
}

With this behavior, we can limit the number of arguments passed to our function, as shown in the previous example. If more than two arguments are passed, an exception is thrown.

But what’s the point of limiting the number of arguments, if until now we haven’t been able to manipulate the arguments passed to the function? And it is exactly at this time that the function func_get_arg comes in, which returns the arguments passed by the function, according to the index. See the add function, which does exactly that:

function sum()
{
    $arguments = func_num_args();

    if ($arguments > 2) {
        throw new \InvalidArgumentException();
    }

    $a = func_get_arg(0);
    $b = func_get_arg(1);

    print $a + $b;
}

With this code, we can now manipulate the values ​​passed by the function using func_get_arg, where we specify the index at which the argument was passed. In the previous code, we expect two numbers that are passed as a parameter to be added together, but we have a small catch.

Before proceeding, try to interpret the code and find out what it will display. Let’s use the sum function, created in the previous example.

add(10);

This code will display a WARNING, as we are not passing the second argument that we will use within the function:

PHP Warning: func_get_arg(): Argument 1 not passed to function in /my_dir/functions/func_get_arg.php on line 22
PHP stack trace:
PHP 1. {main}() /my_dir/functions/func_get_arg.php:0
PHP 2. sum() /my_dir/functions/func_get_arg.php:27
PHP 3. func_get_arg() /my_dir/functions/func_get_arg.php:22
10

However, we will have the result 10. PHP will display WARNING, but the result is not affected. In other words, WARNING will be displayed and, shortly thereafter, the value 10. Notice the last line displayed in the response.

You might be imagining that, until now, we must specify the argument index with each call, and in this context we are tied to knowing exactly how many arguments will be sent. What if we don’t want to limit that number? How would we use the sum() function call with an undefined number of arguments? This is when the last function we will see in this topic comes in, func_get_args.

function sum()
{
    $total = 0;
    foreach (func_get_args() as $parameter) {
        $total += $parameter;
    }

    print $total;
}

With this small modification, using func_get_args, we eliminate the chance of WARNING from the previous example occurring and, even better, we can now pass any number of parameters to the function.

add(); //0
add(10, 10); //20
add(20, 20, 10); //50

We have these types of behavior because the func_get_args function returns us in an array which arguments were passed to the function. See that, in the following example, when we pass just one parameter, it automatically goes to the parameter list, making its use much more flexible:

add(10);

Array
(
    [0] => 10
)

Also note that the order of arguments is respected, that is, if the first parameter is 10, it goes to index 0, the second argument to index 1 and so on.

add(10, 20);

Array
(
    [0] => 10
    [1] => 20
)

call_user_func

PHP provides us with a very interesting functionality that, for anyone in the world of JavaScript, is already familiar with: the famous callback. Callbacks, by definition, are snippets of code (functions) that will be executed right after a certain action is completed.

Callback execution flow {w=100%}

We can use this flow through the call_user_func function. See the following example of a simple call to an existing function:

function displayMessage()
{
    print 'Hello!';
}

call_user_func('displayMessage');

When executing the code, we obtain the following result:

Hello!

In addition to executing the function we want, we can also pass parameters to the function to be executed:

function add($a, $b)
{
    return $a + $b;
}

print call_user_func('sum', 10, 20);

When we run the code, we get 30 as a result. Note that our function (callback) expects two parameters, which are passed by the call_user_func function.

Closures

Closures (or anonymous functions) are functions that do not require a name to be created. Generally, such functions are used in callbacks of other functions, and closures can also be used as variable values.

In PHP, we are used to seeing it used in functions:

$musicstyles = [
    'POP',
    'Rock',
];

array_map(function($item) {
    print $item;
}, $estilosMusiccais);

We use a closure (or anonymous function) as a callback to the array_map function, and for each item in the $estilosMusicais array.

We can also use closures with variables:

$closure = function() {
    return 'Hello';
};

$closure();

And we obtain as a result of this code the string Hello. We can also use closure inside other functions:

function greeting() {
    return function() {
        return 'Good morning!';
    };
}

$closure = greeting();

print $closure(); //Good morning!

So far, nothing too difficult, everything very simple. But we must be careful when using closures, as they work in a different scope. Look at the following example and try to guess what the result will be.

$name = 'Matheus';

$greeting = function() {
    return 'Good morning, ' . $name;
};

print $greeting();

The script will display a NOTICE along with our message Good morning,, but it will not display the contents of the variable $nome:

PHP Notice: Undefined variable: name in /my_dir/functions/closure.php on line 6
PHP stack trace:
PHP 1. {main}() /my_dir/functions/closure.php:0
PHP 2. {closure:/my_dir/functions/closure.php:5-7}() /my_dir/functions/closure.php:9

Notice: Undefined variable: name in /my_dir/functions/closure.php on line 6

Call Stack:
    0.0002 231608 1. {main}() /my_dir/functions/closure.php:0
    0.0002 232192 2. {closure:/my_dir/functions/closure.php:5-7}() /my_dir/functions/closure.php:9

Good morning,

This is because the closure has its own scope, and does not inherit any scope from outside it. In other words, inside our closure, the variable $nome does not exist.

However, sometimes we purposely want to inherit this scope, as in the case of our example, as we want to display the contents of the variable $nome. We need the outer scope variable in our inner scope of the closure to display the name along with the greeting message. For this, PHP provides us with the use of the reserved word use.

$name = 'My name';

$greeting = function() use ($name) {
    return 'Good morning, ' . $name;
};

print $greeting(); // Good morning, My name

Now, with the reserved word use, we can introduce the variable we want into the scope of the closure. In addition to inheriting the scope from outside our closure, with the reserved word use, we can also pass parameters to be used inside the closure. Watch:

$name = 'My name';

$greeting = function($treatment) use ($name) {
    return 'Good morning, ' . $treatment . ' ' . $name;
};

print $greeting('Mr.');

You can find some more information in the official PHP documentation about closures by consulting this link: http://php.net/manual/pt_BR/functions.anonymous.php. If you want to go even deeper, see the part that talks about only for closures, at http://php.net/manual/pt_BR/class.closure.php. There the implementation of the Closure class is explained, which provides us with this functionality internally.

Forcing a value type

In some cases, while we are programming, we want to make sure that if someone is going to use a function that we have programmed, they use it correctly.

In our example below, imagine that we are creating a recipe application, in which we will create a function to add the ingredients to the pan.

Let’s look at the function below, which takes two arguments, the first being the $pan and the second being the $ingredients that we are going to add to our pan.

function prepareLunch($pan, $ingredients) {
    // cycle through the ingredients
    foreach ($ingredients as $ingredient) {
        $panela[] = $ingredient; // add the ingredient to the pan
    }

    return $pan; // returns the pan with the ingredients
}

Can you see any problems? Yes, we have a problem, because anyone who wants to use the function can send us any type of variable as a parameter.

prepareLunch('', ''); // Valid

Of course this won’t work, as we need a Pane array and an ingredients array. But, when we execute the previous function, we obtain the following result:

PHP Warning: Invalid argument supplied for foreach() in /my_dir/functions/tipo.php on line 19
PHP stack trace:
PHP 1. {main}() /my_dir/functions/tipo.php:0
PHP 2. prepareAlmoco() /my_dir/functions/tipo.php:24

Warning: Invalid argument supplied for foreach() in /my_dir/functions/tipo.php on line 19

Call Stack:
    0.0002 232808 1. {main}() /my_dir/functions/tipo.php:0
    0.0002 232936 2. prepareLunch() /my_dir/functions/tipo.php:24

To prevent this type of behavior, we can force the type of variable we want to be passed. Let’s then force this type of value so that it is always an array:

function prepareLunch(array $pan, array $ingredients) {
    foreach ($ingredients as $ingredient) {
        $panela[] = $ingredient;
    }

    return $pan;
}

Now, when we try to pass the same parameters (that is, two blank values), we get the following error:

PHP Catchable fatal error: Argument 1 passed to preparaAlmoco() must be of the type array, string given, called in /my_dir/functions/tipo.php on line 26 and defined in /my_dir/functions/tipo.php on line 18
PHP stack trace:
PHP 1. {main}() /my_dir/functions/tipo.php:0
PHP 2. prepareAlmoco() /my_dir/functions/tipo.php:26

Catchable fatal error: Argument 1 passed to preparaAlmoco() must be of the type array, string given, called in /my_dir/functions/tipo.php on line 26 and defined in /my_dir/functions/tipo.php on line 18

Call Stack:
    0.0001 232112 1. {main}() /my_dir/functions/tipo.php:0
    0.0001 232208 2. prepareLunch() /my_dir/functions/tipo.php:26

Although an error occurred, we now know that it is not our problem and that our function is “shielded” from invalid arguments. It is clear to whoever uses it, as they will know that it is necessary to pass an array, and no other type of data will be accepted.

$pan = [];
$ingredients = [
    'salt',
    'meat',
];

$ingredientesNaPanela = prepareLunch($panela, $ingredients);

As expected, now we have the ingredients in the pan when the prepareLalmoco function returns.

Array
(
    [0] => salt
    [1] => meat
)

With array, it was very easy, but what else can we use to force parameters to be passed to a function? PHP provides us with a list of what we can use:

  1. Classes and interfaces;
  2. Array;
  3. Callable.

In version 7 of PHP, it will be possible to force some more types of parameters such as bool, float, int and string. But for the certification exam we are demonstrating here (PHP 5.5), these types are not allowed. For more information, see the official documentation at http://php.net/manual/pt_BR/functions.arguments.php.

Summary

Functions may seem like a boring subject to read about, however, you can’t live without knowing them. Yes, it seems like a strange statement, but so much of our PHP world revolves around functions. Imagine having to separate a string and transform it into an array without using an explode? It would be a bit of a hassle to do this, but the functions are there to help.

In the test, you may find some gotchas in the functions, such as: missing parameters, syntax errors, passing invalid parameters, and so on. We tried to explore all possible items related to functions in this chapter, but of course there could always be something more. Don’t be scared, remember that we have an exclusive section on functions in our good old documentation, which can be accessed at http://php.net/manual/pt_BR/language.functions.php.

But don’t try to memorize PHP functions parameter by parameter. The best thing is for you to understand how it works, so that when the need arises, you won’t be in trouble.