PHP — Reference

Source: https://www.php.net/manual/en/

PHP

  • Created: 1994 by Rasmus Lerdorf (originally “Personal Home Page tools”; reinterpreted as the recursive “PHP: Hypertext Preprocessor”)
  • Latest stable: 8.5 (released 2025-11-20, https://www.php.net/releases/8.5/en.php; 8.5.x patch series in 2026 — verify exact patch on https://www.php.net/downloads.php)
  • Paradigms: Multi-paradigm — imperative, object-oriented (full class/interface/trait/enum support since 8.1), functional-leaning (first-class callables, arrow fns, closures), reflective
  • Typing: Dynamic by default; gradual typing with optional declarations (parameter, return, property since 7.4, readonly since 8.1, union since 8.0, intersection since 8.1, never since 8.1, true/false/null pseudo-types since 8.2). declare(strict_types=1) enforces strict scalar checks per file
  • Memory: Garbage-collected — reference counting + cycle collector for circular refs (synchronous, deterministic destruction is the default)
  • Compilation: Source → opcodes (Zend opcodes) → executed by the Zend Engine. OPcache caches opcodes in shared memory; JIT (PHP 8.0+) compiles hot opcodes to x86-64/ARM64 native code via DynASM
  • Primary domains: Server-side web (still ~75% of all websites per W3Techs), CMSes (WordPress, Drupal, Joomla), e-commerce (Magento, WooCommerce, Shopware), CLI tooling (Composer itself, Laravel Artisan)
  • Official docs: https://www.php.net/manual/en/

At a glance

  • Steward: The PHP Group + php-src maintainers; governance by PHP RFCs (https://wiki.php.net/rfc) requiring 2/3 majority of voting members.
  • Major release cadence: roughly one minor per November (8.0 → 2020-11, 8.1 → 2021-11, …, 8.5 → 2025-11). Active support 2 years + 1 year security-only after release.
  • Reference implementation: Zend Engine (in C); OPcache is bundled and on-by-default in modern PHP-FPM / SAPI configs.
  • Alternative runtimes: HHVM (Facebook; diverged into Hack), Swoole / OpenSwoole (event-loop coroutine runtime), FrankenPHP (Caddy-embedded, app-server mode), RoadRunner (Go-based PHP app server), php-wasm (browser/WASI).
  • License: PHP License (3.01 — BSD-style, with a clause forbidding products called “PHP”).

Getting started

  • Install: Linux: distro packages (apt install php8.5, dnf install php); macOS: brew install php; Windows: official ZIP from windows.php.net or via WSL/XAMPP/Laragon. Multi-version managers: phpbrew, phpenv, brew install shivammathur/php/php@8.4 for tap-based switching, asdf-php, Docker images (php:8.5-fpm).

  • Hello world (hello.php):

    <?php
    echo "Hello, world!\n";

    Run: php hello.php. Built-in dev server: php -S localhost:8000 (since 5.4).

  • Project layout (Composer / PSR-4): composer.json, vendor/ (auto-generated, gitignored), src/ for autoloaded code, tests/, bin/, public/ for web entrypoint, composer.lock (commit it).

  • Package manager: Composer (https://getcomposer.org) is universal and non-negotiable for modern PHP — packagist.org is the public registry. PIE (PHP Installer for Extensions, official) replaces PECL for installing C extensions since 2024.

  • REPL: php -a opens an interactive shell (limited — no autocomplete, no multi-line state on some builds). PsySH (composer global require psy/psysh) is the de-facto power REPL with tab completion, syntax highlighting, and dump. Symfony’s bin/console tinker and Laravel’s php artisan tinker wrap PsySH with framework boot. (https://www.php.net/manual/en/features.commandline.interactive.php)

Basics

  • Types/literals: scalar — int, float, string, bool; compound — array (ordered hash map), object; special — null, resource, callable, iterable. Numeric: 1_000_000 (since 7.4), 0xFF, 0b1010, 0o777 (since 8.1), 1.5e3. Strings: single-quoted (literal except \\ and \'), double-quoted (interpolation + escapes), heredoc (<<<EOT), nowdoc (<<<'EOT').
  • Variables: $camelCase mandatory sigil. Variable variables ($$x). No declarations — first assignment creates. Constants via const FOO = 1; (compile-time, class-scope) or define('FOO', 1) (runtime).
  • Scoping: Function-scoped; no closures over enclosing locals by defaultfunction () use ($x) { ... } to capture, use (&$x) for by-reference. Arrow fns (fn($x) => $x * 2, since 7.4) auto-capture by value.
  • Control flow: if/elseif/else, switch (loose ==), match (since 8.0 — strict ===, expression, exhaustive), for, foreach ($arr as $k => $v), while, do-while. Alternative syntax for templates: if (...): ... endif;. try/catch/finally.
  • Functions: function name(int $a, int $b = 0): int { return $a + $b; }. Named args (since 8.0), default args, variadic (...$args), spread (f(...$args)), nullable (?int), union (int|string), intersection (A&B), never return.
  • Strings: Concatenation with . (not +). Interpolation in double-quoted: "Hello $name", "Hello {$obj->prop}". sprintf, printf, number_format. str_contains / str_starts_with / str_ends_with since 8.0.
  • Collections: array is the data structure — both an ordered list and an associative map (preserves insertion order). [1, 2, 3] and ['a' => 1, 'b' => 2] use the same type. SplObjectStorage, SplStack, SplQueue, SplPriorityQueue, WeakMap (since 8.0), WeakReference. ArrayObject for OO interface over arrays.

Intermediate

  • Type system depth: strict mode per file (declare(strict_types=1)); without it, scalar types coerce. Generics are not native — Psalm/PHPStan provide them via docblocks (@template T). Covariant returns + contravariant params since 7.4. Property promotion in constructors (since 8.0): public function __construct(public string $name) {}.
  • Modules: PHP has namespaces (namespace App\\Foo;) but no module system per se. PSR-4 autoloading via Composer maps namespaces to filesystem paths. use App\\Foo\\Bar; for imports.
  • Error handling: Throwable is the root; Error (engine errors, type errors) and Exception (user errors) descend from it. try { } catch (Throwable $e) { } finally { }. Multi-catch: catch (A | B $e). The PHP error-reporting system (E_NOTICE, E_WARNING, etc.) is separate from exceptions — convert via set_error_handler or ErrorException wrapping. PHP 8 promoted many warnings to errors/exceptions.
  • Concurrency primitives:
    • Fibers (since 8.1): native coroutines (new Fiber($callable) + Fiber::suspend()/->resume()). Powers async libraries like ReactPHP, Amp v3, Revolt event loop.
    • No threads in the official runtime by default (the pthreads and parallel extensions exist but are CLI-only and require ZTS builds).
    • PCNTL (pcntl_fork) for true process parallelism (Unix only).
    • Process model: traditional PHP-FPM is request-per-process — concurrency is the web server’s job. Swoole / RoadRunner / FrankenPHP flip the model to long-running app servers with goroutine/fiber-like primitives.
  • I/O: file_get_contents, fopen/fread/fwrite/fclose, SplFileObject, stream wrappers (http://, php://stdin, php://memory, compress.zlib://, custom via stream_wrapper_register), curl (PECL bundled by default), Guzzle (guzzlehttp/guzzle) for HTTP.
  • Stdlib highlights: SPL (Standard PHP Library — datastructures, iterators, file objects), json_encode/json_decode (with JSON_THROW_ON_ERROR), DateTime/DateTimeImmutable/DateInterval/DatePeriod, IntlDateFormatter/NumberFormatter (intl ext), mbstring (multibyte strings), PDO (DB abstraction), password_hash/password_verify (Argon2/bcrypt), random_int/random_bytes (CSPRNG), Reflection*.

Advanced

  • Memory/GC: Reference counting (refcount per zval) gives deterministic destruction; the cycle collector runs periodically to clean up reference cycles (algorithm based on Bacon-Rajan synchronous mark-and-sweep). gc_collect_cycles(), gc_status(), gc_disable()/gc_enable(). Object destructors fire deterministically when refcount drops to zero — useful for RAII-style resource cleanup.
  • OPcache + JIT (PHP 8.x): OPcache compiles each .php to opcodes once and stores them in shared memory (opcache.memory_consumption, opcache.max_accelerated_files). Preloading (since 7.4) loads classes once at server start, eliminating per-request resolution. JIT (since 8.0) tiers up hot opcodes to native code via DynASM; tuned by opcache.jit=tracing (default) or function, opcache.jit_buffer_size. Big wins on CPU-bound workloads; modest on typical I/O-bound web. (https://php.net/manual/en/opcache.configuration.php)
  • FFI: ext-ffi (since 7.4) lets PHP call into C libraries with cdef strings — preload .h files, FFI::cdef('int strlen(const char*);', 'libc.so.6'). Same speed as a C extension for simple cases but no need to compile. Disabled in non-CLI by default (ffi.enable=preload).
  • Reflection: ReflectionClass, ReflectionMethod, ReflectionProperty, ReflectionAttribute (since 8.0), ReflectionEnum (since 8.1), ReflectionFiber (since 8.1). Used by every DI container, ORM, and serializer in the ecosystem (Symfony, Doctrine, Laravel).
  • Performance tools: Xdebug (debugger + profiler — slow but ubiquitous), spx (lightweight built-in-style profiler), Blackfire.io (commercial, callgraph + assertions), Tideways (commercial APM), opcache.preload for cold-start wins, pcov for fast coverage. Built-in microtime(true), memory_get_peak_usage(), getrusage(). VLD extension dumps opcodes (php -d vld.active=1 script.php).

God mode

  • OPcache + JIT internals: opcodes live in shared memory; the JIT compiler emits native code into a JIT buffer. Tracing JIT records hot loops; function JIT compiles whole functions on first call. Inspect with opcache_get_status(). Tune with opcache.jit_max_root_traces, opcache.jit_hot_loop. Disable selectively for problem code with function_exists guards or opcache.jit_blacklist.
  • FFI deep: preload .h definitions in opcache.preload so cdef cost is paid once; pass C structs back and forth via FFI::new/FFI::addr. Lets you ship near-C performance without writing a Zend extension.
  • Fiber concurrency: Fiber::suspend($value) returns control to whoever called ->resume(); ->resume($value) becomes the return value of Fiber::suspend. Build event loops on top (Revolt / Amp v3 / ReactPHP 3 do this). The runtime keeps Fiber stacks separately allocated; cheap in millions per process.
  • Attributes (PHP 8): native annotations — #[Route('/users')] parsed by the engine (no docblock string parsing). Read with ReflectionMethod::getAttributes(). Powers Symfony routing, Doctrine ORM v3, validators.
  • Readonly classes (since 8.2): readonly class Point { ... } makes all properties readonly. Asymmetric visibility (public(set)/private(set)) added in 8.4 — finer-grained mutation control.
  • Enum internals (since 8.1): enum Status { case Active; case Inactive; } (pure) or enum Status: string { case Active = 'a'; } (backed). Backed enums are singletons — Status::Active === Status::from('a'). Methods, interfaces, constants allowed; no constructors, no instantiation.
  • Reflection API depth: ReflectionParameter::getDefaultValueExpression() (since 8.4), ReflectionFunction::getClosureUsedVariables() for inspecting use captures, ReflectionGenerator for paused generators. Combined with eval it’s a full metaprogramming kit — Doctrine builds proxies this way.
  • Opcode dumping (vld): php -d vld.active=1 -d vld.execute=0 script.php prints the opcode stream — invaluable when diagnosing JIT or OPcache misbehavior. (https://github.com/derickr/vld)
  • Preloading: in php.ini, opcache.preload=/app/preload.php runs that file once at server startup, permanently loading classes/functions into shared memory. Saves ~5-15% on real apps. Beware: preloaded classes can’t be redeclared without restarting PHP-FPM.
  • php-internals: the engine is C; the test suite is .phpt files; RFCs at https://wiki.php.net/rfc/. The php-src repo (https://github.com/php/php-src) is the canonical source. The Zend Engine spec is de facto (no formal language standard).
  • Stream wrappers: register custom protocols — stream_wrapper_register('s3', S3StreamWrapper::class) lets fopen('s3://bucket/key', 'r') work. Used by Flysystem, Symfony filesystem.

Idioms & style

  • Naming: camelCase for methods/variables, PascalCase for classes/interfaces/traits/enums, SCREAMING_SNAKE_CASE for constants. Files: one class per file, file name matches class name (PSR-4).
  • Formatter / linter: PSR-12 (https://www.php-fig.org/psr/psr-12/) is the de-facto coding standard, succeeded by PER-CS (PHP Evolving Recommendation — Coding Style). Tools: PHP_CodeSniffer (phpcs/phpcbf), PHP-CS-Fixer (php-cs-fixer fix), Pint (Laravel’s wrapper around PHP-CS-Fixer). PHPStan and Psalm for static analysis (level 0–9 / level 1–8 strictness ramps).
  • Idiomatic patterns: type-declare everything (params, returns, properties); use DTOs with readonly constructor-promoted properties; throw exceptions, don’t return false-on-error in new code; embrace named arguments for clarity; immutable value objects; Stringable interface over __toString.
  • Expert review focus: missing declare(strict_types=1), mixed array of unknown shape (use shape annotations or DTOs), un-disposed Fibers, untyped Composer dependencies, opcache-unsafe code (e.g., eval of dynamic class definitions), preload contamination, reliance on global state for request-scoped data (broken on Swoole/RoadRunner).

Ecosystem

  • Web frameworks: Laravel (the dominant full-stack — Eloquent, Blade, Livewire, Inertia), Symfony (component-based; many other frameworks use Symfony components), CakePHP, CodeIgniter, Yii, Slim (micro), Mezzio (PSR middleware), Phalcon (C-extension perf).
  • CMS / e-commerce: WordPress, Drupal, Joomla, Magento 2, Shopware, Statamic, October CMS.
  • API: API Platform (Symfony-based, JSON-LD/Hydra/GraphQL), Laravel API Resources, Lumen (deprecated for Laravel slim mode).
  • App servers / async: FrankenPHP (Caddy module + worker mode), RoadRunner (Go), Swoole / OpenSwoole (C ext), ReactPHP (Fiber-based now), Amp v3 (Fibers).
  • Testing: PHPUnit (de-facto), Pest (developer-friendly Laravel-flavored layer over PHPUnit), Codeception (BDD), Behat (Gherkin), Infection (mutation testing), Mockery, Prophecy.
  • DB / ORM: Doctrine (Symfony default — data mapper), Eloquent (Laravel — Active Record), Cycle ORM, raw PDO.
  • Docs: phpDocumentor (phpdoc), Doctum (Symfony’s), Sami (deprecated). Native php.net docs are exemplary — community comments + man-page-style reference.
  • Notable users: Facebook/Meta (origin; now uses Hack on HHVM, but still PHP-derived), Wikipedia/Wikimedia, Slack, Etsy, MailChimp, Tumblr, Yahoo, Vimeo, WordPress.com (Automattic), most of Drupal-based gov sites.

Gotchas

  • == does loose comparison with type juggling — 0 == "abc" was true until PHP 8 fixed it; 0 == "" was true until 8.0 too. Always prefer ===.
  • array is both list and dict — count($arr) works on both, but array_values() re-indexes. JSON encode of associative array → object; of sequential → array. array_is_list() (since 8.1) checks.
  • null propagation: pre-8 most stdlib functions accepted null silently; many now Type cannot be null deprecation/warning in 8.1+, error in 9.x.
  • foreach by reference (foreach ($arr as &$v)) — the reference persists after the loop; unset($v) after, or you’ll mutate the last element on next assignment.
  • Static analysis grade-3+ catches almost everything, but the language won’t — adopt PHPStan/Psalm early.
  • Trait conflicts must be resolved with insteadof/as. Order of use matters.
  • __construct is not inherited by name — child overrides parent and must call parent::__construct() explicitly.
  • private in a trait means private-per-using-class (each class gets its own copy).
  • OPcache cache invalidation: in production with opcache.validate_timestamps=0, you must restart PHP-FPM (or opcache_reset) after deploys.
  • JIT and Xdebug are mutually exclusive (Xdebug disables JIT).
  • PHP-FPM session locking: default file-based sessions hold an exclusive lock on the session file for the whole request — concurrent AJAX requests serialize. Switch to Redis or call session_write_close() early.
  • Default error_reporting in dev should include E_ALL; many silent-bug pitfalls only surface with notices on.
  • int is 64-bit on 64-bit builds, 32-bit on 32-bit (rare today). PHP_INT_MAX to check.

Citations