Open-source libraries used

1 libraries are bundled into this tool's code.

PHP Cheatsheet — Quick Reference

A concise quick-reference for PHP 8.3 syntax, types, arrays and the most-used standard library — covers ~80% of everyday needs.

Php

PHP PHP 8.3 (LTS-style)

Zend Engine (PHP runtime) · Imperative, OO, functional · Dynamic (with optional static types)

Recommended Learning Path

First get comfortable running `php hello.php` and the built-in `php -S` server → master variables, types and control flow → go deeper into functions, closures and arrays → understand reference semantics and object handles → organize code with classes, interfaces and exceptions → then on-demand learn file I/O, cURL, time and regex → handle concurrency with Fiber/pcntl. The FAQ section is great for a second pass to avoid pitfalls.

1.Hello World & Runtime

Running PHP programs, the built-in web server, command-line arguments, and exit codes.

Minimal program

A PHP file starts with <?php; run with `php filename` on the CLI. `echo` prints strings.

1
2
3
4
5
6
7
8
9
echo "Hello, world!\n";
// Single quotes do not parse escapes or interpolation
echo 'Hello, world!\n';
echo "\n";
// Variable interpolation in double quotes
$name = 'PHP';
echo "Hello, $name!\n";

CLI run

The `php` command runs scripts directly. `php -l` checks syntax; `php -r` executes a one-liner.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Run a script file:
// $ php hello.php
// outputs Hello, world!
// Execute one line of code directly:
// $ php -r 'echo "hi\n";'
// Check syntax (do not execute):
// $ php -l hello.php
// Start an interactive shell:
// $ php -a
// Check version:
// $ php -v

Built-in web server

`php -S` starts the built-in server using a directory as the doc root — great for local dev.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Start in the project directory:
// $ php -S localhost:8000
// Specify a document root:
// $ php -S localhost:8000 -t public
// Specify a router script:
// $ php -S localhost:8000 router.php
// After starting, browse to:
// http://localhost:8000/index.php
// Fails to start if the port is already in use

File structure

`<?php` and `?>` tags split PHP from HTML. Pure-PHP files should omit the closing tag.

1
2
3
4
5
6
7
echo "Pure PHP file: write code directly at the top\n";
// Recommended to omit the closing tag ?>
// avoids the trailing newline being treated as output
// When mixing templates, use ?> to switch back to HTML:
// <p><?= $title ?></p>
// Short tag <?= is equivalent to echo

Command-line arguments

`$argv` is the arguments array; `$argc` is the count; `$argv[0]` is the script name.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Run: $ php args.php a b c
var_dump($argc); // 4
var_dump($argv);
// ["args.php", "a", "b", "c"]
// Get business arguments:
$name = $argv[1] ?? 'guest';
echo "hello, $name\n";
// Iterate over all arguments:
foreach (array_slice($argv, 1) as $arg) {
echo "arg: $arg\n";
}

Exit codes

`exit()` or `die()` ends the script and returns a status code: 0 for success, non-zero for failure.

1
2
3
4
5
6
7
8
9
10
11
12
// Normal end (returns 0):
exit(0);
// Exit with an error message:
die("an error occurred\n"); // same as exit
// Exit with a status code:
exit(1); // non-zero means failure
// Natural end of script is equivalent to exit(0)
// CLI check success/failure:
// $ php run.php && echo "ok"

File inclusion

`require` pulls in a file that must exist; `include` only warns on failure; `_once` prevents double inclusion.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Include a file that must exist:
require 'config.php';
// Include an optional file (failure only warns):
include 'optional.php';
// Prevent duplicate inclusion:
require_once 'lib/functions.php';
include_once 'helper.php';
// Use __DIR__ to build an absolute path:
require __DIR__ . '/../src/bootstrap.php';
// include returns a boolean you can use to judge success/failure

Environment check

`php -i` dumps config; `php -m` lists extensions; `phpinfo()` outputs the full report.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// View config from the command line:
// $ php -i
// View a specific config item:
// $ php -i | grep memory_limit
// List loaded extensions:
// $ php -m
// Confirm an extension is installed:
// $ php -m | grep mbstring
// Output full info on a page:
phpinfo();
// Be sure to disable in production to avoid leaking config

2.Variables & Constants

Variable declarations, constants, type declarations, strict mode, and scope.

Variable declaration

Variables start with `$`; no type declaration needed, created on first assignment; names are case-sensitive.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// $ prefix + name, no type declaration:
$count = 0;
$name = 'Rex';
$price = 19.99;
$active = true;
// Variable names are case-sensitive:
$Foo = 1;
$foo = 2; // two different variables
// Reading an undefined variable triggers a warning:
// echo $undefined;
// Names cannot start with a digit

Value-copy assignment

Plain assignment copies by value; scalars are independent. Arrays are also copied but with copy-on-write underneath.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Scalar assignments do not affect each other:
$a = 5;
$b = $a;
$b = 10;
echo $a; // still 5
// Array assignment is also a copy:
$arr1 = [1, 2, 3];
$arr2 = $arr1;
$arr2[0] = 99;
echo $arr1[0]; // 1 (not affected)
// Use & for reference assignment, see the references section

Constants

`const` defines namespaced constants; `define()` defines global ones; immutable after creation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Namespace-level constant:
const MAX_SIZE = 1024;
// Global constant:
define('APP_NAME', 'demo');
// Constants can be used in expressions:
$half = MAX_SIZE / 2;
// Check whether defined:
defined('APP_NAME'); // true
// Read constant by name dynamically:
constant('MAX_SIZE'); // 1024
// Convention: constant names are all uppercase

Magic constants

`__DIR__`, `__FILE__`, `__LINE__` etc. resolve at compile-time — handy in log output.

1
2
3
4
5
6
7
8
9
10
11
echo __DIR__; // directory containing the file
echo __FILE__; // full path of the file
echo __LINE__; // current line number
echo __FUNCTION__; // current function name
echo __CLASS__; // current class name
echo __NAMESPACE__; // current namespace
// Log the location:
error_log(__FILE__ . ':' . __LINE__);
// __DIR__ is the safest way to build paths

Scope & static

Variables inside functions are local by default; `global` exposes globals; `static` persists across calls.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$global_count = 0;
function countUp(): void
{
static $n = 0; // persisted across calls
$n++;
echo $n;
}
// Access global from inside a function:
function readGlobal(): int
{
global $global_count;
return $global_count;
}
countUp(); // 1
countUp(); // 2

Parameter & return types

Parameters and returns can be type-declared; weak mode auto-coerces, `strict_types` makes it strict.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Parameter and return types:
function add(int $a, int $b): int
{
return $a + $b;
}
// Nullable type ?int:
function find(?int $id): ?array
{
return $id === null ? null : [$id];
}
// Union type int|float:
function area(int|float $w): float
{
return (float) $w * 2;
}

Strict mode

`declare(strict_types=1)` strictly checks types and throws `TypeError` on mismatch.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Must be the first line of the file:
declare(strict_types=1);
function add(int $a, int $b): int
{
return $a + $b;
}
add(1, 2); // 3
// add('1', 2); // strict mode throws TypeError
// in weak mode '1' is auto-coerced to 1
// Declared per file, does not inherit across files

Naming conventions

Classes & namespaces UpperCamelCase; functions & variables camelCase; constants UPPER_CASE; PSR-12.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Class names in PascalCase:
class UserProfile {}
// Functions and variables in camelCase:
function getUserName() {}
$userName = 'Rex';
// Constants in ALL_CAPS:
const MAX_ITEMS = 100;
// Private members may use an underscore prefix:
class Demo
{
private $privateData;
}
// PSR-12: classes in PascalCase, methods in camelCase

3.Data Types

Scalars, arrays, null, enums, and the type system, plus how to think about loose conversion and type checking.

Scalar types

`int`, `float`, `string`, `bool` are the four scalars. Strings are byte sequences; Chinese is multibyte.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$age = 30; // int
$pi = 3.14; // float
$ok = true; // bool
$name = 'Rex'; // string
// Integer overflow auto-promotes to float:
$big = PHP_INT_MAX + 1; // float
// Strings are byte sequences:
$s = 'Hello, 世界';
// Explicit type casting:
(int) '42'; // 42
(string) 123; // '123'

Arrays

A PHP array is an ordered map with int and string keys — it doubles as a list and a dict.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// List:
$colors = ['red', 'green', 'blue'];
// Dictionary:
$user = ['name' => 'Rex', 'age' => 5];
// Mixed keys:
$mixed = [1, 'a', true];
// Append an element:
$colors[] = 'yellow';
// Get length:
count($colors); // 4
// Key order is insertion order

null & empty values

`null` means no value. Unset variables, explicit `null`, and `null` returns are all `null`.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$value = null; // explicit null
// Check whether it is null:
$value === null; // true
is_null($value); // true
// Nullable parameter type:
function greet(?string $name): void
{
echo $name ?? 'guest';
}
// Use ?? to provide a default value:
$name = $input['name'] ?? 'guest';

callable & iterable

`callable` is anything invokable; `iterable` is any array or `Traversable` object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// iterable parameter accepts an array or an object:
function sumAll(iterable $items): int
{
$total = 0;
foreach ($items as $n) {
$total += $n;
}
return $total;
}
sumAll([1, 2, 3]); // 6
// callable accepts a function name or a closure:
function apply(callable $fn, int $x): int
{
return $fn($x);
}
apply(fn($x) => $x * 2, 5); // 10

mixed & void

`mixed` is any type; `void` is no return; `never` means never returns.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// mixed: any type:
function debug(mixed $v): void
{
var_dump($v);
}
// void: no return value:
function logMsg(string $msg): void
{
echo $msg . "\n";
}
// never: never returns:
function abort(): never
{
throw new \RuntimeException('terminate');
}
// a never function must throw or exit

enum

`enum` (8.1+) defines a set of named cases — pure or backed, with optional methods.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Pure enum:
enum Color
{
case Red;
case Green;
case Blue;
}
// Backed enum:
enum Status: string
{
case Ok = 'ok';
case NotFound = '404';
}
// Get value and compare:
$s = Status::Ok;
$s->value; // 'ok'
$s === Status::Ok; // true
// Pair with match for safe matching

