aliases_local_type expands a single alias. This file asks whether an
alias may mention another alias, and whether the mentioned name has to
appear above the one that uses it. Psalm 6.19.2 started requiring
dependency order (vimeo/psalm#12149); tools that resolve names after
collecting every @psalm-type on the class still expand both blocks.
References:
- psalm#12149
- PHPStan phpdoc-types: Local type aliases| Analyzer | Version | Result | Diagnostics |
|---|---|---|---|
| phan | 6.0.11Phan 6.0.11 php-ast version 1.1.3 PHP version used to run Phan: 8.5.11 | Unrecognized | aliases_declaration_order.php:32: Doc-block of $users in takeUsers contains phpdoc param type \Conformance\Tests\AliasesDeclarationOrder\UserList which is incompatible with the param type array declared in the signature [PhanTypeMismatchDeclaredParam] aliases_declaration_order.php:52: Doc-block of $users in takeUsers contains phpdoc param type \Conformance\Tests\AliasesDeclarationOrder\ReverseList which is incompatible with the param type array declared in the signature [PhanTypeMismatchDeclaredParam]
|
| phpstan | 2.3.5PHPStan - PHP Static Analysis Tool 2.3.5 | Enforced reported Lv.5+ | aliases_declaration_order.php:61: Parameter #1 $users of method Conformance\Tests\AliasesDeclarationOrder\Forward::takeUsers() expects list<array{id: int, name: string}>, array{array{id: 'x', name: 'a'}} given. [reported-from-level=5]
aliases_declaration_order.php:65: Parameter #1 $users of method Conformance\Tests\AliasesDeclarationOrder\Reverse::takeUsers() expects list<array{id: int, name: string}>, array{array{id: 'x', name: 'a'}} given. [reported-from-level=5]With strict-rulesaliases_declaration_order.php:61: Parameter #1 $users of method Conformance\Tests\AliasesDeclarationOrder\Forward::takeUsers() expects list<array{id: int, name: string}>, array{array{id: 'x', name: 'a'}} given.
aliases_declaration_order.php:65: Parameter #1 $users of method Conformance\Tests\AliasesDeclarationOrder\Reverse::takeUsers() expects list<array{id: int, name: string}>, array{array{id: 'x', name: 'a'}} given.
|
| psalm | 6.20.9Psalm 6.20.9@f67170ac3c3b931f5088681e21d6c8babc601c08 next: 7.0.4 |
Enforced (pzoom≠) | aliases_declaration_order.php:61: Argument 1 of Conformance\Tests\AliasesDeclarationOrder\Forward::takeUsers expects list<array{id: int, name: string}>, but list{array{id: 'x', name: 'a'}} provided [InvalidArgument]
aliases_declaration_order.php:65: Argument 1 of Conformance\Tests\AliasesDeclarationOrder\Reverse::takeUsers expects list<array{id: int, name: string}>, but list{array{id: 'x', name: 'a'}} provided [InvalidArgument]pzoom (Psalm port)aliases_declaration_order.php:39: Docblock-defined class, interface or enum named ReverseId does not exist [UndefinedDocblockClass]
aliases_declaration_order.php:39: Docblock-defined class, interface or enum named ReverseUser does not exist [UndefinedDocblockClass]
aliases_declaration_order.php:61: Argument 1 of Conformance\Tests\AliasesDeclarationOrder\Forward::takeUsers expects list<array{id: int, name: string}>, but list{array{id: 'x', name: 'a'}} provided [InvalidArgument]
aliases_declaration_order.php:64: Argument 1 of Conformance\Tests\AliasesDeclarationOrder\Reverse::takeUsers expects list<array{id: Conformance\Tests\AliasesDeclarationOrder\ReverseId, name: string}>, but list{array{id: 1, name: 'a'}} provided [InvalidArgument]
aliases_declaration_order.php:65: Argument 1 of Conformance\Tests\AliasesDeclarationOrder\Reverse::takeUsers expects list<array{id: Conformance\Tests\AliasesDeclarationOrder\ReverseId, name: string}>, but list{array{id: 'x', name: 'a'}} provided [InvalidArgument]
|
| mago | 1.59.0mago 1.59.0 | Enforced | aliases_declaration_order.php:61: Possible argument type mismatch for argument #1 of `Conformance\Tests\AliasesDeclarationOrder\Forward::takeUsers`: expected `list<array{'id': int, 'name': string}>`, but possibly received `list{array{'id': string('x'), 'name': string('a')}}`. [possibly-invalid-argument]
aliases_declaration_order.php:65: Possible argument type mismatch for argument #1 of `Conformance\Tests\AliasesDeclarationOrder\Reverse::takeUsers`: expected `list<array{'id': int, 'name': string}>`, but possibly received `list{array{'id': string('x'), 'name': string('a')}}`. [possibly-invalid-argument]
|
| mir | 0.91.0mir 0.91.0 | Not enforced | No diagnostics reported.
|
| phpantom | 0.11.1phpantom_lsp 0.11.1 | Enforced | aliases_declaration_order.php:61: Argument 1 ($users) expects list<array{id: int, name: string}>, got array{array{id: 'x', name: 'a'}} [type_mismatch_argument]
aliases_declaration_order.php:65: Argument 1 ($users) expects list<array{id: int, name: string}>, got array{array{id: 'x', name: 'a'}} [type_mismatch_argument]
|
| intelephense | 1.18.5intelephense 1.18.5 | Unrecognized | aliases_declaration_order.php:42: PHPDoc undefined type 'Conformance\Tests\AliasesDeclarationOrder\ReverseUser'. [P1133] aliases_declaration_order.php:43: PHPDoc undefined type 'Conformance\Tests\AliasesDeclarationOrder\ReverseId'. [P1133]
|
| phpactor | 2026.10.07.0Phpactor 2026.10.07.0 | Not enforced | No diagnostics reported.
|
| phpy | 1.0.19490phpy 1.0.19490 | Not enforced | No diagnostics reported.
|
| qodana | 262.11879.104PhpStorm 262.11879.104 | Not enforced | No diagnostics reported.
|
| noverify | 0.5.5NoVerify, version 0.5.5: built on: 2025.04.22 14:36:46 OS: r-kuchinskas arm64 Commit: 4774b82f8adc53fbef38f95e97af4b953b4d1529 | Unrecognized | aliases_declaration_order.php:32: Class or interface named \Conformance\Tests\AliasesDeclarationOrder\UserList does not exist [undefinedClass] aliases_declaration_order.php:34: param $users miss matched with phpdoc type <<UserList>> [funcParamTypeMissMatch] aliases_declaration_order.php:52: Class or interface named \Conformance\Tests\AliasesDeclarationOrder\ReverseList does not exist [undefinedClass] aliases_declaration_order.php:54: param $users miss matched with phpdoc type <<ReverseList>> [funcParamTypeMissMatch]
|
| steins | 0.1.8steins 0.1.8 (2026-09-21 revision 76306a9) | Not enforced | No diagnostics reported.
|
| phpstan-strict | 2.3.5PHPStan - PHP Static Analysis Tool 2.3.5 | Enforced | aliases_declaration_order.php:61: Parameter #1 $users of method Conformance\Tests\AliasesDeclarationOrder\Forward::takeUsers() expects list<array{id: int, name: string}>, array{array{id: 'x', name: 'a'}} given.
aliases_declaration_order.php:65: Parameter #1 $users of method Conformance\Tests\AliasesDeclarationOrder\Reverse::takeUsers() expects list<array{id: int, name: string}>, array{array{id: 'x', name: 'a'}} given.
|
| psalm-next | 7.0.4Psalm 7.0.4@fa5b1f6e665cb743838ffd865b8fc96207f9409f | Enforced | aliases_declaration_order.php:61: Argument 1 of Conformance\Tests\AliasesDeclarationOrder\Forward::takeUsers expects list<array{id: int, name: string}>, but list{array{id: 'x', name: 'a'}} provided [InvalidArgument]
aliases_declaration_order.php:65: Argument 1 of Conformance\Tests\AliasesDeclarationOrder\Reverse::takeUsers expects list<array{id: int, name: string}>, but list{array{id: 'x', name: 'a'}} provided [InvalidArgument]
|
<?php
declare(strict_types=1);
namespace Conformance\Tests\AliasesDeclarationOrder;
/**
* Nested local type aliases, including reverse declaration order.
*
* `aliases_local_type` expands a single alias. This file asks whether an
* alias may mention another alias, and whether the mentioned name has to
* appear above the one that uses it. Psalm 6.19.2 started requiring
* dependency order (vimeo/psalm#12149); tools that resolve names after
* collecting every `@psalm-type` on the class still expand both blocks.
*
* References:
* - https://github.com/vimeo/psalm/pull/12149
* - PHPStan phpdoc-types: Local type aliases
*/
/**
* @phpstan-type UserId int
* @phpstan-type User array{id: UserId, name: string}
* @phpstan-type UserList list<User>
* @psalm-type UserId = int
* @psalm-type User = array{id: UserId, name: string}
* @psalm-type UserList = list<User>
*/
final class Forward // T: @phpstan-type / @psalm-type
{
/**
* @param UserList $users
*/
public function takeUsers(array $users): void // T: UserList
{
}
}
/**
* Mentioned names declared after the aliases that use them.
*
* @phpstan-type ReverseList list<ReverseUser>
* @phpstan-type ReverseUser array{id: ReverseId, name: string}
* @phpstan-type ReverseId int
* @psalm-type ReverseList = list<ReverseUser>
* @psalm-type ReverseUser = array{id: ReverseId, name: string}
* @psalm-type ReverseId = int
*/
final class Reverse // T: @phpstan-type / @psalm-type
{
/**
* @param ReverseList $users
*/
public function takeUsers(array $users): void // T: ReverseList
{
}
}
$forward = new Forward();
$forward->takeUsers([['id' => 1, 'name' => 'a']]); // V
$forward->takeUsers([['id' => 'x', 'name' => 'a']]); // E?: UserId is int, so id must be int
$reverse = new Reverse();
$reverse->takeUsers([['id' => 1, 'name' => 'a']]); // V
$reverse->takeUsers([['id' => 'x', 'name' => 'a']]); // E?: ReverseId is still int when aliases are collected before expansion