PHP RFC: Type Aliases
- Version: 1.1
- Date: 2025-12-02
- Author: Robert Landers, landers.robert@gmail.com
- Status: Under Discussion
- Implementation: https://github.com/php/php-src/pull/20635
Introduction
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; } ?>
Proposal
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.
Syntax
Defining Type Aliases
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:
- Primitives:
int,string,float,bool,array,object,mixed,null,true,false - Classes and interfaces:
\App\User,Countable - Nullable types:
?string,?int - Union types:
int|float,string|Stringable - Intersection types:
Countable&Traversable - DNF types:
User|(Admin&Authenticated)
Excluded type expressions:
void- cannot match any valuenever- cannot match any value
Importing Type Aliases
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 ?>
Behavior
Compile-Time Resolution
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 {} ?>
Nested Aliases
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 ?>
Scope Rules
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; } ?>
Shadowing
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 ?>
Namespace Resolution
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; ?>
Examples
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; } } ?>
Backward Incompatible Changes
New Keywords
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.
Proposed PHP Version(s)
Next PHP 8.x (PHP 8.5 or later)
RFC Impact
To the Ecosystem
IDEs and Language Servers: Will need updates to understand the new syntax and provide:
- Autocomplete for type aliases
- Go-to-definition for aliases
- Expansion of aliases in hover information
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.
To Existing Extensions
No impact. Type aliases are resolved at compile time and do not affect runtime behavior or extension APIs.
To SAPIs
No impact.
Open Issues
None currently.
Future Scope
The following features are explicitly not part of this RFC but could be considered in future proposals:
- Global/exportable type aliases: Type aliases that can be autoloaded and used across the project without explicit imports
- Reflection support: Runtime reflection of type aliases via ReflectionTypeAlias or similar
- Callable signatures:
use type callable(int): string as IntToString; - Array shapes:
use type array{x: int, y: int} as Point; - Generic type aliases:
use type array<K, V> as Map;
Voting Choices
Primary vote requiring a 2/3 majority:
Patches and Tests
Proof of concept implementation: https://github.com/php/php-src/pull/20635
The implementation includes:
- Lexer and parser changes for
use typeandinclude typessyntax - Compiler changes for type alias storage and resolution
- Test coverage for basic usage, nested aliases, error cases
Implementation
After the RFC is implemented, this section should contain:
- the version(s) it was merged into
- a link to the git commit(s)
- a link to the PHP manual entry for the feature
References
- Previous type alias discussions on php-internals
- Pattern Matching RFC (related type system work)
Rejected Features
- Runtime type aliases (type_alias() function): Rejected in favor of compile-time-only approach for simplicity and performance
- Global exportable aliases: Deferred to future scope due to autoloading complexity
- Aliases as special classes: Rejected in favor of simple compile-time substitution
- Namespace support in types files: Rejected to keep types files simple and avoid namespace resolution complexity
Changelog
- 2025-12-02 v1.1: Clarified that type aliases follow standard PHP
usescoping rules (namespace-scoped, not file-scoped). Added detailed examples for different namespace configurations. - 2025-12-02 v1.0: Initial draft with type aliases and include types syntax