Union & intersection types

`int|float` accepts either; intersection types must satisfy multiple interface constraints at once.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Union type (8.0+):
function format(int|float $n): string
{
return number_format((float) $n, 2);
}
// Nullable shorthand ?T is equivalent to T|null:
function maybe(?string $s): string
{
return $s ?? 'empty';
}
// Intersection type (8.1+):
function dump(\Countable&\Traversable $c): void
{
echo count($c);
}
// DNF type 8.2+: (A&B)|null

Weak type coercion

In weak mode operations auto-coerce types; string/number comparisons have special rules — see FAQ.

1
2
3
4
5
6
7
8
9
10
11
12
13
// String and number arithmetic auto-coerces:
$n = '3' + 2; // 5
$n = '3' * '2'; // 6
// Coerce string to number:
(int) '42px'; // 42
(float) '3.5.9'; // 3.5
// Boolean coercion:
(bool) '0'; // false
(bool) '0.0'; // true (not '0')
// In strict mode these raise an error

Type-check functions

`is_int`, `is_string`, and the rest of the `is_*` family check types and return booleans.

1
2
3
4
5
6
7
8
9
10
11
12
is_int(3); // true
is_float(3.0); // true
is_string('x'); // true
is_bool(true); // true
is_array([1]); // true
is_null(null); // true
is_numeric('42'); // true (coercible to number)
is_callable('strlen'); // true
// For loose checks use is_numeric:
is_int('42'); // false
is_numeric('42'); // true

4.References & Memory Semantics

PHP has no raw pointers: variable references, copy-on-write, object handles, and null handling.

What a reference is

A PHP reference is an alias for a variable — both names point to the same content and update together.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Reference assignment: $b becomes an alias of $a:
$a = 1;
$b = &$a;
$b = 2;
echo $a; // 2 (changed together)
// Array elements can also be referenced:
$arr = [1, 2];
$last = &$arr[1];
$last = 99;
echo $arr[1]; // 99
// References are not address pointers
// you cannot take an address or do pointer arithmetic

Copy-on-write

Arrays and objects share data on assignment; the copy happens only when you write, saving memory.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Assignment shares first, no immediate copy:
$a = [1, 2, 3];
$b = $a;
echo $b[0]; // 1, read-only no copy
// Copy triggers on write:
$b[0] = 99;
echo $a[0]; // 1 (not affected)
// Assigning large arrays is cheap:
// only modifications incur copy cost
// When modifying inside foreach, build a new array

Reference assignment

`unset` only breaks the binding for that name — the underlying value still lives through other aliases.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$a = 'original';
$b = &$a;
// unset only breaks $b's reference:
unset($b);
echo $a; // original (still there)
// Reference between array elements:
$data = [1, 2];
$alias = &$data[0];
$data[0] = 100;
echo $alias; // 100
// Observe the reference count:
// debug_zval_dump($a)

Reference parameters

Prefix a parameter with `&` to pass by reference — changes inside the function affect the caller's variable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Reference parameter modifies the original variable:
function increment(int &$n): void
{
$n++;
}
$x = 5;
increment($x);
echo $x; // 6
// Return value with status:
function parse(string $s, &$ok): int
{
$ok = is_numeric($s);
return (int) $s;
}
parse('42', $valid); // $valid becomes true

Object handles

An object variable holds a handle — assignment and pass-by-value still point to the same instance; use `clone` for a copy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Counter
{
public int $n = 0;
}
$c1 = new Counter();
$c2 = $c1; // same instance
$c2->n = 5;
echo $c1->n; // 5 (shared)
// Use clone for an independent copy:
$c3 = clone $c1;
$c3->n = 0;
echo $c1->n; // still 5
// === checks whether they are the same instance:
$c1 === $c2; // true

null handling

`isset` checks present and not `null`; `is_null` checks for `null`; `empty` checks for empty.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$arr = ['a' => null, 'b' => 1];
// isset: exists and is not null:
isset($arr['a']); // false (value is null)
isset($arr['b']); // true
isset($arr['x']); // false (does not exist)
// is_null: whether it is null:
is_null($arr['a']); // true
// Accessing a missing key directly warns:
// $v = $arr['x'];
// Use ?? for safe reading:
$v = $arr['x'] ?? 'default';

Null coalescing ??

`??` returns the right side if the left is missing or `null` — chainable and pairs with `throw`.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Read a key that may not exist:
$name = $_GET['name'] ?? 'guest';
// Chained null coalescing:
$config = $a ?? $b ?? 'default';
// Combined with throw (8.0+):
$value = $input['key'] ?? throw new \InvalidArgumentException('missing key');
// Does not trigger an undefined-key warning:
// only direct subscript access warns
// Assignment operator ??= (8.0+)

Variable variables

`$$var` uses a variable's value as another variable's name — poor readability; prefer arrays.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Variable variables:
$name = 'foo';
$foo = 42;
echo $$name; // 42 (i.e. $foo)
// Array access is clearer:
$values = ['foo' => 42];
echo $values[$name]; // 42
// Nested variable variables are confusing:
// $$$name is nearly unreadable
// Prefer associative arrays instead

unset & memory

`unset` drops a name and decrements the refcount; the GC handles remaining cyclic references.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Release a large variable:
$big = str_repeat('x', 1000000);
unset($big); // can be reclaimed immediately
// Release an array element:
$arr = [1, 2, 3];
unset($arr[1]);
// keys do not reindex, value becomes null
// Circular references:
class Node
{
public ?Node $next = null;
}
$a = new Node();
$b = new Node();
$a->next = $b;
$b->next = $a; // forms a cycle
unset($a, $b);
// cleaned up by the cycle garbage collector

5.Control Flow

Conditionals, loops, switch, match, and break/continue.

if / else

`if` evaluates a condition; `elseif` tries the next; `else` is the fallback.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$n = 0;
if ($n > 0) {
echo "positive";
} elseif ($n === 0) {
echo "zero";
} else {
echo "negative";
}
// One-liner shorthand:
if ($n > 0) echo "positive";
// Use return to exit early:
// reduces nesting levels

Ternary operator

Condition `?` then `:` else is an expression you can assign; nesting hurts readability.

1
2
3
4
5
6
7
8
9
10
$score = 85;
$grade = $score >= 90 ? 'A' : 'B';
echo $grade; // B
// ?: vs ?? difference:
$x = $a ?: 'default'; // uses default when $a is falsy
$y = $a ?? 'default'; // uses default when $a is null
// Nested ternaries are hard to read:
// prefer splitting into if or match

switch statement

`switch` uses loose comparison by default; remember `break` to avoid fall-through; `default` is the catch-all.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$status = 200;
switch ($status) {
case 200:
case 201:
echo "success";
break;
case 404:
echo "not found";
break;
default:
echo "other";
}
// case '0' loosely matches 0 — be careful
// for complex checks prefer match

match expression

`match` (8.0+) compares strictly, returns a value, no `break` needed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$status = 404;
$label = match ($status) {
200, 201 => 'success',
404, 410 => 'not found',
default => 'other',
};
echo $label; // not found
// Without default and no match throws
// an UnhandledMatchError
// match true for conditional checks:
$result = match (true) {
$score >= 90 => 'A',
$score >= 80 => 'B',
default => 'C',
};

for loop

`for(init; cond; step)` is the classic counted loop — the step is fully under your control.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for ($i = 0; $i < 3; $i++) {
echo "$i ";
} // 0 1 2
// Decreasing step:
for ($i = 10; $i >= 0; $i -= 2) {
echo "$i ";
} // 10 8 6 4 2 0
// Nested loops:
for ($i = 0; $i < 3; $i++) {
for ($j = 0; $j < 2; $j++) {
echo "$i$j ";
}
}

foreach iteration

`foreach` iterates arrays and objects, fetching key and value; watch out for reference-residue bugs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$items = ['a' => 1, 'b' => 2];
// Values only:
foreach ($items as $v) {
echo "$v ";
} // 1 2
// Keys and values:
foreach ($items as $k => $v) {
echo "$k=$v ";
} // a=1 b=2
// Modify elements by reference:
foreach ($items as &$v) {
$v *= 2;
}
unset($v); // critical! break the reference

while & do-while

`while` checks first and may run zero times; `do-while` runs at least once.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// while checks first:
$n = 0;
while ($n < 3) {
echo $n++;
} // 012
// do-while runs at least once:
$i = 10;
do {
echo $i;
} while ($i < 3); // 10
// Common pattern for line-by-line file reading:
$h = fopen('log.txt', 'r');
while (($line = fgets($h)) !== false) {
echo $line;
}
fclose($h);

break & continue

`break` exits the loop or switch; `continue` skips to the next iteration; pass a count to jump multiple levels.

1
2
3
4
5
6
7
8
9
10
11
12
13
for ($i = 0; $i < 10; $i++) {
if ($i % 2 === 0) continue; // skip even numbers
if ($i > 5) break; // end early
echo "$i ";
} // 1 3 5
// Break out of two levels:
for ($i = 0; $i < 3; $i++) {
for ($j = 0; $j < 3; $j++) {
if ($j === 1) break 2;
echo "$i$j ";
}
} // 00 10

6.Functions & Closures

Function definitions, default parameters, variadic parameters, named arguments, closures, and generators.

Function definition

`function` defines a function with typed params and a `return`. Function names are case-insensitive.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function add(int $a, int $b): int
{
return $a + $b;
}
// Call:
add(1, 2); // 3
// Function names are case-insensitive:
ADD(1, 2); // 3
// Calling before definition is also OK:
sayHi();
function sayHi(): void
{
echo "hi\n";
}
// Redeclaring a function with the same name is a fatal error

Default parameters

Parameters can take default values that may be omitted at call time; defaults must be constant expressions.

1
2
3
4
5
6
7
8
9
10
11
12
13
function greet(string $name, string $title = 'Mr.'): string
{
return "$title $name";
}
greet('Nick'); // Mr. Nick
greet('Anna', 'Ms.'); // Ms. Anna
// Default values must be constant expressions:
// cannot be function calls or variables
// Place optional parameters after required ones:
// function f($a, $b = 1, $c = 2)

Variadic parameters

