Type Reader Component
The reader component bridges PHP's Reflection API to the TypeLang AST. Given a reflected constant, property, function or parameter, it reads the native type declared on it and returns the matching TypeLang\Type\TypeNode — the very same node graph the parser produces and the printer renders.
That makes it the entry point whenever a type originates from real PHP code rather than a string: read it once into an AST, then traverse, rewrite or print it with the rest of the toolkit.
Installation
Requirements:
PHP >= 8.4ext-mbstringoptional
Quick Start
TypeLang\Reader\ReflectionReader is the entry point. Each of its methods takes a Reflection object and returns a TypeNode, or null when the element carries no type declaration at all.
The returned nodes are the plain AST objects of the type-lang/types package. Throughout the rest of this page they are shown rendered back to a string with the pretty printer — $printer->print($type) — since that reads more easily than a full dump:
The Reader Interface
ReflectionReader implements TypeLang\Reader\ReaderInterface, which composes four focused interfaces — one per kind of declaration:
findConstantType(ReflectionClassConstant)- class constantfindPropertyType(ReflectionProperty, PropertyAccessDirection)- propertyfindFunctionType(ReflectionFunctionAbstract)- function or methodfindParameterType(ReflectionParameter)- parameter
Every method returns ?TypeNode: a node when a type is declared, or null when it is absent. An untyped constant, a bare $property, a parameter with no hint — all read back as null rather than as mixed, so a missing type is never confused with an explicit one.
Reading Types
Constants
Properties
A property may declare a different type for reading and for writing; which one is returned is controlled by the second argument — see Read vs. Write below.
Functions and Methods
findFunctionType() reads a return type, from either a free function or a method (both are a ReflectionFunctionAbstract).
Parameters
How Types Are Converted
The reader mirrors PHP's own type system into the AST, with a couple of normalizations worth knowing:
- Class names become fully qualified
A class or interface name is emitted fully qualified, with a leading
\—Examplereads back as\Example. Built-in and special types (int,string,static, ...) stay as written.- Nullable types are wrapped
A nullable declaration becomes a
NullableTypeNode:?FooandFoo|nullboth read as?\Foo. Thenullandmixedtypes, which are nullable on their own, are left unwrapped.- Composite types map one-to-one
A union becomes a
UnionTypeNodeand an intersection anIntersectionTypeNode, each member converted by the same rules.- An absent type is null
When Reflection reports no type, the method returns
null.
Read vs. Write
Since PHP 8.4 a property can accept a wider (or narrower) type on write than it exposes on read, by giving its set hook a typed parameter. findPropertyType() takes a TypeLang\Reader\PropertyAccessDirection to pick which side to read:
- PropertyAccessDirection::Read
The property's declared (get) type. This is the default.
- PropertyAccessDirection::Write
The type accepted on assignment — the parameter type of the property's
sethook, when one narrows or widens it. Falls back to the read type when the property has no such hook.
Converting a Reflection Type Directly
When you already hold a ReflectionType — rather than the element it came from — getType() converts it straight to a TypeNode:
Unlike the find* methods, getType() requires a non-null ReflectionType; it is the primitive the others are built on.
Error Handling
Every exception the component throws implements TypeLang\Reader\Exception\ReaderExceptionInterface. The concrete one raised when a reflected type cannot be mapped to the AST — an unknown ReflectionType implementation — is an UnrecognizedTypeException, rethrown by the find* methods as the matching UnrecognizedConstantTypeException, UnrecognizedPropertyTypeException, UnrecognizedFunctionTypeException or UnrecognizedParameterTypeException so the failing element is named in the message.
The standard reflection type kinds — named, union and intersection — are all supported, so in practice this only surfaces for an exotic or future ReflectionType the reader does not yet understand.
Reading a Whole Class
Putting it together — read and print every type on a class, using the printer to render each result:
What's Next
- Type Printer
Render the AST the reader returns back into a string — faithfully or as a PHP-compatible type. See Type Printer Component.
- Type Parser
Read a type from a string instead of from Reflection, into the same AST. See Type Parser Component.
- PHPDoc Parser
Read the richer types written in docblocks, which Reflection cannot see. See PHPDoc Parser Component.