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

Annotation Reader Laravel Package

ibrows/annotation-reader

ibrows/annotation-reader parses PHP docblock annotations into structured objects, letting you read class, method and property metadata at runtime via reflection. A lightweight API for annotation scanning, useful for custom routing, ORMs, validation or framework features in Laravel and plain PHP proj...

View on GitHub
Deep Wiki
Context7

Getting Started

Know what you're installing. This is a plain Composer library, not a Laravel package. There is no service provider, no facade, no config/annotation-reader.php to publish, and no auto-discovery. You wire it up yourself. With 0 stars and 0 dependents, the README.md and vendor/ibrows/annotation-reader/src/ are the documentation — read both before writing a line of consuming code, and confirm the actual class/method names rather than trusting snippets (including these).

Step 1 — install and let Composer tell you if it's viable.

composer require ibrows/annotation-reader

If Composer refuses on your PHP version, that's your answer: stop and move to native PHP 8 attributes (ReflectionClass::getAttributes()). If it installs, immediately inspect composer.lock — the library historically leans on Doctrine-style docblock parsing, so check whether doctrine/annotations / doctrine/common arrived transitively. That transitive dependency is often the real parser.

Step 2 — spike one class, one annotation. Do this in a throwaway branch with the exact PHP/Laravel version you'll ship on. Confirm the reader constructs and returns something before designing anything around it:

/**
 * @Config(key="sync", ttl=300)
 */
class OrderSyncer {}

// Verify the constructor signature in vendor/ before copying this.
$reader = app(\Ibrows\AnnotationReader\Reader::class);
dump($reader->getClassAnnotations(OrderSyncer::class));

Step 3 — bind it once. Register the reader as a singleton in AppServiceProvider::register() so parsing is not repeated on every resolution. This single decision separates a usable integration from an unusable one.

First real use case. The lowest-risk, highest-value job is turning a class-level annotation into a config array at boot — e.g. a FeatureFlags class whose annotations are read once into config(). It's small, testable, and trivially replaceable later with attributes, so you get the shape of the integration without betting on the library.

Implementation Patterns

Wrap it behind an application-owned interface. Never let controllers, models, or services call the package directly. Define App\Metadata\MetadataReader with methods like classMetadata(string $class): array, and implement it with the package. Everything downstream depends on your interface, not on an abandoned dependency — this is the seam that makes migration to attributes a one-file change later.

Singleton binding (the usual integration shape).

$this->app->scoped(\App\Metadata\MetadataReader::class, function () {
    return new \App\Metadata\AnnotationMetadataReader(
        new \Ibrows\AnnotationReader\Reader(
            new \Doctrine\Common\Annotations\AnnotationReader()
        )
    );
});

Annotation objects as DTOs. Define your own annotation classes with public properties (or a constructor that validates), and let the parser hydrate them. This gives you typed, IDE-friendly consumers and lets you fail loudly on missing required parameters inside the DTO rather than discovering null mid-request.

Cache parsed metadata — always. Parsing docblocks via reflection costs milliseconds per class; across dozens of models per request it dominates. Read once and store:

$meta = Cache::rememberForever(
    'annotations:'.$class.':'.filemtime((new ReflectionClass($class))->getFileName()),
    fn () => app(\App\Metadata\MetadataReader::class)->classMetadata($class)
);

Key by class plus file mtime (or flush on deploy) so edits invalidate cleanly. A plain $class key silently serves stale metadata after a refactor — one of the most confusing bugs you'll hit.

Prefer a build-time metadata dump. A small artisan annotations:cache command that walks your scan paths, reads annotations, and writes a single PHP array or cache entry is the most robust pattern. Requests then do array lookups, not reflection. It also gives you one place to log malformed annotations.

Validation layer. The raw reader is permissive — unknown or malformed annotations are typically ignored rather than throwing. Wrap it in an AnnotationRegistry that (a) reads, (b) asserts required annotations/properties, (c) throws a domain exception. Surface failures at boot in development, not at 2am in production.

Handler/plugin discovery. Method-level annotations work well for declaring handlers — e.g. @Handler(event="order.paid") on a service method — with a boot-time or build-time scan building a map. Keep the scan list explicit and small; do not crawl app/ on every request.

Testing. Bind a fake MetadataReader returning pre-built annotation DTOs for all behavioural tests. Keep one or two integration tests against the real parser so the fragile third-party code stays quarantined and its breakage is localized when you upgrade PHP.

Document the exit criterion now. Because this is a contain-don't-adopt dependency, make the migration target explicit (attributes on PHP 8.1+, or a maintained reflection library) and treat the wrapper as the migration boundary.

Gotchas and Tips

  • Treat it as abandoned. 0 stars, 0 dependents, near-zero score: nobody is maintaining it. Pin the exact version in composer.json, vendor/fork if you must keep it, and audit before every PHP upgrade. Don't let it spread beyond the legacy boundary.
  • Docblocks only — no PHP 8 attributes. If you're on PHP 8.1+, ReflectionAttribute in the standard library does the same job with no dependency. This package adds nothing there.
  • The classic failure is annotation autoloading. Doctrine's parser skips or throws on annotation classes it can't autoload. You usually need AnnotationRegistry::registerLoader('class_exists') (or register class names) before reading. Symptom: "the annotations are just being ignored" — expect a painful debugging session if you miss it.
  • opcache.save_comments=0 strips docblocks. Every annotation silently disappears in environments where comments aren't retained. If it works locally and fails on a tuned prod box, check this first.
  • Formatters/IDE reformatting can mangle annotations. Reordered imports change which annotation class a docblock name resolves to; aggressive docblock wrapping can break parsing.
  • Silent misparses are the norm. Bad quoting, whitespace, or an unresolved use in the docblock often yields null instead of an exception. Assert on returned annotations during boot in dev, and log which classes were scanned.
  • Debug top-down. When an annotation isn't found, dump (new ReflectionClass($class))->getDocComment() first. Missing comment → upstream problem (opcache, formatter, wrong file). Comment present → autoloading or syntax inside the docblock. Only then suspect the parser.
  • The class must be autoloadable. Reflection can't read what Composer can't resolve; dynamically generated or conditional classes need explicit require or a PSR-4-compliant path.
  • Annotations are passive data. No app(), no config(), no DI inside an annotation. Resolve dependencies in your consumer.
  • Never parse at request time on a hot path. Build-time or boot-time only, cached aggressively; this is the difference between "milliseconds" and "tens of milliseconds per request".
  • Name collision risk. If doctrine/annotations is already present for other reasons, align the reader version with it, or you'll get two parsers and divergent behaviour.
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.
takashato/laravel-vietqr
aitor24/laralang
kraenzle-ritter/entity-relations
dindigital/din-image
pomirleanu/image-colors
sawmainek/laravel-api-generator
delatbabel/jsonresponses
mouhamedfd/permissions-generator
eduplus/qr
laravel2580/idehelpers
delatbabel/viewpages
anavel/gettext
muhamadrezaar/highcharts
delatbabel/keylists
frankkessler/salesforce-laravel-oauth2-rest
anavel/foundation
wdmtech/enviricons
eii/laravel-captcha
muka/shape-reader
tdt/json