`...$args` collects the remaining arguments into an array; it must be last in the parameter list.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function sum(int ...$nums): int
{
return array_sum($nums);
}
sum(1, 2, 3); // 6
sum(10, 20); // 30
sum(); // 0
// Spread an array when passing:
$arr = [1, 2, 3];
sum(...$arr); // 6
// The type declaration constrains every element:
// int ...$nums ensures all are int

Named arguments

Pass args as `name: value` (8.0+) to skip optional ones and order them freely.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function makeProfile(
string $name,
string $email = '',
int $age = 0,
): array {
return [$name, $email, $age];
}
// Skip email and pass age directly:
makeProfile(
name: 'Rex',
age: 5,
);
// ['Rex', '', 5]
// Combined with array spread:
$args = ['name' => 'Rex', 'age' => 5];
makeProfile(...$args);

Return types

The return type comes after the colon: `void`, nullable `?T`, union types and `array` are supported.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function nothing(): void {} // no return value
function maybe(): ?int // may be null
{
return null;
}
function both(): int|string // union return
{
return 'ok';
}
function shape(): array // returns array
{
return ['name' => 'Rex'];
}
function never(): never // never returns
{
throw new \Exception('terminate');
}
// return type mismatch throws TypeError

Closures

Anonymous functions use `function()`; `use` captures outer variables; assign them or pass as callbacks.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Assign an anonymous function to a variable:
$square = function (int $x): int {
return $x * $x;
};
$square(4); // 16
// use captures by value:
$factor = 2;
$mul = function ($x) use ($factor): int {
return $x * $factor;
};
$mul(5); // 10
// use captures by reference to modify outer scope:
$count = 0;
$inc = function () use (&$count): void {
$count++;
};
$inc();
echo $count; // 1

Arrow functions

`fn() => expr` auto-captures outer variables by value — concise single-expression callbacks.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$factor = 2;
$double = fn(int $x) => $x * $factor;
$double(10); // 20
// Concise array callbacks:
$nums = [1, 2, 3, 4];
$evens = array_filter(
$nums,
fn($n) => $n % 2 === 0,
); // [2, 4]
// Arrow functions have a single expression:
// for multiple statements use a regular closure
// Captures by value; to modify outer scope use closure with use &

First-class callables

8.1+ syntax `strlen(...)` creates a first-class callable, making functions first-class values.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// First-class callable (8.1+):
$len = strlen(...);
$len('hello'); // 5
// Object method:
$c = new Counter();
$inc = $c->increment(...);
$inc();
// callable type accepts a parameter:
function run(callable $fn): void
{
echo $fn();
}
run(fn() => 'done');
// call_user_func is the old syntax
// prefer the (...) syntax in new code

Generators

Functions that `yield` values are generators — lazy by nature, ideal for memory-efficient large data.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function rangeGen(int $n): Generator
{
for ($i = 0; $i < $n; $i++) {
yield $i;
}
}
foreach (rangeGen(5) as $v) {
echo "$v "; // 0 1 2 3 4
}
// Generators are lazy: produce values as you consume them:
$gen = rangeGen(3);
$gen->current(); // 0
$gen->next();
$gen->current(); // 1
// Key-value yield:
yield 'key' => 'value';

7.Strings

String literals, interpolation, concatenation, multibyte handling, and the functions you reach for most.

String literals

Single quotes output literally; double quotes interpret escapes and variables. Both styles are common.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Single quotes output as-is:
echo 'Hello\n'; // no escape processing
// Double quotes parse escapes:
echo "Hello\n"; // newline
// Variable interpolation in double quotes:
$name = 'Rex';
echo "hi $name"; // hi Rex
// Single quotes inside single quotes need escaping:
echo 'it\'s';
// Use concatenation or sprintf for multi-line text
// heredoc is sensitive to indentation

Variable interpolation

Inside double quotes `$var` interpolates; complex expressions need braces like `{$arr['k']}`.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$name = 'Rex';
$age = 5;
// Simple variable:
echo "name $name"; // name Rex
// Array element with curly braces:
$user = ['name' => 'Rex'];
echo "{$user['name']}"; // Rex
// Object property:
class Dog
{
public string $name = '旺财';
}
$dog = new Dog();
echo "{$dog->name}"; // 旺财
// Wrap complex expressions in curly braces
// use . concatenation for simple cases

String concatenation

`.` concatenates two strings; `.=` appends. For many joins, `implode` or `sprintf` is clearer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$first = 'Rex';
$last = 'Wang';
// Concatenate:
$full = $first . ' ' . $last;
echo $full; // Rex Wang
// Append:
$msg = '';
$msg .= 'hello ';
$msg .= 'world';
echo $msg; // hello world
// Array to string:
implode('-', [1, 2, 3]); // 1-2-3
// Numbers are auto-coerced to strings on concatenation:
echo 'No.' . 3 . ' place'; // No.3 place

Multibyte functions

For Chinese text use the `mb_*` family — they count characters while `strlen` counts bytes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$s = 'Hello, 世界';
// Bytes vs characters:
strlen($s); // 12 (bytes)
mb_strlen($s); // 8 (characters)
// Substring by character:
mb_substr($s, 0, 5); // 'Hello'
// byte-level substr can break Chinese text
// Case conversion:
mb_strtoupper('hello');
// Split into character array:
mb_str_split($s);
// For Chinese text always use the mb_ functions

Formatting sprintf

`sprintf`/`printf` use `%s`, `%d` placeholders — much more readable than concatenation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$name = 'Rex';
$age = 5;
// Format:
$line = sprintf('%s is %d years old', $name, $age);
echo $line; // Rex is 5 years old
// Print directly:
printf('%.2f', 3.14159); // 3.14
// Common placeholders:
// %s string %d integer %f float
// %b binary %x hexadecimal
// %-5s left-aligned %05d zero-padded
// Argument order can be swapped:
sprintf('%2$s-%1$s', 'a', 'b'); // b-a

Search & replace

`strpos` finds a position; `str_contains` tests inclusion; `str_replace` does bulk replacement.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$s = 'Hello, world!';
// Check contains (8.0+):
str_contains($s, 'world'); // true
// Find position:
strpos($s, 'world'); // 7
strpos($s, 'xyz'); // false
// Replace:
str_replace('world', 'PHP', $s);
// 'Hello, PHP!'
// Case-insensitive replace:
str_ireplace('HELLO', 'Hi', $s);
// Replace by position:
// substr_replace($s, 'X', 0, 5)

Split & join

`explode` splits a string by a delimiter into an array; `implode` joins an array back.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Split:
$parts = explode(',', 'a,b,c');
// ['a', 'b', 'c']
// Limit count:
explode(',', 'a,b,c', 2);
// ['a', 'b,c']
// Empty elements preserved:
explode(',', 'a,,c');
// ['a', '', 'c']
// Join:
implode(' | ', ['a', 'b']);
// 'a | b'
// Split on whitespace:
preg_split('/\s+/', 'a b c');
// ['a', 'b', 'c']

Case & trim

`strtolower`/`strtoupper` change case; `trim` strips whitespace at both ends; `ltrim`/`rtrim` do one side.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$s = ' Hello World ';
// Trim whitespace:
trim($s); // 'Hello World'
// Trim one side only:
ltrim(' hi'); // 'hi'
rtrim('hi '); // 'hi'
// Case:
strtolower('ABC'); // 'abc'
strtoupper('abc'); // 'ABC'
ucfirst('hello'); // 'Hello'
ucwords('hello world');// 'Hello World'
// Trim specific characters:
trim('...hi...', '.'); // 'hi'

8.Arrays & Collections

Creating arrays, adding, removing, updating and looking up elements, iteration, sorting, and Spl data structures.

Creating arrays

Use `[]` or `array()` to create arrays. Literals auto-assign increasing integer keys.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Create with literal:
$list = [1, 2, 3];
// Specify keys:
$map = ['a' => 1, 'b' => 2];
// Omit keys for auto-increment:
$list = ['x', 'y']; // keys 0,1
// Empty array:
$empty = [];
// Append with key:
$arr[5] = 'five';
$arr[] = 'six'; // key 6
// Numeric string keys are auto-coerced to int

Access & modify

Subscript notation accesses elements; `[]` appends; `unset` deletes. Reading a missing key warns.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$user = ['name' => 'Rex', 'age' => 5];
// Access:
echo $user['name']; // Rex
// Modify:
$user['age'] = 6;
// Append:
$user['city'] = '北京';
// Delete:
unset($user['age']);
// Safe read avoids warnings:
$age = $user['age'] ?? 'unknown';
// Nested access:
$data['a']['b']['c'];

Push, pop & count

`array_push`/`array_pop` work the tail; `array_shift`/`unshift` work the head; `count` returns the size.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$stack = [1, 2, 3];
// Push to tail:
$stack[] = 4; // recommended
array_push($stack, 5, 6);
// Pop from tail:
$last = array_pop($stack); // 6
// Pop from head:
$first = array_shift($stack); // 1
// Insert at head:
array_unshift($stack, 0);
// Count:
count($stack);
// Check existence:
in_array(2, $stack); // true

Iterating arrays

`foreach` walks values or key/value pairs; list destructuring works for 2D arrays.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$users = [
['name' => 'Rex', 'age' => 5],
['name' => 'Anna', 'age' => 30],
];
// Values only:
foreach ($users as $u) {
echo $u['name'];
}
// Key and value:
foreach ($users as $i => $u) {
echo "$i: {$u['name']}";
}
// List destructuring:
foreach ($users as ['name' => $n]) {
echo $n;
}
// while + each is the old syntax, do not use

Map & filter

`array_map` transforms each element; `array_filter` keeps those that match; `array_reduce` folds.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$nums = [1, 2, 3, 4];
// Map:
$doubled = array_map(
fn($n) => $n * 2,
$nums,
); // [2, 4, 6, 8]
// Filter:
$evens = array_filter(
$nums,
fn($n) => $n % 2 === 0,
); // [2, 4]
// Reduce:
$sum = array_reduce(
$nums,
fn($acc, $n) => $acc + $n,
0,
); // 10
// Map across multiple arrays:
array_map(fn($a, $b) => $a + $b, [1, 2], [3, 4]);

Sorting

