PHP & Laravel Development
Scoped to framework-level PHP. Work on php-src internals or a native PHP extension is C, not PHP: the ia-c-systems skill covers it, including the Zend API conventions (gen_stub arginfo, the request-scoped allocator, custom object handlers, .phpt).
Working rules
- Keep simple CRUD simple; extract cross-model orchestration only when it has a concrete use.
- Validate and authorize at request boundaries; serialize through explicit resources and validate third-party responses.
- Preserve deployed migration history, queued payload compatibility, and concurrent writes.
- Verify cache compilation, queue execution, and HTTP behavior through their real entrypoints when those paths change.
Code Style
declare(strict_types=1)in every file- Happy path last -- guards and errors first, success at the end. Early returns, no
else. - Comments explain why, never what. Never comment tests. If code needs a "what" comment, rename or restructure.
- No single-letter variables --
$exceptionnot$e,$requestnot$r ?stringnotstring|null. Always specifyvoid. Import classnames, never inline FQN.- Widening one parameter to
?Tobliges auditing every call site that forwards the same value -- the sibling call still declaresstring, andnullthrows aTypeErrorthere even with nodeclare(strict_types=1), because coercive mode never coercesnullinto a scalar. Strictness is decided by the file the CALL is written in, never by the callee's file. Full mechanism in common-pitfalls.md. - Validation uses array notation
['required', 'email']for easier custom rule classes - PHPStan level 8+ (
phpstan analyse --level=8); aim for 9 on new projects.@phpstan-type/@phpstan-paramfor generic collection types. The missing-iterable-value-type check lands at level 6 (and every level above it), so any project at 8+ inherits it: use the generic form on every iterable --@return Collection<int, User>,@param array<int, MyObject>-- and array-shape notationarray{first: SomeClass, second: SomeClass}for fixed-key returns; a bareCollectionorarraywill not clear it.
Discipline
- Simplicity first -- every change as simple as possible, minimal code impact
- Only touch what's necessary -- no unrelated changes
- No hacky workarounds -- if a fix feels wrong, step back and implement the clean solution
- New abstraction requires 3+ usage sites; otherwise inline it
- No empty catch blocks -- log or rethrow, never swallow
- Verify before declaring done:
./vendor/bin/phpstan analyse --level=8 && ./vendor/bin/phpunitwith zero warnings - Checkpoint per stage, not only at the end:
migrate:statusafter a migration,route:list --path=<prefix>after routing changes,queue:work --onceafter adding a job,pint --testbefore the PR -- each catches its failure class while the change is small
References
- laravel-ecosystem.md -- Notifications, Task Scheduling, Custom Casts
- testing.md -- PHPUnit essentials, data providers, running tests
- feature-testing.md -- Auth, validation, API, console, DB assertions
- mocking-and-faking.md -- Facade fakes, action mocking, Mockery
- factories.md -- States, relationships, sequences, afterCreating hooks
- production-performance.md -- OPcache, JIT, preloading, deploy caches
- common-pitfalls.md -- event-layer bypasses, FK cascades, pivot writes, resource and request-shape traps
- pitfalls-deep.md -- afterCommit alternatives, observer desync, jsonb race, savepoints, validation-rule internals
Task-specific references
Read the relevant reference before implementing or reviewing the matching behavior:
- For PHP features, controller/action design, routing, resources, or external APIs: framework-patterns.md.
- For migrations, Eloquent writes, casts, queues, job payloads, or production startup: persistence-and-jobs.md.
- For PHPUnit work or changes affecting events, serialization, validation, or lifecycle behavior: testing-and-pitfalls.md.
Existing specialized references, when the corresponding topic applies: