It would be too trivial to say that following strict types and avoiding implicit conversions reduces code magic, which
leads to stability and reliability.
That's why in Spiral, Yii, Cycle
and other projects where I contribute, we use strict types whenever possible.
It's kind of obvious, but... This is PHP. And that means you can't do without nuances 💩
declare(strict_types=1);
Straight from the documentation: by default, PHP will coerce values of the wrong type into the expected scalar type declaration if possible. ... It is possible to enable strict mode on a per-file basis. In strict mode, only a value corresponding exactly to the type declaration will be accepted, otherwise a TypeError will be thrown. The only exception to this rule is that an int value will pass a float type declaration.
Warning Function calls from within internal functions will not be affected by the strict_types
declaration.
Note the warning. Many core functions do not follow type strictness.
For example, array_map()
and array_filter()
will make an implicit type conversion.
print_r(array_map(
fn(int $a, int $b) => $a + $b,
[1, '10', 3],
[4, 5, '1e2'],
));
// output:
Array (
[0] => 5
[1] => 15
[2] => 103
)
But call_user_func()
will complain about type mismatch.
Reflection doesn't follow strict types too. So instead of calling newInstanceArgs()
/newInstance()
in the
Container Factory,
we have
$instance = new $class(...$arguments);
It may be a little slower, but it's more reliable.
Now let's go to hacks.
With a simple hack you can bind a type to a variable.
function makeInt(int &$i): void
{
static $list = [];
$list[] = $obj = new class() {
public int $i;
};
$obj->i = &$i;
}
$int = 1;
makeInt($int);
$int = 42; // 42
$int = 'foo'; // Fatal error: Uncaught TypeError: Cannot assign string...
Don't use it because it leaks.
The null
, false
and true
types can now be used stand-alone.
What is this for? To provide covariance. For example, when extending a method, the return value with bool
can be
narrowed down to true
or false
, and nullable (?Foo
) can be narrowed to less specific null
. There are a lot of
such cases in libraries.
The DNF (Disjunctive Normal Form) has arrived.
Now you may meet such monster in code:
(Countable&Traversable)|array
ℹ️ Curious but nullable-sugar (?Foo
) was added in PHP 7.1, before Union Types.
ℹ️ Always explicitly specify the type of nullable parameters (?Foo $foo = null
) rather than relying only on the
default null value (Foo $foo = null
).
ℹ️ The never
type was added in PHP 8.1. But you don't need it if you use RoadRunner.
ℹ️ The callable
type exists only in function and method signatures. It can't be used in properties, so you can't put
it into Promoted properties either. That's because in different contexts callable can be different.
So how can we get a callable
and write it to a property? For example like this:
// A class declaration
private \Closure $callback;
public function __construct(callable $callback)
{
$this->callback = $callback(...);
}