`sort` sorts values, `ksort` by key, `asort` by value while preserving keys, `usort` uses a custom comparator.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$nums = [3, 1, 4, 1, 5];
// Sort by value (reindexes keys):
sort($nums); // [1, 1, 3, 4, 5]
rsort($nums); // descending
// Sort preserving keys:
$ages = ['a' => 30, 'b' => 20];
asort($ages); // sort by value ascending, keep keys
ksort($ages); // sort by key
// Custom comparison:
usort(
$users,
fn($x, $y) => $x['age'] <=> $y['age'],
);
// Multi-column sort:
array_multisort($ages, SORT_DESC);

Merge & slice

`array_merge` joins arrays, `array_slice` extracts a sub-array, `array_combine` zips keys and values.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Merge (later string keys override):
$a = ['a' => 1, 'b' => 2];
$b = ['b' => 3, 'c' => 4];
$m = array_merge($a, $b);
// ['a' => 1, 'b' => 3, 'c' => 4]
// Numeric keys get reindexed on merge:
array_merge([1], [2]); // [1, 2]
// Slice:
$nums = [1, 2, 3, 4, 5];
array_slice($nums, 1, 2); // [2, 3]
// Combine keys and values:
array_combine(['a', 'b'], [1, 2]);
// ['a' => 1, 'b' => 2]
// Deduplicate:
array_unique([1, 1, 2]); // [1, 2]

Destructuring assignment

List `[]` or keyed `[]` destructuring assigns array elements to multiple variables — also in `foreach`.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// List destructuring:
[$a, $b] = [1, 2];
echo $a; // 1
// Destructure by key:
[0 => $x, 2 => $z] = [1, 2, 3];
echo $x; // 1
// Key-value destructuring:
['name' => $n] = ['name' => 'Rex'];
echo $n; // Rex
// Gather the rest:
[$first, ...$rest] = [1, 2, 3];
// $rest = [2, 3]
// Destructure an array returned from a function:
[$min, $max] = minMax($nums);

Spl data structures

`SplStack`, `SplQueue`, `SplFixedArray` provide fixed-purpose data structures.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Stack (LIFO):
$stack = new SplStack();
$stack->push('a');
$stack->push('b');
$stack->pop(); // 'b'
// Queue (FIFO):
$queue = new SplQueue();
$queue->enqueue('a');
$queue->enqueue('b');
$queue->dequeue(); // 'a'
// Fixed-size array:
$fixed = new SplFixedArray(3);
$fixed[0] = 'hi';
$fixed->setSize(5);
// Object container:
$store = new SplObjectStorage();
$store->attach($obj, 'meta');
$store->contains($obj);

9.Memory & Performance

Reference counting, the cyclic-reference GC, WeakReference, and measuring memory.

Reference counting

`zval` tracks a refcount; when it hits zero the memory is reclaimed immediately. Assignments and calls increment it.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Each variable has a reference count:
$a = 'hello'; // count 1
$b = $a; // count 2
unset($a); // count 1
unset($b); // count 0, reclaimed
// Copy-on-write: copy happens only on write
// Observe the count (requires xdebug):
// xdebug_debug_zval('a')

Cyclic-reference GC

When objects reference each other, refcounts never reach zero — the cyclic GC reclaims them periodically.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Node
{
public ?Node $next = null;
}
$a = new Node();
$b = new Node();
$a->next = $b;
$b->next = $a; // forms a cycle
unset($a, $b);
// reference count stays at 1, never reaches zero
// GC scans automatically when its buffer fills:
gc_collect_cycles(); // manual collection
// For long-lived objects, break the chain explicitly:
// $a->next = null;

WeakReference

`WeakReference` (8.0+) holds an object without preventing collection — great for caches.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Cache
{
public array $data = [];
}
$obj = new Cache();
// weak reference does not increase the strong refcount:
$weak = WeakReference::create($obj);
$weak->get(); // Cache instance
unset($obj);
// after the object is reclaimed:
$weak->get(); // null
// WeakMap 8.4+:
// good for attaching extra data to objects

Memory measurement

`memory_get_usage` reports current usage; `memory_get_peak_usage` reports the peak.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Current memory usage (bytes):
$used = memory_get_usage();
// Peak usage:
$peak = memory_get_peak_usage();
// Display in MB:
printf('%.2f MB', $peak / 1048576);
// Adjust memory limit:
// ini_set('memory_limit', '512M');
// Pass at CLI startup:
// $ php -d memory_limit=1G script.php
// unset large arrays after processing

Timely release

`unset` large variables when done so refcounts drop, keeping peaks low and batches steady.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Free large arrays after processing:
$lines = file('big.log');
unset($lines);
// Process in batches inside a loop:
$result = [];
for ($i = 0; $i < 1000; $i++) {
$result[] = str_repeat('x', 100000);
if ($i % 100 === 99) {
// flush batch to disk, then clear:
$result = [];
}
}
// Null out object references to help GC:
// $obj = null;

OPcache

OPcache caches compiled bytecode, avoiding recompilation per request — always on in production.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Confirm enabled:
// $ php -m | grep opcache
// Recommended php.ini settings:
// opcache.enable=1
// opcache.memory_consumption=128
// opcache.max_accelerated_files=10000
// Not enabled by default for CLI:
// $ php -d opcache.enable=1 script.php
// Caches bytecode, skips compilation:
// high-traffic pages get a noticeable throughput boost
// May be disabled temporarily in dev

Large-data processing

Huge arrays eat memory — use generators, streaming reads, and `SplFixedArray` to keep usage flat.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Generators instead of full arrays:
function readLines(string $file): Generator
{
$h = fopen($file, 'r');
while (($line = fgets($h)) !== false) {
yield rtrim($line);
}
fclose($h);
}
// Read and process on the fly, memory stays flat:
foreach (readLines('big.log') as $line) {
process($line);
}
// Use SplFixedArray for huge integer sets:
$fixed = new SplFixedArray(1000000);
// Avoid reading an entire large file at once

Performance tips

Avoid concatenation-in-loops, pre-allocate, reuse connections, and cut redundant queries.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Avoid in-loop string concatenation:
$parts = [];
foreach ($items as $it) {
$parts[] = $it->name;
}
$result = implode(',', $parts);
// Reuse connection objects:
$pdo = new PDO($dsn); // share as singleton
// Hoist count() out of the loop:
$count = count($list);
// Avoid repeated DB queries:
// fetch once, filter in memory
// Combine with opcache and buffering
// keep hot paths simple

10.Object-Oriented

Classes, constructors, visibility, inheritance, interfaces, traits, and readonly properties.

Class definition

`class` defines a class with typed properties and methods. `new` creates instances.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class User
{
public string $name = '';
public int $age = 0;
public function describe(): string
{
return "{$this->name}, {$this->age}";
}
}
$u = new User();
$u->name = 'Rex';
$u->age = 5;
echo $u->describe(); // Rex, 5
// Typed properties must be initialized at declaration
// or assigned in the constructor

Constructor & property promotion

`__construct` initializes the object. 8.0+ promotes constructor parameters directly to properties.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Traditional style:
class OldUser
{
private string $name;
public function __construct(string $name)
{
$this->name = $name;
}
}
// Property promotion (8.0+):
class User
{
public function __construct(
public string $name,
public int $age,
) {}
}
$u = new User('Rex', 5);
echo $u->name; // Rex
// Promoted parameters automatically become properties

Visibility

`public` is open, `protected` is subclass-visible, `private` is class-only — default is `public`.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class BankAccount
{
public string $owner = '';
protected float $balance = 0;
private string $secret = 'xx';
public function deposit(float $n): void
{
$this->balance += $n;
}
}
class Child extends BankAccount
{
public function peek(): float
{
return $this->balance; // protected is accessible
}
}
$acc = new BankAccount();
$acc->owner = 'Rex';
// $acc->balance = 1; // not accessible

Inheritance

`extends` inherits from the parent; override methods; `parent::` calls the parent version.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Animal
{
public function __construct(
protected string $name,
) {}
public function speak(): string
{
return "$this->name: ...";
}
}
class Dog extends Animal
{
public function speak(): string
{
return parent::speak() . ' woof';
}
}
$dog = new Dog('旺财');
echo $dog->speak(); // 旺财: ... woof
// Subclass overrides parent methods and properties
// final methods cannot be overridden

Interfaces

`interface` defines method signatures; `implements` fulfils them. A class can implement many.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
interface Speakable
{
public function speak(): string;
}
interface Runnable
{
public function run(): void;
}
// Implement multiple interfaces:
class Robot implements Speakable, Runnable
{
public function speak(): string
{
return 'beep';
}
public function run(): void
{
echo "running\n";
}
}
// Interfaces can extend other interfaces:
// interface X extends Speakable {}
// Program to interfaces for easy substitution

Abstract classes

`abstract class` cannot be instantiated; abstract methods must be implemented by subclasses.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
abstract class Shape
{
abstract public function area(): float;
public function describe(): string
{
return 'area: ' . $this->area();
}
}
class Circle extends Shape
{
public function __construct(
private float $r,
) {}
public function area(): float
{
return 3.14159 * $this->r * $this->r;
}
}
$c = new Circle(2);
echo $c->describe(); // area: 12.56636

trait reuse

`trait` is horizontal reuse across classes; `use` brings it in; you can combine several traits.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
trait Loggable
{
public function log(string $msg): void
{
echo "[LOG] $msg\n";
}
}
trait Timestampable
{
public function createdAt(): string
{
return date('Y-m-d H:i:s');
}
}
class Order
{
use Loggable, Timestampable;
public function create(): void
{
$this->log('create order');
}
}
$order = new Order();
$order->create(); // [LOG] create order
// Methods defined in the class take precedence over traits

Static members

`static` properties and methods belong to the class, not instances; access via `self::` or `ClassName::`.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Counter
{
private static int $total = 0;
public static function hit(): void
{
self::$total++;
}
public static function get(): int
{
return self::$total;
}
}
Counter::hit();
Counter::hit();
echo Counter::get(); // 2
// No $this inside static methods:
// cannot access non-static members
// Static properties are shared across instances
// late static binding uses static::

readonly properties

