← All results

Nested local type aliases, including reverse declaration order

Group: Type aliases · Category: ecosystem · File: aliases_declaration_order.php

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 results

AnalyzerVersionResultDiagnostics
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]
  • Recognition: spelling not resolved — reported on declaration line(s) 32, 52
  • Enforcement: 0/2 of the expected violations reported — incidental, since the spelling was not resolved
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-rules
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.
  • Recognition: spelling resolved
  • Enforcement: 2/2 of the expected violations reported
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]
  • Recognition: spelling resolved
  • Enforcement: 2/2 of the expected violations reported
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]
  • Recognition: spelling resolved
  • Enforcement: 2/2 of the expected violations reported
mir 0.91.0mir 0.91.0 Not enforced

No diagnostics reported.

  • Recognition: spelling resolved
  • Enforcement: 0/2 of the expected violations 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]
  • Recognition: spelling resolved
  • Enforcement: 2/2 of the expected violations reported
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]
  • Recognition: spelling not resolved — reported on declaration line(s) 42, 43
  • Enforcement: 0/2 of the expected violations reported — incidental, since the spelling was not resolved
phpactor 2026.10.07.0Phpactor 2026.10.07.0 Not enforced

No diagnostics reported.

  • Recognition: spelling resolved
  • Enforcement: 0/2 of the expected violations reported
phpy 1.0.19490phpy 1.0.19490 Not enforced

No diagnostics reported.

  • Recognition: spelling resolved
  • Enforcement: 0/2 of the expected violations reported
qodana 262.11879.104PhpStorm 262.11879.104 Not enforced

No diagnostics reported.

  • Recognition: spelling resolved
  • Enforcement: 0/2 of the expected violations 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]
  • Recognition: spelling not resolved — reported on declaration line(s) 32, 34, 52, 54
  • Enforcement: 0/2 of the expected violations reported — incidental, since the spelling was not resolved
steins 0.1.8steins 0.1.8 (2026-09-21 revision 76306a9) Not enforced

No diagnostics reported.

  • Recognition: spelling resolved
  • Enforcement: 0/2 of the expected violations 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.
  • Recognition: spelling resolved
  • Enforcement: 2/2 of the expected violations reported
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]
  • Recognition: spelling resolved
  • Enforcement: 2/2 of the expected violations reported

Source

<?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