In PHP using 'type-hints' to define either the allowed parameter types for a function, or the return type of a function, performs two useful roles:
- Using types allows the PHP engine to enforce the correct type of variable are passed to, or returned from, a function.
- Using types makes it easy to reason about what types need to be passed to, or can be returned from a function. This makes it easier for both humans, and static code analysis tools, to determine about whether code is correct or not.
For a lot of functions in PHP, each parameter will only be one type. Similarly, for the majority of functions, the return value of a function, will only ever be of one type.
However, for a significant number of functions, the acceptable parameters, or the possible return values, can be of more than one type. For example consider the 'stripos' function where the return value varies based on:
- if the needle exists it returns an integer.
- if the needle is not found, a boolean of value false is returned.
In the documentation on php.net, the two possible return types are documented as 'mixed' - however this does not actually document what the possible return types are, only that there is more than one possible type returned. Also, it is not possible to define a 'mixed' type in PHP code.
Currently in userland code, when a parameter for a function can be one of a multiple but limited set of types, or the return value from a function can be one of a multiple but limited set of types, there can be no type information supplied, and so it is not possible for the PHP engine to enforce any types passed to/from that function, and it is not easy for people using that function to reason about the types passed to/from that function.
This RFC seeks to address this limitation.
This RFC proposes the ability to define multiple possible types for parameter and return types. To define a 'union type' a single vertical bar (OR) is placed between types e.g. 'int|bool' represents the union type of either integer or boolean. For these 'union types' a value passes the type check if the value would pass any one of the types in the union.
Additionally this RFC proposes that the values 'true', 'false' and 'null' will be usable as types in both parameter types and return type definitions.
There can be more than two types in the union.
A function that requires either a string or an array is passed to it as the parameter:
function print_each(array | string $in) {
foreach ((array) $in as $value) {
echo $value, PHP_EOL;
}
}
print_each(['Bob', 'Joe', 'Levi']); // ok
print_each('Levi'); // ok
print_each(new stdclass()); // not ok
A class instance method that requires that either a string or a ParameterGenerator object is passed as the parameter.
//From zend-code
class MethodGenerator extends AbstractMemberGenerator
{
...
public function setParameter(ParameterGenerator|string $parameter) {
...
}
}
A userland definition of stripos
function:
function stripos(string $haystack, string $needle, int $offset = 0): int|bool
{
$lowerHaystack = strtolower($haystack);
$lowerNeedle = strtolower($needle);
return strpos($lowerHaystack, $lowerNeedle, $offset);
}
Bob Weinand and Joe Watkins have made a patch: php/php-src#1887 which is needs some small polishing but implements the proposed features.