`readonly` properties (8.1+) can be assigned only at declaration or in the constructor — read-only afterwards.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Point
{
public function __construct(
public readonly float $x,
public readonly float $y,
) {}
}
$p = new Point(1.0, 2.0);
echo $p->x; // 1
// $p->x = 3; // readonly error
// Readonly class (8.2+):
readonly class Config
{
public function __construct(
public string $dsn,
) {}
}
// Great for value objects and immutable config
// property promotion + readonly is a common pairing

11.Error Handling

The exception hierarchy, try/catch/finally, custom exceptions, and error handling.

try / catch

Exceptions thrown in `try` are caught by `catch` — execution continues after the catch block.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
try {
$n = 1 / 0; // 8.0+ throws DivisionByZeroError
} catch (\DivisionByZeroError $e) {
echo 'division by zero';
}
// Catch an exception:
try {
throw new \RuntimeException('something went wrong');
} catch (\RuntimeException $e) {
echo $e->getMessage(); // something went wrong
}
// Uncaught exceptions become fatal errors
// the script terminates with exit code 255

Throwable hierarchy

`Throwable` is the base; `Error` is engine-level; `Exception` is application-level.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Throwable
// ├── Error (engine-level)
// │ ├── TypeError
// │ ├── ValueError
// │ └── DivisionByZeroError
// └── Exception (program-level)
// ├── LogicException
// └── RuntimeException
// Catch any error/exception:
try {
risky();
} catch (\Throwable $e) {
echo $e->getMessage();
}
// Error and Exception both implement Throwable
// catch them separately for precise handling

Custom exceptions

Extend `Exception` or `RuntimeException` to define business exceptions with extra fields.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class ValidationException extends \RuntimeException
{
public function __construct(
public readonly string $field,
string $message = '',
) {
parent::__construct(
$message ?: "field {$field} is invalid",
);
}
}
// Throw:
throw new ValidationException('email');
// Read the field on catch:
try {
validate($input);
} catch (ValidationException $e) {
echo $e->field; // email
echo $e->getMessage();
}

Multiple catch

Separate multiple exception types in `catch` with `|`; order matters — subclass before parent.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
try {
parse($input);
} catch (\InvalidArgumentException | \LengthException $e) {
// handle both exception types the same way
echo $e->getMessage();
} catch (\Exception $e) {
// handle other exceptions separately
logError($e);
}
// Matching is top-down:
// write the specific exceptions before the parent
// 8.0+ can omit the variable name:
// catch (\Exception) { ... }

finally

`finally` runs whether or not an exception is thrown — perfect for resource cleanup.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function process(): string
{
$conn = openConnection();
try {
return doWork($conn);
} finally {
$conn->close(); // always runs
}
}
// finally runs even when returning
// Release file handles:
$h = fopen('a.txt', 'r');
try {
// processing
} finally {
fclose($h);
}
// You can use finally alone, without catching

Global exception handler

`set_exception_handler` is the catch-all for uncaught exceptions — central logging and response.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
set_exception_handler(function (\Throwable $e): void {
// log it:
error_log($e->getMessage());
// return 500:
http_response_code(500);
echo 'the server is having a moment';
});
// All subsequent uncaught exceptions go here
// Restore the default handler:
restore_exception_handler();
// set_error_handler handles non-exception errors
// 8.0+ most errors are also exceptions

Error vs Exception

Engine errors are `Error` (types/args); application problems are `Exception` — catch them separately.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// TypeError: type mismatch:
function add(int $a, int $b): int
{
return $a + $b;
}
try {
add('a', 'b'); // throws TypeError
} catch (\TypeError $e) {
echo 'argument type error';
}
// ValueError: invalid value:
try {
array_chunk([1, 2], 0); // throws ValueError
} catch (\ValueError $e) {
echo 'length must be positive';
}
// Most engine errors extend Error
// catch them separately from Exception

Exception chaining

When throwing a new exception from `catch`, pass the original as the third argument; `getPrevious` walks the chain.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
try {
$data = fetchFromApi();
} catch (\HttpException $e) {
throw new \RuntimeException(
'failed to fetch data',
0,
$e, // previous exception
);
}
// Trace the cause:
try {
loadAll();
} catch (\RuntimeException $e) {
$prev = $e->getPrevious();
if ($prev) {
echo $prev->getMessage();
}
}
// Log the full call stack:
// error_log($e->getTraceAsString());

12.File I/O

Reading and writing files, JSON, CSV, and the standard streams.

Reading files

`file_get_contents` reads an entire file into a string — first choice for small files; returns `false` on failure.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Read the entire file:
$text = file_get_contents('data.txt');
if ($text === false) {
echo 'read failed';
}
// Read into an array (one element per line):
$lines = file('log.txt');
// Ignore newlines and empty lines:
$lines = file(
'log.txt',
FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES,
);
// Remote read (requires allow_url_fopen):
$json = file_get_contents('https://example.com/api');

Writing files

`file_put_contents` writes a string; pass `FILE_APPEND` to append; it returns the bytes written.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Overwrite write:
$n = file_put_contents('out.txt', 'Hello!');
// Append mode:
file_put_contents('log.txt', "more\n", FILE_APPEND);
// Write an array:
$lines = ['a', 'b'];
file_put_contents('list.txt', implode("\n", $lines));
// Check write failure:
if (file_put_contents('x.txt', 'data') === false) {
echo 'write failed';
}
// Permission issues return false

Line-by-line reading

`fopen` + `fgets` read line by line with constant memory — ideal for large files.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$h = fopen('big.log', 'r');
if ($h === false) {
exit('cannot open file');
}
while (($line = fgets($h)) !== false) {
// strip trailing newline:
$line = rtrim($line, "\r\n");
process($line);
}
fclose($h);
// A generator wrapper is friendlier:
// function lines(string $f): Generator
// Stream large files; do not read them whole

Open modes

`fopen` modes `r`/`w`/`a` control read/write; `w` truncates the existing file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// r: read-only, file must exist
$h = fopen('a.txt', 'r');
// r+: read-write, file must exist
$h = fopen('a.txt', 'r+');
// w: write, truncate or create
$h = fopen('a.txt', 'w');
// a: append, write to end
$h = fopen('a.txt', 'a');
// b: binary mode (needed on Windows)
$h = fopen('img.bin', 'wb');
// Remember to fclose after writing
// handle leaks accumulate in long-running processes

JSON encode/decode

`json_encode` makes JSON; `json_decode` parses it. Pass `true` as the second argument to get arrays.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$data = ['name' => 'Rex', 'age' => 5];
// Encode:
$json = json_encode($data);
// {"name":"Rex","age":5}
// Pretty-print and keep Unicode unescaped:
json_encode(
$data,
JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE,
);
// Decode into array:
$arr = json_decode($json, true);
// Decode into object:
$obj = json_decode($json);
// Check error:
if (json_last_error() !== JSON_ERROR_NONE) {
echo json_last_error_msg();
}

CSV read/write

`fgetcsv` parses a CSV row; `fputcsv` writes one — quoting and escaping are handled for you.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Read:
$h = fopen('data.csv', 'r');
$headers = fgetcsv($h);
while (($row = fgetcsv($h)) !== false) {
$item = array_combine($headers, $row);
echo $item['name'];
}
fclose($h);
// Write:
$h = fopen('out.csv', 'w');
fputcsv($h, ['name', 'age']);
fputcsv($h, ['Rex', 5]);
fclose($h);
// Commas/quotes are escaped automatically
// delimiter can be customized: fgetcsv($h, 0, ';')

Directories & glob

`glob` matches files by pattern; `scandir` lists a directory; `mkdir` creates one.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Glob match files:
foreach (glob('data/*.csv') as $file) {
echo $file;
}
// List a directory:
$items = scandir('.');
// Type checks:
is_dir('src'); // true
is_file('a.txt'); // true
// Create directory (including parents):
mkdir('logs/2026', 0755, true);
// Delete a file:
unlink('tmp.txt');
// Check existence:
file_exists('a.txt');

Standard streams

`STDIN`, `STDOUT`, `STDERR` are constants for the standard streams — common in CLI scripts.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Read one line of input:
$line = fgets(STDIN);
$name = trim($line);
// Write to stdout:
fwrite(STDOUT, "hello $name\n");
// Write to stderr:
fwrite(STDERR, "warning: something went wrong\n");
// Process line by line:
while (($line = fgets(STDIN)) !== false) {
echo strtoupper($line);
}
// Pair with a pipe:
// echo "hi" | php app.php

13.Common Pitfalls

The pitfalls you are most likely to hit in everyday development, and how to write it correctly.

== vs ===

`==` is loose (coerces); `===` is strict (type + value). Prefer `===` for comparisons.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// BAD: surprises from loose comparison
0 == 'foo'; // true
0 == ''; // true
'1' == 1; // true
// GOOD: strict comparison
0 === 'foo'; // false
'1' === 1; // false
// Checking zero requires strictness:
$count = 0;
if ($count == false) { /* misfires */ }
if ($count === 0) { /* correct */ }
// Convention: use === unless there's a specific reason not to

foreach reference pitfall

After `foreach` modifies with `&`, the last element still aliases the array — reusing `$item` will pollute it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$items = [1, 2, 3];
// BAD: $item keeps its reference after the loop
foreach ($items as &$item) {
$item *= 2;
}
// reusing $item later accidentally mutates the array
// GOOD: unset to break the reference
foreach ($items as &$item) {
$item *= 2;
}
unset($item);
// Or update via index:
foreach ($items as $i => $v) {
$items[$i] = $v * 2;
}

isset & key existence

`isset` treats a key whose value is `null` as missing; `array_key_exists` reports true existence.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$arr = ['a' => null];
// BAD: key exists but value is null
isset($arr['a']); // false
// GOOD: check whether the key actually exists
array_key_exists('a', $arr); // true
// isset is good for pre-read checks:
$name = isset($arr['b']) ? $arr['b'] : 'default';
// ?? is equivalent to isset plus a default:
$name = $arr['b'] ?? 'default';
// When you need to tell null apart from missing, use array_key_exists

Weak-comparison pitfall

