Reducing Errors With Type Hinting in PHP
Honeybadger Staff
Posted on March 14, 2023
This article was originally written by Adebayo Adams on the Honeybadger Developer Blog.*
Errors, also known as bugs, are developers' biggest nightmare. Bugs are so ubiquitous that everything you do after building a website or app involves finding and fixing them. What if there was a way to reduce the number of errors you must fix after pushing a project to production?
In this article, I will explain everything you need to know about type-hinting in PHP, which will help you achieve this goal. We’ll begin by exploring what type-hinting in PHP is, and then I’ll show you how to start using it in your applications. However, before we begin, let's examine what you need to know to get the most out of this article.
Prerequisites
To get the most out of this tutorial, you should have at least the following:
- Basic knowledge of PHP
- Familiarity with Object-Oriented PHP
With the prerequisites out of the way, we’ll look at what type-hinting is in the next section.
Type-Hinting
Type-hinting means explicitly stating the expected type of declarations in your code. This allows you to enforce specific types in your code. PHP allows you to type-hint function parameters, return values, and class properties to write more robust code.
Note: A declaration is any function, class, variable, or value declared in your code.
Although it’s greatly beneficial, type-hinting in PHP is a consistently under-learned concept because most tutorials and tutors treat fixing bugs and errors as something you do later when building applications. We’ll look at the different type declarations in PHP in the next section.
Different Types in PHP
As of PHP 8.0, PHP has thirteen different types you can specify for declarations in your code. Let's take a look at each of them below:
-
string
- Value must be a string. -
int
- Value must be an integer. -
float
- Value must be a floating-point number. -
bool
- Value must be Boolean (i.e., eithertrue
orfalse
). -
array
- Value must be an array. -
iterable
- Value must be an array or object that can be used with theforeach
loop. -
callable
- Value must be a callable function. -
parent
- Value must be an instance of the parent to the defining class. This can only be used on class and instance methods. -
self
- Value must be either an instance of the class that defines the method or a child of the class. This can only be used on class and instance methods. -
interface name
- Value must be an object that implements the given interface. -
class name
- Value must be an instance of the given class name. -
mixed
- Value can be any type. -
void
- Value must be nothing. It can only be used in function returns.
Now that you've learned all the different types supported in PHP and what value each of them allows, let's look at how to type-hint function parameters in the next section.
Type-Hinting Function Parameters
Specifying the types of arguments a function accepts might be the most important part of a program where type hinting is most beneficial. Type hinting has been available for function parameters since PHP version 5.
You can type-hint function parameters in your code like so:
function add(int $a, int $b) {
return $a + $b;
}
The code above declares an add
function that takes two numbers and returns the sum of both numbers. However, there's something different about this function: the int
keyword. This keyword changes only one thing about how the function will work; the function will only accept the int
type and throw an error if given any other type.
However, you could give the function two string
numbers, like so:
echo add("2","2"); // returns 4
The code above will return 4, which is the correct result, which means the int
keyword is not working. This is because PHP attempts to convert wrong scalar values into the correct type, which it did successfully in this case. However, if you gave a string value of echo add("2","three");
, you will get an error:
The image above shows an error that states "Argument #2 ($b) must be of type int, string given", which means it accepts the first string "2"
that was given because it successfully converted the string to an integer.
Strict Types
Type hinting the function parameters does not enforce the types automatically; you need to declare strict types at the very top of your file:
declare(strict_types=1);
Note: The
strict_types
declaration must be the very first statement in the script.
The code above tells the script to enforce strict types. Now the int
keyword will work as expected, so your code should now return an error:
The image above shows a type error that says,
Fatal error: Uncaught TypeError: add(): Argument #1 ($a) must be of type int, string given. This tells you the problem and the fix in one message, which is Argument #1 ($a) must be of type int.
Furthermore, depending on which integrated development environment (IDE) you use, your editor detects the strict_types
and warns you inside the editor before running your code. For example, I use the Intelephense extension inside Visual Studio Code, which warns me about wrong parameters before I run the code, like so:
The image shows my editor warning me about the type error before running the code. Now that you have learned how to type-hint function parameters, let's look at how to type-hint function returns.
Type-Hinting Function Returns
The PHP 7 update shipped with type-hinting support for function returns to prevent unexpected return values. You can type-hint return values by adding the intended type after the parameter list prefixed with a colon (:
), like so:
function add(int $a, int $b): int {
return $a + $b;
}
The code above adds type-hinting to the add
function from the previous section. The code should still work as intended, but you could change the return value to a string:
function add(int $a, int $b): int {
return "Hello";
}
The function above will return a TypeError, as shown below:
Note: Only types that are allowed for function parameters are for return types.
Void Returns
Sometimes, you might not want to return anything from a function; if you would like to enforce this, you can use the void
type:
function like(): void{
$post->likes + 1;
return;
}
The code above declares a like
function that adds 1 to the likes on a post and returns nothing.
Static Returns
Alternatively, you might want to return the instance of the object that defines a function from the same function. You can use the static
type for this purpose:
class Person
{
public function returnPerson(): static
{
return new Person();
}
}
The code above defines a class, Person
, with a function, returnPerson
, that returns a Person object. Next, we’ll look at how to type-hint optional declarations in the next section.
Nullable Types
Sometimes, you want a function to take a particular type, but the parameter is not required for the function to run properly. In this case, you can make the parameter nullable:
function greeting(?string $username)
{
echo $username === null ? 'Hello User!' : "Hello $username";
}
The function above defines a greeting
function that returns "Hello John!" when given the name John and "Hello User!" if the function is given null
.
Note: You must give
null
if the$username
is not available because you'll get an error if the function is called:
greeting()
Nullable Return Types
You can also make a return type nullable by prefixing the return type with a ?
:
function greeting(?string $username) : ?string
{
if ($username) {
return "Hello, $username!";
}
return null;
}
The function above defines a greeting
function that returns either a string
value or null
and will throw an error if any other type is returned.
Now that you know how to type-hint optional declarations, let's look at how to allow multiple types for one declaration.
Union Types
The PHP version 8 update introduced union types, which allow you to specify more than one type for a single declaration. For example, a function that formats prices should be able to take an int
or a float
:
function formatPrice(float | int $price): string
{
return '$' . number_format($price, 2);
}
The function above declares a formatPrice
function that accepts either int
or float
. By using the |
to separate the different types you want the function to accept, the function can now work with either int
or float
types. Therefore, you can run the function like so:
echo formatPrice(5.99);
echo "<br/>";
echo formatPrice(5);
The code above will run without errors. However, if you give the function a string or any other type that is not int
or float
, you will get an error:
The image above shows Fatal error: Uncaught TypeError: formatPrice(): Argument #1 ($price) must be of type int|float, string given. Next, lets look at how to type-hint class properties and variables.
Type-Hinting Class Properties
In the second section of this article, you learned that PHP allows you to type-hint function parameters, return values, and class properties. In this section, I will show you how to type-hint class properties.
Since PHP 7.4, PHP has allowed developers to specify the values that class properties can hold. You can type-hint class properties like so:
class Person
{
public string $name;
public int $age;
public float $height;
public bool $is_married;
public function __construct($name, $age, $height, $is_married)
{
$this->name = $name;
$this->age = $age;
$this->height = $height;
$this->is_married = $is_married;
}
}
The code above declares a Person
class with $name
, $age
, $height
, and $is_married
type-hinted properties. PHP will only allow specified types to be assigned to the properties.
Note: You can assign all the available types to the property except the
callable
andvoid
types, but you can only type-hint variables and properties inside a class.
Now that you have learned how to type-hint class properties, we’ll look at how to use the callable
type in the next section.
Using the callable
Type
The PHP 5.4 update came with the callable
type, which allows you to force a function to accept another function as an argument. For example, if you want to type-hint a function that accepts another function as a parameter, you can do it like so:
function sortArray(callable $sort_function, array $array)
{
$sort_function($array);
return $array;
}
The code above takes an array and a function, and then it uses the function to sort the array and returns the sorted array. To use the function above, you need to define the function that will be passed as an argument to the sortArray
. In my case, I'll use a bubble sort function:
function bubbleSort(array $array): array
{
$sorted = false;
while (!$sorted) {
$sorted = true;
for ($i = 0; $i < count($array) - 1; $i++) {
if ($array[$i] > $array[$i + 1]) {
$temp = $array[$i];
$array[$i] = $array[$i + 1];
$array[$i + 1] = $temp;
$sorted = false;
}
}
}
print_r($array);
return $array;
}
The code above takes an array of numbers, sorts it, and returns the sorted array. You can now call the sortArray
function:
sortArray('bubbleSort', [1, 3, 2, 5, 4]);
The function name should be wrapped in quotes without parentheses. If you give the sortArray
any other type instead of a callable function, you'll get an error:
Fatal error: Uncaught TypeError: sortArray(): Argument #1 ($sort_function) must be of type callable, string given.
Note: Bubble sort is an algorithm that loops through a list, compares adjacent elements, and swaps them if they are in the wrong order. The loop is repeated until the list is sorted. Bubble sort is also sometimes referred to as sinking sort.
Now that you know the different types allowed in PHP and how to use them, let's look at the benefits of using type-hinting in your code.
Benefits of Type-Hinting
Type-hinting your code is one of the first steps to writing error-free code. In this section, we’ll look at some of the major benefits of type-hinting.
Eliminate Type Errors
Type errors are one of the main causes of bugs during development, and some errors even make it to production. Type-hinting allows you to crush such bugs in development because your code will force you to accept and return the expected types before testing your code.
Improve IDEs
As you saw in the Type-hinting Function Parameters section, type-hinting your code allows your IDEs and linters to indicate when you’ve used the wrong type, even before running your code. This might not be a big deal, but it can significantly improve the developer experience.
Readability
Readability might not seem like something you should consider when working on a side or personal project, but it's especially important when working in teams because you are probably going to write some complex code in your career. If every new developer on your team needed to talk with you before understanding your code, it would be stressful and unproductive.
Type-hinting helps with readability because, just by glancing through your code, other developers can understand the types you’ve used and easily avoid creating a bug in the program by using the wrong types.
Documentation
Type-hinting your code makes it significantly easier to document your code as a solo developer or when working in a team.
Type-hinting allows you to easily note the types a function accepts without testing the code with each type, which is the next most important step when writing documentation, after explaining what the function is used for.
It gets even better when everyone knows that type-hinting is being used in the code; this way, you can skip the part where you explain the types each function accepts.
Downsides of Type-hinting
Although the benefits of type-hinting are significantly greater than the drawbacks, I’ll discuss some of the drawbacks in this section.
More Code
Type-hinting helps you save a lot of debugging time, but it might result in writing more code. This, in turn, increases development time.
No Automatic Strict Typing
As you have learned in the previous sections, PHP requires you to declare strict types in every file you want to use type-hinting. However, when working on a large project, having to write declare(strict_types=1);
every time you create a new file can be a little repetitive. This is not a big deal; it's just a line of code, but it can be better with strict automatic typing.
Unexpected Nullable Type Behavior
As you have learned in the previous sections, the null
type doesn't work as expected.
For example, if a function accepts one argument, and the argument is nullable, I expect to be able to run the function like so: greeting();
. However, this won't work and returns a type error instead.
Conclusion
Thanks for reading!
Hopefully, this article has taught you all you need to know about type-hinting, how to use it, its benefits, and the drawbacks of using it in PHP. I also hope this article will help you make a more informed decision about whether to add type-hinting to your PHP code in the future.
Posted on March 14, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.