PHP RFC: Reserving primitive types
- Version: 0.9
- Date: 2015-02-08
- Author: Timm Friebe, thekid@php.net
- Status: Draft
- First Published at: https://wiki.php.net/rfc/reserve_primitives
This RFC would like to discuss options for reserving primitive type names int
, integer
, float
, double
, bool
, boolean
and string
.
Introduction
Reserving the names as suggested e.g. by the scalar types hints RFC will create problems if people have used them as class, interface or trait names. Creating BC problems slows adoption, so they should be outweighed with benefits.
Proposal
The motivation for reserving the names stems from a possible ambiguity allowing them would create.
class int { ... } function sum(int $a, int $b): int { ... }
In this case, it's not clear what the int
in the method signature stands for: PHP's native primitive or the userland class?
Using the above is a bit unfair towards the real-life situation: The majority of frameworks in PHP nowaday uses a) namespaces and b) would capitalize the type name, giving us:
namespace types; class Int { ... } // ... another file: function sum(\types\Int $a, \types\Int $b): \types\Int { ... } // ... yet another file: use types\Int; function sum(Int $a, Int $b): Int { ... }
Purely looking from a reader's perspective, the first example is surely not to be confused with anything that is a primitive. For the second example, it's a bit harder to judge, though to me personally, it would still be OK.
The options this RFC suggests discussing are:
Option 1: Reserve
This first option suggests reserving primitive type names.
- BC break: No global class, interface or trait can be named after a primitive type name.
- BC break: No namespaced class, interface or trait can be named after a primitive type name.
- BC break: No class, interface or trait named after a primitive name can be imported with
use
Option 2: Allow namespaced
This second option suggests allowing primitive type names in combination with namespaces, but disallowing them in global scope.
- BC break: No global class, interface or trait can be named after a primitive type name
- BC break: No class, interface or trait named after a primitive name can be imported with
use
The following would work:
namespace types; class Int { ... } // ... another file: function sum(\types\Int $a, \types\Int $b): \types\Int { ... }
Option 3: Allow namespaces and use
This would allow primitive type names in combination with namespaces, and accept imports, overriding primitive definitions.
- BC break: No class, interface or trait in the global namespace can be named after a primitive type name
The following would work:
namespace types; class Int { ... } // ... another file: use types\Int; function sum(Int $a, Int $b): Int { ... }
To the reader, it's *probably* clear because of the uppercasing. If a lowercase class was used, this would be confusing. Looking at real-life frameworks and libraries, we'd usually find the first case.
Option 4: Case insensitivity exception
This option would add special handling to the primitive types names and allow all situations in which a different casing was used.
- BC break: No class, interface or trait in the global namespace can be named *exactly* after a primitive type name
Or, in code:
class int { } // Parse error: Cannot use primitive type name "int" class Int { } // OK
Option 5: Use cast-tokens
This would make any situation unambigous, reuse already existing parser tokens, and create no BC breaks:
// The primitive function sum((int) $a, (int) $b): (int) { ... } // Always the int class function sum(\types\Int $a, \types\Int $b): \types\Int { ... } function sum(Int $a, Int $b): Int { ... } function sum(int $a, int $b): int { ... }
Fair enough, this is counter-intuitive to the syntax used so far for arrays and callables as well as for value types; and the majority of other programming languages.
Option 6: Do not reserve
The type names would not be reserved. No BC breaks occur, while it's also not possible to use them for parameter and return type hints. For both situations, alternative suggestions, e.g. along a “design by contract” RFC are discussed.
Proposed PHP Version(s)
PHP 7.0
RFC Impact
In all situations except the last two options, reserving primitive type names causes a BC break. The options above sketch out how we can cope with this, balancing the usefulness of being able to use these tokens and backwards compatibility on the other side.
These frameworks would be affected by reserving the word “string” (incomplete list):
- CakePHP -
lib/Cake/Utility/String.php
- Joomla -
src/Joomla/Filesystem/Stream/String.php
- ZF2 -
library/Zend/XmlRpc/Value/String.php
- Drupal8 -
core/lib/Drupal/Component/Utility/String.php
- XP Framework -
src/main/php/lang/types/String.class.php
This code search for a PHP String class suggests various more libraries will be affected. Same goes for “Boolean”, “Integer”, “Int”, “Float” and “Double”
Future Scope
Extend these rules to resource
and the pseudo-type mixed
, as well as possibly even array
and callable
.
Proposed Voting Choices
Voting on options to give the discussion a direction.
Patches and Tests
(TODO)
Implementation
(TODO)
References
- https://wiki.php.net/rfc/scalar_type_hints_v_0_1#open_issues (Discussion about reserving names)
Rejected Features
(TODO)