PHP
What this covers
A reference guide to PHP development, from the competency checklist expected of a senior PHP developer through coding standards, testing, the Laravel/Symfony frameworks, security hardening, performance tuning, and Docker-based deployment.
When to use it
- Checking what a senior PHP developer role is expected to cover, as a skills checklist
- Applying PSR coding standards or structuring try/catch/finally error handling
- Writing PHPUnit unit tests or Behat BDD feature files for a PHP codebase
- Scaffolding or reviewing a Laravel controller or a Symfony controller/service
- Hardening PHP code against SQL injection, XSS, or CSRF
- Tuning PHP-FPM pool settings, OPcache, or APCu, or optimizing database queries
- Containerizing a PHP application with Docker and wiring a CI/CD pipeline
PHP Developer Levels Overview
Senior PHP Developer
Core Competencies:
- PHP runtime, I/O, error handling, PHP-FPM, garbage collection, performance optimization
- Frameworks: Symfony, Laravel
- Data structures and algorithms (Graphs, Trees, Arrays, Collections)
- Authentication & Authorization: JWT, OAuth
- Non-functional requirements (security, performance, scalability, resilience)
Development Tools:
- IDE: PHPStorm
- DevTools: Postman, DataGrip
Service Communication:
- Messaging: RabbitMQ, Pub/Sub, SNS/SQS, Kafka
- REST(ful), WebSocket, RPC/gRPC, HATEOAS
- Message exchange patterns
Caching Strategies:
- Cache aside, read through, write through
- Cache replacement policies: RR, LIFO, LRU
- Cache coherence
Database Skills:
- RDBMS: MySQL, PostgreSQL
- NoSQL: MongoDB, Redis, Elasticsearch
- Performance: ANALYZE, EXPLAIN, VACUUM, Query Planner, Slow Queries, Indexes
- Normalization/Denormalization
Security:
- SQL injections, CSRF, XSS, CORS
Cloud Computing:
- AWS, GCP (VMs, Containers EKS/ECS, file storage, cloud functions, messaging, application tools)
Testing & Quality:
- Testing: acceptance criteria, verification, automated tests (unit, integration, e2e)
- Quality tools: PHPUnit, Behat, PHPSpec
- Quality Gates: SonarQube, PHP Quality Tools (phpmd, phpcs, phpstan, psalm)
- Code review, monitoring, logging, technical debt management
Computer Science:
- OOP, SOLID, GRASP, DRY, KISS, YAGNI, DDD
- Algorithms & Data structures
- GoF Patterns, Enterprise Application Patterns, Enterprise Integration Patterns
- Distributed Systems Patterns, 12 Factor methodology
Software Architecture:
- Monolith, Micro-services/SOA, Event Driven, Hexagonal, Event sourcing, CQRS
- Architecture diagrams: UML, C4, 4+1, Flow, Actors
- API Documentation: OpenAPI/Swagger
Containerization & Deployment:
- Docker, Kubernetes
- CI/CD: GitLab, Jenkins, GitHub Actions
- Branching strategies: git-flow
- Deployment strategies: Blue-green, Canary
- Multi-region applications
Server Administration:
- *nix, bash
- Networking: TCP/IP, UDP, HTTP
- API gateway, SSL termination, Load Balancing (L4, L7)
- Proxies, Service Mesh
PHP Best Practices
Code Quality
PSR Standards:
<?php
// PSR-1: Basic Coding Standard
// PSR-2: Coding Style Guide
// PSR-4: Autoloading Standard
namespace Vendor\Package;
use FooInterface;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;
class Foo extends Bar implements FooInterface
{
public function sampleMethod($a, $b = null)
{
if ($a === $b) {
bar();
} elseif ($a > $b) {
$foo->bar($arg1);
} else {
BazClass::bar($arg2, $arg3);
}
}
final public static function bar()
{
// method body
}
}
Error Handling
<?php
try {
// Code that may throw an exception
$result = riskyOperation();
} catch (SpecificException $e) {
// Handle specific exception
logError($e->getMessage());
} catch (Exception $e) {
// Handle general exception
logError('General error: ' . $e->getMessage());
} finally {
// Cleanup code that always runs
cleanup();
}
Performance Optimization
Opcode Caching:
; php.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=7963
opcache.revalidate_freq=0
Database Optimization:
<?php
// Use prepared statements
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([$userId]);
$user = $stmt->fetch();
// Use indexes effectively
// CREATE INDEX idx_users_email ON users(email);
// CREATE INDEX idx_users_status_created ON users(status, created_at);
Testing
PHPUnit
Basic Test Structure:
<?php
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase
{
public function testAddition()
{
$calculator = new Calculator();
$result = $calculator->add(2, 3);
$this->assertEquals(5, $result);
}
public function testDivisionByZero()
{
$this->expectException(\InvalidArgumentException::class);
$calculator = new Calculator();
$calculator->divide(10, 0);
}
}
Behat (BDD)
Feature File:
Feature: User login
In order to access my account
As a registered user
I need to be able to log in
Scenario: Successful login
Given I am on the login page
When I enter valid credentials
And I click the login button
Then I should be logged in
And I should see a welcome message
Context Class:
<?php
use Behat\Behat\Context\Context;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
class FeatureContext implements Context
{
/** @Given I am on the login page */
public function iAmOnTheLoginPage()
{
// Implementation
}
/** @When I enter valid credentials */
public function iEnterValidCredentials()
{
// Implementation
}
}
Frameworks
Laravel
Basic Application Structure:
app/
├── Console/
├── Exceptions/
├── Http/
│ ├── Controllers/
│ ├── Middleware/
│ └── Requests/
├── Models/
├── Providers/
└── Services/
routes/
├── api.php
├── web.php
└── channels.php
database/
├── migrations/
├── seeders/
└── factories/
Controller Example:
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
class UserController extends Controller
{
public function index()
{
return User::paginate(15);
}
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|string|min:8',
]);
$user = User::create($validated);
return response()->json($user, 201);
}
public function show(User $user)
{
return $user;
}
public function update(Request $request, User $user)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email,' . $user->id,
]);
$user->update($validated);
return $user;
}
public function destroy(User $user)
{
$user->delete();
return response()->noContent();
}
}
Symfony
Controller Example:
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
class ApiController extends AbstractController
{
#[Route('/api/users', name: 'api_users', methods: ['GET'])]
public function getUsers(): JsonResponse
{
// Implementation
return $this->json(['users' => []]);
}
}
Service Configuration:
# config/services.yaml
services:
App\Service\UserService:
arguments:
$entityManager: '@doctrine.orm.entity_manager'
Security
Common Vulnerabilities
SQL Injection Prevention:
<?php
// Vulnerable (DON'T DO THIS)
$userId = $_GET['id'];
$query = "SELECT * FROM users WHERE id = $userId";
// Safe - Prepared statements
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$userId]);
XSS Prevention:
<?php
// Vulnerable
echo $_GET['name'];
// Safe - Escape output
echo htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');
CSRF Protection:
<?php
// Generate token
$token = bin2hex(random_bytes(32));
$_SESSION['csrf_token'] = $token;
// In form
<input type="hidden" name="csrf_token" value="<?php echo $token; ?>">
// Verify token
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
die('CSRF token validation failed');
}
Performance Optimization
PHP-FPM Configuration
; php-fpm.conf
[www]
user = www-data
group = www-data
listen = /var/run/php/php8.3-fpm.sock
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
Caching Strategies
OPcache:
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=7963
opcache.revalidate_freq=0
opcache.save_comments=0
Application Caching:
<?php
// Using APCu for user cache
apcu_store('user_' . $userId, $userData, 3600);
$userData = apcu_fetch('user_' . $userId);
Deployment
Docker
Dockerfile:
FROM php:8.3-fpm-alpine
# Install system dependencies
RUN apk add --no-cache \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
zip \
unzip
# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
# Get latest Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Set working directory
WORKDIR /var/www
# Copy application
COPY . .
# Install dependencies
RUN composer install --optimize-autoloader --no-dev
# Set permissions
RUN chown -R www-data:www-data /var/www
# Expose port
EXPOSE 9000
CMD ["php-fpm"]
CI/CD Pipeline
GitLab CI Example:
stages:
- test
- build
- deploy
test:
stage: test
script:
- composer install
- ./vendor/bin/phpunit
- ./vendor/bin/phpstan analyse
build:
stage: build
script:
- docker build -t myapp:$CI_COMMIT_SHA .
only:
- main
deploy:
stage: deploy
script:
- docker-compose up -d
only:
- main