====== PHP RFC: Literal Scalar Types ====== * Version: 0.1 * Date: 2026-06-15 * Author: Seifeddine Gmati, * Status: Under Discussion * Target Version: PHP 8.6 * Implementation: https://github.com/php/php-src/pull/22314 * First Published at: http://wiki.php.net/rfc/literal_scalar_types * Discussion thread: https://news-web.php.net/php.internals/131249 ===== Introduction ===== PHP type declarations can name a scalar type such as ''int'', ''float'' or ''string'', but they cannot restrict a value to a specific member of that type. The [[rfc:true-type|true type]] and the earlier ''false'' type already allow a single boolean value to be used as a type. This RFC generalises that idea to integer, floating point and string literals, so that a parameter, property or return type can be limited to an exact set of values: function setLogLevel('debug'|'info'|'warning'|'error' $level): void {} function setSign(-1|0|1 $sign): int {} ===== Proposal ===== Integer, floating point and string literals may be used as a type, both standalone and as members of a union type. A value satisfies a literal type when it is equal to one of the listed literals, after applying the usual scalar coercion rules of the calling mode. function f(1|2|3 $x): int { return $x; } f(1); // int(1) f(4); // TypeError: f(): Argument #1 ($x) must be of type 1|2|3, int given ==== Supported literals ==== * Integer literals, including negative ones: ''1'', ''0'', ''-1''. * Floating point literals, including negative ones: ''1.5'', ''-0.5''. * Single or double quoted string literals. The two quoting styles are equivalent and escape sequences are resolved at compile time. Interpolation is not allowed; a double quoted string containing a variable is a parse error. This RFC concerns integer, float and string literals only. ''true'', ''false'' and ''null'' are already standalone types in their own right and are unrelated to this proposal. ==== Coercion ==== A literal type coerces exactly like its base scalar type. In coercive typing mode the value is coerced to the base type and then checked for membership: function f(1|2|3 $x): int { return $x; } f("2"); // int(2) f(2.0); // int(2) f(true); // int(1) f("4"); // TypeError, 4 is not a member of 1|2|3 When a literal is combined with a wider type, the literal member is preferred when the value matches it, otherwise the wider member applies: function f(1|string $x) { return $x; } f(1); // int(1), matches the literal f("1"); // string("1"), matches the wider type f(2); // string("2"), coerced to string Under ''strict_types=1'' no coercion occurs, with the same single exception that already exists for the ''float'' type: an ''int'' is accepted where a ''float'' literal is expected. declare(strict_types=1); function f(1.5|2.0 $x): float { return $x; } f(2); // float(2) f("2"); // TypeError ==== Redundancy checks ==== A union may not contain a literal that is already implied by another member. Both cases are compile time errors: function f(1|1 $x) {} // Literal type 1 is redundant as it is already present in the union function f(1|int $x) {} // Literal type 1 is redundant as the union already allows its base type ==== Intersection types ==== A literal denotes a single value and carries no interface, so it cannot take part in an intersection type. ''1&Foo'' is a parse error. ==== Default values ==== A default value must be equal to one of the type's literals: function f(1|2 $x = 3) {} // Cannot use int as default value for parameter $x of type 1|2 ==== Variance ==== A literal type is a subtype of its base scalar type, and a union of literals is a subtype of the union of the corresponding base types. The standard variance rules therefore apply: return types may be narrowed and parameter types may be widened. class A { public function r(): int { return 1; } public function m(1|2 $x): void {} } class B extends A { public function r(): 1|2 { return 1; } // ok, covariant return public function m(1|2|3 $x): void {} // ok, contravariant parameter } ==== Reflection ==== A new ''ReflectionLiteralScalarType'' class is added, extending ''ReflectionType'' and exposing the literal value: class ReflectionLiteralScalarType extends ReflectionType { public function getValue(): int|float|string {} } Standalone literals, including nullable ones, and the members of a literal union are reported as ''ReflectionLiteralScalarType''. A union such as ''1|2|'foo''' is a ''ReflectionUnionType'' whose members are each a ''ReflectionLiteralScalarType''. The inherited ''allowsNull()'' and ''__toString()'' behave as for any other type, rendering for example ''1'', ''?1'' or ''1|2|'foo'''. ===== Backward Incompatible Changes ===== None. Literal scalar types are a new construct and do not change the behaviour of any existing declaration. The only addition to userland is the ''ReflectionLiteralScalarType'' class. ===== Proposed PHP Version ===== PHP 8.6. ===== RFC Impact ===== ==== To Reflection ==== Adds the ''ReflectionLiteralScalarType'' class described above. ==== To Opcache ==== None. ===== Proposed Voting Choices ===== Single yes/no vote, requiring a 2/3 majority to pass. ===== Patches and Tests ===== Implementation: [[https://github.com/php/php-src/pull/22314|php/php-src#22314]]. Tests live under ''Zend/tests/type_declarations/literal_types/''. ===== References ===== * [[rfc:union_types_v2|PHP RFC: Union Types 2.0]] * [[rfc:true-type|PHP RFC: Add true type]]