PHP Code Quality

A practical checklist to review PHP code for quality, security and speed, helping you keep every pull request production ready.

What We Cover in the PHP Analysis​

Here’s what this checklist helps you review before approving a pull request.

Code & Design Quality – correctness, clarity, and maintainable PHP/OOP structure

Security & Performance – protection against vulnerabilities, efficient queries, caching and scalability
Operational Readiness – testing strategy, observability, CI/CD and safe deployments
Integration & Data – APIs and webhooks, database schema & migrations, configuration and multi-tenant data handling

PHP Code Review Checklist

Export this Checklist as Markdown

Use these points as a starting guide to automate code reviews with your agent.

Files declare strict typing (declare(strict_types=1);) and use <?php only (no short tags).

Names follow conventions: classes PascalCase, methods/variables camelCase, constants UPPER_SNAKE_CASE.

One class per file; single responsibility; functions/methods are short and intention-revealing.

No commented-out/dead code; TODOs tracked in issues (not left in source).

PSR compliance: PSR-1/PSR-12 coding style; PSR-4 autoloading configured.

Consistent formatting enforced via PHP-CS-Fixer or PHP_CodeSniffer (ruleset committed).

Clear namespaces and directory structure; no cyclic dependencies between modules.

Comments explain why, not what; public APIs have PHPDoc for non-obvious behavior/generic types.

No wildcard imports; imports (use) sorted and minimal; fully qualified names avoided in code body.

Avoid magic numbers/strings; extract constants or enums (PHP 8.1+).

Control flow is straightforward: early returns > deep nesting; match used where clearer than chained ifs.

Strings use interpolation carefully; prefer sprintf for complex formatting.

Favor immutability: readonly properties/readonly classes where possible (PHP 8.2+).

Visibility is minimal (private > protected > public); no public mutable state.

Use interfaces to program to contracts; prefer composition over inheritance.

Constructors contain no heavy logic or I/O; use factories/builders for complex setup.

Type safety: scalar/union/intersection types present; return/param types and property types declared.

Nullable types used intentionally; avoid returning null for collections—use [].

enum for closed sets; value objects instead of primitive obsession (e.g., Email, Money).

Exceptions are domain-specific; no using return codes for error conditions.

Collections use SPL or dedicated classes; avoid leaking arrays where richer types help.

Avoid static state/singletons; use DI container (Laravel/Symfony container) for wiring.

Magic methods (__get, __set, __call) avoided unless implementing a clear proxy pattern.

Input validation at trust boundaries; centralize validation (Form Requests in Laravel, Validators in Symfony).

SQL Injection: only parameterized queries/QueryBuilder/ORM; never string-concatenate SQL.

XSS: templates auto-escape by default (Blade/Twig); safely escape when outputting raw HTML; sanitize rich text on input.

CSRF: tokens enabled/validated for state-changing requests; excluded routes justified and documented.

AuthN/Z: password hashing via password_hash() (Argon2id preferred) or framework hashing; role/permission checks on every sensitive action (not just UI).

Session security: secure, HttpOnly, SameSite cookies; session fixation prevention; short TTL for sensitive sessions.

IDOR: server-side ownership checks for resource access; do not trust client-supplied IDs alone.

File upload: strict MIME/extension allow-list; size limits; store outside webroot; randomize filenames; image processing sandboxed.

SSRF: restrict outbound HTTP destinations; disable local/metadata IPs; validate URLs; set allow_url_fopen=0 when possible.

Deserialization: avoid unserialize() on untrusted data; prefer JSON; if unavoidable, allowed classes list enforced.

Command injection: use Symfony\Process with argument arrays; never pass unsanitized shell strings.

CORS & Headers: least-permissive CORS; set HSTS, X-Content-Type-Options, X-Frame-Options/CSP as appropriate.

Secrets: loaded from env/secret store; never committed; rotate regularly; audit use.

Dependencies: composer.lock committed; run security audits (e.g., composer audit, tools like Symfony Security Checker); address CVEs.

Rate limiting & abuse: throttling on auth and expensive endpoints; captcha/challenge where appropriate.

Auditability: sensitive operations logged (non-PII), traceable to user/request.

Composer autoload optimized in prod (composer dump-autoload -o); dev dependencies excluded in production.

Opcache enabled and tuned; preloading considered for hot classes (PHP-FPM).

Avoid N+1 DB queries; eager load relations; use projections/select specific columns.

Pagination/streaming used for large result sets; never load unbounded collections into memory.

Caching strategy defined (Redis/APCu): keys, TTLs, invalidation rules; avoid unbounded cache growth.

Expensive computations/background tasks offloaded to queues; idempotent workers; concurrency tuned.

HTTP clients have connection pools, timeouts, and retries with backoff (idempotent only).

String concatenation in loops avoided; use buffers/implode; minimize temporary allocations.

JSON encode/decode hotspots profiled; avoid double encoding; consider Ext-JSON options judiciously.

Algorithmic complexity reviewed; avoid quadratic behavior on user-supplied list sizes.

PHP-FPM process manager tuned (pm, children, memory); no blocking I/O in request threads when avoidable.

Static asset and HTTP caching (ETag/Last-Modified/Cache-Control) configured at the edge.

