Type aliases provide a mechanism to create more descriptive and readable type hints in PHP code. This RFC proposes type aliases that are resolved at compile time, improving code readability and maintainability while keeping the implementation simple and performant. Type aliases follow the same scoping rules as other use imports (classes, functions, constants).
<?php use type int|float as Number; use type Number|string as Scalar; function add(Number $a, Number $b): Number { return $a + $b; } function process(Scalar $value): Scalar { return $value; } ?>
This RFC proposes type aliases that are resolved entirely at compile time. Unlike approaches that create runtime type representations, these aliases are simple compile-time substitutions - the alias is replaced with its underlying type during compilation and has no runtime existence. Type aliases follow the same scoping rules as other use imports to maintain consistency with existing PHP behavior.
Type aliases are defined using the extended use statement with the type keyword:
<?php use type int|float as Number; use type ?string as OptionalString; use type Countable&Traversable as CountableTraversable; use type User|(Admin&Authenticated) as AuthUser; ?>
The syntax is: use type <type-expression> as <AliasName>;
Supported type expressions:
int, string, float, bool, array, object, mixed, null, true, false\App\User, Countable?string, ?intint|float, string|StringableCountable&TraversableUser|(Admin&Authenticated)Excluded type expressions:
void - cannot match any valuenever - cannot match any value
Type aliases can be shared across files using include types:
<?php // types.php - a types-only file use type int|float as Number; use type Number|string as Scalar; use type ?string as OptionalString; ?>
<?php // main.php include types 'types.php'; function calculate(Number $a, Number $b): Number { return $a + $b; } ?>
The include types statement imports type aliases into the current scope, following the same scoping rules as use type:
<?php namespace App\Math { include types 'math_types.php'; // Types available only in this namespace block function add(Number $a, Number $b): Number { return $a + $b; } } namespace App\Other { // Number is NOT available here } ?>
Important: Types files must only contain type alias declarations. Any other code (functions, classes, statements) will result in a compile error:
<?php // invalid_types.php - THIS WILL ERROR use type int|float as Number; function foo() {} // Error: Types file must only contain type aliases ?>
Type aliases are resolved at compile time through simple substitution. When the compiler encounters an alias in a type position, it replaces it with the underlying type expression:
<?php use type int|float as Number; // This function signature: function add(Number $a, Number $b): Number {} // Is compiled as: function add(int|float $a, int|float $b): int|float {} ?>
Aliases can reference other aliases defined earlier in the same scope:
<?php use type int|float as Number; use type Number|string as Scalar; // Expands to int|float|string ?>
Type aliases follow the same scoping rules as other use import statements (classes, functions, constants). This ensures consistent and predictable behavior:
No namespace - type aliases apply to the whole file:
<?php use type int|float as Number; function add(Number $a, Number $b): Number { // Works return $a + $b; } ?>
Curly brace scoped namespace - type aliases apply only within the namespace block:
<?php namespace App\Math { use type int|float as Number; function add(Number $a, Number $b): Number { // Works return $a + $b; } } namespace App\Other { // Number is NOT defined here - would need its own use type statement function process(int $value): int { return $value; } } ?>
Single unscoped namespace - type aliases apply to the whole file:
<?php namespace App\Math; use type int|float as Number; function add(Number $a, Number $b): Number { // Works return $a + $b; } class Calculator { public function multiply(Number $a, Number $b): Number { // Works return $a * $b; } } ?>
Multiple unscoped namespaces - type aliases apply until the next namespace declaration:
<?php namespace App\Math; use type int|float as Number; function add(Number $a, Number $b): Number { // Works return $a + $b; } namespace App\Other; // Number is NOT defined here - would need its own use type statement function process(int $value): int { return $value; } ?>
Type aliases follow the same shadowing rules as class imports. An alias shadows any class with the same name in the current scope:
<?php use type int|float as DateTime; // Shadows the DateTime class function process(DateTime $value) {} // Accepts int|float function other(\DateTime $value) {} // Accepts the actual DateTime class ?>
Class names in type expressions are resolved according to standard PHP namespace rules at the point of alias definition:
<?php namespace App\Models; class User {} use type User|Admin as AuthUser; // User resolves to App\Models\User ?>
For include types, types files must not contain namespace declarations. Any class references in types files must use fully qualified names:
<?php // types.php - correct usage use type \App\Models\User|\App\Models\Admin as AuthUser; ?>
Basic type alias usage:
<?php use type int|float as Number; function add(Number $a, Number $b): Number { return $a + $b; } var_dump(add(1, 2)); // int(3) var_dump(add(1.5, 2.5)); // float(4) ?>
Type error shows expanded type:
<?php use type int|float as Number; function add(Number $a): Number { return $a; } add("string"); // TypeError: add(): Argument #1 ($a) must be of type int|float, string given ?>
Shared types across files:
<?php // math_types.php use type int|float as Number; use type Number|string as Scalar; ?>
<?php // calculator.php include types 'math_types.php'; class Calculator { public function add(Number $a, Number $b): Number { return $a + $b; } public function format(Scalar $value): string { return (string) $value; } } ?>
This RFC introduces type and types as context-sensitive keywords. They can still be used as class, function, and method names, but have special meaning in the following contexts:
use type ... - defines a type aliasinclude types ... - imports type aliases from a file
Impact assessment using grep.app and GitHub search shows minimal usage of type and types as identifiers in positions that would conflict.
Next PHP 8.x (PHP 8.5 or later)
IDEs and Language Servers: Will need updates to understand the new syntax and provide:
Static Analyzers (PHPStan, Psalm): Will need to parse the new syntax and resolve aliases during analysis. The compile-time nature makes this straightforward.
Documentation Tools: May want to document type aliases or show expanded types.
No impact. Type aliases are resolved at compile time and do not affect runtime behavior or extension APIs.
No impact.
None currently.
The following features are explicitly not part of this RFC but could be considered in future proposals:
use type callable(int): string as IntToString;use type array{x: int, y: int} as Point;use type array<K, V> as Map;Primary vote requiring a 2/3 majority:
Proof of concept implementation: https://github.com/php/php-src/pull/20635
The implementation includes:
use type and include types syntaxAfter the RFC is implemented, this section should contain:
use scoping rules (namespace-scoped, not file-scoped). Added detailed examples for different namespace configurations.