phpnomad / phpstan-rules
PHPStan rules for PHPNomad framework conventions
Package info
github.com/phpnomad/phpstan-rules
Type:phpstan-extension
pkg:composer/phpnomad/phpstan-rules
v1.0.0
2026-04-14 20:53 UTC
Requires
- php: ^8.2
- phpstan/phpstan: ^2.0
Requires (Dev)
- phpstan/extension-installer: ^1.0
- phpunit/phpunit: ^10.0 || ^11.0
README
Custom PHPStan rules that enforce PHPNomad framework conventions.
These rules catch architectural anti-patterns at static analysis time, including models with inline serialization, service locator usage, raw SQL, singleton abuse, missing interface implementations, database scope violations, cache misuse, and more.
Installation
composer require --dev phpnomad/phpstan-rules
If you use phpstan/extension-installer, the rules activate automatically.
Otherwise, add to your phpstan.neon:
includes: - vendor/phpnomad/phpstan-rules/extension.neon
Rules
Models
| Identifier | Description |
|---|---|
phpnomad.model.notFinal |
Models implementing DataModel must be declared final. |
phpnomad.model.setter |
Models must not have setter methods (set*). Models are immutable. |
phpnomad.model.arrayMethod |
Models must not have toArray() or fromArray(). Use a separate adapter class. |
Adapters
| Identifier | Description |
|---|---|
phpnomad.adapter.notImplementing |
Classes named *Adapter or in Adapters namespaces must implement ModelAdapter. Exempts MutationAdapter implementations. |
Events
| Identifier | Description |
|---|---|
phpnomad.event.notImplementing |
Classes in Events namespaces or named *Event must implement PHPNomad\Events\Interfaces\Event. |
phpnomad.event.notFinal |
Event classes should be declared final. |
phpnomad.event.notReadonly |
Event properties and promoted constructor parameters should be readonly. |
phpnomad.listener.notImplementing |
Classes in Listeners namespaces or named *Listener must implement CanHandle. |
DI / Container
| Identifier | Description |
|---|---|
phpnomad.di.serviceLocator |
InstanceProvider::get() must not be called inside business classes. Use constructor injection. Calls inside initializers and bootstrappers are allowed. |
phpnomad.di.missingTrait |
Classes implementing CanSetContainer must use the HasSettableContainer trait. |
Initializers
| Identifier | Description |
|---|---|
phpnomad.initializer.useInterface |
Initializers should use Has* interfaces (e.g., HasListeners, HasTaskHandlers) instead of manually calling registration methods like EventStrategy::attach(). |
Facades
| Identifier | Description |
|---|---|
phpnomad.facade.notExtending |
Classes in Facades namespaces or named *Facade must extend PHPNomad\Facade\Abstracts\Facade. |
phpnomad.facade.noAbstractInstance |
Concrete Facade subclasses must implement abstractInstance(). |
Database
| Identifier | Description |
|---|---|
phpnomad.database.rawSql |
Raw SQL strings (SELECT ... FROM, INSERT INTO, etc.) must not appear in code. Use PHPNomad datastore patterns. |
phpnomad.database.scope |
QueryBuilder, ClauseBuilder, and QueryStrategy must only be used inside Datastore classes. |
phpnomad.database.concreteTableHint |
Constructor parameters should type-hint the Datastore interface, not concrete Table subclasses. |
Controllers
| Identifier | Description |
|---|---|
phpnomad.controller.noStatus |
Controller getResponse() must explicitly set an HTTP status code via setStatus() or setError(). |
Console
| Identifier | Description |
|---|---|
phpnomad.command.notImplementing |
Classes in Commands namespaces or named *Command must implement PHPNomad\Console\Interfaces\Command. |
phpnomad.command.handleReturnType |
Command handle() methods must declare an int return type. |
Tasks
| Identifier | Description |
|---|---|
phpnomad.task.notImplementing |
Classes in Tasks namespaces or named *Task must implement PHPNomad\Tasks\Interfaces\Task. |
phpnomad.taskHandler.notImplementing |
Task handler classes must implement PHPNomad\Tasks\Interfaces\CanHandleTask. |
phpnomad.task.directHandle |
Task handlers must be dispatched via TaskStrategy::dispatch(), not by calling handle() directly. |
Cache
| Identifier | Description |
|---|---|
phpnomad.cache.directStrategy |
Business classes should not inject CacheStrategy directly. Use CacheableService or a Facade. |
phpnomad.cache.uncaughtException |
CacheStrategy::get() throws CachedItemNotFoundException. Ensure the call is wrapped in a try/catch. |
phpnomad.cache.hardcodedTtl |
Do not hardcode TTL values as integer literals. Use a CachePolicy or configuration constant. |
phpnomad.cache.stringConcatKey |
Use the HasCacheKey interface for cache key generation instead of string concatenation. |
General
| Identifier | Description |
|---|---|
phpnomad.general.singletonInBusiness |
::instance() singleton calls must not be used outside of Facade classes. Use dependency injection. |
phpnomad.general.globalKeyword |
The global keyword must not be used. Use dependency injection. |
Suppressing Rules
Use PHPStan's built-in ignore syntax:
// @phpstan-ignore phpnomad.model.notFinal class User implements DataModel { // ... }
Or suppress in phpstan.neon via baseline:
vendor/bin/phpstan --generate-baseline
Requirements
- PHP 8.2+
- PHPStan 2.0+
License
MIT