PHP
WebOverview
Widely-used server-side scripting language. Powers a huge portion of the web including WordPress, Laravel, and Symfony.
Resources
Popular learning and reference links:
Installation & Getting Started
PHP is pre-installed on many systems. For local development, use a package manager or a bundled environment.
# macOS (Homebrew)
brew install php
# Ubuntu / Debian
sudo apt install php php-cli php-mbstring php-xml
# Windows — use XAMPP, Laragon, or Scoop
scoop install php
# Check version
php --version
# REPL — interactive mode
php -a
# Run a script
php script.php
# Quick one-liner
php -r "echo 'Hello, PHP!' . PHP_EOL;"
# Built-in development server
php -S localhost:8000
# Built-in server with router
php -S localhost:8000 router.php Project Scaffolding
Use Composer to create projects from templates. Laravel and Symfony have dedicated installers.
# Basic project with Composer
mkdir my-project && cd my-project
composer init
# Laravel — full-stack web framework
composer create-project laravel/laravel my-app
cd my-app && php artisan serve
# Laravel via installer
composer global require laravel/installer
laravel new my-app
# Symfony
composer create-project symfony/skeleton my-app
cd my-app && symfony server:start
# Slim (micro-framework)
composer create-project slim/slim-skeleton my-app
# Run development server
php -S localhost:8000 -t public Package Management
Composer is the standard dependency manager for PHP. Packages are hosted on Packagist.
# Install Composer
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
# Install a package
composer require guzzlehttp/guzzle
# Install as dev dependency
composer require --dev phpunit/phpunit
# Install all dependencies
composer install
# Update dependencies
composer update
# Autoload classes
composer dump-autoload
// composer.json
{
"require": {
"php": "^8.2",
"guzzlehttp/guzzle": "^7.0",
"laravel/framework": "^11.0"
},
"require-dev": {
"phpunit/phpunit": "^11.0"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
} Tooling & Formatter/Linter
PHP has mature code quality tools for formatting, linting, and static analysis.
# PHP CS Fixer — code formatter
composer require --dev friendsofphp/php-cs-fixer
./vendor/bin/php-cs-fixer fix src/
# PHP_CodeSniffer — linter & fixer
composer require --dev squizlabs/php_codesniffer
./vendor/bin/phpcs src/ # Lint
./vendor/bin/phpcbf src/ # Auto-fix
# PHPStan — static analysis
composer require --dev phpstan/phpstan
./vendor/bin/phpstan analyse src/ --level=max
# Psalm — static analysis (alternative)
composer require --dev vimeo/psalm
./vendor/bin/psalm
# Rector — automated refactoring
composer require --dev rector/rector
./vendor/bin/rector process src/
// .php-cs-fixer.dist.php
<?php
return (new PhpCsFixer\Config())
->setRules([
'@PSR12' => true,
'array_syntax' => ['syntax' => 'short'],
'no_unused_imports' => true,
'ordered_imports' => true,
])
->setFinder(
PhpCsFixer\Finder::create()->in(__DIR__ . '/src')
); Build & Compile Model
Interpreted with optional OPcache. PHP scripts are compiled to bytecode at runtime. OPcache stores precompiled bytecode in shared memory.
# No compile step — run directly
php app.php
# Built-in web server (development)
php -S localhost:8000 -t public
# Production — typically served via PHP-FPM + Nginx/Apache
# PHP-FPM compiles and caches bytecode via OPcache
# Check OPcache status
php -r "var_dump(opcache_get_status());"
Execution model:
- Source → Lexer → Parser → AST → Opcodes → Zend VM execution
- OPcache: Stores compiled opcodes in shared memory (skip parse/compile on each request)
- JIT compiler (PHP 8.0+): Compiles hot opcodes to machine code via DynASM
- Preloading (PHP 7.4+): Load files into memory at server start
- No standalone binary output — always requires PHP runtime
- RoadRunner / FrankenPHP: Keep PHP workers alive between requests (no per-request boot)
Libraries & Frameworks
PHP has a mature ecosystem with powerful frameworks and libraries:
Web Frameworks - Laravel, Symfony, Slim, CodeIgniter, CakePHP, Laminas (Zend)
CMS - WordPress, Drupal, Joomla, Craft CMS, Statamic
API - API Platform (Symfony), Laravel API Resources, Lumen
Database / ORM - Eloquent (Laravel), Doctrine, Propel, RedBeanPHP
Templating - Blade (Laravel), Twig (Symfony), Plates
HTTP Clients - Guzzle, Symfony HttpClient, cURL wrapper
Testing - PHPUnit, Pest, Mockery, Codeception
Utilities - Carbon (dates), Flysystem (filesystem), Monolog (logging), League packages
Queue / Async - Laravel Queues, Symfony Messenger, ReactPHP, Swoole
Testing
PHPUnit is the standard testing framework. Pest provides a more expressive syntax.
// PHPUnit
use PHPUnit\Framework\TestCase;
class MathTest extends TestCase
{
public function testAddition(): void
{
$this->assertEquals(3, 1 + 2);
$this->assertTrue(true);
$this->assertCount(3, [1, 2, 3]);
}
public function testException(): void
{
$this->expectException(\InvalidArgumentException::class);
throw new \InvalidArgumentException('bad input');
}
}
// Pest — expressive testing framework
test('addition works', function () {
expect(1 + 2)->toBe(3);
});
it('handles arrays', function () {
expect([1, 2, 3])->toContain(2)->toHaveCount(3);
});
# Run tests
./vendor/bin/phpunit
./vendor/bin/pest
# With coverage
./vendor/bin/phpunit --coverage-text
XDEBUG_MODE=coverage ./vendor/bin/pest --coverage Debugging
Xdebug is the primary debugger. Laravel has Telescope and Debugbar for web debugging.
// Basic debugging
var_dump($variable); // Type + value
print_r($array); // Human-readable array
echo gettype($variable); // Type name
debug_print_backtrace(); // Stack trace
// Error reporting
error_reporting(E_ALL);
ini_set('display_errors', '1');
// Logging
error_log('Something happened');
error_log(print_r($data, true)); // Log complex data
# Xdebug — install and configure
pecl install xdebug
# php.ini configuration
zend_extension=xdebug
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_port=9003
# VS Code — install "PHP Debug" extension
# launch.json:
{
"type": "php",
"request": "launch",
"name": "Listen for Xdebug",
"port": 9003
}
# Laravel Telescope — request/job/query inspector
composer require laravel/telescope
php artisan telescope:install
# Laravel Debugbar
composer require barryvdh/laravel-debugbar --dev
# Automatically shows debug info in browser Variables
Variables start with $. PHP is dynamically typed — no type declaration needed for variables.
$name = 'Alice';
$age = 30;
$isActive = true;
$price = 19.99;
// Constants
const MAX_SIZE = 100;
define('APP_NAME', 'MyApp');
// Variable variables
$varName = 'hello';
$$varName = 'world'; // $hello = 'world'
// Null coalescing
$value = $input ?? 'default';
// Destructuring (list / array)
[$a, $b, $c] = [1, 2, 3];
['name' => $name, 'age' => $age] = ['name' => 'Bob', 'age' => 25]; Types
PHP is dynamically typed but supports type declarations (PHP 7+). Scalar types: string, int, float, bool. Compound: array, object, callable.
// Type declarations (PHP 7+)
function add(int $a, int $b): int {
return $a + $b;
}
// Union types (PHP 8.0+)
function format(string|int $value): string {
return (string) $value;
}
// Intersection types (PHP 8.1+)
function process(Countable&Iterator $items): void { }
// Nullable types
function find(?string $name): ?User {
return $name ? User::find($name) : null;
}
// Type checking
gettype($var); // 'string', 'integer', etc.
is_string($var); // true/false
is_int($var);
is_array($var);
$var instanceof MyClass;
// Enums (PHP 8.1+)
enum Color: string {
case Red = 'red';
case Blue = 'blue';
case Green = 'green';
}
$c = Color::Red;
$c->value; // 'red' Data Structures
PHP arrays are versatile ordered maps. The SPL library provides additional data structures.
// Arrays — ordered map (used as list, map, set, stack, queue)
$list = [1, 2, 3];
$map = ['name' => 'Alice', 'age' => 30];
// Array operations
$list[] = 4; // Append
array_push($list, 5);
array_pop($list); // Remove last
array_shift($list); // Remove first
array_unshift($list, 0); // Prepend
in_array(2, $list); // Search
array_key_exists('name', $map); // Key exists
count($list); // Length
// Array manipulation
array_merge($a, $b); // Merge arrays
array_slice($list, 1, 2); // Slice
array_unique($list); // Remove duplicates
array_reverse($list); // Reverse
sort($list); // Sort in-place
usort($list, fn($a, $b) => $a <=> $b); // Custom sort
// Spread operator
$merged = [...$list1, ...$list2];
// SPL Data Structures
$stack = new SplStack();
$stack->push('a');
$stack->pop();
$queue = new SplQueue();
$queue->enqueue('a');
$queue->dequeue();
$heap = new SplMinHeap();
$set = new SplObjectStorage(); // Object set Functions
Functions support type hints, default parameters, named arguments, and closures.
// Basic function
function greet(string $name): string {
return "Hello, {$name}!";
}
// Default parameters
function power(int $base, int $exp = 2): int {
return $base ** $exp;
}
// Named arguments (PHP 8.0+)
power(exp: 3, base: 2); // 8
// Variadic parameters
function sum(int ...$nums): int {
return array_sum($nums);
}
// Arrow functions (PHP 7.4+)
$double = fn(int $x): int => $x * 2;
// Closures
$multiplier = function (int $factor): Closure {
return fn(int $x) => $x * $factor;
};
$triple = $multiplier(3);
$triple(5); // 15
// First-class callable syntax (PHP 8.1+)
$fn = strlen(...);
$fn('hello'); // 5 Conditionals
Standard if/else, ternary, null coalescing, and match expression.
// If / else
if ($x > 0) {
echo 'positive';
} elseif ($x === 0) {
echo 'zero';
} else {
echo 'negative';
}
// Ternary
$label = $x > 0 ? 'positive' : 'non-positive';
// Elvis operator (short ternary)
$name = $input ?: 'default';
// Null coalescing
$value = $data['key'] ?? 'fallback';
$value ??= 'fallback'; // Null coalescing assignment
// Match expression (PHP 8.0+) — strict comparison, returns value
$result = match ($status) {
'active' => 'User is active',
'banned', 'suspended' => 'User is restricted',
default => 'Unknown status',
};
// Switch
switch ($color) {
case 'red':
echo '#f00';
break;
case 'blue':
echo '#00f';
break;
default:
echo 'unknown';
} Loops
Standard for, foreach, while, and do-while loops.
// For loop
for ($i = 0; $i < 5; $i++) {
echo $i;
}
// Foreach — arrays and iterables
foreach ([1, 2, 3] as $item) {
echo $item;
}
// Foreach with key
foreach (['a' => 1, 'b' => 2] as $key => $value) {
echo "$key: $value";
}
// While
$n = 0;
while ($n < 3) {
$n++;
}
// Do-while
do {
$n--;
} while ($n > 0);
// Array functions (functional-style)
$doubled = array_map(fn($x) => $x * 2, [1, 2, 3]);
$evens = array_filter([1, 2, 3, 4], fn($x) => $x % 2 === 0);
$sum = array_reduce([1, 2, 3], fn($carry, $x) => $carry + $x, 0); Generics & Type System
PHP has no native generics. Type safety is achieved through type declarations, interfaces, and static analysis tools (PHPStan/Psalm) that support generics via docblocks.
// No native generics — use type declarations
function first(array $items): mixed {
return $items[0] ?? null;
}
// PHPStan/Psalm generics via docblocks
/**
* @template T
* @param T[] $items
* @return T|null
*/
function firstItem(array $items): mixed {
return $items[0] ?? null;
}
/**
* @template T
* @implements Iterator<int, T>
*/
class TypedCollection implements Iterator {
/** @var T[] */
private array $items = [];
/** @param T $item */
public function add(mixed $item): void {
$this->items[] = $item;
}
}
// Union types (PHP 8.0+)
function process(string|int $value): string { return (string) $value; }
// Intersection types (PHP 8.1+)
function handle(Countable&Traversable $items): void { }
// DNF types (PHP 8.2+)
function parse((Stringable&Countable)|string $input): string { }
Static analysis tools (PHPStan level 9, Psalm) provide generics-like type safety through annotations.
Inheritance & Composition
Single inheritance with interfaces and traits for composition. PHP supports abstract classes and final methods.
// Class inheritance
class Animal {
public function __construct(
protected string $name
) {}
public function speak(): string {
return "{$this->name} makes a sound";
}
}
class Dog extends Animal {
public function speak(): string {
return "{$this->name} barks";
}
}
// Abstract classes
abstract class Shape {
abstract public function area(): float;
}
// Interfaces
interface Serializable {
public function serialize(): string;
}
interface Loggable {
public function log(): void;
}
// Implementing multiple interfaces
class User extends Animal implements Serializable, Loggable {
public function serialize(): string { return json_encode($this); }
public function log(): void { error_log($this->name); }
}
// Traits — horizontal code reuse
trait HasTimestamps {
public \DateTime $createdAt;
public \DateTime $updatedAt;
public function touch(): void {
$this->updatedAt = new \DateTime();
}
}
class Post {
use HasTimestamps;
}
// Readonly classes (PHP 8.2+)
readonly class Point {
public function __construct(
public float $x,
public float $y,
) {}
} Functional Patterns
PHP supports closures, higher-order functions, and functional array operations.
// Higher-order functions
function apply(callable $fn, int $x): int {
return $fn($x);
}
$double = fn(int $x) => $x * 2;
apply($double, 5); // 10
// Array functional operations
$nums = [1, 2, 3, 4, 5];
$squared = array_map(fn($x) => $x ** 2, $nums);
$evens = array_filter($nums, fn($x) => $x % 2 === 0);
$sum = array_reduce($nums, fn($carry, $x) => $carry + $x, 0);
// Closures with captured variables
function counter(int $start = 0): Closure {
$count = $start;
return function () use (&$count): int {
return ++$count;
};
}
$inc = counter();
$inc(); // 1
$inc(); // 2
// Currying
$add = fn(int $a) => fn(int $b) => $a + $b;
$add5 = $add(5);
$add5(3); // 8
// Pipe / compose (manual)
function pipe(mixed $value, callable ...$fns): mixed {
foreach ($fns as $fn) {
$value = $fn($value);
}
return $value;
}
$result = pipe(
5,
fn($x) => $x * 2,
fn($x) => $x + 1,
fn($x) => "Result: $x",
);
// "Result: 11"
// First-class callable syntax (PHP 8.1+)
$lengths = array_map(strlen(...), ['hello', 'world']); Concurrency
PHP is traditionally single-threaded and request-scoped. Concurrency is achieved through process-level parallelism, async libraries, or Fibers (PHP 8.1+).
// Fibers (PHP 8.1+) — cooperative multitasking
$fiber = new Fiber(function (): void {
$value = Fiber::suspend('paused');
echo "Resumed with: $value";
});
$result = $fiber->start(); // 'paused'
$fiber->resume('hello'); // 'Resumed with: hello'
// curl_multi — concurrent HTTP requests
$handles = [];
$mh = curl_multi_init();
foreach ($urls as $url) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_multi_add_handle($mh, $ch);
$handles[] = $ch;
}
do {
curl_multi_exec($mh, $running);
curl_multi_select($mh);
} while ($running > 0);
// ReactPHP — event-driven async I/O
$loop = React\EventLoop\Loop::get();
$browser = new React\Http\Browser();
$browser->get('https://api.example.com/data')
->then(function ($response) {
echo $response->getBody();
});
// Parallel execution with promises
React\Async\parallel([
fn() => $browser->get('/api/users'),
fn() => $browser->get('/api/posts'),
])->then(function ($responses) { });
// AMPHP — alternative async framework
// Swoole / OpenSwoole — coroutine-based server
// Laravel Octane — high-performance app server (Swoole/RoadRunner) Modules & Imports
PHP uses namespaces and autoloading (PSR-4) via Composer. Files are included with require/include.
// Defining a namespace
namespace App\Models;
class User {
public string $name;
}
// Importing classes
use App\Models\User;
use App\Services\{AuthService, MailService}; // Group use
// Aliasing
use App\Models\User as AppUser;
// Functions and constants
use function App\Helpers\formatDate;
use const App\Config\MAX_RETRIES;
// File inclusion (legacy, rarely needed with autoloading)
require 'config.php'; // Fatal error if missing
require_once 'config.php'; // Include only once
include 'optional.php'; // Warning if missing
include_once 'optional.php';
// PSR-4 autoloading via Composer (composer.json)
// "autoload": { "psr-4": { "App\\": "src/" } }
// Then: composer dump-autoload
// Using autoloaded classes — just use/import them
use App\Models\User;
$user = new User(); Error Handling
PHP uses try/catch/finally for exceptions. PHP 8 introduced union catch types and match for cleaner handling.
// Try / catch / finally
try {
$data = json_decode($input, true, 512, JSON_THROW_ON_ERROR);
} catch (\JsonException $e) {
echo "JSON error: {$e->getMessage()}";
} finally {
echo 'always runs';
}
// Catching multiple exception types
try {
riskyOperation();
} catch (InvalidArgumentException | RuntimeException $e) {
echo "Error: {$e->getMessage()}";
} catch (\Throwable $e) {
echo "Unexpected: {$e->getMessage()}";
}
// Throwing exceptions
function divide(float $a, float $b): float {
if ($b == 0) {
throw new \DivisionByZeroError('Cannot divide by zero');
}
return $a / $b;
}
// Custom exceptions
class ValidationException extends \RuntimeException {
public function __construct(
public readonly array $errors,
string $message = 'Validation failed',
) {
parent::__construct($message);
}
}
// Global error handler
set_exception_handler(function (\Throwable $e) {
error_log($e->getMessage());
http_response_code(500);
}); Memory Management
Garbage collected with reference counting. PHP uses a reference-counting GC with a cycle collector. Memory is freed at the end of each request in traditional PHP-FPM setups.
// Memory is automatically managed
$data = range(1, 10000); // Allocated
unset($data); // Freed immediately (refcount → 0)
// Check memory usage
echo memory_get_usage(); // Current usage (bytes)
echo memory_get_peak_usage(); // Peak usage (bytes)
echo memory_get_usage(true); // Real allocated memory
// Memory limit
ini_set('memory_limit', '256M');
// Generators — memory-efficient iteration
function bigRange(int $start, int $end): Generator {
for ($i = $start; $i <= $end; $i++) {
yield $i;
}
}
// Yields one value at a time — O(1) memory
foreach (bigRange(1, 1000000) as $num) { }
// WeakMap (PHP 8.0+) — keys are weakly referenced
$cache = new WeakMap();
$obj = new stdClass();
$cache[$obj] = 'metadata';
// When $obj is unset, entry is automatically removed
PHP memory model:
- Request-scoped: In PHP-FPM, all memory is freed at the end of each request
- Reference counting: Objects freed when refcount hits 0
- Cycle collector: Detects and frees circular references
- Long-running processes (Swoole, RoadRunner): Must be careful about memory leaks since memory persists between requests
Performance Profiling
Xdebug and Blackfire are the primary profiling tools. SPX is a lightweight alternative.
// Manual timing
$start = microtime(true);
doExpensiveWork();
$elapsed = microtime(true) - $start;
echo "Took {$elapsed}s";
// Memory profiling
$before = memory_get_usage();
$data = processLargeDataset();
$used = memory_get_usage() - $before;
echo "Used " . ($used / 1024 / 1024) . " MB";
# Xdebug profiling (generates cachegrind files)
# php.ini: xdebug.mode=profile
# php.ini: xdebug.output_dir=/tmp/xdebug
php script.php
# Open cachegrind file with KCacheGrind / QCacheGrind / Webgrind
# Blackfire.io — production-safe profiler
blackfire run php script.php
# Or profile via browser extension
# SPX — lightweight profiler (built-in web UI)
# php.ini: extension=spx.so
# Access /?SPX_KEY=dev&SPX_UI_URI=/ in browser
# Tideways — production monitoring + profiling
# XHProf — Facebook's hierarchical profiler
pecl install xhprof
# Laravel-specific
php artisan route:list # Check registered routes
php artisan optimize # Cache config, routes, views
composer dump-autoload -o # Optimized autoloader
# Benchmark with Apache Bench
ab -n 1000 -c 10 http://localhost:8000/ Interop
PHP interops with C via FFI (PHP 7.4+), system commands, and various protocol integrations.
// FFI — call C libraries directly (PHP 7.4+)
$ffi = FFI::cdef("
double sqrt(double x);
int abs(int x);
", "libm.so.6"); // Or "libm.dylib" on macOS
echo $ffi->sqrt(16.0); // 4.0
echo $ffi->abs(-42); // 42
// FFI with struct
$ffi = FFI::cdef("
typedef struct { int x; int y; } Point;
");
$point = $ffi->new("Point");
$point->x = 10;
$point->y = 20;
// System commands
$output = shell_exec('ls -la');
$output = exec('whoami', $lines, $exitCode);
$handle = popen('tail -f /var/log/syslog', 'r');
// Process control
$process = proc_open(
'python3 script.py',
[['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']],
$pipes
);
fwrite($pipes[0], "input data");
$result = stream_get_contents($pipes[1]);
proc_close($process);
// Database interop
$pdo = new PDO('mysql:host=localhost;dbname=app', 'user', 'pass');
$pdo = new PDO('pgsql:host=localhost;dbname=app', 'user', 'pass');
$pdo = new PDO('sqlite:/path/to/db.sqlite');
// REST / SOAP / gRPC
$client = new SoapClient('https://example.com/api?wsdl');
// gRPC via grpc/grpc PHP extension
// Redis, Memcached — native extensions
$redis = new Redis();
$redis->connect('127.0.0.1'); Packaging & Distribution
Publish libraries to Packagist (Composer registry). Deploy apps via Docker, PHP-FPM, or platform services.
# Publish a library to Packagist
# 1. Create a GitHub repo with composer.json
# 2. Register at https://packagist.org
# 3. Submit the repo URL — auto-updates via webhook
# composer.json for a library
{
"name": "vendor/my-library",
"type": "library",
"autoload": {
"psr-4": { "Vendor\\MyLib\\": "src/" }
},
"require": { "php": "^8.2" }
}
# Tag a release
git tag v1.0.0
git push --tags
# Docker deployment
FROM php:8.3-fpm-alpine
RUN docker-php-ext-install pdo_mysql opcache
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader
COPY . .
CMD ["php-fpm"]
# Laravel Forge / Vapor — managed deployment
# Platform.sh, Render, Railway — PaaS deployment
# Phar archive — single-file distribution
php -d phar.readonly=0 create-phar.php
# Creates my-tool.phar — run with: php my-tool.phar
# Static binary (experimental)
# FrankenPHP can embed PHP app into a single Go binary