---
language: php
extensions: [".php", ".phtml", ".php3", ".php4", ".php5", ".phps"]
---
# PHP — Language-Specific Review Notes
Load this file alongside `rules/universal.md`. Universal rules are not repeated here — only PHP-specific rules and idioms.
---
## PR Analyzer — PHP Risk Signals
- `var_dump` / `print_r` / `echo` debug statements left in production code
- `@` error suppression operator — masks real errors; verify it is justified
- `// phpcs:ignore` / `// phpstan-ignore` comments — verify they are justified
- Hardcoded credentials, database passwords, or API keys in source
- `eval()` anywhere — almost always a security issue
- `$_GET` / `$_POST` / `$_REQUEST` / `$_COOKIE` used without sanitization
---
## Code Quality — PHP Checks
- Missing type declarations on function parameters and return types
- `mixed` return type used broadly — tighten to specific types
- Global variables (`global $var`) — pass dependencies explicitly
- Long functions (>50 lines) — PHP functions tend to accumulate logic
- `isset()` / `empty()` used to mask type errors instead of fixing the root cause
- Missing `strict_types=1` declaration at the top of the file
---
## Security
- Flag `$_GET` / `$_POST` / `$_REQUEST` used directly in SQL queries — require PDO prepared statements
- Flag `mysqli_query($conn, "SELECT ... WHERE id = " . $_GET['id'])` — SQL injection
- Flag `echo $_GET['name']` or any unescaped output — XSS; use `htmlspecialchars()` with `ENT_QUOTES`
- Flag `include` / `require` with user-controlled paths — local/remote file inclusion
- Flag `eval()` — remote code execution risk; no legitimate use in application code
- Flag `shell_exec` / `exec` / `system` / `passthru` with user-controlled input — command injection
- Flag `unserialize()` on untrusted data — arbitrary object instantiation and code execution
- Flag `move_uploaded_file` without MIME type validation and extension whitelist — file upload attack
- Flag `header("Location: " . $_GET['url'])` without validation — open redirect
- Flag missing CSRF token validation on state-changing form endpoints
---
## Async / Concurrency
- Flag long-running synchronous operations in a request cycle — offload to a queue (Laravel Queue, RabbitMQ)
- Flag `sleep()` used inside a request handler — blocks the PHP-FPM worker
- Flag shared mutable state in `static` properties accessed across requests in long-running processes (Swoole, RoadRunner)
- Flag missing idempotency in queued jobs — jobs can be retried on failure
---
## Resource Management
- Flag database connections not closed or returned to the pool (`$pdo = null` or `$conn->close()`)
- Flag `fopen` / `fwrite` without a matching `fclose` on all paths
- Flag `curl_init` without `curl_close` — leaks the curl handle
- Flag unbounded file uploads with no size or type restriction
- Flag sessions not explicitly closed (`session_write_close()`) before long operations — session locking blocks other requests
---
## Exception Handling
- Flag empty `catch` blocks — swallowed exceptions
- Flag `catch (Exception $e) {}` without logging — silent failure
- Flag `die()` / `exit()` used for error handling in library code — use exceptions
- Flag `@` operator used to suppress errors from functions that can fail — check return values instead
- Flag `trigger_error` used in new code — prefer exceptions
---
## Performance
- Flag N+1 Eloquent / Doctrine queries — use eager loading (`with()`, `load()`, `join`)
- Flag `count($array)` called repeatedly in a loop condition — cache the result
- Flag `array_push($arr, $val)` — use `$arr[] = $val` which is faster
- Flag `in_array` on large arrays without the strict third argument — use `isset` on a flipped array for O(1) lookup
- Flag `file_get_contents` on remote URLs in a request cycle — use an HTTP client with timeout and async where possible
- Flag Eloquent `all()` without pagination — loads entire table into memory
---
## Idioms and Best Practices
### Type Safety
- Always declare `declare(strict_types=1)` at the top of every file
- Use union types (`int|string`) and nullable types (`?string`) rather than `mixed`
- Use typed properties on classes — avoid untyped `public $foo`
- Use constructor promotion for simple value objects
### Modern PHP (8.x)
- Prefer `match` expressions over `switch` — strict comparison, no fall-through
- Use named arguments for functions with many optional parameters
- Use `enum` for fixed sets of values instead of class constants
- Use `readonly` properties for immutable data
- Use nullsafe operator (`?->`) instead of nested `isset` checks
- Use `first-class callable syntax` (`strlen(...)`) instead of string references
### Laravel / Symfony Specific
- Keep controllers thin — logic belongs in service classes or action classes
- Use form requests for validation — never validate in the controller directly
- Prefer Eloquent relationships over manual joins for readability
- Flag raw queries where the ORM can express the same intent safely