跳至正文

类型参考

为了防止类型注释的冲突或重复,该库尝试处理 [PHPStan] 和 [Psalm] 接受的大多数类型注释。

提示

当属性、参数或返回类型使用库尚无法解析的 PHPStan 或 Psalm 语法时,例如条件类型($a is 1 ? int : null) ,专用的@valinor-var 、 @valinor-param和@valinor-return注释可用于为库提供它理解的类型。它们优先于所有其他注释,因此静态分析工具在库使用覆盖时继续使用它们自己的类型:

final class SomeClass
{
    /**
     * @phpstan-param ($a is 1 ? int : null) $b
     * @valinor-param int|null $b
     */
    public function __construct(
        public readonly int $a,
        public readonly ?int $b,
    ) {}
}

标量

final readonly class SomeClass
{
    public function __construct(
        public bool $boolean,

        public float $float,

        public int $integer,

        /** @var positive-int */
        public int $positiveInteger,

        /** @var negative-int */
        public int $negativeInteger,

        /** @var non-positive-int */
        public int $nonPositiveInteger,

        /** @var non-negative-int */
        public int $nonNegativeInteger,

        /** @var int<-42, 1337> */
        public int $integerRange,

        /** @var int<min, 0> */
        public int $integerRangeWithMinRange,

        /** @var int<0, max> */
        public int $integerRangeWithMaxRange,

        public string $string,

        /** @var non-empty-string */
        public string $nonEmptyString,

        /** @var numeric-string */
        public string $numericString,

        /** @var class-string */
        public string $classString,

        /** @var class-string<SomeInterface> */
        public string $classStringOfAnInterface,

        /** @var value-of<SomeEnum> */
        public string $valueOfEnum,

        /** @var scalar */
        public bool|string|int|float $scalar,
    ) {}
}

对象

final readonly class SomeClass
{
    public function __construct(
        public SomeClass $class,

        public DateTimeInterface $interface,

        /** @var SomeInterface&AnotherInterface */
        public object $intersection,

        /** @var SomeCollection<SomeClass> */
        public SomeCollection $classWithGeneric,
    ) {}
}

/**
 * @template T of object 
 */
final readonly class SomeCollection
{
    public function __construct(
        /** @var array<T> */
        public array $objects,
    ) {}
}

泛型

类可以使用@template注解声明模板类型;当引用该类时,它们会被填充为具体类型。模板可以使用of绑定到类型,并且可以使用=声明默认类型。

引用类时可以省略声明默认类型的模板,在这种情况下将使用默认类型。未声明的模板必须始终填写。

final readonly class SomeUser
{
    public function __construct(
        public string $name,
    ) {}
}

/**
 * @template TValue
 * @template TMeta of array<string, mixed> = array<string, string>
 */
final readonly class SomePage
{
    public function __construct(
        /** @var list<TValue> */
        public array $items,

        /** @var TMeta */
        public array $meta,
    ) {}
}

final readonly class SomeClass
{
    public function __construct(
        // `TMeta` is not filled in, its default type is used
        /** @var SomePage<SomeUser> */
        public SomePage $pageWithDefaultMeta,

        // `TMeta` is filled in, overriding its default type
        /** @var SomePage<SomeUser, array{cursor: int}> */
        public SomePage $pageWithCursorMeta,
    ) {}
}

声明默认类型的模板必须放在最后:没有默认类型的模板不能在具有默认类型的模板之后声明。当有一个默认类型时,默认类型还必须与使用of声明的边界相匹配。

注意

默认类型使得可以将模板添加到已在其他地方引用的类中:现有的引用不会填充新模板,继续解析为其默认类型,并且可以在以后变得更加精确。

数组和列表

final readonly class SomeClass
{
    public function __construct(
        /** @var string[] */
        public array $simpleArray,

        /** @var array<string> */
        public array $arrayOfStrings,

        /** @var array<string, SomeClass> */
        public array $arrayOfClassWithStringKeys,

        /** @var array<int, SomeClass> */
        public array $arrayOfClassWithIntegerKeys,

        /** @var array<non-empty-string, string> */
        public array $arrayOfClassWithNonEmptyStringKeys,

        /** @var array<'foo'|'bar', string> */
        public array $arrayOfClassWithStringValueKeys,

        /** @var array<42|1337, string> */
        public array $arrayOfClassWithIntegerValueKeys,

        /** @var array<positive-int, string> */
        public array $arrayOfClassWithPositiveIntegerValueKeys,

        /** @var non-empty-array<string> */
        public array $nonEmptyArrayOfStrings,

        /** @var non-empty-array<string, SomeClass> */
        public array $nonEmptyArrayWithStringKeys,

        /** @var list<string> */
        public array $listOfStrings,

        /** @var non-empty-list<string> */
        public array $nonEmptyListOfStrings,

        /** @var array{foo: string, bar: int} */
        public array $shapedArray,

        /** @var array{foo: string, bar?: int} */
        public array $shapedArrayWithOptionalElement,

        /** @var array{string, bar: int} */
        public array $shapedArrayWithUndefinedKey,

        /** @var array{foo: string, ...} */
        public array $unsealedShapedArray,

        /** @var array{foo: string, ...array<string>} */
        public array $unsealedShapedArrayWithExplicitType,

        /** @var array{foo: string, ...array<int, string>} */
        public array $unsealedShapedArrayWithExplicitKeyAndType,

        /** @var array{foo: string, ...<string>} */
        public array $unsealedShapedArrayWithShorthandType,

        /** @var array{foo: string, ...<int, string>} */
        public array $unsealedShapedArrayWithShorthandKeyAndType,

        /** @var list{string, int, float} */
        public array $shapedList,

        /** @var list{0: string, 1: int} */
        public array $shapedListWithExplicitKeys,

        /** @var list{0: string, 1?: int} */
        public array $shapedListWithOptionalElement,

        /** @var list{string, int, ...} */
        public array $unsealedShapedList,

        /** @var list{string, int, ...list<float>} */
        public array $unsealedShapedListWithExplicitType,

        /** @var list{string, ...<float>} */
        public array $unsealedShapedListWithShorthandType,
    ) {}
}

