This is an old revision of the document!
PHP RFC: Add Stringable interface
- Version: 0.9
- Date: 2020-01-15
- Author: Nicolas Grekas, nicolas.grekas+php@gmail.com
- Status: Under Discussion
- First Published at: http://wiki.php.net/rfc/stringable
Introduction
This RFC introduces a new Stringable
interface that can be added to classes that implement the __toString()
method.
It has two goals:
- allow using
string|Stringable
to expressstring|object-with-__toString()
- provide a forward upgrade path from PHP 7 to 8
Proposal
Goal 1. is to allow using the string|Stringable
union type in PHP 8, to accept both strings and objects that implement __toString()
and declare this interface. This is critically missing currently, on codes that deal with stringable objects: they can't be made type-safe.
Classes that implement __toString()
aren't required to declare the interface as that would prevent seamless forward/backward compatibility with PHP 7.
By being simple and without any magic capabilities on its own, this interface is trivially polyfilled on PHP < 8.
Once a polyfill becomes widely available (e.g. as part of symfony/polyfill-php80), we can expect code style checkers to be able to enforce declaring the interface when __toString()
is used. For projects that don't use cs checkers, they'll notice quickly that they missed adding the interface when their users will ask for it if they do.
Here is the stub declaration of the interface:
interface Stringable { public function __toString(): string; }
Because it adds the string
return type, this interface has the potential to force a BC break on any existing libraries that want to adopt it.
In order to ease forward and backward compatibility, this RFC also proposes to automatically add the return type at compile time when a __toString()
method doesn't do it explicitly already. Returning a string is already enforced by the engine so this doesn't change any semantics.
This way, code moving to PHP8 won't be forced to add the return type explicitly (which would break BC on their side), and code in PHP < 8 can adopt a polyfill interface immediately (one that doesn't declare the return type for the same BC reasons.)
Providing an easy forward-path is the second goal of this RFC.
For reference, here are some annotations in Symfony, added by contributions from real-world use cases and that currently cannot be expressed precisely enough using any union types in PHP 8.
Backward Incompatible Changes
Codes that declare a symbol named Stringable
in the root namespace will have to rename it.
Proposed PHP Version(s)
PHP 8.0
RFC Impact
To Existing Extensions
Extensions will need to add the string
return type when they declare __toString()
methods.
Open Issues
none
Unaffected PHP Functionality
Declaring this interface is not mandatory to benefit from the magic of the __toString()
method.
Future Scope
void
Proposed Voting Choices
Accept the proposed interface for PHP 8.0: yes/no
Patches and Tests
Implementation
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
- a link to the language specification section (if any)
References
Rejected Features
- Adding a new
stringable
special type (likeiterable
,callable
, etc.) is not considered in this RFC because it would require adding a new reserved keyword in the language. This would break BC more heavily and would defeat goal #2 mentioned previously (ability to polyfill on PHP7.) - Consistently, this RFC doesn't embed any
is_stringable()
function. If we were to consider one, defining what happens when e.g. an int is passed has no simple answers. - it has been proposed on the GitHub PR that the method attached to the interface could be named
toString()
instead of__toString()
. This idea goes further than strictly required to achieve goal #1 and has been objected as not necessary in the following comments. For reference, here is a naive implementation of the proposal. What the patch highlights is that this increases the complexity of the engine, for a reason that would need to be justified.