PHP Functions - Basics

Chapter 33 24 mins

Learning outcomes:

  1. A quick recap of functions in PHP
  2. The idea of local variables only inside functions
  3. What are callables
  4. Checking if a function exists using function_exists()
  5. Checking if a value is a function using is_callable()
  6. Things to note regarding functions

A quick recap

So far in this course, we've learnt a great deal of information regarding functions in PHP. Before we move on to explore yet even more avenues of functions in the language, let's quickly take a recap of what we already know.

If you feel that you're confident in your knowledge on PHP functions, you're free to skip this section.

Starting with the very first thing:

A function represents a block of code that is executed whenever the function is called.

To perform its respective task, a function can take in a given set of values. The values provided to a function when calling it are called arguments whereas the identifiers that represent those values inside the function are called parameters.

A function can also return a value, which is what its invocation expression gets replaced with. This is done using the return keyword.

In PHP, a function is created using the function keyword.

In the code below, we define a function sum() that computes the sum of two given numbers and returns it, and then call it on given sets of numbers:

<?php

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

echo sum(10, 20), "\n";
echo sum(-5, -3), "\n";
echo sum(1, 99), "\n";
30 -8 100

In PHP, it's invalid to call a function with less arguments than parameters:

<?php

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

echo sum(10), "\n";
Fatal error: Uncaught ArgumentCountError: Too few arguments to function sum(), 1 passed in <file> on line 7 and exactly 2 expected in <file>:3

However, it's alright to call a function with more arguments than parameters — surplus is always better for PHP:

<?php

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

echo sum(10, 20, 30), "\n";
30

By default, arguments are passed to function parameters in PHP by value. That is, the values of the arguments are copied on to the parameters. We can change this behavior and instead get arguments to be passed by reference.

To learn about pass-by-reference in depth, refer to PHP References.

What happens in this case is that instead of copying the values of the arguments, their references are copied and then these are used inside the functions. The consequence is that modifying the parameters inside the functions effectively modifies the corresponding entities outside the functions as well.

To get an argument to be passed by reference when passing it to a function, we ought to label the corresponding parameter as a reference parameter, preceding it with the ampersand (&) symbol.

The following code distinguishes between pass-by-value and pass-by-reference:

<?php

function pass_by_value($val) {
   $val = 'new';
}

function pass_by_reference(&$val) {
   $val = 'new';
}

$str = 'old';

pass_by_value($str);
echo "After pass_by_value(): $str\n";

pass_by_reference($str);
echo "After pass_by_reference(): $str\n";
After pass_by_value(): old After pass_by_reference(): new

When the variable $str is provided to pass_by_value(), its value doesn't change since we pass the variable by value.

However, when the variable is provided to pass_by_reference(), its value does change and that's because the variable is passed by reference — the function gets a reference to $str to work with.

And this is pretty much all about functions that we've learnt uptil this point in this course.

Local variables only

If you're coming from another programming language, such as Python or JavaScript, one particular feature of PHP functions would surely amaze you.

That is, a function in PHP can only work with local variables.

We can NOT directly work with outer or global variables in a function in PHP, as we can otherwise do in other programming languages.

There can be one and only one kind of variables inside PHP functions and they are local variables. (Keep in mind that parameters are just a special kind of local variables.)

But then how do we work with global variables, if we really want to? And what about outer variables that are not necessarily global variables?

Well, you'd remember the global keyword that we encountered in the PHP Scoping chapter. It's used to obtain access to a global variable inside a function in PHP.

An example follows:

<?php

$x = 10;

function f() {
   global $x;
   echo $x;
}

f();
10

The global $x statement is used to gain access to the global variable $x inside the function f().

However, it's absolutely paramount to realize that $x inside the function f() here is NOT a global variable. Instead, it's again just a local variable that turns out to hold a reference value pointing to the global variable $x.

Remember what references are in PHP and how they work?

Well, what global $x actually does is that it creates a local variable $x that's internally assigned the value of the global $x, by-reference. We are still working with a local variable inside the function.

If we change $x inside f(), the global $x would change likewise, as illustrated below:

<?php

$x = 10;

function f() {
   global $x;
   $x = 50; // Change $x from within f()
}

f();
echo $x;
50

Perhaps, for people new to PHP, who already have experience of another language, this sure is a surprising feature of PHP.

A similar rule applies for outer variables — we can't access outer variables inside a PHP function. But there are even more surprises here.

For named functions, there is absolutely no way to access outer variables. This makes sense from the perspective of PHP because all named functions are global and so their outer variables are just global variables, accessing which is done using the global keyword.

For anonymous functions, however, which we'll explore briefly in this chapter and then in detail in PHP Function Closures, there is a way to access outer variables and that is by using the use keyword.

Nonetheless, again, these outer variables are accessed by creating local variables inside the function that ultimately reference the outer variables' values.

What are callables?

