DEBI PRAHARADIKA
← Back to Blog Index
Development2025-06-0910 min read

Best Practices of Modern PHP

Guide for writing efficient, secure, and maintainable PHP code according to modern standards.

PHP ecosystem has changed a lot, far beyond not structural procedural era. With the release of PHP 8.x, this language has become OOP language in enterprise level that very strict, fast, and elegant.

This article will summarize best practices for PHP modern in 4 chapter and is wrote special for facilitating large scale software development that secure, high performance, and maintainable.


Foundation & Code Architecture

This section are how we build the foundation of application. The good code starts from solid architectural design, neat folder structure, and strict type system implementation.

Layered Architecture Blueprint Blueprint 1: Separation of responsibility layers (Layered Architecture) from Controller, Service, to Repository.

Environment & Modern Version

  • Use new stable version of PHP (minimal PHP 8.1+, ideally 8.2/8.3). You can take advantage of JIT compiler, enum, readonly properties, and fibers.
  • Use vlucas/phpdotenv for environment configuration. Never hardcode credentials. Store environment settings in a .env file.
  • Activate error_reporting(E_ALL) in development, and turn off display_errors in production (log to file, don't display to user).

Wrting Standart & Type System

  • Follow PSR (PHP Standards Recommendations): Use PSR-1 (Basic), PSR-12 (Coding Style), PSR-4 (Autoloading), and PSR-7 (HTTP Message).
  • Usedeclare(strict_types=1); in first line of file for strict type data.
  • Absolute Type hinting in all parameter and return type.
    public function calculateScore(int $score, string $comment): array
    
  • Use Enums & Readonly (PHP 8.1+): Use readonly properties for immutable data and enum for fixed value representation instead of class constant.
    enum SurveyType: string {
        case NPS = 'nps';
        case CSAT = 'csat';
        case CES = 'ces';
    }
    

OOP Modeling & System Design

  • SOLID Principles: Focus on Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion.
  • Layered Architecture & Repository Pattern: Separate Controller, Service, Repository, and Model. Controller must not contain business logic (Service Layer). Abstract data access with Repository.
  • Dependency Injection: Don't create objects (new Object()) hardcode in class. Always inject dependencies via constructor.
    class SurveyService {
        public function __construct(
            private readonly SurveyRepositoryInterface $repository
        ) {}
    }
    

Data Management & Security

Performance and security can not added later, both must be main consideration (by design) since create database architecture until endpoint. Here some best practice that need to applied in data management and security:

Data Security & Optimization Blueprint Blueprint 2: Optimize cycle data using OPcache and Redis Cache Layer.

Optimal Database Access

  • Absolute Prepared Statements: Prevent SQL Injection by always using parameter binding, avoid manual string query concatenation.
    $stmt = $pdo->prepare('SELECT * FROM surveys WHERE id = :id');
    $stmt->execute(['id' => $id]);
    
  • Avoid N+1 Query Problem: When using ORM (such as Eloquent or Doctrine), use eager loading (such as with()) when fetching relationship data.
  • Use Database Transactions: Ensure multi-step operations that are related to each other are wrapped in a transaction block so that data consistency is maintained.

Performa Caching & Asynchronous

  • Activate OPCache in Production: Very crucial. Save PHP bytecode in memory to avoid re-parsing on every request.
  • Caching Layer Implementation: Use Redis or Memcached for the data often accessed but rarely changed.
  • Message Queue: Use Message Broker for slow operations (sending email, PDF report generation) so it doesn't block HTTP user response.

Application Security Standart

  • Filter In, Escape Out: Validate all user input and clean (escape) output according to context before rendering (e.g. with htmlspecialchars() for HTML).
  • Credential Management: Hash your secret value (password, API key) with bcrypt or argon2 algorithm using password_hash() function.
  • Rate Limiting & Security Header: Protect public / sensitive endpoint with request boundary, for to HTTPS connection, and set header such as CSP and HSTS.

Reliability & Testing

The reliable system is never fail without user known (silent failure) and has safety net before code goes to production server. Here are best practice that need to be implemented:

Reliability & Testing Blueprint Blueprint 3: Visualization of code testing integration and centralized log recording.

Exception Handling

  • Forbidden Return Boolean/Null for Error: Use Exception (Throw) instead of throwing false if function fails to complete essential task.
  • Custom Exceptions: Create domain-specific error class so it easy to catch (example: SurveyNotFoundException).
  • Global Error Handler: Register top-level exception handler with Sentry or Bugsnag to track stack trace in production. Don't log sensitive data (password, PII) in log file!

TDD & Automatic Test

  • Unit Test (PHPUnit): Test business logic in isolation. Use mocking technique to simulate database connection or external API.
  • Pola Arrange-Act-Assert: Use this pattern so that test case is structured clearly and explicitly.
    public function test_nps_score_is_calculated_correctly(): void
    {
        // Arrange
        $responses = [9, 10, 6, 8];
        
        // Act
        $result = $this->npsCalculator->calculate($responses);
        
        // Assert
        $this->assertEquals(25, $result);
    }
    

Ecosystem & Lifecycle

Developing a modern ecosystem also means involving industry-standart tools, start from third-party dependencies to release strategy. Here are best practice that need to be implemented:

CI/CD Pipeline Blueprint Blueprint 4: The flow of continuous delivery (CI/CD) from local repository to production environment.

Dependency Management (Composer)

  • Commit composer.lock file: This file ensures that all development team (including CI server) installs the exact same version of third-party libraries.
  • Separate Dependencies: Use --dev when installing testing tools so that the size of vendor in production stays lean and safe from debugging feature exploitation.
  • Routine Audit: Get used to executing the composer audit command to verify the existence of published vulnerabilities.

Modern API Design

  • Consistent Response Format: All API outputs (especially JSON) must have a standard wrapper schema for both success and failure cases.
    {
        "success": true,
        "data": {...},
        "message": null
    }
    
  • Versioning: Add version parameter to URI (/api/v1/...) since day one for ensuring backward compatibility in the future.

Deployment & CI/CD Pipeline

  • Automated Pipeline: Configure Bitbucket Pipelines, GitHub Actions, or GitLab CI to execute test suite automatically before build.
  • Zero-Downtime Deployment: Transition to new version release using symlink (such as Envoyer pattern) or Blue-Green Deployment to avoid network traffic rejection when updates are in progress.

We not only write PHP code that works, but also create a system that is resistant to time (future-proof) and can be read by our fellow engineers.