Skip to content
Back to skills

Dotkernel Psr Standards

ASecurity

Use when writing or reviewing any PHP in a Dotkernel project — which PSR applies, how to type handlers and middleware (PSR-7/PSR-15), how to obtain services (PSR-11, no service locator), logging (PSR-3), caching (PSR-6/PSR-16), building responses (PSR-17), autoloading (PSR-4), and coding style (PSR-1/PSR-12 via the Laminas Coding Standard). Also use when phpcs or phpstan complains, or when tempted to type-hint a concrete framework class instead of an interface.

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
devopsgophpgit

Security analysis

A100/100

Scanned November 7, 2026

npx -y skills add dotkernel/dotboost --skill dotkernel-psr-standards --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Dotkernel Psr Standards?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Dotkernel Psr Standards
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/dotkernel-dotkernel-psr-standards/badge)](https://www.skillsdirectory.com/skills/dotkernel-dotkernel-psr-standards)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: dotkernel-psr-standards
description: >
  Use when writing or reviewing any PHP in a Dotkernel project — which PSR applies, how to
  type handlers and middleware (PSR-7/PSR-15), how to obtain services (PSR-11, no service
  locator), logging (PSR-3), caching (PSR-6/PSR-16), building responses (PSR-17), autoloading
  (PSR-4), and coding style (PSR-1/PSR-12 via the Laminas Coding Standard). Also use when phpcs
  or phpstan complains, or when tempted to type-hint a concrete framework class instead of an
  interface.
---

# PSR compliance

```text
Applies to
  Projects:  - all
  Packages:  - all
```

Program against PSR interfaces. Type-hint the interface, inject the implementation. This applies
identically in every Dotkernel project.

## PSR-4 — autoloading

Namespace mirrors directory, case-sensitive, one class per file, file named after the class.

```text
<App>\User\Handler\User\GetUserResourceHandler → src/User/src/Handler/User/GetUserResourceHandler.php
Core\User\Entity\User                          → src/Core/src/User/src/Entity/User.php
```

Mappings live in `composer.json` under `autoload.psr-4` (and `autoload-dev` for tests). Adding a
mapping edits `composer.json` → needs user approval, then `composer dump-autoload`.

## PSR-7 — HTTP messages

Messages are **immutable**; `with*()` returns a new instance.

```php
$response = $response->withHeader('sunset', $date);   // correct
$response->withHeader('sunset', $date);               // silently does nothing
```

Type-hint `Psr\Http\Message\ServerRequestInterface` / `ResponseInterface`, never a concrete
Diactoros class. Read the body once via `(array) $request->getParsedBody()` — the body-params
middleware has already parsed it; re-reading the raw stream needs a `rewind()`.

## PSR-15 — handlers and middleware

```php
final class XMiddleware implements MiddlewareInterface
{
    public function process(
        ServerRequestInterface $request,
        RequestHandlerInterface $handler
    ): ResponseInterface {
        // before…
        $response = $handler->handle($request);
        // after…
        return $response;
    }
}
```

- Handlers implement `RequestHandlerInterface`; extend the project's abstract handler for its
  response helpers.
- One responsibility per middleware. Order is declared in `config/pipeline.php`, and changing it
  affects every request — justify any edit.
- Middleware that only enriches the request passes data via `$request->withAttribute(...)`.

## PSR-11 — container

Get services by constructor injection, resolved by the container:

```php
#[Inject(BookServiceInterface::class, CreateBookInputFilter::class)]
public function __construct(
    protected BookServiceInterface $bookService,
    protected CreateBookInputFilter $inputFilter,
) {
}
```

`dot-dependency-injection` reads `#[Inject]`; register the class with
`AttributedServiceFactory::class` in the module `ConfigProvider`. The stock Light does not install
`dot-dependency-injection` and uses hand-written factories instead; match that there. `#[Inject]` can also pull
config values by dotted key.

**Do not inject `ContainerInterface` and call `get()` inside a class** — that is the
service-locator anti-pattern. The only legitimate `$container->get()` sites are factories and
delegators, where you also declare `ContainerExceptionInterface` and
`NotFoundExceptionInterface`.

## PSR-3 — logging

Type-hint `Psr\Log\LoggerInterface`. Use levels deliberately: `error` for actionable failures,
`warning` for recoverable, `info` for lifecycle. Pass context as an array rather than
concatenating. **Never log credentials, tokens, password hashes, auth request bodies, or PII
beyond an identifier.**

## PSR-6 / PSR-16 — caching

Type-hint `Psr\Cache\CacheItemPoolInterface` (PSR-6) or `Psr\SimpleCache\CacheInterface`
(PSR-16). Always set an explicit TTL, never cache per-user-authorized data under a global key,
and provide an invalidation path when the underlying entity changes.

## PSR-17 — HTTP factories

Type-hint `ResponseFactoryInterface` / `StreamFactoryInterface` rather than instantiating a
concrete response — except inside the abstract handler, which already wraps the project's
response factory. Prefer its helpers over building responses yourself.

## PSR-1 / PSR-12 — style, enforced by the Laminas Coding Standard

`phpcs.xml` applies `LaminasCodingStandard`, a PSR-12 superset. It will fail you on:

- `declare(strict_types=1);` after `<?php` and one blank line.
- 4-space indent, LF endings, one trailing newline, no trailing whitespace, ~120-char lines.
- Sorted `use` statements: class imports first, blank line, then `use function` / `use const` —
  global functions **must** be imported, not called as `\count()`.
- Short array syntax; short closures where applicable.
- `! $foo`, not `!$foo`.
- Aligned `=>` in array literals and aligned `=` in consecutive assignments.
- One class per file; `final` where the class is not designed for extension.
- Type declarations on every parameter, property and return; nullable as `?Type`.

Run the repo's `cs-fix` for the mechanical parts. Never add sniff exclusions to `phpcs.xml` or
sprinkle `// phpcs:ignore` to get green.

## phpstan expectations

Dotkernel projects run a high level (commonly 8) with the Doctrine and PHPUnit extensions:

- No `mixed` leaking — cast or narrow at the boundary.
- Array shapes documented: `@param array<non-empty-string, mixed>`, `@return X[]`, and
  `@phpstan-type` / `@phpstan-import-type` for shapes reused across classes.
- Generics on collections: `@var Collection<int, UserRole>`.
- `instanceof` checks before using a nullable repository result — that is how the services here
  raise not-found errors.
- Do not silence with `@phpstan-ignore-*`. Fix the type.

## Framework-specific but non-negotiable

- Status codes via `Fig\Http\Message\StatusCodeInterface::STATUS_*`, not integer literals.
- Throw the project's typed exceptions with their static `::create(...)` constructors;
  messages from the shared message catalogue.
- Where the project provides a resource-resolution attribute on `handle()`, use it instead of
  fetching the entity manually.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.