Library / SDK
phpDocumentor/TypeResolver avatar
phpDocumentor/TypeResolver

TypeResolver: what a partial DocBlock name needs before it can be resolved

A PSR-5 based resolver of Class names, Types and Structural Element Names

9,151 stars51 forksPHPMIT

At a glance

What is it?
phpDocumentor/TypeResolver parses DocBlock type expressions and structural element names into value objects, but the expansion of any partial name depends on a Context the caller supplies.
Who is it for?
phpDocumentor/TypeResolver fits a documentation generator that must resolve partial class names into fully qualified ones, and it does not fit a tool that needs to parse arbitrary annotations. Before adopting the 2.x branch, check that every file you parse can hand the resolvers a Context, that your type walkers unwrap Nullable nodes, and that the PHP version you deploy runs outside the php:7.4 image the Makefile validates against.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 47 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • phpDocumentor/TypeResolver9,151 ★ · -5
Star history
Repository2026-09-222026-11-14
phpDocumentor/TypeResolver9.2k9.2k

-5 Stars in the last 53 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

TypeResolver and FqsenResolver accept different vocabularies

The package ships two resolvers, and sending an expression to the wrong one is the first mistake a new caller makes. TypeResolver turns a DocBlock type expression into a value object: a primitive or pseudo-primitive such as `string` or `void`, a composite such as `string[]`, a compound such as `string|integer`, an array expression such as `(string|TypeResolver)[]`, or an object or interface name. FqsenResolver covers the other half of the notation: constants, functions, classes, interfaces, traits, class constants, properties and methods, each with its own suffix syntax. Neither class stands in for the other, so a tool walking a DocBlock has to choose the grammar per tag and then read value objects off the result instead of parsing strings. The default branch is 2.x, and 2.1.0 followed 2.0.0 and 1.12.0.

A partial name stays partial until a Context is passed in

Resolving `string|integer` needs nothing but the resolver. Resolving `Types\Context` needs more, because nothing in the expression says that `Types` is an import of `\phpDocumentor\Reflection\Types` in the file where the tag appears. That is the job of `\phpDocumentor\Reflection\Types\Context`, which carries the surrounding namespace plus the aliases in play, and it goes in as the second argument to `resolve`. Leave it out and the resolver has no information about the namespace or aliases in play, so it cannot expand the partial name into the right fully qualified one. You can build the Context by hand when you already know the namespace and its aliases:

php
$context = new \phpDocumentor\Reflection\Types\Context(
    '\My\Example',
    [ 'Types' => '\phpDocumentor\Reflection\Types']
);

What the library cannot do is guess an alias you failed to hand it.

Nullable strips the question mark and returns a wrapper

A nullable annotation arrives as a wrapper rather than the type you wrote. Given `?string`, the resolver handles the underlying expression exactly as it handles `string`, then places the result inside `\phpDocumentor\Reflection\Types\Nullable`, and `Nullable` offers a method to fetch the actual type. Callers walking a type tree therefore test for the wrapper at every node before reaching a `String_` object, and an instanceof chain written against non-nullable annotations walks straight past it and reports nothing. That is the cost of the design: no exception, no warning, just a different object shape than the annotation text suggests. The README notes that PHP 7.1 introduced nullable types.

The composer package name and the class namespace disagree

The install line names a package whose name does not match the namespace you call:

bash
$ composer require phpdocumentor/type-resolver

The directory Composer creates is `vendor/phpdocumentor/type-resolver`, yet every class you instantiate begins with `\phpDocumentor\Reflection`: TypeResolver, FqsenResolver and Types\Context all sit under that second namespace. A search for the composer name inside your code turns up nothing. The naming split also explains the absence of any wiring step. There is no plugin to register, no service to configure and no container entry to add, because both resolvers are constructed with `new` at the point of use. The repository also records no homepage, so the composer package page is the closest thing to a project site you can hand a new user.

Every Makefile target runs inside a php:7.4 container

The developer workflow depends on Docker, and the images are pinned to one interpreter:

bash
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/phpunit

`code-style` and `fix-code-style` use `phpdoc/phpcs-ga:latest`, `static-code-analysis` runs phpstan against phpstan.neon and psalm against psalm.xml in php:7.4, `dependency-analysis` invokes composer-require-checker with composer-require-checker.json, and `benchmark` runs phpbench with phpbench.json. Only the `vendor` target calls composer on the host, which validates composer.json and installs from composer.lock. Two consequences follow. The Makefile's own checks run in PHP 7.4 images, so they say nothing about a newer interpreter. And a contributor with no Docker daemon cannot run any of it, since every other target is a container invocation rather than a local binary.

No documented outcome for an expression nobody recognises

The README is explicit about what the two resolvers accept, and the value objects it describes all come back from successful calls. Before relying on the library in a generator, check how it reports an expression it does not recognise, so that a malformed annotation becomes a warning you can see in a log rather than a broken entry in the output. Write your own guard around each call and treat any return value outside the documented value objects as unresolved.

Seven example files carry the workflow the prose omits

The examples directory holds seven files, and between them they supply the sequence the prose leaves implicit. `01-resolving-simple-types.php` and `02-resolving-classes.php` cover the two resolver paths, `03-resolving-all-elements.php` walks every structural element form the FqsenResolver claims, and the last three show three different ways to obtain a Context: `04-discovering-the-context-using-class-reflection.php`, `05-discovering-the-context-using-method-reflection.php` and `06-discovering-the-context-using-file-contents.php`, alongside `Classy.php`, the example class the README also uses. Read the file-contents variant first when you write your own extraction, because `createForNamespace` parses the source you hand it to find the imports, and no equivalent lookup exists inside the resolver itself.

Editorial conclusion

phpDocumentor/TypeResolver fits a documentation generator that must resolve partial class names into fully qualified ones, and it does not fit a tool that needs to parse arbitrary annotations. Before adopting the 2.x branch, check that every file you parse can hand the resolvers a Context, that your type walkers unwrap Nullable nodes, and that the PHP version you deploy runs outside the php:7.4 image the Makefile validates against.

Frequently asked questions

When does TypeResolver need a Context object?

Only when an expression carries a partial class name. Without a Context passed as the second argument to `resolve`, the resolver has no way to learn which namespace or import alias applies, so a name written as `Types\Context` is never expanded into its fully qualified form.

What does TypeResolver return for a nullable type such as ?string?

It resolves the underlying type first, without the question mark, and wraps the result in a `\phpDocumentor\Reflection\Types\Nullable` object. That wrapper carries its own method for fetching the actual type, so callers have to step through it before they reach the inner value object.

How do I install TypeResolver and run its own checks?

Composer installs it with `composer require phpdocumentor/type-resolver`. The Makefile then runs phpcs, phpstan, psalm, phpunit, composer-require-checker and phpbench inside Docker containers, most of them pinned to the php:7.4 image rather than to a current interpreter.

Official sources

  1. Issues
  2. License: MIT
  3. phpDocumentor/TypeResolver on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/phpdocumentor-typeresolver.svg)](https://hysenlabs.com/projects/phpdocumentor-typeresolver)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/phpdocumentor-typeresolver.svg)](https://hysenlabs.com/projects/phpdocumentor-typeresolver)

Was this useful?