====== PHP RFC: Stringable Enums ======
* Version: 0.1
* Date: 2025-11-07
* Author: Daniel Scherzer, daniel.e.scherzer@gmail.com
* Status: Under Discussion
* Implementation: https://github.com/php/php-src/pull/20415
* Discussion thread: https://news-web.php.net/php.internals/129540
===== Introduction =====
Enumerations (enums) were introduced in PHP 8.1 ([[rfc:enumerations]]). The RFC specified that magic methods other than __call(), __callStatic(), and __invoke() would not be permitted in enums, noting that most other magic methods involve state, which enum instances do not have. However, the exclusion also applies to __toString(), which does not need to involve state. This RFC proposes allowing the __toString() magic method on enums.
name . ' = ' . $this->value;
}
}
echo Foo::Bar; // produces "Foo::Bar = Baz"
?>
===== Proposal =====
The error when trying to implement __toString() on an enumeration is removed. The default validation of that magic method (visibility, arguments, return type) is applied if the method is present, and if present the Stringable interface is added as normal ([[rfc:stringable]]).
==== Examples ====
Simple example for a unit enum:
name . ' is a case of the ' . __CLASS__ . ' enum';
}
}
echo Foo::Bar; // produces "Bar is a case of the Foo enum"
?>
Simple example for a backed enum:
name . ' = ' . $this->value;
}
}
echo Foo::Bar; // produces "Foo::Bar = Baz"
?>
==== Status quo ====
The status quo, where enums cannot implement __toString(), leads to various workarounds. For example
* the brick/date-time package defines a toString method for its Month enum that does the same thing as __toString would ([[https://github.com/brick/date-time/blob/2e0a96ec408c106a2f578781e4b2160e02c56758/src/Month.php#L140|source]]). Note that here, the method is on an //int// backed enum, and the string used is not just the value, nor is it the same as the case name.
* the roomies/fraudable package implements a toString method for its Label enum, and the returned value is based on configuration function that allows the representation to be controlled in the laravel settings ([[https://github.com/roomies-com/fraudable/blob/0e3a37311867a395f2a9baf353ca53e6c664ba39/src/Label.php#L15|source]]).
* the tempest/framework framework manually checks for enums with a toString method to use for the display value of an option holding an enum case ([[https://github.com/tempestphp/tempest-framework/blob/fcf35f27a3393b520c90fa5f8d385c47b1ffa88c/packages/console/src/Components/Option.php#L23|source]]).
In the first two cases, we can see that the desired string representation is not just the name or value of an enum case, and in the third, that the existing restriction of __toString leads to workarounds that could be mitigated by allowing the magic method.
This is not meant as an exhaustive list, and are just a few examples found in a quick GitHub search.
===== Backward Incompatible Changes =====
There should be no backward incompatible changes - this is the removal of an existing error.
===== Proposed PHP Version(s) =====
Next PHP version (PHP 8.6).
===== RFC Impact =====
==== To the Ecosystem ====
Analysis tools that warn about trying to define a __toString() method on enums would need to be updated to not warn when the code is for PHP 8.6+.
==== To Existing Extensions ====
There are some built-in enums, e.g. \Random\IntervalBoundary - in the future, they may have __toString() methods, but this RFC does not include such additions.
===== Future Scope =====
Perhaps some of the other magic methods should also be allowed.
===== Voting Choices =====
Please consult [[https://github.com/php/policies/blob/main/feature-proposals.rst#voting|the php/policies repository]] for the current voting guidelines.
* Yes
* No
* Abstain
===== Patches and Tests =====
https://github.com/php/php-src/pull/20415
===== Implementation =====
After the RFC 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
===== References =====
Links to external references, discussions, or RFCs.
===== Rejected Features =====
==== Automatic implementation ====
In writing this RFC I was pointed to [[rfc:auto-implement_stringable_for_string_backed_enums]], which proposed that string-backed enums automatically provide __toString() methods that just returned their underlying values. That RFC was eventually withdrawn without a vote, but some concerns raised there are worth addressing. That RFC proposed that
> E.g. defining an enum like this one:
>
>
enum Suit: string {
case Hearts = 'H';
// ...
}
> Would virtually define this:
>
enum Suit: string implements Stringable {
case Hearts = 'H';
// ...
public function __toString(): string
{
return $this->value;
}
}
However, such automatic implementations assume that the value of a string-backed enum is a good string representation of the object. For the given example of card suits, a better representation might be the name ("Hearts") instead of the value ("H").
That RFC also specified that
> This RFC proposes that string-backed enums auto-implement Stringable, while still disallowing user-land implementations of the method.
Automatic implementations in this manner would thus continue to restrict userland code from specifying desired string representations of enums. While the prohibition on user-land implementations could be removed, the default automatic implementation could be confusing.
Automatic implementation might also result in confusion regarding int-backed enums. After all, if //all// string-backed enums can be cast to strings, why can't int-backed enums be cast to ints? By avoiding the automatic implementation, we align the casting of enums with the casting of objects - you can't cast either to an int, and can only cast them to strings if they implement the __toString() method.
===== Changelog =====
If there are major changes to the initial proposal, please include a short summary with a date or a link to the mailing list announcement here, as not everyone has access to the wikis' version history.