php-best-practices

51 rules for writing clean, type-safe, modern PHP 8.x code aligned with PSR standards and SOLID principles. Covers 7 rule categories: type system, modern PHP features (8.0–8.5), PSR standards, SOLID principles, error handling, performance, and security Version-aware guidance that detects PHP version from composer.json and runtime, recommending only features available in the target version Organized by priority: type safety and security are critical; modern features and SOLID principles are high-impact Includes quick reference patterns for constructor promotion, enums, match expressions, nullsafe operators, property hooks, and pipe operators

INSTALLATION
npx skills add https://github.com/asyrafhussin/agent-skills --skill php-best-practices
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

$2a

Feature Availability by Version

Feature

Version

Rule Prefix

Union types, match, nullsafe, named args, constructor promotion, attributes

8.0+

type-, modern-

Enums, readonly properties, intersection types, first-class callables, never, fibers

8.1+

modern-

Readonly classes, DNF types, true/false/null standalone types

8.2+

modern-

Typed class constants, #[\Override], json_validate()

8.3+

modern-

Property hooks, asymmetric visibility, #[\Deprecated], new without parens

8.4+

modern-

Pipe operator

>

8.5+

Only suggest features available in the detected version. If the user asks about upgrading or newer features, mention what becomes available at each version.

When to Apply

Reference these guidelines when:

  • Writing or reviewing PHP code
  • Implementing classes and interfaces
  • Using PHP 8.x modern features
  • Ensuring type safety
  • Following PSR standards
  • Applying design patterns

Rule Categories by Priority

Priority

Category

Impact

Prefix

Rules

1

Type System

CRITICAL

type-

9

2

Modern PHP Features

CRITICAL

modern-

16

3

PSR Standards

HIGH

psr-

6

4

SOLID Principles

HIGH

solid-

5

5

Error Handling

HIGH

error-

5

6

Performance

MEDIUM

perf-

5

7

Security

CRITICAL

sec-

5

Quick Reference

1. Type System (CRITICAL) — 9 rules

  • type-strict-mode - Declare strict types in every file
  • type-return-types - Always declare return types
  • type-parameter-types - Type all parameters
  • type-property-types - Type class properties
  • type-union-types - Use union types effectively
  • type-intersection-types - Use intersection types
  • type-nullable-types - Handle nullable types properly
  • type-void-never - Use void/never for appropriate return types
  • type-mixed-avoid - Avoid mixed type when possible

2. Modern PHP Features (CRITICAL) — 16 rules

8.0+:

  • modern-constructor-promotion - Constructor property promotion
  • modern-match-expression - Match over switch
  • modern-named-arguments - Named arguments for clarity
  • modern-nullsafe-operator - Nullsafe operator (?->)
  • modern-attributes - Attributes for metadata

8.1+:

  • modern-enums - Enums instead of constants
  • modern-enums-methods - Enums with methods and interfaces
  • modern-readonly-properties - Readonly for immutable data
  • modern-first-class-callables - First-class callable syntax
  • modern-arrow-functions - Arrow functions (7.4+, pairs well with 8.1 features)

8.2+:

  • modern-readonly-classes - Readonly classes

8.3+:

  • modern-typed-constants - Typed class constants (const string NAME = 'foo')
  • modern-override-attribute - #[\Override] to catch parent method typos

8.4+:

  • modern-property-hooks - Property hooks replacing getters/setters
  • modern-asymmetric-visibility - public private(set) for controlled access

8.5+:

  • modern-pipe-operator - Pipe operator (|>) for functional chaining

3. PSR Standards (HIGH) — 6 rules

  • psr-4-autoloading - Follow PSR-4 autoloading
  • psr-12-coding-style - Follow PSR-12 coding style
  • psr-naming-classes - Class naming conventions
  • psr-naming-methods - Method naming conventions
  • psr-file-structure - One class per file
  • psr-namespace-usage - Proper namespace usage

4. SOLID Principles (HIGH) — 5 rules

  • solid-srp - Single Responsibility: one reason to change
  • solid-ocp - Open/Closed: extend, don't modify
  • solid-lsp - Liskov Substitution: subtypes must be substitutable
  • solid-isp - Interface Segregation: small, focused interfaces
  • solid-dip - Dependency Inversion: depend on abstractions

5. Error Handling (HIGH) — 5 rules

  • error-custom-exceptions - Create specific exceptions for different errors
  • error-exception-hierarchy - Organize exceptions into meaningful hierarchy
  • error-try-catch-specific - Catch specific exceptions, not generic \Exception
  • error-finally-cleanup - Use finally for guaranteed resource cleanup
  • error-never-suppress - Never use @ error suppression operator

6. Performance (MEDIUM) — 5 rules

  • perf-avoid-globals - Avoid global variables, use dependency injection
  • perf-lazy-loading - Defer expensive operations until needed
  • perf-array-functions - Use native array functions over manual loops
  • perf-string-functions - Use native string functions over regex
  • perf-generators - Use generators for large datasets

7. Security (CRITICAL) — 5 rules

  • sec-input-validation - Validate and sanitize all external input
  • sec-output-escaping - Escape output based on context (HTML, JS, URL)
  • sec-password-hashing - Use password_hash/verify, never MD5/SHA1
  • sec-sql-prepared - Use prepared statements for all SQL queries
  • sec-file-uploads - Validate file type, size, name; store outside web root

Essential Guidelines

For detailed examples and explanations, see the rule files:

Key Patterns (Quick Reference)

<?php

declare(strict_types=1);

// 8.0+ Constructor promotion + readonly (8.1+)

class User

{

    public function __construct(

        public readonly string $id,

        private string $email,

    ) {}

}

// 8.1+ Enums with methods

enum Status: string

{

    case Active = 'active';

    case Inactive = 'inactive';

    public function label(): string

    {

        return match($this) {

            self::Active => 'Active',

            self::Inactive => 'Inactive',

        };

    }

}

// 8.0+ Match expression

$result = match($status) {

    'pending' => 'Waiting',

    'active' => 'Running',

    default => 'Unknown',

};

// 8.0+ Nullsafe operator

$country = $user?->getAddress()?->getCountry();

// 8.3+ Typed class constants + #[\Override]

class PaymentService extends BaseService

{

    public const string GATEWAY = 'stripe';

    #[\Override]

    public function process(): void { /* ... */ }

}

// 8.4+ Property hooks + asymmetric visibility

class Product

{

    public string $name { set => trim($value); }

    public private(set) float $price;

}

// 8.5+ Pipe operator

$result = $input

    |> trim(...)

    |> strtolower(...)

    |> htmlspecialchars(...);

Output Format

When auditing code, output findings in this format:

file:line - [category] Description of issue

Example:

src/Services/UserService.php:15 - [type] Missing return type declaration

src/Models/Order.php:42 - [modern] Use match expression instead of switch

src/Controllers/ApiController.php:28 - [solid] Class has multiple responsibilities

How to Use

Read individual rule files for detailed explanations:

rules/modern-constructor-promotion.md

rules/type-strict-mode.md

rules/solid-srp.md
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card