Compare

Lang Compare

PHP

Web

Overview

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

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