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.
This is a small, single-purpose Symfony bundle whose job is to pre-populate Doctrine's annotation reader cache. Instead of paying the annotation-parsing cost on the first web request after a deploy, you run the warmer during cache:warmup and the PHP file cache is already populated.
The package's last meaningful release was 0.3.0, and it has essentially no adoption, so treat the README and the source as the source of truth for exact class and config names rather than any blog post or assumption.
Heads-up on 0.3.0: this release removed the default values for config and added service-definition guards. Practically that means:
Minimal path to a first working setup:
Install it (expect to pin):
composer require happyr/annotation-warmer
The package predates modern Symfony Flex recipes, so it will very likely not be auto-registered. Add the bundle to config/bundles.php manually if it isn't there.
Confirm the bundle class and config key. Look at the repository's src/ for the *Bundle class and DependencyInjection/Configuration.php. That file defines the exact config tree. On 0.3.0, pay attention to which nodes lost their defaults — those are now effectively required. Also skim tests/ — for small Happyr packages the tests are usually the most accurate usage documentation.
Declare the config explicitly. Create config/packages/annotation_warmer.yaml (name per README) and list the directories you want scanned. Under 0.3.0 an empty or missing block is no longer safe.
Point it at the code you care about. The first-use case is almost always "warm annotations for my entities (and optionally form types / validators / controllers)" so the first request after deploy doesn't stall on AnnotationReader::getClassAnnotations().
Wire it into your deploy. Run bin/console cache:warmup --env=prod (or whatever dedicated command the bundle exposes) as part of your release step, after the cache has been cleared.
The first realistic win: a Symfony app with a large entity graph where the first request after cache:clear is noticeably slower. If your annotation cache is set to file/PhpFileCache, this bundle should collapse that cold-start penalty.
Pair it with a file-based annotation cache. The warmer is only useful if your annotation reader actually persists to a cache you can pre-fill. In practice that means doctrine/annotations configured with PhpFileCache (or Symfony's framework.annotations.cache: file on older versions). If you use an in-memory or Redis-backed cache, a compile-time warmer either does nothing useful or writes to a cache the runtime never reads.
Configure directories, not individual classes. The natural config shape for this kind of bundle is a list of source directories (or a list of bundles) to scan. Prefer globbing over src/Entity and src/Form rather than enumerating classes — it survives refactors, and the warmer is cheap enough to over-scan.
Check the service wiring on 0.3.0. Because the release added hasDefinition()-style guards, the warmer only activates when the services it expects are registered under the names it looks for. If you use a custom Doctrine annotation reader service id, or you've replaced the default cache adapter, the warmer may compile fine and do nothing. Grep src/ for the service ids it checks before you assume it ran.
Standard deploy workflow:
# in your deploy script, prod env
bin/console cache:clear --no-warmup --env=prod
bin/console cache:warmup --env=prod # warmer runs here
If the bundle exposes a dedicated command instead of riding on cache:warmup, run it explicitly between clear and the first request. Never run it against the dev cache and expect prod to benefit — the cache directories are separate.
Use it as a CI guard, secondarily. Running the warmer in CI on a clean cache is a cheap way to catch annotations that reference classes that no longer exist or have invalid import targets — failures that would otherwise only show up as a 500 on the first production request. Note that with the new service-definition guards, some "failures" now become silent skips, so assert on the output (cache file count) rather than exit code alone.
Verify the effect rather than trusting it. After warmup, inspect the annotation cache directory (commonly var/cache/prod/annotations/ for the PHP file cache) and confirm files were generated. Then hit a cold endpoint and compare timing with/without the warmer. If the directory is empty, the warmer isn't scanning what you think it is — or it skipped itself because its services weren't found.
Keep the scan scope aligned with runtime. Only warm classes that the runtime annotation reader will actually touch. Warming src/Command or src/MessageHandler classes that are never annotation-read is wasted deploy time and noisy cache.
src/"), you must now add explicit config on upgrade from 0.2.x. Check Configuration.php for which nodes became required, and pin 0.3.0 deliberately rather than floating.hasDefinition() checks make the bundle more defensive, but the failure mode moved from "fatal error at warmup" to "warmup succeeds and warms nothing." Always verify the annotation cache directory is populated after warmup; don't trust the green deploy.CacheWarmerInterface implementation is a realistic exit path if the package breaks against a newer Symfony.doctrine/annotations before investing.www-data at runtime) can leave cache files unreadable. Make sure the writable cache dir is owned consistently.cache:clear --no-warmup), then run warmup with -vvv. If the bundle has a scan command, run it and check it reports the classes you expect. Compare the annotation cache file count before/after. On 0.3.0, also dump the container (bin/console debug:container | grep -i annotation) to confirm the warmer service and its dependencies are registered.Finder results and calls getClassAnnotations() / getMethodAnnotations() on each — same effect, full control over scope, service wiring, and error handling.How can I help you explore Laravel packages today?