Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision |
rfc:stringable [2020/01/22 15:20] – nicolasgrekas | rfc:stringable [2020/05/12 13:37] – nicolasgrekas |
---|
* Version: 0.9 | * Version: 0.9 |
* Date: 2020-01-15 | * Date: 2020-01-15 |
* Author: Nicolas Grekas, nicolas.grekas+php@gmail.com | * Author: Nicolas Grekas, nicolasgrekas@php.net |
* Status: Draft | * Status: Accepted |
* First Published at: http://wiki.php.net/rfc/stringable | * First Published at: http://wiki.php.net/rfc/stringable |
| |
===== Introduction ===== | ===== Introduction ===== |
This PR introduces a new ''Stringable'' interface that can be added to classes that implement the ''%%__toString()%%'' method. | |
| This RFC introduces a new ''Stringable'' interface that is automatically added to classes that implement the ''%%__toString()%%'' method. |
It has two goals: | It has two goals: |
- allow using ''string|Stringable'' to express ''string | object-with-%%__toString()%%'' | - allow using ''string|Stringable'' to express ''string|object-with-%%__toString()%%'' |
- provide a forward upgrade path from PHP 7 to 8 | - provide a forward upgrade path from PHP 7 to 8 |
| |
===== Proposal ===== | ===== 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. | Goal 1. is to allow using the ''string|Stringable'' union type in PHP 8, to accept both strings and objects that implement ''%%__toString()%%''. 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. | Classes that implement ''%%__toString()%%'' can also declare the interface explicitly. Classes that don't declare the interface explicitly will still declare it implicitly. This allows both forward compatibility and backward compatibility: using a polyfill, classes can declare the interface on PHP 7; and on PHP 8, classes that don't do so will still be compatible with the ''string|Stringable'' union type. |
| |
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. [[https://github.com/symfony/polyfill/pull/224|as part of symfony/polyfill-php80]]), code style checkers could be able to enforce declaring the interface when ''%%__toString()%%'' is declared explicitly, for people that prefer doing so. |
| |
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 because their users will ask for it when they'll want to pass the ''string|Stringable'' union type. | |
| |
Here is the stub declaration of the interface: | Here is the stub declaration of the interface: |
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. | 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, the PR also adds the return type to existing ''%%__toString()%%'' methods when it's not explicitly declared already. Returning a string is already enforced by the engine so this doesn't change any semantics. | 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.) | 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 PR. | Providing an easy forward-path is the second goal of this RFC. |
| |
For reference, [[https://github.com/symfony/symfony/search?q=%22%40param+string%7Cobject%22+stringable&unscoped_q=%22%40param+string%7Cobject%22+stringable|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. | For reference, [[https://github.com/symfony/symfony/search?q=%22%40param+string|stringable%22|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 ===== | ===== Backward Incompatible Changes ===== |
===== RFC Impact ===== | ===== RFC Impact ===== |
==== To Existing Extensions ==== | ==== To Existing Extensions ==== |
Extensions will need to add the ''string'' return type when they declare ''%%__toString()%%'' methods. | Extensions will need to declare both the interface and the ''string'' return type when they declare ''%%__toString()%%'' methods if they want to pass the ''string|Stringable'' union type. |
| |
===== Open Issues ===== | ===== Open Issues ===== |
Make sure there are no open issues when the vote starts! | none |
| |
===== Unaffected PHP Functionality ===== | ===== Unaffected PHP Functionality ===== |
Declaring this interface is not required to benefit from the magic of the ''%%__toString()%%'' method. | Declaring this interface is not mandatory to benefit from the magic of the ''%%__toString()%%'' method. |
| |
===== Future Scope ===== | ===== Future Scope ===== |
none | void |
| |
===== Proposed Voting Choices ===== | ===== Proposed Voting Choices ===== |
Accept the proposed interface for PHP 8.0: yes/no | yes/no |
| |
| ===== Vote ===== |
| <doodle title="Adopt the Stringable interface as proposed in this RFC?" auth="nicolasgrekas" voteType="single" closed="true"> |
| * Yes |
| * No |
| </doodle> |
| |
===== Patches and Tests ===== | ===== Patches and Tests ===== |
===== Rejected Features ===== | ===== Rejected Features ===== |
| |
- Adding a new ''%%stringable%%'' special type (like ''iterable'', ''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.) | - Adding a new ''%%stringable%%'' special type (like ''iterable'', ''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 [[https://externals.io/message/98424|has no simple answers]]. | - 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 [[https://externals.io/message/98424|has no simple answers]]. |
- it has been proposed [[https://github.com/php/php-src/pull/5083#issuecomment-573899924|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, [[https://github.com/php/php-src/pull/5088|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. | - it has been proposed [[https://github.com/php/php-src/pull/5083#issuecomment-573899924|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, [[https://github.com/php/php-src/pull/5088|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. |