If you spend a couple of minutes going through the official documentation of PHP, you'd notice one thing.

That is, whenever a function that expects a function as an argument, for e.g. array_map(), is documented, the type of the corresponding parameter is labeled as a callable.

The question is: what is a callable?

A callable refers to a value in PHP that can be called.

The word 'called' here means that we could follow the value with a pair of parentheses (()) in order to get some underlying block of code to be executed.

A callable in PHP can be either of the following:

  1. A string referring to a global function's name.
  2. An anonymous function.
  3. An array whose first element specifies a class instance and the second element specifies the name of its instance method to call.
  4. An array whose first element specifies a class and the second element specifies the name of its static method to call.
  5. An instance of a class that implements the __call() magic method.

The last three options above require knowledge of some object-oriented programming principles of PHP and will, therefore, be explored later in the PHP OOP unit, once we learn about classes, instances, and methods, both instance and static.

For now, we'll keep ourselves confined to the first two options. That is, a callable could be either a string representing the name of a global function or it could be an anonymous function.

Consider the following code:

<?php

function greet() {
   echo 'Hello World!';
}

$func = 'greet'; // $func is a callable.

First we define a function greet() and then a variable $func holding a string, containing the name of the this function. Since, $func refers to the name of the function greet(), it's callable.

Being a callable simply means that we could call $func by following it with a pair of parentheses (()).

Let's try doing so:

<?php

function greet() {
   echo 'Hello World!';
}

$func = 'greet';
$func(); // Call the callable.
Hello World!

As you can see, the output confirms that the $func() call did have an effect — it got the greet() function to be executed, and that's because it held the name 'greet'.