Weak comparisons have surprising string/number rules — `'0' == false` is true; convert explicitly.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// BAD: surprises from weak comparison
'0' == false; // true
'0' == null; // true
'abc' == 0; // true
'1e3' == 1000; // true (scientific notation)
// GOOD: coerce explicitly before comparing
(int) 'abc' === 0; // makes intent clear
filter_var('1e3', FILTER_VALIDATE_INT);
// For hash comparison use hash_equals:
// hash_equals($a, $b) prevents timing attacks
// Watch the return types from database queries

Undefined-key warning

Reading a missing array key triggers a warning (8.0+); use `??` or `isset` first.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$user = ['name' => 'Rex'];
// BAD: accessing a missing key directly
$city = $user['city'];
// PHP Warning: Undefined array key
// GOOD: null coalescing for a default
$city = $user['city'] ?? 'unknown';
// Or check then access:
$city = isset($user['city'])
? $user['city']
: '';
// Default parameter values:
function f(array $cfg = []) {
$x = $cfg['x'] ?? 0;
}

Output before header()

Once output has been sent, `header()` fails — either send headers first or use output buffering.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// BAD: output before sending response headers
echo "hello\n";
// header('Location: /login'); // error
// GOOD: send response headers first, then output
header('Location: /login');
exit;
// When you cannot control output, start buffering:
ob_start();
echo "later\n";
ob_end_flush();
// Check whether headers were already sent:
// headers_sent()

empty vs isset

`empty` is true for `0`, `''`, `'0'`, …; `isset` only cares about `null` — pick the one that fits your meaning.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$age = 0;
$name = '';
// BAD: empty treats 0 as empty
if (empty($age)) { /* misfires as not set */ }
// GOOD: the number 0 is a valid value
if (!isset($age) || $age === '') { /* not set */ }
// empty semantics:
empty(0); // true
empty('0'); // true
empty(''); // true
empty(null); // true
// Form empty checks:
// trim($val) !== '' is more explicit

SQL injection prevention

String-concatenated SQL invites injection — use PDO prepared statements with bound parameters.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$id = $_GET['id'];
// BAD: string-concatenated SQL (injection risk)
// $sql = "SELECT * FROM users WHERE id = $id";
// GOOD: prepared statements with bound parameters
$pdo = new PDO($dsn, $user, $pass);
$stmt = $pdo->prepare(
'SELECT * FROM users WHERE id = ?',
);
$stmt->execute([$id]);
$rows = $stmt->fetchAll();
// Named parameters:
$stmt = $pdo->prepare(
'SELECT * FROM users WHERE id = :id',
);
$stmt->execute([':id' => $id]);
// Input validation:
// filter_var($id, FILTER_VALIDATE_INT)

14.Concurrency & Coroutines

PHP's concurrency models: multiple processes, coroutines, Fiber, and third-party extensions.

Process model

PHP is single-threaded by default; each request runs in its own process and variables die with it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// PHP is single-threaded by default:
// each request runs in its own process/thread
// Shared memory is limited, copy-on-write:
// child processes do not duplicate the full memory
// No shared-state issues:
// variables are released when the request ends
// Concurrency options:
// 1. multi-process via pcntl_fork
// 2. coroutines: Fiber / Swoole
// 3. real threads via the parallel extension
// CLI server mode:
// $ php -S localhost:8000

pcntl_fork multi-process

`pcntl_fork` spawns a child process; both run independently — CLI only.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// CLI only, requires the pcntl extension:
// $ php -m | grep pcntl
$pid = pcntl_fork();
if ($pid === -1) {
exit('fork failed');
}
if ($pid === 0) {
// child process: return value is 0
echo "child PID " . getmypid() . "\n";
exit(0);
}
// parent: wait for the child
pcntl_wait($status);
echo "parent PID " . getmypid() . "\n";
// children share code and a memory snapshot
// changes do not affect each other (copy-on-write)

Signal handling

`pcntl_signal` registers a handler; `dispatch` flushes pending signals.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
declare(ticks = 1);
// Register a signal handler:
pcntl_signal(SIGINT, function (int $signo): void {
echo "received signal $signo, exiting\n";
exit(0);
});
// Dispatch pending signals:
while (true) {
pcntl_signal_dispatch();
usleep(100000);
}
// Common signals:
// SIGTERM terminate, SIGINT Ctrl+C
// SIGHUP hangup, SIGCHLD child exited
// Listen for signals in production for graceful shutdown

parallel extension

`parallel` is a PECL extension providing real multithreaded workers — not built in.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// parallel is a PECL extension, not bundled:
// $ pecl install parallel
use parallel\Runtime;
use parallel\Future;
// Launch a worker thread:
$rt = new Runtime();
$future = $rt->run(function (int $n): int {
return $n * $n;
}, [7]);
// Block on the result:
$result = $future->value(); // 49
// Shared values must be passed explicitly
// closures cannot capture outer variables
// Good for CPU-bound tasks in production
// web environments usually prefer multi-process for stability

Swoole coroutines

Swoole is a high-performance network framework with coroutine scheduling and resident memory — great for high concurrency.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Swoole is a high-performance networking extension:
// $ pecl install swoole
// Create a coroutine HTTP server:
use Swoole\Coroutine\Http\Server;
$server = new Server('0.0.0.0', 9501);
$server->handle('/', function ($req, $res) {
// one coroutine per connection:
$res->end("hello\n");
});
$server->start();
// Resident memory + coroutine scheduling:
// uses far less memory than threads under heavy concurrency
// Also supports multi-process workers:
// configure worker_num on Swoole\Server

Fiber coroutines

Fiber (8.1+) is the native coroutine: `suspend` to pause, `resume` to continue — manually scheduled.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Fiber (8.1+) coroutine, manual switching:
$fiber = new Fiber(function (): void {
$value = Fiber::suspend('first');
echo "resumed, received $value\n";
});
// start, runs until the first suspend:
$value = $fiber->start();
echo "suspend returned $value\n"; // first
// resume and pass a value:
$fiber->resume('second');
// resumed, received second
// Cooperative within a single thread, not parallel
// useful for turning async code into sync-style flow

Async libraries

No built-in `async`/`await` — use event-loop libraries like ReactPHP or Amp for async I/O.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// No built-in async/await keywords:
// use an event-loop library for async I/O
// ReactPHP event loop example:
use React\EventLoop\Factory;
$loop = Factory::create();
$loop->addTimer(1.0, function () {
echo "ok\n";
});
$loop->run();
// Common async abstractions:
// Promise / Deferred / await
// For simple concurrency, multi-process is enough
// for high-concurrency I/O use Swoole/ReactPHP

Inter-process communication

Share data across processes via message queues, files, or shared memory — avoid concurrent edits to the same file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Sharing data across processes:
// 1. files / database
// 2. message queue (Redis, etc.)
// 3. shared memory via shmop
// 4. pipes
// Simple message-queue example:
// Redis list as a task queue:
// $redis->lpush('jobs', $task);
// Send a signal to a child process:
// posix_kill($pid, SIGUSR1);
// Collect worker results:
// each worker writes to its own file
// Common production systems:
// RabbitMQ / Kafka / Redis Stream
// Avoid multiple processes writing to the same file

15.Networking

HTTP clients, curl, URL parsing, sessions, and sockets.

HTTP requests

`file_get_contents` sends simple requests; pair it with `stream_context_create` for timeouts and headers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Simple GET (requires allow_url_fopen):
$body = file_get_contents(
'https://example.com/api',
);
// With a timeout:
$ctx = stream_context_create([
'http' => ['timeout' => 5],
]);
$body = file_get_contents(
'https://example.com',
false,
$ctx,
);
// For production prefer curl or Guzzle
// supports status codes and fine-grained control

curl extension

`curl_init` + `curl_exec` give you full request control, status codes, and error info.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// curl extension (bundled in CLI):
$ch = curl_init(
'https://api.example.com/data',
);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer xxx',
],
]);
$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
// Check errors:
if (curl_errno($ch)) {
echo curl_error($ch);
}
// POST data:
// CURLOPT_POST + CURLOPT_POSTFIELDS

POST & Guzzle

`stream_context` can POST JSON, but in real projects prefer the Guzzle HTTP client.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// POST JSON:
$data = json_encode(['name' => 'Rex']);
$ctx = stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\n",
'content' => $data,
],
]);
$res = file_get_contents(
'https://example.com/api',
false,
$ctx,
);
// Guzzle is more idiomatic for real projects:
// $client->request('POST', $url, [
// 'json' => $payload,
// ]);
// Separate request headers with CRLF

Request & response headers

`header()` sends response headers; `http_response_code` sets the status — both must run before output.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Read response headers:
$ctx = stream_context_create([
'http' => ['ignore_errors' => true],
]);
$body = file_get_contents(
'https://example.com',
false,
$ctx,
);
$headers = $http_response_header ?? [];
// Send response headers:
header('Content-Type: application/json');
header('Cache-Control: max-age=3600');
// Set the status code:
http_response_code(404);
// Read request headers:
// getallheaders() returns an associative array
// Headers must be sent before any output

URL parsing

`parse_url` splits URL parts; `parse_str` parses query strings; `http_build_query` builds them.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$url = 'https://user:[email protected]:8080/a?q=1#top';
// Parse each component:
$parts = parse_url($url);
// [
// 'scheme' => 'https',
// 'host' => 'example.com',
// 'port' => 8080,
// 'path' => '/a',
// 'query' => 'q=1',
// 'fragment' => 'top',
// ]
// Query string:
parse_str('a=1&b=2', $out);
// $out = ['a' => '1', 'b' => '2']
// Build an encoded query string:
http_build_query(['a' => 1, 'b' => 2]);
// a=1&b=2

Cookies & sessions

`session_start` starts a session; `setcookie` writes cookies; read from `$_COOKIE`.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Start a session:
session_start();
// auto-generates the session cookie
// Read/write session data:
$_SESSION['user_id'] = 123;
echo $_SESSION['user_id'];
// Set a cookie:
setcookie('theme', 'dark', [
'expires' => time() + 3600,
'path' => '/',
'httponly' => true,
'samesite' => 'Lax',
]);
// Read:
echo $_COOKIE['theme'] ?? 'default';
// Destroy the session:
session_destroy();
// Session data lives on the server; the cookie only holds the id

Sockets

