AST node classes for the TypeLang type system.
TypeLang is a declarative type language inspired by static analyzers like PHPStan and Psalm.
Read documentation pages for more information.
The package is available as a Composer repository and can be installed using the following command in a root of your project:
composer require type-lang/types- PHP 8.4+
All nodes extend the abstract Node class and expose a single common property:
// Byte offset of the token in the original source string.
public int $offset = 0;A single name segment such as string, MyClass, or non-empty-string.
Virtual identifiers (containing -) are common in PHPStan/Psalm type aliases.
$id = Identifier::createFromString(' example '); // trims whitespace
$id->value; // 'example'
$id->isVirtual; // false
$id->isBuiltin; // false
$id->isSpecial; // false
//
// Virtual vs. Builtin vs. Special
//
// e.g. "array-key", "positive-int", etc.
$virtual = new Identifier('non-empty-string');
$virtual->isVirtual; // true (contains "-")
$virtual->isBuiltin; // false
$virtual->isSpecial; // false
// e.g. "float", "bool", "null", "true", etc.
$builtin = new Identifier('int');
$builtin->isVirtual; // false
$builtin->isBuiltin; // true
$builtin->isSpecial; // false
// e.g. "self", "parent", etc.
$special = new Identifier('static');
$special->isVirtual; // false
$special->isBuiltin; // false
$special->isSpecial; // trueA fully- or partially-qualified name composed of Identifier segments.
$name = Name::createFromString('\TypeLang\Parser\Node');
$name->isFullyQualified; // true
$name->isSimple; // false
$name->first->value; // 'TypeLang'
$name->last->value; // 'Node'
$name->toString(); // '\TypeLang\Parser\Node'
$name->slice(1)
->toString(); // 'Parser\Node'
$name->toUnqualified()
->toString(); // 'TypeLang\Parser\Node'
$name->mergeWith(Name::createFromString('Node\Sub'))
->toString(); // '\TypeLang\Parser\Node\Sub'All type nodes extend the abstract TypeNode class.
The most common node β a named type with optional template arguments and shape fields.
// int
new NamedTypeNode(Name::createFromString('int'));
// array<string, int>
new NamedTypeNode(
name: Name::createFromString('array'),
arguments: new TemplateArgumentListNode([
new TemplateArgumentNode(new NamedTypeNode(
Name::createFromString('string'),
)),
new TemplateArgumentNode(new NamedTypeNode(
Name::createFromString('int'),
)),
]),
);Wraps a type to make it nullable (?Type).
new NullableTypeNode(new NamedTypeNode(Name::createFromString('string')));Represent A|B|C and A&B&C respectively. Nested unions (or intersections)
of the same kind are automatically flattened.
$union = new UnionTypeNode(
new NamedTypeNode(Name::createFromString('int')),
new NamedTypeNode(Name::createFromString('string')),
new NamedTypeNode(Name::createFromString('null')),
);
$intersection = new IntersectionTypeNode(
new NamedTypeNode(Name::createFromString('int')),
new NamedTypeNode(Name::createFromString('string')),
new NamedTypeNode(Name::createFromString('null')),
);Represents the array-shorthand Type[].
// int[]
new TypesListNode(new NamedTypeNode(Name::createFromString('int')));Represents an indexed access type T[K].
new TypeOffsetAccessNode(
type: new NamedTypeNode(Name::createFromString('T')),
access: new NamedTypeNode(Name::createFromString('K')),
);Represents a callable signature.
// callable(int, string): bool
new CallableTypeNode(
name: Name::createFromString('callable'),
parameters: new CallableParameterListNode([
new CallableParameterNode(
type: new NamedTypeNode(Name::createFromString('int')),
),
new CallableParameterNode(
type: new NamedTypeNode(Name::createFromString('string')),
),
]),
type: new NamedTypeNode(Name::createFromString('bool')),
);Represents a conditional type expression subject is Target ? Then : Else.
new TernaryExpressionNode(
condition: new EqualConditionNode(
subject: new NamedTypeNode(Name::createFromString('T')),
target: new NamedTypeNode(Name::createFromString('string')),
),
then: new NamedTypeNode(Name::createFromString('non-empty-string')),
else: new NamedTypeNode(Name::createFromString('T')),
);Represent constant references and wildcard masks.
// Status::ACTIVE
new ClassConstNode(
class: Name::createFromString('Status'),
constant: new Identifier('ACTIVE'),
);
// Status::*
new ClassConstMaskNode(class: Name::createFromString('Status'));
// Foo\Bar\*
new ConstMaskNode(name: Name::createFromString('Foo\Bar'));All literals extend LiteralNode and expose $value (native PHP type)
and $raw (original source token). Most support a static parse() factory.
// value: true, raw: 'true'
BoolLiteralNode::parse('true');
// value: false, raw: 'False'
BoolLiteralNode::parse('False');
// value: 255, raw: '0xFF', decimal: '255'
IntLiteralNode::parse('0xFF');
// value: 10, raw: '0b1010'
IntLiteralNode::parse('0b1010');
// value: 1000, raw: '1_000'
IntLiteralNode::parse('1_000');
// value: 150.0, raw: '1.5e2'
FloatLiteralNode::parse('1.5e2');
// value: null, raw: 'Null'
new NullLiteralNode('Null');
// decodes escape sequences
StringLiteralNode::parse('"hello\nworld"');
// no escape decoding
StringLiteralNode::parse("'raw'");
// value: 'name' (no $), raw: '$name'
VariableLiteralNode::parse('$name');Used as the $condition of TernaryExpressionNode. All extend Condition
and hold public TypeNode $subject and public TypeNode $target.
| Class | Meaning |
|---|---|
EqualConditionNode |
subject is target |
NotEqualConditionNode |
subject is not target |
GreaterThanConditionNode |
subject > target |
GreaterThanOrEqualConditionNode |
subject >= target |
LessThanConditionNode |
subject < target |
LessThanOrEqualConditionNode |
subject <= target |
Shape fields describe the entries of a structured array type.
array{key: string, 0: int, 'literal': bool, ...}
| Class | Key type | Example |
|---|---|---|
ImplicitFieldNode |
none (positional) | array{string} |
NamedFieldNode |
Identifier |
array{key: string} |
StringNamedFieldNode |
StringLiteralNode |
array{'key': string} |
NumericFieldNode |
IntLiteralNode |
array{0: string} |
ClassConstFieldNode |
ClassConstNode |
array{Foo::BAR: string} |
ClassConstMaskFieldNode |
ClassConstMaskNode |
array{Foo::BAR_*: string} |
ConstMaskFieldNode |
ConstMaskNode |
array{Foo\*: string} |
All field nodes inherit public TypeNode $type, public bool $isOptional, and
public ?AttributeGroupListNode $attributes from FieldNode. Explicit fields
also expose a string $index property for the key's string representation.
FieldsListNode collects the fields and marks the shape as sealed or unsealed
($sealed = true means no extra keys allowed; ... in source makes it unsealed).
// array<covariant T, int>
new TemplateArgumentListNode([
new TemplateArgumentNode(
value: new NamedTypeNode(Name::createFromString('T')),
hint: new Identifier('covariant'),
),
new TemplateArgumentNode(
value: new NamedTypeNode(Name::createFromString('int')),
),
]);// callable(int $a, string ...$b): void
new CallableParameterListNode([
new CallableParameterNode(
type: new NamedTypeNode(Name::createFromString('int')),
name: VariableLiteralNode::parse('$a'),
),
new CallableParameterNode(
type: new NamedTypeNode(Name::createFromString('string')),
name: VariableLiteralNode::parse('$b'),
isVariadic: true,
),
]);Constraints enforced by CallableParameterNode:
- At least one of
$typeor$namemust be provided. $isVariadicand$isOptionalcannot both betrue.
Represent PHP-attribute-style annotations that some type systems attach to type positions.
#[Pure, Deprecated('Use X instead')]
AttributeGroupListNode (#[X] #[Y])
βββ AttributeGroupNode (#[A, B, C])
βββ AttributeNode (Pure, Deprecated)
βββ AttributeArgumentListNode
βββ AttributeArgumentNode (value: StringLiteralNode 'Use X instead')
All list containers extend NodeList and implement IteratorAggregate,
ArrayAccess, and Countable.
$list = new TemplateArgumentListNode([...]);
count($list); // number of items
$list[0]; // first item
$list->first; // first item
$list->last; // last item
$index = $list->findIndex($node); // position by identity, or null
foreach ($list as $item) { ... }