happyr/annotation-warmer
Warms up PHP annotation caches ahead of runtime so your first request doesn't pay the parsing cost. Scans classes for annotations, primes the reader cache, and exposes an Artisan command, making it a small performance add-on for Laravel and Doctrine-based apps.
Architecture fit. happyr/annotation-warmer is a deploy-time cache-warming utility, not a request-path library — the only architecturally correct place for it is a build step or a release command. That is a narrow target: it only pays off when (a) the app actually loads doctrine/annotations in production, (b) that reader is wrapped in a persistent cache pool, and (c) the annotation surface is large enough that cold-cache parsing is measurable. In a Laravel codebase all three conditions are unusual. Laravel core has no annotation reader; annotations only appear transitively through packages such as laravel-doctrine/doctrine/orm or a third-party attribute-scanning library. So the package's fit is contingent on a dependency the framework does not endorse, and it shrinks further as doctrine/annotations moves to 2.0 (reader removed) and Symfony 7 drops its annotation cache warmer.
What 0.3.0 changes about that picture: nothing material. The release ("removed default values for config; check for service definitions before use") is a Symfony DI hygiene patch. It is informative in two ways, both confirming rather than revising the prior read:
ContainerBuilder; it is not, and does not want to be, a Laravel package. A Laravel bridge remains something you write.0.x under semver, but it means config shape is not a stable contract. Adopters inherit that churn on top of the fork maintenance.The release adds no Laravel service provider, no config publish, no PHP 8 compatibility work, no doctrine/annotations 2.0 story, no docs, no tests of note. The reason-for-existing question is untouched.
Integration feasibility. Low-to-moderate, and unchanged by 0.3.0. The cost is verification rather than construction. The package is small and MIT-licensed, so it is readable and vendorable in an afternoon. What it lacks is everything that makes third-party integration cheap: no Laravel service provider, no config publish, no facade, no docs, no issue history, no evidence of downstream adoption. A Laravel integration requires writing the bridge yourself (a service provider plus an Artisan command or a post-autoload-dump hook) and validating that the cache keys it writes match the keys your runtime reader reads. A mismatch produces a silent no-op: the command succeeds, the cache stays cold, and you get a false sense of coverage. Note that 0.3.0's config-default removal slightly increases bridge work — you now must supply config explicitly rather than relying on inherited defaults, and you must pin the version you forked against.
Technical risk: high, marginally improved on the maintenance axis only.
T_NAME_QUALIFIED, T_ATTRIBUTE), which broke most 2019-era annotation tooling. Any custom parsing must be audited regardless of the new release.symfony/console/symfony/framework-bundle to ^3.4|^4.x while Laravel 10/11 require symfony/console ^6.2. The release notes suggest no constraint widening. Resolution fails outright, or you add a replace/alias hack — a strong signal to stop.doctrine/annotations 2.0 removes AnnotationReader; PHP 8 attributes need no warming because they are instantiated lazily by the engine. The package's entire reason for existing is being deprecated out of the ecosystem, and 0.3.0 does not pivot (e.g. to attribute-scanning warmup, which would be a different and more competitive product).AnnotationsCacheWarmer; Laravel ships artisan optimize (config:cache, route:cache, event:cache, view:cache).Key questions to answer before any further evaluation.
symfony/console ^6.x and PHP 8.1+? (If not, the Composer conflict is dispositive.)doctrine/annotations major does the constraint set actually permit?CachedReader/pool the runtime uses?composer why doctrine/annotations is the first command to run.)Verdict. Unchanged: reject for production adoption. The 0.3.0 release is a DI tidy-up that confirms Symfony coupling and pre-1.0 config instability without addressing any blocker from the original review. The problem is legitimate; this package is not the answer.
Stack fit. Poor as-is, and best treated as a reference implementation rather than a dependency. 0.3.0 does not change this. The realistic Laravel profiles where the underlying problem exists:
laravel-doctrine (Doctrine ORM entities configured via docblock annotations, metadata cache in Redis/file).If none of those describe the app, stop here — there is nothing to warm.
Measure before you migrate. Instrument a cache-cold boot: start with an empty cache pool, hit a representative endpoint, and capture wall time in the annotation reader via Blackfire or a one-off microtime wrapper around CachedReader. If cold-cache parsing is in the low single-digit milliseconds, the correct action is "do nothing." If it is tens to hundreds of milliseconds per node on a large codebase, the problem is real — but the fix is still probably native.
Migration path (in order of preference).
php artisan optimize in the release pipeline; for laravel-doctrine, run the metadata-cache warmers during deploy (doctrine:ensure-production-settings plus a metadata warmup) so entity metadata is populated before traffic arrives.RUN step after composer install --no-dev --classmap-authoritative) rather than a container entrypoint. This eliminates per-node warmup cost and autoscaler stampedes entirely.getClassAnnotations/getMethodAnnotations, write into the same pool the runtime reader uses. You own it, it is PHP-8-safe, and it has no Symfony coupling. This is almost certainly cheaper over a 24-month horizon than forking and maintaining happyr/annotation-warmer — and 0.3.0's config change is a reminder that the fork target is still a moving one.Compatibility work required if forking. Verify constraint sets against your PHP and Symfony-component versions; replace any Symfony DI wiring — including the service-definition lookups touched by 0.3.0 — with a Laravel service provider; supply the config values whose defaults were removed; confirm the tokenizer handles PHP 8 syntax; assert the cache key matches the runtime reader's key derivation; add a test that seeds an empty pool, runs the warmer, and asserts the reader hits cache (not disk) on the next parse.
Sequencing. (1) Dependency audit — does doctrine/annotations exist in prod at all? (2) Constraint check against the 0.3.0 release — does it even install on Laravel 10/11? (3) Cold-cache measurement — is there a number worth optimizing? (4) Native-warmer spike in a branch, one week, timeboxed. (5) Canary deploy with cache-cold nodes, compare p95 startup against baseline. (6) Only if all prior steps confirm a residual gap, escalate to build-time warming or the in-house command. (7) Parallel, non-negotiable track: migrate docblock annotations to PHP 8 attributes, which retires the problem permanently.
Maintenance. The 2019-era release line has been touched once, with a config/service-definition patch. Treat this as unmaintained-with-a-pulse rather than maintained: there is still no roadmap, no support commitment, and no evidence the maintainer intends to track doctrine/annotations 2.0 or PHP 8.3. If adopted, the package becomes yours — you own PHP upgrades, the 2.0 removal, and any security surface. Code volume is small enough that this is affordable, but it is a permanent, unglamorous line item with no upstream to defer to.
Support. No documentation, no issue corpus, no StackOverflow footprint, no maintainer SLA. Every operational question — "why didn't the warmer populate the cache?", "which paths does it scan?", "which config keys are now mandatory?" — gets answered by reading the source. The 0.3.0 config change makes this sharper: anyone on a prior version picking up 0.3.0 has an undocumented breaking config change to discover at runtime. Internal runbook and a source-annotated README are prerequisites, not nice-to-haves.
Scaling. The dangerous pattern is warming on container boot. In an autoscaling fleet, each cold node re-parses the full annotation surface: CPU burn at exactly the moment you are scaling to absorb traffic, plus added boot latency that can trip health-check timeouts and cascade into failed deploys. Two mitigations, both preferable to the package's default posture: warm at image build time (best — zero boot cost), or warm once per deploy into a shared Redis/Memcached pool. The shared-pool route introduces a correctness hazard: the cache namespace must be versioned per release, or a deploy that renames or deletes an entity can be served stale metadata from the previous release. That is a data-integrity bug class, not a performance bug, and it needs a deliberate design decision.
Failure modes to instrument for.
| Mode | Symptom | Detection |
|---|---|---|
| Cache-key mismatch | Command succeeds, no benefit; runtime still parses | Assert cache-hit ratio on the reader post-warm |
| Silent partial warm | Some classes hot, others cold — worst of both worlds | Count warmed classes vs. scannable classes; alert on divergence |
| Missing config after 0.3.0 | Warmer no-ops or throws on absent defaults | Pin config explicitly; assert warm-step exit code and warm count |
| Warmer aborts deploy | Release blocked by a metadata problem | Make the step non-blocking with a loud warning, or blocking with a fast, clear error — pick one deliberately |
| Reflection fatal | One bad/missing class kills the whole warm | Isolate per-file; log and continue |
| OOM in CI | Large codebase exceeds the build container limit | Memory-cap and monitor the warm step |
| Stale-after-change | Code deployed without warmer run (hotfix path) | Enforce warmer in the only deploy path; forbid manual deploys |
Ramp-up. Learning curve is trivial (it is one command); verification cost is the real tax. A competent engineer spends one to two days reading the source, validating PHP 8 compatibility, and reconciling the 0.3.0 config contract, plus three to five days to fork, de-Symfony, add a service provider, and write regression tests — before producing a single measurable improvement. Add the measurement and canary work and you are at two engineer-weeks to solve a problem that artisan optimize and an attribute migration may solve for less.
Bottom line for the roadmap. Do not add this dependency. Run the dependency audit and the cold-cache measurement; if a gap exists, close it with build-time warming or a small in-house Artisan command, and fund the annotation-to-attribute migration as the durable fix. 0.3.0 does not change this recommendation — it supplies one more data point that the artifact is Symfony-shaped, pre-1.0, and not being modernized toward either Laravel or attributes. Keep it in the evaluation record as a rejected option, with "problem is real, artifact is stale and framework-mismatched" as the rationale.
How can I help you explore Laravel packages today?