What do strict types do in PHP?
I've seen the following new line in PHP 7, but nobody really explains what it means. I've googled it and all they talk about is will you be enabling it or not like a poll type of thing.
declare(strict_types = 1);
What does it do? How does it affect my code? Should I do it?
Some explanation would be nice.
From the Treehouse blog:
With PHP 7 we now have added Scalar types. Specifically: int, float, string, and bool.
By adding scalar type hints and enabling strict requirements, it is hoped that more correct and self-documenting PHP programs can be written. It also gives you more control over your code and can make the code easier to read.
By default, scalar type-declarations are non-strict, which means they will attempt to change the original type to match the type specified by the type-declaration. In other words, if you pass a string that starts with a number into a function that requires a float, it will grab the number from the beginning and remove everything else. Passing a float into a function that requires an int will become int(1).
By default, PHP will cast values of the wrong type into the expected scalar type if possible. For example, a function that is given an integer for a parameter that expects a string will get a variable of type string.
Strict types disabled (eval):
<?php
function AddIntAndFloat(int $a, float $b) : int
{
return $a + $b;
}
echo AddIntAndFloat(1.4, '2');
/*
* without strict typing, PHP will change float(1.4) to int(1)
* and string('2') to float(2.0) and returns int(3)
*/
It is possible to enable strict mode on a per-file basis. In strict mode, only a variable of exact type of the type declaration will be accepted, or a TypeError will be thrown. The only exception to this rule is that an integer may be given to a function expecting a float. Function calls from within internal functions will not be affected by the strict_types declaration.
To enable strict mode, the declare statement is used with the strict_types declaration:
Strict types enabled (eval):
<?php declare(strict_types=1);
function AddIntAndFloat(int $a, float $b): int
{
return (string) $a + $b;
}
echo AddIntAndFloat(1.4,'2');
// Fatal error: Uncaught TypeError: Argument 1 passed to AddIntAndFloat() must be of the type int, float given
echo AddIntAndFloat(1,'2');
// Fatal error: Uncaught TypeError: Argument 2 passed to AddIntAndFloat() must be of the type float, string given
// Integers can be passed as float-points :
echo AddIntAndFloat(1,1);
// Fatal error: Uncaught TypeError: Return value of AddIntAndFloat() must be of the type integer, string returned
Working example:
<?php
declare(strict_types=1);
function AddFloats(float $a, float $b) : float
{
return $a+$b;
}
$float = AddFloats(1.5,2.0); // Returns 3.5
function AddFloatsReturnInt(float $a, float $b) : int
{
return (int) $a+$b;
}
$int = AddFloatsReturnInt($float,1.5); // Returns 5
function Say(string $message): void // As in PHP 7.2
{
echo $message;
}
Say('Hello, World!'); // Prints "Hello, World!"
function ArrayToStdClass(array $array): stdClass
{
return (object) $array;
}
$object = ArrayToStdClass(['name' => 'azjezz','age' => 100]); // returns an stdClass
function StdClassToArray(stdClass $object): array
{
return (array) $object;
}
$array = StdClassToArray($object); // Returns array
function ArrayToObject(array $array): object // As of PHP 7.2
{
return new ArrayObject($array);
}
function ObjectToArray(ArrayObject $object): array
{
return $object->getArrayCopy();
}
var_dump( ObjectToArray( ArrayToObject( [1 => 'a' ] ) ) ); // array(1 => 'a');
strict_types
affects type coercion.
Using type hints without strict_types
may lead to subtle bugs.
Prior to strict types, int $x
meant "$x
must have a value coercible to an int." Any value that could be coerced to an int
would pass the type hint, including:
- an int proper (
242
), - a float (
10.17
), - a bool (
true
), -
null
, or - a string with leading digits (
"13 Ghosts"
).
By setting strict_types=1
, you tell the engine that int $x
means "$x must only be an int proper, no type coercion allowed." You have great assurance you're getting exactly and only what was given, without any conversion and potential loss.
Example:
<?php
function get_quantity(): int {
return '100 apples';
}
echo get_quantity() . PHP_EOL;
Yields a potentially confusing result:
Notice: A non well formed numeric value encountered in /Users/bishop/tmp/pmkr-994/junk.php on line 4
100
Most developers would expect, I think, an int
hint to mean "only an int". But it doesn't, it means "anything like an int". Enabling strict_types gives the likely expected and desired behavior:
<?php declare(strict_types=1);
function get_quantity(): int {
return '100 apples';
}
echo get_quantity() . PHP_EOL;
Yields:
Fatal error: Uncaught TypeError: Return value of get_quantity() must be of the type int, string returned in example.php:4
I think there's two lessons here, if you use type hints:
- Use
strict_types=1
, always. - Convert notices to exceptions, in case you forget to add the
strict_types
pragma.