Table of Contents

PHP RFC: Type Aliases

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:

Excluded type expressions:

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:

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:

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:

Voting Choices

Primary vote requiring a 2/3 majority:

Accept Type Aliases as described in this RFC?
Real name Yes No Abstain
Final result: 0 0 0
This poll has been closed.

Patches and Tests

Proof of concept implementation: https://github.com/php/php-src/pull/20635

The implementation includes:

Implementation

After the RFC is implemented, this section should contain:

  1. the version(s) it was merged into
  2. a link to the git commit(s)
  3. a link to the PHP manual entry for the feature

References

Rejected Features

Changelog