PHP RFC: Class name type
- Version: 0.1
- Date: 2017-04-29
- Author: Andrea Faulds, ajf@ajf.me
- Status: Draft
- First Published at: http://wiki.php.net/rfc/class_and_interface_name_types
Introduction
There are various cases where the fully-qualified name of a class or interface may be returned from or passed as an argument to a function. While PHP has a type declaration for function names, callable
, it does not yet have such a type declaration for class or interface names.
Proposal
This RFC proposes the introduction of a new type declaration for use on parameters and return values, the class
type declaration. It would accept the fully-qualified name of a class or interface.
It would validate its value using the following steps:
- If the value is not a string, reject it.
- If there is no currently-loaded class, interface, or trait with the name given by the value, attempt to autoload it.
- If there is now no currently-loaded class or interface with the name given by the value, reject the value.
- Otherwise, accept the value.
As a parameter type, it would permit default values of NULL
(to make the parameter null) and constant strings (including ::class
syntax).
A simple example of usage:
<?php function accepts_class(class $class = stdClass::class) { var_dump($class); } accepts_class(); // string(8) "stdClass" accepts_class(stdClass::class); // string(8) "stdClass" accepts_class("stdClass"); // string(8) "stdClass" accepts_class(ArrayAccess::class); // string(11) "ArrayAccess" accepts_class("nonexistant"); // Fatal error: Uncaught TypeError: Argument 1 passed to accepts_class() must be of the type class, string given…
Rationale and Points for Discussion
Naming: "class" vs "classname"?
This RFC currently proposes to call this type declaration class
. Hack, PHP's sister language, already includes a similar type for class names, but calls it classname
instead.
The name class
has a few advantages:
- It is short
- It is already a reserved word, so introducing it requires no backwards-incompatibility break
- It resembles the
::class
syntax for obtaining the name of a class - It avoids the redundancy of saying “name”, given PHP does not represent classes as values in any other way (excepting reflection)
- It aligns with how PHP treats a class and its fully-qualified name the same in several places (e.g.
new $class
and$class::$property
)
However, there are also arguments in favour of classname
:
- It conveys more directly that the value is a string containing a class name
- It is already in use by Hack, so using this would prevent further divergence between Hack and PHP
Should "class" include only classes, or both classes and interfaces?
This RFC currently proposes that there would be a single new type, class
, which would accept the names of both classes and interfaces. There might, however, be a case to be made for accepting only classes, and/or having a separate type for interface names (interface
, say), or even a third type for accepting both classes and interfaces.
Having class
accept both classes and interfaces has a few arguments in its favour:
- It limits the number of new types that are introduced (one versus two or three)
- It aligns with the internal
zend_parse_parameters()
type“C”
, used by some functions in the PHP standard library, which likewise accepts both class and interface names - It aligns with how
::class
works for both class and interface names - Interfaces are essentially glorified abstract classes
- PHP internally represents interfaces as a kind of class
- Reflection represents interfaces as a kind of class
- The type declaration need not be perfectly fine-grained, as the function body can check if the value given is an interface itself
That being said, there are also arguments in favour of class
accepting only classes:
- Class and interface definitions use different keywords
class_exists()
treats interfaces as non-existent- It might be surprising to see the name of an interface be passed as an argument to a parameter typed “
class
”
There is an advantage to a separate interface
type, insofar as having PHP distinguish classes and interfaces in type declarations makes for clearer function signatures and avoids potential manual testing of whether a value is class or an interface.
If there was a separate interface
type, there would be an argument for having the class
type also accept interfaces, since classes are a superset of interfaces, and classes can subclass (“implement”) interfaces, but not the other way around.
If none of these approaches are palatable, there could be a third type accepting both classes and interfaces, perhaps named classlike
. However, this might be reaching the point of diminishing returns.
All the possibilities in this section can and have been implemented provisionally, and the branch on GitHub contains all of them in its commit history.
Backward Incompatible Changes
The class
keyword is already a reserved word, so there is no backwards-compatibility break created by its reuse here.
Proposed PHP Version(s)
This is proposed for the next PHP 7.x, currently PHP 7.2.
RFC Impact
To Opcache
Testing revealed that Opcache's type inference needed updating to accommodate the new type declaration. This has been done and it seems to work, but it is possible other areas of incompatibility exist and have been missed.
Unaffected PHP Functionality
Classes, interfaces and traits continue not to be objects in themselves, and are only referenced by name.
Future Scope
A supertype could be introduced, which could name not only classes and interfaces, but also primitive PHP types (int
, string
etc.) and possibly pseudo-types.
The potential future ability to use variables containing type names in place of literal type names in source code could facilitate generic programming.
Proposed Voting Choices
This would require a 2/3 majority, as a language change. The vote would be a Yes/No vote as to whether to accept the RFC for the appropriate future version of PHP.
Patches and Tests
There is a patch here which contains tests: https://github.com/php/php-src/compare/master...hikari-no-yume:class_type_declaration
The commits for versions with multiple new types do not handle inheritance concerns.
There is not yet a pull request.
There is not yet a patch for the language specification.
Implementation
After the project is implemented, this section should contain
- the version(s) it was merged to
- a link to the git commit(s)
- a link to the PHP manual entry for the feature
- a link to the language specification section (if any)
References
: Links to external references, discussions or RFCs