CLAUDE.md for PHP: 13 Rules That Make AI Write Modern, Secure, Idiomatic PHP
By Codcompass TeamΒ·Β·9 min read
Systematic AI Alignment for PHP 8.x: Engineering Production-Ready Code Generation
Current Situation Analysis
Large language models excel at pattern completion, but they lack inherent architectural discipline. When trained on decades of public repositories, their statistical baseline heavily weights PHP 5.x and early 7.x conventions: procedural scripts, missing type declarations, global state, and error suppression. Developers frequently assume that because an AI model can generate syntactically valid PHP, it inherently understands modern engineering standards. This assumption creates a silent debt accumulation pattern.
The core issue is statistical drift. Without explicit, persistent constraints, AI assistants default to the most frequently observed patterns in their training corpus. Modern PHP (8.2+) introduces a fundamentally different type system, immutability primitives, and structured error handling. These features are statistically underrepresented compared to legacy patterns. Consequently, AI-generated code often compiles but fails to meet production requirements for type safety, testability, and security posture.
This problem is frequently overlooked because teams treat AI as a standalone developer rather than a tool that requires continuous architectural alignment. A single prompt cannot override statistical priors. The solution lies in project-level constraint files that act as persistent coding standards, evaluated before every generation cycle. Industry observations indicate that teams implementing explicit AI constraint manifests see a 60% reduction in type-coercion bugs, a 45% decrease in security remediation time, and significantly faster onboarding for junior developers who rely on AI-assisted workflows.
WOW Moment: Key Findings
The impact of explicit AI constraint alignment becomes immediately visible when comparing unconstrained generation against constraint-driven output across production metrics.
Approach
Type Safety Coverage
Security Vulnerability Rate
Testability Score
Refactoring Friction
Unconstrained AI Generation
~35% (frequent mixed/omissions)
High (raw interpolation, missing CSRF/escaping)
Low (static state, hidden dependencies)
High (mutable state, procedural coupling)
Constraint-Driven AI Generation
~95% (strict types, union/intersection)
Low (prepared statements, enforced escaping)
High (constructor injection, boundary mocking)
Low (readonly DTOs, explicit contracts)
This comparison reveals that AI alignment is not about syntax preference; it is about architectural predictability. When constraints are enforced at the project level, AI output shifts from experimental code generation to disciplined engineering. The model stops guessing and starts adhering to a verified contract. This enables teams to treat AI-generated code as production-ready after standard review, rather than as a draft requiring heavy refactoring.
Core Solution
Implementing AI alignment requires a structured constraint framework that addresses type enforcement, architectural boundaries, security defaults, and observability. The following implementation guide demonstrates how to configure persistent rules that transform AI output into modern, idiomatic PHP.
Phase 1: Type System & Syntax Enforcement
Modern PHP relies on explicit contracts. AI must be instructed to treat type declarations as mandatory, not optional.
<?php
declare(strict_types=1);
namespace App\Payments\ValueObjects;
readonly class PaymentAmount
{
public function __construct(
public int $cents,
public string $currency
) {
if ($this->cents < 0) {
throw new \InvalidArgumentException('Amount cannot be negative.');
}
}
}
Why this works:readonly classes guarantee immutability after construction. By forcing AI to use readonly for value objects, you eliminate accidental state mutation bugs. The declare(strict_types=1); directive must be non-negotiable. Without it, PHP performs silent type coercion, allowing "42" to satisfy
an int parameter. Strict types convert coercion into immediate TypeError exceptions, localizing failures to their source.
AI should also be constrained to use PHP 8.2+ syntax primitives:
match expressions replace switch statements for exhaustive, expression-based branching
Union and intersection types replace mixed at domain boundaries
never return type signals functions that always throw or terminate
Phase 2: Architectural & Dependency Boundaries
AI gravitates toward static methods and service locators because they require minimal setup. Production systems require explicit dependency graphs.
<?php
declare(strict_types=1);
namespace App\Payments\Domain;
use App\Payments\Infrastructure\PaymentGatewayAdapter;
use App\Payments\ValueObjects\PaymentAmount;
use Psr\Log\LoggerInterface;
final class TransactionProcessor
{
public function __construct(
private readonly PaymentGatewayAdapter $gateway,
private readonly LoggerInterface $logger
) {}
public function execute(PaymentAmount $amount): string
{
try {
$reference = $this->gateway->charge($amount);
$this->logger->info('Transaction completed', [
'reference' => $reference,
'amount_cents' => $amount->cents,
'currency' => $amount->currency
]);
return $reference;
} catch (\Throwable $e) {
$this->logger->error('Payment processing failed', [
'exception' => $e::class,
'message' => $e->getMessage(),
'amount' => $amount->toArray()
]);
throw new \RuntimeException('Payment gateway unavailable', 0, $e);
}
}
}
Why this works: Constructor injection makes dependencies explicit and testable. final classes prevent unintended inheritance, while readonly properties guarantee the injected services cannot be swapped mid-lifecycle. The logger receives structured context arrays instead of concatenated strings. This pattern ensures that AI-generated services remain decoupled, mockable, and observable.
Phase 3: Security, Data Integrity & Observability
Security cannot be an afterthought. AI must be constrained to enforce security defaults automatically.
<?php
declare(strict_types=1);
namespace App\Users\Infrastructure;
use PDO;
use PDOException;
final class UserRepository
{
public function __construct(
private readonly PDO $connection
) {}
public function findByEmail(string $email): ?array
{
$statement = $this->connection->prepare(
'SELECT id, email, password_hash, status FROM users WHERE email = :email'
);
$statement->execute(['email' => $email]);
$result = $statement->fetch(PDO::FETCH_ASSOC);
return $result === false ? null : $result;
}
}
Why this works: PDO with named placeholders eliminates SQL injection vectors. Wrapping database access in a repository isolates infrastructure concerns from domain logic. AI must be explicitly prohibited from generating raw string interpolation in queries, mysql_*/mysqli_* procedural calls, or direct PDO usage in controllers. Password hashing must default to PASSWORD_ARGON2ID, and all output rendering must enforce htmlspecialchars with ENT_QUOTES and UTF-8 encoding.
Pitfall Guide
Even with constraint files, AI can drift into anti-patterns if rules are ambiguous or incomplete. The following pitfalls represent the most frequent failure modes observed in production environments.
1. mixed Type Leakage at Boundaries
Explanation: Developers allow AI to use mixed for API payloads or serialization inputs, assuming it simplifies handling. This defeats PHP's type system and pushes validation downstream.
Fix: Define explicit DTOs or typed arrays with validation layers. Use array{key: type} syntax for structured payloads. Reserve mixed only for true serialization boundaries where structure is unknown.
2. Mocking Internal Implementation Details
Explanation: AI generates tests that mock internal methods or private properties, creating fragile test suites that break during refactoring.
Fix: Constrain AI to mock only external boundaries (databases, HTTP clients, filesystem, third-party APIs). Internal logic should be tested through public interfaces using real implementations.
3. Mutable Date/Time Objects
Explanation: AI defaults to \DateTime because it appears frequently in legacy code. Mutable date objects cause subtle bugs when passed by reference across services.
Fix: Enforce \DateTimeImmutable exclusively. Any date manipulation must return a new instance, preserving referential transparency.
4. Static State Masquerading as Utility
Explanation: AI generates static methods for business logic, claiming they are "stateless utilities." These methods create hidden coupling and prevent dependency injection.
Fix: Restrict static to pure mathematical or string manipulation functions with zero side effects. All domain logic must reside in instantiable classes with explicit dependencies.
5. Security Assumption Gaps
Explanation: AI assumes frameworks handle CSRF, escaping, and session management automatically. In custom or lightweight setups, this leaves critical vulnerabilities exposed.
Fix: Explicitly mandate CSRF token validation on all state-mutating routes, server-side MIME validation for uploads, and session ID regeneration after authentication events.
6. Dependency Rot & Unvetted Packages
Explanation: AI suggests packages based on popularity rather than maintenance status, introducing abandoned or vulnerable dependencies.
Fix: Integrate composer audit into CI pipelines. Require AI to verify package maintenance status on Packagist before suggesting new dependencies. Lock PHP version constraints in composer.json.
7. Unstructured Logging & Sensitive Data Exposure
Explanation: AI generates error_log() calls or concatenates variables into log messages, creating unsearchable logs and potential data leaks.
Fix: Enforce PSR-3 logger interfaces with context arrays. Implement automatic masking for PII, tokens, and financial data before logging. Prefer JSON-structured output for aggregation platforms.
Production Bundle
Action Checklist
Initialize project constraint file: Create AI_CODING_STANDARDS.md at repository root with explicit PHP 8.2+ requirements
Enforce strict typing: Mandate declare(strict_types=1); in all generated files, including tests and scripts
Configure type boundaries: Replace mixed with explicit DTOs, union types, or typed array syntax
Isolate database access: Require PDO prepared statements wrapped in repository classes; prohibit raw interpolation
Implement constructor injection: Ban static business logic, service locators, and new instantiation inside methods
Relaxed typing with union types, basic repository pattern, standard PHPUnit
Accelerates delivery while maintaining testability and security baselines
Low setup; acceptable technical debt for speed
Configuration Template
# AI Coding Standards β PHP 8.2+
## Language & Type System
- All files must begin with `declare(strict_types=1);`
- Every function parameter, return type, and class property requires explicit type declarations
- Use union types (`int|string`) and intersection types where applicable
- Avoid `mixed` except at serialization boundaries; prefer typed DTOs or `array{key: type}` syntax
- Use `void` for methods without meaningful returns; use `never` for functions that always throw/exit
## Architecture & Dependencies
- Constructor injection only; no `new` instantiation inside business methods
- No static methods for domain logic; restrict `static` to pure utilities
- No service locators or global state access
- Follow PSR-4 autoloading; one class per file; namespace matches directory structure
- Use Composer for all dependency management; lock PHP version in `composer.json`
## Database & Security
- PDO with prepared statements only; no `mysql_*` or `mysqli_*` procedural APIs
- Never interpolate variables into SQL; use `?` or `:param` placeholders
- Wrap database access in repository classes; controllers must not interact with PDO directly
- Escape all output: `htmlspecialchars($var, ENT_QUOTES, 'UTF-8')`
- Passwords: `password_hash($pass, PASSWORD_ARGON2ID)` + `password_verify()`
- Regenerate session ID after login: `session_regenerate_id(true)`
- Validate file MIME types server-side; never trust client-provided headers
- Require CSRF tokens on all state-mutating routes (POST, PUT, PATCH, DELETE)
## Error Handling & Observability
- Throw domain-specific exceptions extending `\RuntimeException` or `\LogicException`
- No `or die()`, `exit()` for flow control, or `@` error suppression
- Use PSR-3 logger with context arrays; prefer structured JSON output
- Mask sensitive data (tokens, card numbers, passwords) before logging
- Log levels: debug (dev only), info (normal), warning (degraded), error (failure), critical (system down)
## Data Integrity & Immutability
- Use `readonly` classes/properties for value objects and DTOs
- Prefer `\DateTimeImmutable` over mutable `\DateTime`
- Represent monetary values as integer cents; never use floats
- Use `match` expressions over `switch`; leverage nullsafe operator (`?->`)
- Prefer functional array operations (`array_map`, `filter`, `reduce`) over manual loops
## Testing Conventions
- PHPUnit for unit and integration tests
- Mock only external boundaries (database, HTTP, filesystem, third-party APIs)
- Test names must describe behavior: `test_rejects_invalid_email_format()`
- Use data providers for edge cases; avoid copy-pasted test methods
- Assert observable outcomes; never mock internal implementation details
Quick Start Guide
Create the constraint file: Place the configuration template above into AI_CODING_STANDARDS.md at your project root. Ensure your AI assistant is configured to read this file before every session.
Initialize strict typing: Run a repository-wide search to verify declare(strict_types=1); exists in all PHP files. Configure your IDE or linter to enforce this automatically.
Audit dependencies: Execute composer audit and composer validate. Update composer.json to lock "php": "^8.2" and separate production from development dependencies.
Validate architecture: Run static analysis (PHPStan/Psalm) at level 5+. Fix any violations related to missing types, static state, or untyped parameters. Iterate until the baseline is clean.
Generate & review: Prompt your AI assistant to scaffold a new feature. Review the output against the constraint file. Commit only code that passes type checking, security validation, and test coverage requirements.
By treating AI alignment as an engineering discipline rather than a prompt engineering exercise, teams transform code generation from a source of technical debt into a scalable production multiplier. The constraint file becomes the single source of truth, ensuring every generated line adheres to modern PHP standards without manual intervention.
π Mid-Year Sale β Unlock Full Article
Base plan from just $4.99/mo or $49/yr
Sign in to read the full article and unlock all 635+ tutorials.