rfc:reserve_primitives

PHP RFC: Reserving primitive types

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

Rejected Features

(TODO)

rfc/reserve_primitives.txt · Last modified: 2017/09/22 13:28 (external edit)