联合类型

final readonly class SomeClass
{
    public function __construct(
        public int|string $simpleUnion,

        /** @var class-string<SomeInterface|AnotherInterface> */
        public string $unionOfClassString,

        /** @var array<SomeInterface|AnotherInterface> */
        public array $unionInsideArray,

        /** @var int|true */
        public int|bool $unionWithLiteralTrueType,

        /** @var int|false */
        public int|bool $unionWithLiteralFalseType,

        /** @var 404.42|1337.42 */
        public float $unionOfFloatValues,

        /** @var 42|1337 */
        public int $unionOfIntegerValues,

        /** @var 'foo'|'bar' */
        public string $unionOfStringValues,
    ) {}
}

类常量

final readonly class SomeClassWithConstants
{
    public const FOO = 1337;

    public const BAR = 'bar';

    public const BAZ = 'baz';
}

final readonly class SomeClass
{
    public function __construct(
        /** @var SomeClassWithConstants::FOO|SomeClassWithConstants::BAR */
        public int|string $oneOfTwoCasesOfConstants,

        /** @param SomeClassWithConstants::BA* (matches `bar` or  `baz`) */
        public string $casesOfConstantsMatchingPattern,
    ) {}
}

枚举

enum SomeEnum
{
    case FOO;
    case BAR;
    case BAZ;
}

final readonly class SomeClass
{
    public function __construct(
        public SomeEnum $enum,

        /** @var SomeEnum::FOO|SomeEnum::BAR */
        public SomeEnum $oneOfTwoCasesOfEnum,

        /** @var SomeEnum::BA* (matches BAR or BAZ) */
        public SomeEnum $casesOfEnumMatchingPattern,
    ) {}
}

key-of / value-of

key-of<T>和value-of<T>类型从枚举、数组、列表和指定结构的数组(包括数组常量)中提取键或值类型。它们与 [PHPStan] 和 [Psalm] 接受的相同语法兼容。

key-of

enum SomePureEnum
{
    case FOO;
    case BAR;
}

enum SomeBackedEnum: string
{
    case FOO = 'foo';
    case BAR = 'bar';
}

final readonly class SomeClassWithConstants
{
    public const SOME_ARRAY = ['foo' => 1, 'bar' => 2];
}

final readonly class SomeClass
{
    public function __construct(
        // Accepts 'FOO' or 'BAR' (the case names of the enum)
        /** @var key-of<SomePureEnum> */
        public string $pureEnumKey,

        // Also works with backed enums — still yields the case *names*
        /** @var key-of<SomeBackedEnum> */
        public string $backedEnumKey,

        // Accepts 'foo' or 'bar' (the keys of the shaped array)
        /** @var key-of<array{foo: string, bar: int}> */
        public string $shapedArrayKey,

        // Accepts the key type of the array (string here)
        /** @var key-of<array<string, int>> */
        public string $arrayKey,

        // Accepts the key type of the list (always int)
        /** @var key-of<list<string>> */
        public int $listKey,

        // Accepts 'foo' or 'bar' (the keys of the class constant array)
        /** @var key-of<SomeClassWithConstants::SOME_ARRAY> */
        public string $constantArrayKey,
    ) {}
}

value-of

enum SomeBackedStringEnum: string
{
    case FOO = 'foo';
    case BAR = 'bar';
}

final readonly class SomeClassWithConstants
{
    public const SOME_ARRAY = ['foo' => 1, 'bar' => 2];
}

final readonly class SomeClass
{
    public function __construct(
        // Accepts 'foo' or 'bar' (the backed values of the enum)
        /** @var value-of<SomeBackedStringEnum> */
        public string $enumValue,

        // Accepts string or int (the union of value types in the shaped array)
        /** @var value-of<array{foo: string, bar: int}> */
        public string|int $shapedArrayValue,

        // Accepts the value type of the array (SomeOtherClass here)
        /** @var value-of<array<string, SomeOtherClass>> */
        public SomeOtherClass $arrayValue,

        // Accepts the element type of the list (string here)
        /** @var value-of<list<string>> */
        public string $listValue,

        // Accepts 1 or 2 (the values of the class constant array)
        /** @var value-of<SomeClassWithConstants::SOME_ARRAY> */
        public int $constantArrayValue,
    ) {}
}