Exceptions used for exceptional cases; no swallow/empty catch; add context when rethrowing.

Consistent domain/application/HTTP exception mapping; error responses don’t leak stack traces.

Try-with-resource equivalents (finally blocks/defer patterns) used to close external resources.

Monolog (or equivalent) for structured logs; include correlation/request ID; log levels appropriate (no info-level spam).

Metrics emitted for key flows (latency, error rate, throughput); health/readiness endpoints present.

Distributed tracing (OpenTelemetry/Jaeger/Zipkin) integrated for external calls and DB.

Timeouts on all I/O; circuit breakers/bulkheads where appropriate; retries bounded with jitter.

Unit tests (PHPUnit/Pest) deterministic and fast; meaningful assertions; no mocks of code you don’t own.

Integration tests cover DB, cache, queue, and external HTTP (use Symfony HttpClient test or MockServer).

API/contract tests (OpenAPI validation; Pact for consumer/provider where applicable).

Property-based tests where pure logic benefits; boundary conditions covered.

Regression tests added for fixed prod bugs; fail without the fix.

Static analysis: PHPStan/Psalm at strict level (baseline reviewed, shrinking over time).

Linting: PHP_CodeSniffer/PHPCSFixer enforced in CI; failing gate.

Mutation testing (Infection) on critical modules or security-sensitive code.

Coverage monitored, but quality > %; critical paths and error handling included.

Test data via factories/builders; no shared mutable global state; tests parallelizable.

Modules have clear boundaries; domain code not coupled to framework I/O (ports/adapters).

SRP/SoC respected; no God classes/controllers; fat models avoided (or justified).

DTOs vs Entities separated; mapping centralized (e.g., Symfony Serializer, custom mappers).

Configuration via env/parameters; sensible defaults; no env-specific branches in code.

Feature flags for risky changes; dark launches/canaries supported.

ADRs or lightweight design notes accompany non-trivial changes.

Backward compatibility considered for public APIs; deprecation path documented.

Time handled with DateTimeImmutable and UTC in storage; timezone conversion at edges.

Laravel

Route model binding and policies/guards enforce authorization consistently.

Eloquent: eager load relations; guard against mass assignment; use casts/value objects; avoid logic in models—use services.

Queues (Horizon): jobs idempotent; visibility timeouts and retries configured; failed job handling in place.

Validation via Form Requests; resources/transformers control API shape; Resource Collections for pagination.

Config cached in prod; APP_DEBUG=false; APP_KEY set and rotated; rate limiting middleware configured.

Symfony

Controllers thin; business logic in services; autowiring explicit for complex graphs.

Validation constraints (Assert*) on DTOs/entities; normalizers/serializers configured explicitly.

Security component: voters/attributes for fine-grained authz; firewalls/access control defined.

Messenger for async/queue with retries/DLQ; transport configuration reviewed.

Config split by env; APP_ENV=prod with cache:warmup; HTTP cache set via Cache component/Reverse proxy.

API contract versioned (path/header); breaking changes gated; OpenAPI/JSON Schema up to date.

Consistent error model (problem+json or equivalent); validation errors structured.

Idempotency keys for POST on non-safe operations; replay protection for webhooks (HMAC + timestamp tolerance).

Pagination, filtering, and sorting standardized; maximum page sizes enforced.

Security: OAuth2/JWT/session strategy documented; scopes/claims validated on every request.

Timeouts, retries (idempotent only), and circuit breakers on all outbound calls; request/response logging with redaction.

Webhooks: signature verification, retry with backoff, deduplication, and DLQ.

GraphQL (if used): N+1 prevented (dataloaders), complexity limits, depth limits.

Reproducible builds; composer install --no-dev --prefer-dist --no-interaction --no-progress; lockfile committed.

Static analysis, tests, linters, and security scans run in CI; failing gates block merges.

Container images minimal (distroless/alpine if compatible), non-root user, read-only FS, health checks.

PHP-FPM/Nginx configured with sane limits; realpath_cache, Opcache, and memory tuned.

Env/secrets injected via platform secret store; no secrets in images or repo.

Migrations (Doctrine Migrations/Laravel Migrations) apply automatically with safe rollout/rollback plan.

Blue/green or canary deploys available; rollbacks are fast and documented.

Observability plumbed in prod (logs/metrics/traces); alerts are actionable and tied to SLOs.

Schema indexes match query patterns; no full table scans on hot paths; migrations include down/rollback or compensations.

Charset/collation set (utf8mb4); timezone stored in UTC; DECIMAL for money; lengths/constraints enforced.

PII classified; encryption at rest/in transit; field-level encryption where required (e.g., libsodium/defuse/php-encryption).

Data retention and deletion implemented (GDPR/LGPD); right-to-erasure supported; audit trails for access to sensitive data.

Backups automated, encrypted, and tested; RPO/RTO documented; restore drills performed.

Multi-tenancy isolation enforced in every data path (scopes/filters/tenant IDs).

Exports/imports validated and rate-limited; CSV/Excel parsers hardened.

Turn this checklist into automated reviews

Let Kody automatically check quality, security and performance in every pull request—cloud or self-hosted, in under 2 minutes.