Performance
| Cache pools | |
| OPcache on | |
| Query counts | |
| Measure first |
Cache pools
Cache the slow answers and compute them once. The pool stores the result under a key, and the closure rebuilds it only when the key is missing or expired. Reports, menus, and counters are typical first candidates.
use Symfony\Contracts\Cache\CacheInterface; use Symfony\Contracts\Cache\ItemInterface; class ReportBuilder { public function __construct(private CacheInterface $cache) { } public function monthly(string $month): array { return $this->cache->get("report_" . $month, function (ItemInterface $item): array { $item->expiresAfter(3600); return ["month" => "todo", "total" => 0]; }); } }
OPcache on
In production PHP must not recompile every file on each request. OPcache keeps the compiled code in memory, which is the largest free speedup available, and it needs only two settings to start.
opcache.enable=1, opcache.validate_timestamps=0
PHP serves compiled code from memory, no recompile per request
Query counts
Most slow pages are slow database loops: one query for the list plus one query per row. Fetch the relation in the same query with a join, then compare the query count before and after in the debug toolbar.
$posts = $repo->createQueryBuilder("p") ->leftJoin("p.comments", "c") ->addSelect("c") ->getQuery() ->getResult();
Measure first
Optimize only what you measured. The dev toolbar shows page time and query count on every page, so write both numbers down, change one thing, and compare. One fix at a time beats a full rewrite.
Before: 240 ms, 35 queries
After join fetch: 60 ms, 2 queries
Next: Capstone: blog
Article author: Arthur Isaev