PHP RFC: Add true type
- Version: 0.2
- Date: 2022-04-7
- Author: George Peter Banyard, girgias@php.net
- Status: Accepted
- Target Version: PHP 8.2
- Implementation: https://github.com/php/php-src/pull/8326
- First Published at: http://wiki.php.net/rfc/true-type
Introduction
PHP now has support for null and false as standalone types.
However, true
which is the natural counter part of false
does not even exist as a type.
The motivation in the Union Types 2.0 RFC to include false
but not true
was:
While we nowadays encourage the use of null over false as an error or absence return value, for historical reasons many internal functions continue to use false instead. As shown in the statistics section, the vast majority of union return types for internal functions include false.
A classical example is the
strpos()
family of functions, which returnsint|false
.While it would be possible to model this less accurately as
int|bool
, this gives the false impression that the function can also return a true value, which makes this type information significantly less useful to humans and static analyzers both.For this reason, support for the
false
pseudo-type is included in this proposal. Atrue
pseudo-type is not part of the proposal, because similar historical reasons for its necessity do not exist.
We believe there are now sufficient reasons to include support for the true
type.
With the introduction of ValueError
and many E_WARNING
being promoted to Errors/Exceptions in PHP 8.0.0,
a large amount of internal functions which used to return false
on failure/error no longer do.
And they now always return true
. Ideally these function would be converted to void
but doing so is a BC break.
Moreover a void
function always returns null
which is a falsy value. Therefore the following code would now never hit the happy path:
if (function_which_could_fail_before()) { // Do useful stuff } else { // Bailout and handle error }
Marking these internal functions as having a return type of true
could allow tools, such as static analysers, to mark part of this code unreachable.
Another motivation is providing more precise type information for humans and static analysers while satisfying LSP.
For example a child method which always returns true
instead of bool
:
class User { function isAdmin(): bool } class Admin extends User { function isAdmin(): true { return true; } }
Finally, being able to add a true
return type would help the optimizer and also prevent manually maintaining specialized information for the optimizer.
Proposal
Add support for using true
as a type declaration, wherever type declarations are currently allowed.
The true
type does not allow coercions, exactly as how the false
type currently behaves.
class Truthy { public true $truthy = true; public function foo(true $v): true { /* ... */ *} }
Compile time redundancy checks
As it is the case currently for false
a compile time error will be emitted if true
and bool
are present in union.
For example, the following function signature:
<?php function test(): bool|true {} ?>
Will emit the following error:
Fatal error: Duplicate type true is redundant in %s on line %d
Compile time error for using true|false instead of bool
A compile time error is also emitted when true|false
(or equivalently false|true
) is used instead of bool
.
The following function signature:
<?php function test(): false|true {} ?>
will emit the following error:
Fatal error: Type contains both true and false, bool should be used instead in %s on line %d
The rationale is that the subtyping relation between false|true
and bool
needs to be defined.
As bool
coerces scalar values but false
and true
currently do not.
It is possible to make true|false
follow the same semantics as bool
, however this might impact the implementation of literal types.
Therefore the conservative approach of banning the usage of true|false
is chosen.
Examples
There are many examples of functions which only return true
being internal or in userland.
Some examples of internal functions which always return true
are the array_sort()
functions,
many mysqli_*()
functions, SplFixedArray::setSize()
, etc.
An example of a userland project which can benefit of this change is Composer, which has various cases where they could use a true
type.
[1]
[2]
[3]
[4]
Reflection
The new true
type will behave as any other type in regards to Reflection.
Backward Incompatible Changes
This RFC does not contain any backwards incompatible changes.
Proposed PHP Version
Next minor version, i.e. PHP 8.2.
Proposed Voting Choices
As per the voting RFC a yes/no vote with a 2/3 majority is needed for this proposal to be accepted.
Voting started on 2022-05-29 and will end on 2022-06-12.
Future Scope
The features discussed in the following are not part of this proposal.
Literal Types
The true
(and previously false
) pseudo-type introduced in this RFC is a special case of a “literal type”, such as supported by TypeScript. They allow specifying enum-like types, which are limited to specific values.
type ArrayFilterFlags = 0|ARRAY_FILTER_USE_KEY|ARRAY_FILTER_USE_BOTH; array_filter(array $array, callable $callback, ArrayFilterFlags $flag): array;
A benefit of using a union of literal types instead of an enum, is that it works directly with values of the underlying type, rather than an opaque enum value. As such, it is easier to retrofit without breaking backwards-compatibility.
However, the opinion of the authors is that enums are superior to literal types and the reason to add true
is due to the extremely low implementation complexity, as the PHP engine already treats true
and false
as separate types. And that only having false
and not true
is strange.
Allowing usage of true|false
To allow the usage of false|true
the subtyping relation between false|true
and bool
will need to be defined.
Type Aliases
As types become increasingly complex, it may be worthwhile to allow reusing type declarations. There are two general ways in which this could work. One is a local alias, such as:
use int|float as number; function foo(number $x) {}
In this case number
is a symbol that is only visible locally and will be resolved to the original int|float
type during compilation.
The second possibility is an exported typedef:
namespace Foo; type number = int|float; // Usable as \Foo\number from elsewhere
Implementation
GitHub pull request: https://github.com/php/php-src/pull/8326
After the project is implemented, this section should contain
- the version(s) it was merged into
- a link to the git commit(s)
- a link to the PHP manual entry for the feature