`stream_socket_client` connects to a remote host; `fwrite` sends and `fgets` reads.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Client socket:
$sock = stream_socket_client(
'tcp://example.com:80',
$errno,
$errstr,
10,
);
if (!$sock) {
echo "connect failed: $errstr";
}
fwrite($sock, "GET / HTTP/1.0\r\n\r\n");
while (!feof($sock)) {
echo fgets($sock);
}
fclose($sock);
// Server listen:
// stream_socket_server('tcp://0.0.0.0:9000')
// Use Swoole for production protocol servers
// hand-rolled sockets are prone to boundary issues

SSE & WebSocket

SSE is one-way text streaming; WebSocket is bidirectional — choose by need.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// SSE: server pushes a text stream
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
while (true) {
echo "data: " . json_encode(['t' => time()]) . "\n\n";
flush();
sleep(1);
}
// Client receives via EventSource
// one-way over HTTP, simple and reliable
// WebSocket for bidirectional:
// requires extensions such as Swoole or Ratchet
// Pick SSE for simple push
// use WebSocket only when you need bidirectional

16.Date & Time

Timestamps, formatting, parsing, time zones, and date arithmetic.

Getting current time

`time()` returns the Unix timestamp in seconds; `microtime(true)` adds millisecond precision; `date` formats it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Current timestamp (seconds):
$ts = time();
// Millisecond-level:
$ms = microtime(true);
// Format the current time:
echo date('Y-m-d H:i:s'); // 2026-08-02 12:30:00
// Read the default timezone:
date_default_timezone_get();
// Set the timezone:
// date_default_timezone_set('Asia/Shanghai');
// Old-style: passing time() as date()'s second arg is equivalent

Formatting dates

`date()` renders a timestamp with format specifiers — year, month, day, time and locale-friendly variants.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ts = strtotime('2026-08-02 12:30:00');
// Format:
echo date('Y-m-d', $ts); // 2026-08-02
echo date('H:i:s', $ts); // 12:30:00
echo date('D, d M Y', $ts); // Sun, 02 Aug 2026
echo date('Y\u5e74m\u6708d\u65e5', $ts); // 2026年08月02日
// Common format characters:
// Y four-digit year, m two-digit month, d two-digit day
// H 24-hour, i minutes, s seconds
// D weekday short, M month short
// To timestamp (reverse):
// strtotime('2026-08-02')

Parsing dates

`strtotime` parses date strings and relative phrases; returns `false` on failure.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Parse a date string into a timestamp:
$ts = strtotime('2026-08-02 12:30:00');
echo $ts;
// Relative times:
strtotime('+1 day'); // tomorrow
strtotime('next Monday'); // next Monday
strtotime('-2 weeks'); // two weeks ago
// Parse failure returns false:
$bad = strtotime('not a date');
// false
// For complex parsing use DateTime:
// new DateTime('2026-08-02 12:30:00')
// Be aware that timezone affects the parse result

Timestamps

A Unix timestamp is UTC seconds; `DateTime` converts between timestamps and objects.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Timestamp (epoch seconds):
$ts = time();
// Build a specific moment:
$dt = new DateTime('2026-08-02 12:30:00');
$ts = $dt->getTimestamp();
// Format a timestamp:
$dt = new DateTime();
$dt->setTimestamp($ts);
echo $dt->format('Y-m-d');
// Millisecond timestamp (server-side):
$ms = (int) (microtime(true) * 1000);
// Timestamps are UTC seconds
// display is converted to the local timezone

Time zones

`date_default_timezone_set` sets the default zone; a `DateTime` can carry its own zone.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Set the default timezone:
date_default_timezone_set('Asia/Shanghai');
// List all timezones:
// DateTimeZone::listIdentifiers()
// DateTime with a specific timezone:
$dt = new DateTime('now', new DateTimeZone('UTC'));
echo $dt->format('Y-m-d H:i:s');
// Convert timezones:
$dt->setTimezone(new DateTimeZone('Asia/Tokyo'));
echo $dt->format('Y-m-d H:i:s P');
// In production always store UTC
// convert to the user's timezone on display
// Do not guess the timezone via date('T')

Intervals

`DateInterval` represents a duration; `DateTime::add`/`sub` shifts a date by one.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Difference between two moments:
$start = new DateTime('2026-08-01');
$end = new DateTime('2026-08-02');
$diff = $start->diff($end);
echo $diff->days; // 1
// Add time:
$dt = new DateTime('2026-08-02');
$dt->add(new DateInterval('P3D')); // add 3 days
echo $dt->format('Y-m-d'); // 2026-08-05
// Subtract time:
$dt->sub(new DateInterval('PT2H')); // subtract 2 hours
// DateInterval format:
// P period, D days, T time section
// P1Y one year, P2M two months, PT1H one hour
// Convert interval to total days:
// $diff->days gives the total number of days

Date diff

`DateTime::diff` returns a `DateInterval` — useful for ages, days remaining, etc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Compute age:
$birth = new DateTime('1990-05-20');
$now = new DateTime();
$diff = $birth->diff($now);
echo $diff->y . ' years'; // whole years
// Diff fields:
$diff->y; // years
$diff->m; // months
$diff->d; // days
$diff->h; // hours
$diff->i; // minutes
// Human-readable format:
echo $diff->format('%y years %m months %d days');
// Total seconds:
// $diff->s + $diff->i * 60 + ...
// For negatives flip the invert flag

Sleep & timers

`sleep` blocks; event-loop timers schedule delayed tasks without blocking.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Sleep (seconds):
sleep(2);
// Microsecond sleep:
usleep(500000); // 0.5 seconds
// Deferred task (ReactPHP):
use React\EventLoop\Factory;
$loop = Factory::create();
$loop->addTimer(1.0, function () {
echo "ran after 1 second\n";
});
$loop->run();
// Swoole timer:
// Swoole\Timer::tick(1000, fn() => ...)
// Beware: sleep blocks in long-running CLI tasks
// use event-loop timers for high-concurrency scenarios

17.Processes & CLI

Command-line arguments, input and output, environment variables, and process control.

Command-line arguments

`$argv` holds command-line arguments; `$argv[0]` is the script name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// $ php app.php arg1 arg2
// $argv[0] = 'app.php'
// $argv[1] = 'arg1'
// $argv[2] = 'arg2'
// Argument count:
echo count($argv); // 3
// Iterate ignoring the script name:
foreach (array_slice($argv, 1) as $arg) {
echo $arg;
}
// Option parsing:
// getopt('a:b::') see next section
// Read stdin:
// fgets(STDIN)

Reading input

`fgets(STDIN)` reads one line; `stream_get_contents` reads everything; `trim` strips the newline.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Interactive input:
echo 'enter your name: ';
$name = trim(fgets(STDIN));
// Read line by line in bulk:
while (($line = fgets(STDIN)) !== false) {
echo strtoupper($line);
}
// Read all input:
$all = stream_get_contents(STDIN);
// Detect piped input:
// stream_isatty(STDIN)
// Input may be empty:
// check posix_isatty before prompting

Running external commands

`shell_exec` captures output; `exec` returns the status code; use `escapeshellarg` to escape arguments.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Run an external command and return output:
$out = shell_exec('ls -la');
echo $out;
// Get the exit status code:
exec('git status', $lines, $code);
// $code 0 means success
// Array output, one element per line:
// $lines holds each line as an element
// Never concatenate user input into a command:
// $cmd = 'rm ' . $_GET['f']; // dangerous
// Pass arguments with escapeshellarg:
// exec('cat ' . escapeshellarg($file))

Environment variables

`getenv` reads env vars; `putenv` sets them — sensitive config belongs in env vars.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Read an environment variable:
echo getenv('HOME');
// Set an environment variable:
putenv('APP_ENV=production');
echo getenv('APP_ENV');
// Read them all:
print_r(getenv());
// $_ENV superglobal:
echo $_ENV['PATH'] ?? '';
// Common library for .env files:
// vlucas/phpdotenv
// Keep secrets in environment variables
// never hardcode them into the codebase

Process info

`getmypid` returns the current PID; the `posix_*` family checks user / parent; `hrtime` measures time.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Current process PID:
$pid = getmypid();
// Parent PID:
echo posix_getppid();
// Current user:
echo posix_getuid();
echo posix_getpwuid(posix_getuid())['name'];
// Elapsed time:
// use hrtime() for microseconds:
$start = hrtime(true);
work();
$ms = (hrtime(true) - $start) / 1e6;
echo "$ms ms";
// CLI process info:
// command name / proc/self/cmdline

Process signals

`pcntl_signal` controls signal handling; `pcntl_wait` waits for child exit.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Ignore a signal:
pcntl_signal(SIGTERM, SIG_IGN);
// Default handler:
pcntl_signal(SIGTERM, SIG_DFL);
// Wait for children (non-blocking):
$pid = pcntl_fork();
if ($pid === 0) { exit(0); }
// Blocking wait:
pcntl_wait($status);
// Inspect the exit code:
echo pcntl_wexitstatus($status);
// Send a signal to a process:
// posix_kill($pid, SIGTERM)
// Avoid complex work inside signal handlers

Pipes & terminal

`stream_isatty` detects an interactive terminal; `STDERR` shows progress; `exit` sets the code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Read from stdin via a pipe:
// echo "hi" | php app.php
// Redirect output streams:
// php app.php > out.txt 2>&1
// Check whether the terminal is interactive:
if (stream_isatty(STDIN)) {
echo "interactive mode\n";
} else {
echo "piped input\n";
}
// Write progress to stderr:
fwrite(STDERR, "processing...\n");
// Run in the background and return:
// $ php app.php &
// Exit codes:
exit(0); // success
exit(1); // failure

Exit codes

`exit()` takes a status code: 0 for success, non-zero for failure; shell reads it via `$?`.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Normal exit:
exit(0);
// Business error:
if (!$data) {
exit(1); // generic error
}
// Custom codes:
exit(2); // usage error
exit(3); // invalid input
// Early exit inside a script:
return; // or exit
// Check from shell:
// php app.php
// echo $? // last command's exit code
// 0 success, non-zero failure
// 128+ means terminated by a signal

18.Regular Expressions

Matching, capturing, replacing, splitting, and multibyte patterns.

preg_match