Besides a string (representing a function's name), another way to denote a callable is via an anonymous function. But what is an anonymous function?

We'll learn more about anonymous functions later in this unit but for the sake of better understanding the definition of a callable, let's quickly and briefly see what they are.

An anonymous function, as the name suggests, is a function without a name. Unlike named functions, which denote statements in PHP, anonymous functions denote expressions.

The way to define an anonymous function is pretty simple: everything is the same as defining a named function in PHP but this time we omit the function's name.

An example follows:

<?php

$func = function() {
   echo 'Hello World!';
};

Here we define an anonymous function and store it in the variable $func. Once again, $func is a callable; that is, we could call it.

Alright, then let's do so:

<?php

$func = function() {
   echo 'Hello World!';
};

$func(); // Call the callable.
Hello World!

Great.

Functions are first-class citizens in PHP

Functions in PHP are treated as first-class citizens. This, in simplest of words, means that functions are treated just like any other value in PHP; they can be stored in variables, passed to functions as arguments, returned from functions, and so forth.

Now in order to allow functions to be passed around in this way, just like any other value, PHP defines the idea of callable values (notice the word 'values' here).

A named, global function can be passed around as a string containing its name. On the other hand, an anonymous function doesn't need the protocol of a string to be able to move around; it can be passed around just like any other value in the first place.

Even the methods of objects could be passed around in PHP; we'll explore this in detail in the PHP OOP unit.

To quickly recap it, there are essentially two ways to pass a function around a PHP program, as a callable value:

  1. By encapsulating the name of the function in a string.
  2. By creating the function as an anonymous function.

Checking if a function exists

Sometimes we might be interested in figuring out whether a given function exists before we invoke it.

This makes sense because invoking a non-existing function throws an error in PHP, as in many programming languages, as demonstrated below:

<?php

// There is no greet() function defined.
greet();
Fatal error: Uncaught Error: Call to undefined function greet() in <stdin>:4

In order to perform this check, we have the function_exists() function.

function_exists($name)

It takes the name of the function (global or built-in, like array_map(), floor()) to check, as a string, and then returns a Boolean indicating its existence.

Following, we rewrite the code above using a function_exists() check:

<?php

// There is no greet() function defined.

if (function_exists('greet')) {
   greet();
}
else {
   echo 'The function greet() does not exist!';
}
The function greet() does not exist!

Since the function doesn't exist, we get the else block executed, ultimately making the given output.

Now, let's define the function and rerun the code:

<?php

function greet() {
echo 'Hello World!';
} if (function_exists('greet')) { greet(); } else { echo 'The function greet() does not exist!'; }
Hello World!

Since the function exists this time, function_exists() yields true and our greeting logic executes. Perfect!

The is_callable() function

As you'd already know, PHP follows a naming convention for functions that check the data type of an arbitrary value: that is, is_*(), where * is replaced by the name of the type.

For instance, is_int() checks for integers, is_float() checks for floats, is_bool() checks for Booleans, and so on. In order to check for functions, we have the is_callable() function.

is_callable() takes an argument and determines whether it's a callable value or not.

As described in the section above on What are callables?, a callable, amongst many possible values, is either a string referring to the name of a global function or it's an anonymous function.

Let's see some examples of using is_callable():

<?php

function f() {}
var_dump(is_callable('f'));

var_dump(is_callable('some_func'));

var_dump(is_callable(function() {
   echo 'Hello World!';
}));
bool(true) bool(false) bool(true)

The first is_callable() call returns true as there does exist a global function having the name f. The next call returns false, as there is no such function in the program (or in PHP) called some_func(). The last call returns true again as the given value is an anonymous function, which is a callable.

We can even use is_callable() to check for some built-in functions:

<?php

// Checking built-in functions.
var_dump(is_callable('floor'));
var_dump(is_callable('strcmp'));
var_dump(is_callable('array_map'));
bool(true) bool(true) bool(true)

Things to note

PHP has many gotchas when it comes to the design of the language. In this section, we aim to unravel certain things that you must take special note of when working with PHP.

Especially when you're coming from another dynamically-typed language such as JavaScript, you'd be surprised at some of its arguably weird features.

Function names are case-insensitive

Let's say you define a function called greet() as follows:

<?php

function greet() {
   echo "Hello World!\n";
}

greet();
Hello World!

You might think that the only way to call it is as greet(). Surprisingly that's not the case!

In PHP, function names are case-insensitive. So, the greet() function above can be invoked as GREET(), or Greet(), or even gReEt(), or just about in any other casing of greet().

<?php

function greet() {
   echo "Hello World!\n";
}

greet();
Greet();
GREET();
greeT();
GreeT();
Hello World! Hello World! Hello World! Hello World! Hello World!

Redefining the same function throws an error

If you define a function in PHP that has been defined before, you'd get an error:

Frankly speaking, this is a nice thing that exists in many other programming languages as well. It's important to be aware of it in case you conditionally define a function that turns out to already exist.

This can be seen as follows:

<?php

function greet() {
   echo "Hello World!\n";
}

function greet() {
   echo "Hello Programmer!\n";
}
Fatal error: Cannot redeclare greet() (previously declared in <file>:4) in <file> on line 7

The error message for redefining an existing function is quite self-explanatory, unlike many other kinds of error messages in PHP. It tells us two things: where the function is already defined and where we tried to define it again.

And melding this with the previous point, defining a function with a name that turns out to be the name of an existing function albeit with a different casing would also lead to an error.

This can be seen as follows:

<?php

function greet() {
   echo "Hello World!\n";
}

function GREET() {
   echo "Hello Programmer!\n";
}
Fatal error: Cannot redeclare GREET() (previously declared in <file>:4) in <file> on line 7

Reiterating on it, that's simply because function names in PHP are case-insensitive — GREET() is the same as greet().

Functions can't be passed around as identifiers

Consider the following errorneous code:

<?php

function greet() {
   echo "Hello World!\n";
}

function call_func($func) {
   $func();
}

call_func(greet);
Fatal error: Uncaught Error: Undefined constant "greet" in <file>:11

If you were a newcomer to PHP, you might not find anything problematic about this code. After all, first we define greet() and then pass it to another function call_func() via call_func(greet), as an identifier greet.

What could possibly be wrong here?

Well, surprisingly, it ain't possible to pass functions as identifiers in PHP!

If we wish to pass a named function to another entity, we ought to pass a string containing that function's name. So, the code above could be rectified as follows:

<?php

function greet() {
   echo "Hello World!\n";
}

function call_func($func) {
   $func();
}

call_func('greet');

In this case, we pass the greet() function to call_func() as 'greet'. What'll happen is that $func (in call_func()) will resolve to 'greet' and then the parentheses (()) will be applied on this string.

PHP know what this means: to find a global function having that very name and then invoke it.

Let's try running the program:

Hello World!

It works as desired.

Static local variables

If you have experience of the C programming language, you'll know about a special classification of local variables known as static local variables. PHP also supports these.

So what are static local variables?

Static local variables are local variables that are retained across function calls, and not deleted when the function exits.

Since they are local variables, they are only visible inside the function where they are defined. That is, static local variables have a local scope (as they are just local variables at the end of the day).

A function can have as many static local variables as we want, though in reality, the number is really small.

To declare a local variable as static, precede its declaration with the static keyword.

In general, this could be expressed as follows:

function function_name(...) {
   static $var = value;
   ...
}

It's generally considered a good practice to declare static local variables at the start of a function, prior to other variable declarations.

Let's take the aid of an example to better understand how static local variables work.

In the following code, we have a counter() function that increments a local counter and returns back its value (before the increment):

<?php

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

Using this function, we can call counter() multiple times, and each call will give us back a new incremented number. Let's try it:

<?php

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

echo counter(), "\n";
echo counter(), "\n";
echo counter(), "\n";
0 1 2

Thanks to the static local variable $count, we don't need any global variable to remember the last value of the counter; the local variable itself does this.

A static local variable could be thought of as a global variable in that it exists in memory for the lifetime of the containing PHP program.