Short answer: PHP 8.3 and 8.4 are the versions to target in 2026. Use declare(strict_types=1);, typed parameters and return types, match instead of long switch blocks, fn() => arrow functions for short callbacks, enums and readonly properties for data, and JSON_THROW_ON_ERROR for JSON. PHP 8.4 adds property hooks, private(set) visibility, array_find() and new Foo()->method() without extra brackets. The tables below cover the syntax you reach for every day.
The examples in this PHP cheat sheet were run with PHP 8.4.21 (CLI) on 9 October 2026. Features marked 8.4 do not work on PHP 8.3; everything else runs on both.
Table of Contents
Variables, types and strings
| Syntax | What it does | Example |
|---|---|---|
$name = "web01"; | Variables start with $; no declaration needed | $port = 8080; |
"…$var…" / '…' | Double quotes interpolate; single quotes are literal | "Host {$name}:{$port}" |
<<<TXT … TXT; | Heredoc; the closing marker’s indentation is stripped from every line | Multi-line config templates |
. and .= | String concatenation | $log .= "done\n"; |
str_contains(), str_starts_with(), str_ends_with() | Substring tests that return bool | str_ends_with($f, ".log") |
explode() / implode() | Split a string / join an array | explode(",", "a,b,c") |
sprintf() / printf() | Formatted output | printf("%-8s %5d\n", $h, $p); |
strlen() vs mb_strlen() | Bytes vs characters | strlen("héllo") is 6, mb_strlen gives 5 |
?? and ??= | Null coalescing; no warning for a missing key | $ttl = $cfg['ttl'] ?? 300; |
<=> | Spaceship: returns -1, 0 or 1 | usort($a, fn($x, $y) => $x <=> $y); |
=== | Strict comparison: value and type | "1" === 1 is false |
get_debug_type() | Readable type name for messages | get_debug_type(1.0) gives float |
Since PHP 8.0, 0 == "a" is false: a number is compared with a non-numeric string as a string. Numeric strings still compare as numbers ("10" == "1e1" is true), so prefer === unless you want type juggling.
Arrays and array functions
| Function | What it does | Example |
|---|---|---|
[1, 2, 3], ['k' => 'v'] | List and associative array (both are ordered maps) | $cfg = ['host' => 'db01']; |
$a[] = 4; | Append | $hosts[] = 'web02'; |
[$a, $b] = $pair; | Destructuring, also with keys | ['host' => $h] = $cfg; |
[...$a, ...$b] | Spread arrays into a new one (string keys allowed since 8.1) | $all = [...$web, ...$db]; |
array_map() | Transform each value | array_map(fn($n) => $n * 2, $a) |
array_filter() | Keep matching values; keys are preserved | array_values(array_filter($a)) to reindex |
array_reduce() | Fold to one value | array_reduce($a, fn($c, $i) => $c + $i, 0) |
array_column() | One field from a list of rows, optional key | array_column($users, 'age', 'name') |
in_array($x, $a, true) | Search values; pass true for strict | in_array(3, $ids, true) |
array_key_exists() vs isset() | isset is false when the value is null | array_key_exists('ttl', $cfg) |
array_merge() vs + | Merge overwrites string keys; + keeps the left value | $cfg + $defaults |
sort, rsort, asort, ksort, usort | Sort in place (asort/ksort keep keys) | ksort($cfg); |
array_slice(), array_chunk(), array_unique() | Slice, batch, de-duplicate | array_chunk($ids, 100) |
array_is_list() | True for keys 0, 1, 2… in order | array_is_list([1, 2]) |
Control flow and match
| Syntax | What it does | Example |
|---|---|---|
if / elseif / else | Conditionals | if ($code >= 500) { … } |
match | Expression with strict comparison, no fall-through, returns a value | see below |
switch | Loose comparison, needs break | Legacy code |
foreach ($a as $k => $v) | Iterate an array or iterable | foreach ($cfg as $key => $val) |
for, while, do … while | Classic loops | for ($i = 0; $i < 3; $i++) |
break / continue | Leave or skip; break 2 leaves two loops | if ($i === 1) continue; |
?: and ? : | Short ternary and ternary | $user = $input ?: 'guest'; |
$label = match (true) {
$code >= 500 => 'server error',
$code >= 400 => 'client error',
default => 'ok',
};
echo match ($code) { 200, 204 => 'fine', 404 => 'not found', default => 'other' };
A match with no matching arm and no default throws UnhandledMatchError instead of silently returning null.
Functions, closures and arrow functions
| Syntax | What it does | Example |
|---|---|---|
function f(int $n): string | Typed parameters and return | function greet(string $name): string |
$x = "Hello" in the signature | Default value | function greet(string $n, string $g = "Hello") |
| Named arguments | Pass by name, in any order | greet(greeting: "Hi", name: "Ann") |
int ...$nums | Variadic parameter | total(...[4, 5]) unpacks an array |
?int, int|string | Nullable and union types | function f(?int $n): int|string |
void, never, static | No return value / never returns / late static binding | function fail(): never { throw … } |
function () use ($x) {} | Closure; captures by value (use (&$x) by reference) | $mul = function ($n) use ($f) { … }; |
fn($n) => $n * $f | Arrow function: one expression, captures automatically | array_map(fn($n) => $n * $f, $a) |
strlen(...) | First-class callable from any function or method | $len = strlen(...); |
array &$arr | Pass by reference | function addOne(array &$arr): void |
Add declare(strict_types=1); as the first statement of a file to stop PHP coercing "5" into int for calls made from that file; a mismatch then throws TypeError.
Classes, enums and readonly properties
enum Status: string {
case Active = 'active';
case Suspended = 'suspended';
public function label(): string { return ucfirst($this->value); }
}
final class Server {
public function __construct(
public readonly string $host,
private int $port = 22,
) {}
public function dsn(): string { return "{$this->host}:{$this->port}"; }
}
$s = new Server('db01', 3306);
echo $s->dsn(); // db01:3306
echo Status::from('active')->label(); // Active
var_dump(Status::tryFrom('x')); // NULL
| Feature | Syntax | Note |
|---|---|---|
| Constructor promotion | public function __construct(private int $port) {} | Declares and assigns in one place |
| Readonly property | public readonly string $host | Set once, inside the class; writing again throws Error |
| Readonly class | readonly class Point { … } | Every property is readonly (8.2+) |
| Enum | enum Suit { case Hearts; } | Suit::Hearts->name, Suit::cases() |
| Backed enum | enum Status: string | from() throws on bad input, tryFrom() returns null |
| Interface, abstract, trait | interface, abstract class, use Logs; | Traits copy methods into a class |
| Nullsafe operator | $user?->profile?->email | Returns null instead of an error on null |
| Static factory | public static function local(): static | new static(…) respects subclasses |
What is new in PHP 8.3 and 8.4
| Version | Feature | Example |
|---|---|---|
| 8.3 | Typed class constants | const string VERSION = '1.0'; |
| 8.3 | Dynamic class constant fetch | Cfg::{$name} |
| 8.3 | #[\Override] attribute | Error if the parent has no such method |
| 8.3 | json_validate() | Check JSON without decoding it |
| 8.3 | str_increment(), mb_str_pad() | str_increment('a9') gives b0 |
| 8.3 | DateMalformedStringException | Thrown by new DateTimeImmutable('nonsense') |
| 8.4 | Property hooks | get and set logic on the property itself (below) |
| 8.4 | Asymmetric visibility | public private(set) int $logins = 0; |
| 8.4 | new without wrapping brackets | new Server('h1', 1)->dsn() |
| 8.4 | array_find(), array_find_key(), array_any(), array_all() | array_find($a, fn($n) => $n > 4) |
| 8.4 | mb_trim(), mb_ucfirst() | Multibyte-safe trim and capitalise |
| 8.4 | #[\Deprecated] | Mark your own functions as deprecated |
class User {
public string $email {
set(string $value) {
if (!str_contains($value, '@')) throw new ValueError('bad email');
$this->email = strtolower($value);
}
}
public string $domain { get => substr($this->email, strpos($this->email, '@') + 1); }
}
$u = new User;
$u->email = 'Ann@Example.COM';
echo $u->email, ' ', $u->domain; // ann@example.com example.com
PHP 8.4 also deprecates implicitly nullable parameters such as function f(string $s = null). Write ?string $s = null instead.
Errors and exceptions
| Syntax | What it does | Example |
|---|---|---|
try / catch / finally | Handle exceptions; finally always runs | see below |
catch (A | B $e) | Catch several types | catch (InvalidArgumentException | TypeError $e) |
Throwable | Parent of Exception and Error | catch (\Throwable $e) in a top-level handler |
TypeError, ValueError, DivisionByZeroError | Engine errors (extend Error, not Exception) | intdiv(1, 0) |
class ConfigError extends RuntimeException {} | Your own exception type | throw new ConfigError("missing DB_HOST"); |
$e->getMessage(), getCode(), getTraceAsString() | Details for logging | error_log($e->getMessage()); |
try {
throw new InvalidArgumentException('bad port', 22);
} catch (InvalidArgumentException | TypeError $e) {
echo $e->getMessage(), ' ', $e->getCode();
} finally {
echo "cleanup\n";
}
Files, JSON and the command line
| Task | Code |
|---|---|
| Read a whole file | $txt = file_get_contents('/etc/hostname'); |
| Lines as an array | file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) |
| Stream a large file | while (($line = fgets($fh)) !== false) { … } |
| Append with a lock | file_put_contents($log, $msg, FILE_APPEND | LOCK_EX); |
| Decode JSON safely | json_decode($s, true, flags: JSON_THROW_ON_ERROR) |
| Encode readable JSON | json_encode($a, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR) |
| Dates | (new DateTimeImmutable('now', new DateTimeZone('UTC')))->format(DATE_ATOM) |
| Environment | getenv('DB_HOST') |
For server admins: most PHP problems on a hosting server are configuration, not code. These CLI commands answer the usual questions:
| Command | What it shows |
|---|---|
php -v | Version of the CLI binary (on cPanel, /opt/cpanel/ea-php84/root/usr/bin/php -v for a specific EA4 version) |
php -l file.php | Syntax check without running the file |
php --ini | Which php.ini and extra .ini files are loaded |
php -m | Compiled and loaded extensions |
php -i | grep memory_limit | One setting as the CLI sees it |
php -r 'echo PHP_VERSION, "\n";' | Run one line of code |
php -S 127.0.0.1:8000 -t public/ | Built-in development server (never for production) |
The CLI and the web server can use different PHP versions and different php.ini files. To see what a site really runs, put <?php phpinfo(); in a temporary file, load it once, then delete it.
Official documentation (checked 9 October 2026): PHP manual: types · PHP manual: match · Migrating from PHP 8.3 to 8.4 · PHP 8.4 release announcement · PHP supported versions
PHP Cheat Sheet at a glance


Related: PHP-FPM Calculator: pm.max_children and Spare Servers · JavaScript Cheat Sheet: Modern Syntax for Browser and Node · SQL Cheat Sheet: MySQL, MariaDB and PostgreSQL Syntax.
Reading a lot of our guides? Go ad-free with srvScripts Pro for $6 a month. It also pays for new guides. Cancel any time.