Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Reflection Docblock Laravel Package

phpdocumentor/reflection-docblock

A DocBlock parser fully compatible with the PHPDoc standard. Use its DocBlockFactory to read summaries, descriptions, tags, and annotations from doc comments or reflection classes, so your library can extract embedded documentation and metadata.

View on GitHub
Deep Wiki
Context7

Getting Started

Install it as a dev dependency — in a Laravel app this library belongs in tooling (artisan commands, generators, CI checks), not in the request path:

composer require --dev phpdocumentor/reflection-docblock:^6.0

Pin ^6.0.3 or later on PHP 8.4 projects (explicit nullable deprecations and escaped */ handling were fixed there).

Bind the factory once so it is injectable and swappable in tests:

// app/Providers/DocBlockServiceProvider.php
use phpDocumentor\Reflection\DocBlockFactory;

public function register(): void
{
    $this->app->singleton(DocBlockFactory::class, fn () => DocBlockFactory::createInstance());
}

First use case — an artisan command that dumps what the parser sees for any class or method, which is also your debugging tool for the rest of the work:

use Illuminate\Console\Command;
use phpDocumentor\Reflection\DocBlockFactory;

class DocBlockInspect extends Command
{
    protected $signature = 'docblock:inspect {class} {method?}';

    public function handle(DocBlockFactory $factory): int
    {
        $reflection = $this->argument('method')
            ? new \ReflectionMethod($this->argument('class'), $this->argument('method'))
            : new \ReflectionClass($this->argument('class'));

        $comment = $reflection->getDocComment();

        if ($comment === false) {
            $this->warn('No DocBlock found.');
            return self::FAILURE;
        }

        // create() also accepts any object exposing getDocComment()
        $docblock = $factory->create($reflection);

        $this->line('<info>Summary:</info> ' . $docblock->getSummary());
        $this->line('<info>Description:</info> ' . $docblock->getDescription()->render());

        foreach ($docblock->getTags() as $tag) {
            $this->line(sprintf('  @%s → %s', $tag->getName(), (string) $tag));
        }

        return self::SUCCESS;
    }
}

Then read vendor/phpdocumentor/reflection-docblock/docs/examples and src/DocBlockFactory.php — the factory signatures are the API surface you will actually touch, and they differ slightly between 4.x, 5.x and 6.x.

Implementation Patterns

Annotation-style discovery (the main Laravel use case). Scan a directory, reflect each class, and turn custom tags into registrations inside a service provider. Keep the scan in a compiled/cached step, never per request:

$files = File::allFiles(app_path('Http/Controllers'));

foreach ($files as $file) {
    $class = 'App\\' . str_replace(
        ['/', '.php'],
        ['\\', ''],
        Str::after($file->getPathname(), app_path() . '/')
    );

    if (! class_exists($class)) {
        continue;
    }

    $docblock = $factory->create(new \ReflectionClass($class));

    foreach ($docblock->getTagsByName('tenant-billing') as $tag) {
        $plan = trim($tag->getDescription()->render()); // Generic tag body
        // register $class => $plan somewhere (config, container, route defaults)
    }
}

Read unknown tags without writing tag classes. Any tag the StandardTagFactory does not recognise comes back as phpDocumentor\Reflection\DocBlock\Tags\Generic, which keeps getName() and the raw body. This is the lowest-friction path for a house-style annotation (@owner, @feature-flag, @internal-api). Only write a dedicated Tag class when you need structured parsing (a type, multiple arguments, validation).

Register real custom tags. DocBlockFactory::createInstance() takes an additional-tags map (tag name → class implementing Tag), and the underlying StandardTagFactory exposes tag-handler registration/extension points. Use this when you want typed accessors instead of string parsing — verify the exact signature against the installed version, it changed in 6.0.

Cache the extraction, not the object graph. DocBlock results are serializable again (fixed in 5.0.0-alpha9), so Cache::rememberForever($key, fn () => $factory->create($comment)) works. Safer in practice: cache a plain array of the fields you extracted, keyed by path + mtime + package version, so a package upgrade can never hydrate a stale class shape:

$key = sprintf('docblock:%s:%d:%s', $file->getPathname(), $file->getMTime(), Package::VERSION);

CI linting. A docblock:lint command that walks app/ and returns self::FAILURE when a tag is an InvalidTag (or when a required @param is missing) gives you a cheap, project-specific doc standard gate that PHPStan/Larastan won't enforce for you.

Generators. For OpenAPI/schema/IDE-helper style output, combine @param, @return and @property tags with phpdocumentor/type-resolver to get normalised type strings, then map them to Laravel validation rules or JSON Schema types. @property on Eloquent models is the most common real-world source you will be reading.

Prefer native attributes for new code. PHP 8 attributes (#[Route(...)]) are first-class in Laravel and cheaper; use this package for reading existing docblocks, for anything that must live alongside PHPDoc (generics, @mixin, @method), and for tooling that must also accept legacy annotations.

Gotchas and Tips

  • Malformed tags do not throw. Since 5.0.0-alpha8 an unparsable tag becomes an InvalidTag object in the result set rather than an exception. Always filter with instanceof InvalidTag (and inspect the wrapped exception) before trusting getTags() output.
  • Generic type syntax is the usual source of InvalidTag. Codebases written for Larastan/PHPStan are full of array<string, mixed> and similar; do not assume this parser round-trips them. Test one of your real controller docblocks through docblock:inspect before building on top of it.
  • Guard getDocComment() yourself. It returns false when absent; check it before calling create() rather than relying on the factory to be forgiving.
  • Summary/description splitting is punctuation-sensitive. A blank line or a summary ending in . plus whitespace starts the description. Summaries containing abbreviations ("e.g. ") get truncated in surprising places.
  • getDescription() is not on the Tag interface. It exists on Generic/BaseTag; if you only hold a Tag, use (string) $tag or render(). Do not type-hint getDescription() across arbitrary tags.
  • Tag names are matched literally. Normalise to lowercase when looking names up (getTagsByName('param'), not 'Param') and be consistent in your own annotations.
  • Never parse at request time. Parsing a docblock is reflective + regex work; across a few hundred classes it is measurable. Do it in a command, a build step, or a cached boot-time scan.
  • Version coupling with phpdocumentor/type-resolver. 6.x requires type-resolver 2.x, which can conflict with other dev tooling that pins 1.x. Check composer why phpdocumentor/type-resolver before upgrading in a large app.
  • Windows line endings and escaped */. Both have caused parsing bugs historically; stay on a recent patch release if your repo is developed on mixed platforms.
  • Extension points, in order of cost: Generic tag body → additional-tags map on createInstance() → custom Tag class → custom handler on StandardTagFactory. Climb only as far as you need.
  • Debugging trick: render() on the DocBlock/Description reconstructs a docblock from the parsed model. If render() output doesn't match the source comment, the parser disagrees with you about the structure — a fast way to find where.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log