Search by

silin / magento-perf-check

SilinMykola

Static analysis for Magento 2 and Mage-OS performance and Full Page Cache anti-patterns: PHPStan rules plus XML checks.

Package info

github.com/SilinMykola/magento-perf-check

Type:phpstan-extension

pkg:composer/silin/magento-perf-check

Fund package maintenance!

Ko Fi

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.1 2026-11-06 15:16 UTC

This package is auto-updated.

Last update: 2026-11-06 15:18:34 UTC


README

Finds performance and Full Page Cache anti-patterns in Magento 2 and Mage-OS code before they reach production: database queries in loops, state changes in cacheable GET requests, session objects created on every page, misconfigured layout, events, indexers, queues and GraphQL caching.

CI Packagist License: MIT PHP 8.1+ PHPStan 1.12 and 2.x

$ vendor/bin/magento-perf-check --paths=app/code/Mollie/Payment/Controller/Express/Process.php,app/code/Magefan/Community/Model/GetCategoryByProduct.php
app/code/Magefan/Community/Model/GetCategoryByProduct.php
     73  error    [P-LOOP-01] Magento\Catalog\Api\CategoryRepositoryInterface::get() is called inside a loop: one database query per iteration. Fetch all items before the loop with a collection or getList().
         https://github.com/SilinMykola/magento-perf-check/blob/v0.1.0/docs/rules/P-LOOP-01.md

app/code/Mollie/Payment/Controller/Express/Process.php
     58  error    [P-FPC-06] Mollie\Payment\Controller\Express\Process handles GET only but calls Magento\Quote\Api\CartRepositoryInterface::save(). Change state only on POST or PUT: GET responses can be cached and served without running this code.
         https://github.com/SilinMykola/magento-perf-check/blob/v0.1.0/docs/rules/P-FPC-06.md

2 finding(s): 2 error, 0 warning, 0 info. Analyzed app/code/Mollie/Payment/Controller/Express/Process.php, app/code/Magefan/Community/Model/GetCategoryByProduct.php in 0.8 s.

Real output on two published modules (magefan/module-community 2.4.9 and mollie/magento2 3.2.0); both findings were confirmed by reading the code during calibration.

Quick start

composer require --dev silin/magento-perf-check
vendor/bin/magento-perf-check

Or check a project without adding a dependency to it (the project still needs its own composer install):

git clone https://github.com/SilinMykola/magento-perf-check.git && cd magento-perf-check && composer install
php bin/magento-perf-check --project=/path/to/magento

Calibrated on Magento 2.4.9, where both ways were checked. Stock Magento 2.4.4 and older pin a PHPStan version below 1.12, so composer require cannot work there: use the clone. It ran that way on vendor/magento/module-cms of 2.4.0, 2.4.3 and 2.4.4, without grading the findings (check). The install was not tried on 2.4.5 to 2.4.8, and Magento 2.3 is not checked.

It analyzes app/code and app/design with type-aware PHPStan rules and XML checks, and never writes into the project. No database or container is needed. On a stock Magento 2.4.9 project, install plus the first run took under 20 seconds (measured).

What it finds

29 rules: 24 on by default, 5 experimental. A few of them:

  • Queries in loops: load(), getById() or a new collection inside a loop (P-LOOP-01, P-LOOP-02).
  • Full Page Cache breakers: state changes in GET-only controllers (P-FPC-06), no-cache headers on cacheable pages (P-FPC-05), cache flushes and reindexAll() from request code (P-FPC-09, P-FPC-10), ESI blocks on entity pages (X-LAY-02).
  • Expensive wiring: sessions injected without a Proxy (P-DI-01), events dispatched in constructors (P-DI-03), observers on every model load or block render (X-EVT-01, X-EVT-03).
  • Configuration: sections.xml that reloads all customer data on every POST (X-SEC-01), GraphQL @cache without an identity (X-GQL-01), synchronous queue topics (X-MQ-01), indexers without mview (X-IDX-01), tables without a primary key (X-DB-01).

Full list with severities and sources: docs/rules.md. Every rule has its own page with bad and good examples, how to suppress it and its known false positives.

Found in real modules

From the calibration corpus (Magento 2.4.9 core and the 25 most-downloaded Packagist modules), graded as true positives:

  • P-LOOP-01 in mailchimp/mc-magento2 103.4.86: one product getById() per visible order item on the checkout success page.
    app/code/Ebizmarts/MailChimp/Block/Pixel/Order.php:96
  • P-LOOP-01 in core: category repository get() per anchor category of every product category, on product save and import.
    vendor/magento/module-catalog-url-rewrite/Model/Product/AnchorUrlRewriteGenerator.php:70
  • P-LOOP-01 in core: one getById() per configurable variation on admin product save; variations can number in the hundreds.
    vendor/magento/module-configurable-product/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php:80
  • P-FPC-06 in mollie/magento2 3.2.0: a GET action saves the quote and returns a cacheable page, so a repeated request can be served from cache without running the save.
    app/code/Mollie/Payment/Controller/Express/Process.php:58
  • P-LOOP-03 in adyen/module-payment 11.3.0: a cron job deletes expired rows one by one where one DELETE query would do.
    app/code/Adyen/Payment/Cron/StateDataCleanUp.php:57

What our rules are based on

Every rule in the default set cites at least one primary source:

  • A: official Adobe or Hyvä documentation.
  • B: Magento core code pinned to a specific commit (currently a385d02f), or a magento/magento2 issue with a confirmed reproduction or measurements.

Community sources (C) are used only as supplementary explanation. A rule with only C sources stays experimental and is off by default. Each rule page ends with a Basis section listing its sources, their levels and what each one confirms; the audit of the whole catalog is in docs/source-audit.md.

Precision is measured, not assumed. Every rule ran on Magento 2.4.9 core and the 25 most-downloaded Packagist modules, and up to 30 findings per rule were graded by reading the code (method and per-rule results):

  • the six default-set rules with enough findings to measure (P-LOOP-01, P-LOOP-03, P-DI-01, P-DI-06, P-FPC-01, T-01) were right in 87–100 % of graded findings;
  • five rules measured below their threshold and are experimental, off by default;
  • rules that were right but mostly found low-impact cases were lowered to warning or info;
  • the remaining rules had fewer than 10 findings in the corpus, so their precision is not established yet.

Static analysis gives heuristics, not guarantees: treat each finding as a reason to check, not a verdict.

Related

  • magento2-best-practices: Magento coding rules, test layers, fixtures and PHPUnit conventions; detects a magento/magento2 checkout.

Documentation

  • Rules: the full list, with links to each rule's page.
  • Running and configuring: options, exit codes, magento-perf-check.neon, use inside an existing PHPStan setup.
  • Baseline: adopt the tool on a project with existing findings.
  • CI and Git hooks: GitHub Actions with code scanning, GrumPHP, other CI.
  • Coding agents: an AGENTS.md snippet and a skill that run the tool and act on its findings.
  • Changelog.

Development

composer test        # PHPUnit, with PHPStan 2.x
bin/test-phpstan1    # the same suite against PHPStan 1.12, in a temporary copy
composer analyse     # PHPStan at level max on src/

The design and roadmap are in docs/SPEC.md, architectural decisions in docs/adr/. Contributors: see AGENTS.md for the rules every change follows.

License

MIT, see LICENSE.