Programster's Blog

Tutorials focusing on Linux, programming, and open-source

PHP - Strict Types

PHP

Being able to declare types, such as int, string, or bool was a massive improvement added to PHP with the release of 7.0. However, there is a small but important factor that you might not be aware of.

The Problem

The problem is best explained with code. Below is a function that just adds two integers together:

<?php

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

As one might expect, if you added the line, you would get an output of 3.

print adder(1, 2) . PHP_EOL; // outputs 3

However, what you might not expect is that the following would also "work":

print adder("1", "2") . PHP_EOL; // outputs 3

Even worse, the following would also "work" (but raise 2 notices in the process):

print adder("1a", "2b") . PHP_EOL; // outputs 3

Luckily, something like this where the letters come before the number, will just not work:

print adder("a1", "b2") . PHP_EOL; // throws exception

Declaring Strict Types

To prevent strings being treated as integers, you can put the following at the top of your file (before any other PHP code):

<?php

declare(strict_types = 1);

// ... rest of code here

Now if you try something like:

print adder("1", "2") . PHP_EOL; 

This will now throw an exception.

Including Files

Chances are that you are likely going to run into situations where you will have strict types in some files and not others, especially if people take them out a s a quick-fix to prevent code that was working before from breaking. This is where things can get really confusing.

Let's say you move the adder function, and the strict types declaration, to another file called lib.php which you decide to include from your main.php file like so:

<?php

# lib.php
declare(strict_types = 1);

function adder(int $a, int $b)
{
    return $a + $b;
}
<?php

# Main.php
require_once(__DIR__ . '/lib.php');
print adder("1", "2") . PHP_EOL;

In this case, the code will "incorrectly work" (outputs a 3 instead of an exception), as the strings will be accepted because the declaration of strict types was made where the function was declared, and not where it was called and passed the strings.

However, if you were to do it the opposite way round, with the declaration being made in main.php and not the lib.php, like so:

<?php

# lib.php
function adder(int $a, int $b)
{
    return $a + $b;
}
<?php

# Main.php
declare(strict_types = 1);
require_once(__DIR__ . '/lib.php');
print adder("1", "2") . PHP_EOL;

Then the code will "correctly" throw an exception because strings were passed in, rather than integers.

Return Values

Declaring strict types also has an effect on return values from functions. You might be surprised to find out that the following example will "work" and not throw an exception/error.

<?php

function adder(int $a, int $b) : int
{
    return "1";
}

print adder(1,2) . PHP_EOL;

However, if you were to split that into a lib.php and a main.php such as in the previous examples, and declare strict_types in either file, then the script would correctly throw an exception.

Conclusion

  • Even if you declare parameters as integers, PHP will still continue to accept strings as integers unless you declare strict typing.
  • Strict typing is declared with: declare(strict_types = 1); at the top of your files.
  • You have to declare strict typing on every file, not just include it once at the start of your application and expect it to "propagate" the behaviour to all external files.
  • Strict typing applies to the call to functions, rather than the declaration of the functions. E.g. declaring it on a library will not force users of that library to use integers instead of strings.