`preg_match` finds the first match; capture groups land in `$m`; named groups by name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Match the first result:
$s = 'order A123 and B456';
if (preg_match('/[A-Z]\d{3}/', $s, $m)) {
echo $m[0]; // A123
}
// Capture groups:
preg_match('/([A-Z])(\d{3})/', $s, $m);
echo $m[1]; // A
echo $m[2]; // 123
// Named groups:
preg_match('/(?<type>[A-Z])(?<num>\d{3})/', $s, $m);
echo $m['type']; // A
// A bad regex returns false

preg_match_all

`preg_match_all` finds every match — default grouping is per-group; `PREG_SET_ORDER` groups per match.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$s = 'order A123 and B456';
preg_match_all('/[A-Z]\d{3}/', $s, $m);
print_r($m[0]);
// ['A123', 'B456']
// Group structure:
preg_match_all('/([A-Z])(\d{3})/', $s, $m);
// $m[0] full matches
// $m[1] group 1
// $m[2] group 2
// Per-match key/value:
preg_match_all(
'/(\w+)=(\d+)/',
'a=1&b=2',
$m,
PREG_SET_ORDER,
);
// $m[0] = ['a=1', 'a', '1']
// The second argument controls the return shape

preg_replace

`preg_replace` replaces via regex with `$1`-style backrefs; `preg_replace_callback` uses a callback.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Replace matches:
$s = 'phone 138-1234-5678';
$r = preg_replace('/\d{3}-\d{4}-\d{4}/', '[hidden]', $s);
// 'phone [hidden]'
// Reconstruct via capture groups:
preg_replace(
'/(\d{4})(\d{4})/',
'$1-$2',
'13812345678',
);
// '1381-2345-678' (example)
// Backreference:
preg_replace('/(\w+)\s+\1/', '$1', 'hi hi there');
// 'hi there'
// Array-based bulk replacement:
preg_replace(['/a/', '/b/'], ['A', 'B'], 'abc');
// Callback replacement:
preg_replace_callback('/\d+/', fn($m) => $m[0] * 2, '1 2');

Common patterns

Character classes, quantifiers and greediness — plus delimiters and escaping rules.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Character classes:
\d // digit [0-9]
\w // word character [A-Za-z0-9_]
\s // whitespace
// Quantifiers:
* // 0 or more
+ // 1 or more
? // 0 or 1
{n,m} // between n and m times
// Greedy vs lazy:
'<b>x</b>'
/<.*>/ // greedy: <b>x</b>
/<.*?>/ // lazy: <b>
// Escape:
\. \( \) \\ // need a \\ prefix
// Delimiters:
// the first character is the delimiter, modifiers go at the end
// inside '/' escape as '\/'

Captures & assertions

Capture groups, non-capturing groups, named groups, and lookaround assertions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Capture group:
/(\d{4})-(\d{2})/
// matches 2026-08
// Non-capturing group:
/(?:ab)+/
// groups without capturing, better performance
// Named group:
/(?P<year>\d{4})-(?P<month>\d{2})/
// $m['year']
// Backreference:
/(\w+)\s+\1/
// \1 references group 1's content
// Assertions:
/(?=\d{5}$)/ // lookahead
/(?<=^\d{3})/ // lookbehind
// Alternation inside a group:
/gr(a|e)y/

Anchors & boundaries

`^` line start, `$` line end, `\b` word boundary, `m` modifier for multiline.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Start/end of line:
/^php/ // starts with php
/php$/ // ends with php
// Word boundary:
/\bcat\b/ // matches the standalone word cat
// does not match category or concatenate
// Multi-line mode:
// m modifier makes ^ and $ match each line
preg_match_all(
'/^\d+/m',
"12\nabc\n34",
$m,
);
// ['12', '34']
// Exact match:
// /^...$/ plus \z to guard against trailing newlines
// Word boundaries do not apply to Chinese characters

Regex split

`preg_split` splits by regex — it can keep delimiters and limit the number of pieces.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Split by regex:
$parts = preg_split('/[,;]/', 'a,b;c');
// ['a', 'b', 'c']
// Keep the delimiters:
preg_split('/(,)/', 'a,b', -1, PREG_SPLIT_DELIM_CAPTURE);
// ['a', ',', 'b']
// Limit the number of pieces:
preg_split('/\s+/', 'a b c d', 2);
// ['a', 'b c d']
// Split on whitespace:
preg_split('/\s+/', ' a b ');
// ['', 'a', 'b'] (empty edges)
// To drop empty pieces combine with array_filter
// For simple delimiters explode is enough

Multibyte & u modifier

Add the `u` modifier for Chinese text; `preg_quote` escapes user input.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Use the u modifier for Chinese text:
preg_match('/\p{Han}+/u', '你好世界abc', $m);
echo $m[0]; // 你好世界
// Multibyte character classes:
/\p{Han}/ // Han characters
/\p{L}/ // any letter
/\p{N}/ // any number
// Escaping user input (critical):
// quote user input before embedding in a regex:
$lit = preg_quote($_GET['q'], '/');
// Match using it:
preg_match(
'/' . $lit . '/u',
'a+b',
$m,
);
// The u modifier avoids garbled output

19.Build & Toolchain

Composer dependency management, autoloading, and CLI build workflows.

Initializing a project

`composer init` generates `composer.json` with dependencies and autoload rules.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Initialize a project:
// $ composer init
// Generates composer.json:
// {
// "name": "vendor/demo",
// "type": "project",
// "require": { "php": "^8.1" },
// "autoload": { "psr-4": { "App\\": "src/" } }
// }
// Generated through an interactive wizard
// then run composer install
// Declare the PHP version constraint under require
// ^8.1 means 8.1.x or newer

Installing dependencies

`composer install` installs from the lock file; `composer require` adds new dependencies.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Install dependencies:
// $ composer install
// Install production dependencies only:
// $ composer install --no-dev
// Install a single package:
// $ composer require monolog/monolog
// Remove:
// $ composer remove monolog/monolog
// Update to the latest versions within constraints:
// $ composer update
// Check for dependency issues:
// $ composer validate
// $ composer audit
// Use --no-dev in production
// reduces attack surface and size
// composer.lock must be committed
// pinning exact versions enables reproducible builds

Using dependencies

After `require vendor/autoload.php`, `use` any dependency class — autoloading just works.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Using dependencies inside the project:
require __DIR__ . '/vendor/autoload.php';
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$log = new Logger('app');
$log->pushHandler(
new StreamHandler('php://stderr'),
);
$log->info('startup complete');
// Autoloader is generated on install
// rerun after every autoload config change:
// $ composer dump-autoload
// Class-to-file mapping:
// PSR-4 namespaces map to directories

PSR-4 autoloading

Namespaces map one-to-one to directories; `App\` → `src/` — run `dump-autoload` after edits.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// composer.json:
// {
// "autoload": {
// "psr-4": {
// "App\\": "src/"
// }
// }
// }
// src/User.php:
// namespace App;
// class User {}
// After autoloading:
use App\User;
$u = new User();
// Namespace-to-path one-to-one mapping:
// App\Controller\Home
// → src/Controller/Home.php
// Regenerate after config changes:
// $ composer dump-autoload
// PSR-4 only maps the class prefix
// other namespaces are unaffected

Composer scripts

`composer.json`'s `scripts` define command shortcuts and lifecycle hooks.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// composer.json scripts:
// {
// "scripts": {
// "test": "phpunit",
// "lint": "php -l src/*.php"
// }
// }
// Run:
// $ composer test
// $ composer lint
// Built-in hooks:
// pre-install-cmd, post-install-cmd
// Lifecycle callbacks:
// run cleanup / permission tweaks before and after install
// Organize custom commands under scripts
// avoids having to remember long commands
// Watch cross-platform paths
// use / instead of backslashes

Version constraints

Constraints like `^`, `~`, `>=` control version ranges; the lock file pins exact versions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Version constraint syntax:
// ^8.1 allows 8.1.x (>=8.1 <9)
// ~8.1 allows 8.1.x (<8.2)
// >=8.1 any version >=8.1
// * any version
// 1.2.3 exact version
// Branch references:
// dev-master
// v2.0.0-beta
// Pin exact versions:
// composer.lock records the actual versions
// Install a specific version:
// $ composer require foo/bar:^2.0
// See what can be upgraded:
// $ composer outdated
// In production deploy using the locked versions

CLI publish commands

Framework commands (artisan / console) run migrations, cache tasks and scheduled jobs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// List available commands:
// $ php bin/console list
// Run a custom command:
// $ php artisan migrate
// $ php bin/console cache:clear
// Generate the app key:
// $ php artisan key:generate
// Database migrations:
// $ php artisan migrate --seed
// Scheduled tasks:
// $ crontab -e
// * * * * * php /path/app/cron.php
// In production add --env=production
// integrate with CI for automated deployments
// Common framework command prefixes:
// Symfony: bin/console
// Laravel: artisan

PHP configuration

`php -i` / `php --ini` / `php -m` inspect the runtime; `ini_get` reads a setting.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Inspect config:
// $ php -i full config
// $ php --ini loaded ini paths
// $ php -m loaded extensions
// Check an extension:
// $ php -m | grep curl
// Read at runtime:
echo ini_get('memory_limit');
echo ini_get('upload_max_filesize');
// Adjust at runtime:
ini_set('memory_limit', '512M');
// Pass config via CLI:
// $ php -d error_reporting=E_ALL app.php
// Production checks:
// $ php -v
// $ php --ri opcache

Official Links

Direct links to the official docs and resources.

About this Cheatsheet

This page is a self-contained quick reference for PHP 8.3 covering ~80% of common usage in real projects: the language core and the most-used standard library. It leans toward modern idioms — declare(strict_types=1), enum cases (8.1+), readonly properties, match expressions, named arguments, arrow functions, and first-class callables. For the authoritative reference, see the official PHP manual and PHP The Right Way. 19 sections each focus on a single theme — from your first program through arrays, object-oriented code, and common pitfalls. Each section is split into 8–14 bite-sized topics (5–20 lines of code each) for ~150 topics total. The snippets are deliberately short and self-explanatory. Everything runs in your browser — no uploads, no tracking. This page is part of GuruToolkit's free developer tools; the snippets here are free to use with no warranty.

Version 2.1.0