rfc:class_and_interface_name_types

PHP RFC: Class name type

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:

  1. If the value is not a string, reject it.
  2. If there is no currently-loaded class, interface, or trait with the name given by the value, attempt to autoload it.
  3. If there is now no currently-loaded class or interface with the name given by the value, reject the value.
  4. 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

  1. the version(s) it was merged to
  2. a link to the git commit(s)
  3. a link to the PHP manual entry for the feature
  4. a link to the language specification section (if any)

References

FIXME: Links to external references, discussions or RFCs

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