Loading...
Loading...
Use when writing modern PHP 8.x code — enums, fibers, readonly properties, PSR standards, Composer, static analysis, SOLID patterns. Trigger conditions: PHP code authoring, enum design, readonly DTO creation, PSR-4 autoloading setup, PHPStan or Psalm configuration, PHP CS Fixer or Pint setup, Composer dependency management, SOLID principle application, type safety improvements, custom exception hierarchies, interface-driven design.
npx skill4agent add pixel-process-ug/superkit-agents php-specialistcomposer.jsonrequire.phpcomposer.jsonphpstan.neonpsalm.xmlpint.json.php-cs-fixer.phpSTOP — Do NOT write code without knowing the PHP version and autoloading strategy.
STOP — Do NOT implement without interfaces defined for key boundaries.
STOP — Do NOT skip strict_types declaration in any PHP file.
mixed--classmap-authoritative| Feature | Minimum Version | Use When |
|---|---|---|
| Constructor promotion | 8.0 | Any class with constructor parameters |
| Named arguments | 8.0 | Functions with 3+ params or boolean flags |
| Match expressions | 8.0 | Any switch statement (strict, returns value) |
| Union types | 8.0 | Parameter accepts multiple types |
| Backed enums | 8.1 | Any set of named constants with values |
| Readonly properties | 8.1 | Immutable DTOs, value objects |
| Fibers | 8.1 | Async frameworks (rarely used directly) |
| First-class callables | 8.1 | Functional composition, array_map/filter |
| Readonly classes | 8.2 | All-readonly DTOs (shorthand) |
| DNF types | 8.2 | Complex union + intersection combinations |
| Override attribute | 8.3 | Overriding parent methods (safety check) |
| Property hooks | 8.4 | Computed properties without separate methods |
// Backed enum with methods — replaces class constants and magic strings
enum OrderStatus: string
{
case Draft = 'draft';
case Pending = 'pending';
case Confirmed = 'confirmed';
case Shipped = 'shipped';
case Delivered = 'delivered';
case Cancelled = 'cancelled';
public function label(): string
{
return match ($this) {
self::Draft => 'Draft',
self::Pending => 'Pending Review',
self::Confirmed => 'Confirmed',
self::Shipped => 'Shipped',
self::Delivered => 'Delivered',
self::Cancelled => 'Cancelled',
};
}
public function isFinal(): bool
{
return in_array($this, [self::Delivered, self::Cancelled], true);
}
/** @return list<self> */
public static function active(): array
{
return array_filter(self::cases(), fn (self $s) => ! $s->isFinal());
}
}// Readonly class — all properties are implicitly readonly
readonly class Money
{
public function __construct(
public int $amount,
public string $currency,
) {}
public function add(self $other): self
{
if ($this->currency !== $other->currency) {
throw new CurrencyMismatchException($this->currency, $other->currency);
}
return new self($this->amount + $other->amount, $this->currency);
}
public function isPositive(): bool
{
return $this->amount > 0;
}
}class CreateUserAction
{
public function __construct(
private readonly UserRepository $users,
private readonly Hasher $hasher,
private readonly EventDispatcher $events,
) {}
public function execute(CreateUserData $data): User
{
$user = $this->users->create([
'name' => $data->name,
'email' => $data->email,
'password' => $this->hasher->make($data->password),
]);
$this->events->dispatch(new UserCreated($user));
return $user;
}
}// Improves readability for functions with many parameters or boolean flags
$user = User::create(
name: $request->name,
email: $request->email,
isAdmin: false,
sendWelcomeEmail: true,
);
// Particularly valuable with optional parameters
$response = Http::timeout(seconds: 30)
->retry(times: 3, sleepMilliseconds: 500, throw: true)
->get($url);// match is strict (===), exhaustive, and returns a value
$discount = match (true) {
$total >= 10000 => 0.15,
$total >= 5000 => 0.10,
$total >= 1000 => 0.05,
default => 0.00,
};
// Replaces switch with no fall-through risk
$handler = match ($event::class) {
OrderPlaced::class => new HandleOrderPlaced(),
PaymentFailed::class => new HandlePaymentFailed(),
default => throw new UnhandledEventException($event),
};// Union type — accepts either type
function findUser(int|string $identifier): User
{
return is_int($identifier)
? User::findOrFail($identifier)
: User::where('email', $identifier)->firstOrFail();
}
// Intersection type — must satisfy all interfaces
function processLoggableEntity(Loggable&Serializable $entity): void
{
$entity->log();
$data = $entity->serialize();
}
// DNF types (PHP 8.2) — combine union and intersection
function handle((Renderable&Countable)|string $content): string
{
if (is_string($content)) {
return $content;
}
return $content->render();
}// Create closures from named functions
$slugify = Str::slug(...);
$titles = array_map($slugify, $names);
// Method references
$validator = Validator::make(...);
// Useful for pipeline / collection patterns
$activeUsers = collect($users)
->filter(UserPolicy::isActive(...))
->map(UserTransformer::toArray(...))
->values();// Fibers enable cooperative multitasking — foundation for async frameworks
$fiber = new Fiber(function (): void {
$value = Fiber::suspend('paused');
echo "Resumed with: {$value}";
});
$result = $fiber->start(); // Returns 'paused'
$fiber->resume('hello world'); // Prints: "Resumed with: hello world"
// Practical use: async HTTP client internals, event loops (Revolt, ReactPHP)
// Application developers rarely use Fiber directly — frameworks abstract it| PSR | Name | Relevance |
|---|---|---|
| PSR-1 | Basic Coding Standard | Baseline: |
| PSR-4 | Autoloading | Map namespaces to directories in |
| PSR-7 | HTTP Message Interfaces | Immutable request/response objects for middleware pipelines |
| PSR-11 | Container Interface | Dependency injection container interoperability |
| PSR-12 | Extended Coding Style | Supersedes PSR-2: formatting, spacing, declarations |
| PSR-15 | HTTP Server Middleware | |
| PSR-17 | HTTP Factories | Create PSR-7 objects (RequestFactory, ResponseFactory) |
| PSR-18 | HTTP Client | |
{
"autoload": {
"psr-4": {
"App\\": "app/",
"Domain\\": "src/Domain/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
}
}App\Http\Controllers\UserControllerapp/Http/Controllers/UserController.php| Command | Purpose |
|---|---|
| Add production dependency |
| Add development dependency |
| Preview what would change |
| Show why a package is installed |
| Check for known security vulnerabilities |
| Update version constraints to installed versions |
| Validate |
composer.lock^"laravel/framework": "^12.0"require-devcomposer auditcomposer dump-autoload --classmap-authoritative| Level | What It Checks |
|---|---|
| 0 | Basic: undefined variables, unknown classes, wrong function calls |
| 1 | + possibly undefined variables, unknown methods on |
| 2 | + unknown methods on all expressions (not just |
| 3 | + return types verified |
| 4 | + dead code, always-true/false conditions |
| 5 | + argument types of function calls |
| 6 | + missing typehints reported |
| 7 | + union types checked exhaustively |
| 8 | + nullable types checked strictly |
| 9 | + |
# phpstan.neon
parameters:
level: 9
paths:
- app
- src
excludePaths:
- app/Console/Kernel.php
ignoreErrors: []
checkMissingIterableValueType: true
checkGenericClassInNonGenericObjectType: true
includes:
- vendor/larastan/larastan/extension.neon # Laravel-specific rules// .php-cs-fixer.php — for non-Laravel projects
return (new PhpCsFixer\Config())
->setRules([
'@PER-CS' => true,
'strict_types' => true,
'declare_strict_types' => true,
'ordered_imports' => ['sort_algorithm' => 'alpha'],
'no_unused_imports' => true,
'trailing_comma_in_multiline' => true,
])
->setFinder(
PhpCsFixer\Finder::create()->in([__DIR__ . '/src', __DIR__ . '/tests'])
);pint.json| Principle | Guideline | PHP Mechanism |
|---|---|---|
| S — Single Responsibility | One reason to change per class | Action classes, small services |
| O — Open/Closed | Extend behavior without modifying source | Interfaces, strategy pattern, enums |
| L — Liskov Substitution | Subtypes must be substitutable for base types | Covariant returns, contravariant params |
| I — Interface Segregation | Clients depend only on methods they use | Small, focused interfaces |
| D — Dependency Inversion | Depend on abstractions, not concretions | Constructor injection, interface bindings |
// Contract (abstraction)
interface PaymentGateway
{
public function charge(Money $amount, PaymentMethod $method): PaymentResult;
}
// Implementation (concretion) — can be swapped without changing consumers
final class StripeGateway implements PaymentGateway
{
public function __construct(private readonly StripeClient $client) {}
public function charge(Money $amount, PaymentMethod $method): PaymentResult
{
// Stripe-specific logic
}
}
// Consumer depends on abstraction only
final class ProcessPaymentAction
{
public function __construct(private readonly PaymentGateway $gateway) {}
public function execute(Order $order): PaymentResult
{
return $this->gateway->charge($order->total, $order->paymentMethod);
}
}// Base domain exception
abstract class DomainException extends \RuntimeException {}
// Specific exceptions with factory methods
final class InsufficientFundsException extends DomainException
{
public static function forAccount(Account $account, Money $required): self
{
return new self(sprintf(
'Account %s has %d %s but %d %s is required.',
$account->id,
$account->balance->amount,
$account->balance->currency,
$required->amount,
$required->currency,
));
}
}/** @template T */
readonly class Result
{
/** @param T|null $value */
private function __construct(
public bool $ok,
public mixed $value = null,
public ?string $error = null,
) {}
/** @param T $value */
public static function success(mixed $value): self
{
return new self(ok: true, value: $value);
}
public static function failure(string $error): self
{
return new self(ok: false, error: $error);
}
}
// Usage — caller must handle both paths
$result = $action->execute($data);
if (! $result->ok) {
return response()->json(['error' => $result->error], 422);
}// Prevent accidental mixing of IDs from different entities
readonly class UserId
{
public function __construct(public int $value) {}
public function equals(self $other): bool
{
return $this->value === $other->value;
}
}
readonly class OrderId
{
public function __construct(public int $value) {}
}
// Compiler prevents: processOrder(new UserId(1)) when OrderId is expected
function processOrder(OrderId $orderId): void { /* ... */ }/**
* @template T
* @implements \IteratorAggregate<int, T>
*/
final class TypedCollection implements \IteratorAggregate, \Countable
{
/** @param list<T> $items */
public function __construct(private array $items = []) {}
/** @param T $item */
public function add(mixed $item): void
{
$this->items[] = $item;
}
/** @return \ArrayIterator<int, T> */
public function getIterator(): \ArrayIterator
{
return new \ArrayIterator($this->items);
}
public function count(): int
{
return count($this->items);
}
}| Anti-Pattern | Why It Fails | What To Do Instead |
|---|---|---|
Using | Holes in type safety net | Narrow with union types or generics |
| Stringly-typed code | Runtime errors from typos | Use backed enums for named constants |
| God classes (many responsibilities) | Untestable, high coupling | Split into Action classes |
| Suppressing static analysis | Hides real bugs | Fix the issue, add |
Missing | Silent type coercion bugs | Add to every PHP file |
| Array-shaped domain data | No IDE support, no type safety | Use readonly DTOs or value objects |
Service locator ( | Hidden dependencies, untestable | Constructor injection |
Catching | Swallows unexpected errors | Catch specific exception types |
| Mutable value objects | Shared state bugs | Use |
Ignoring | Known vulnerabilities in production | Run in CI, treat as build failure |
| Deep inheritance (3+ levels) | Fragile base class problem | Prefer composition and interfaces |
Classes not marked | Unintended extension | Default to |
declare(strict_types=1)mixedapp()mcp__context7__resolve-library-idmcp__context7__query-docsphpcomposer| Skill | How It Connects |
|---|---|
| PHP 8.x features power Eloquent casts, enums, readonly DTOs, and typed collections |
| SOLID architecture, interface-driven design, error handling patterns |
| PHPUnit/Pest testing with strong type assertions |
| SOLID, DRY, code smell detection at the PHP level |
| Input validation, type coercion risks, dependency vulnerabilities |
| AI-generated PHP code quality via guidelines and MCP tools |
declare(strict_types=1)