The PHP syntax you forget between projects — and the modern PHP 8 patterns you need every day. This reference covers variables, arrays, strings, OOP, PDO, Composer, and more.
Quick reference
The 25 patterns that cover 90% of daily PHP work.
| Pattern | Example |
|---|---|
| Variable | $name = "Alice"; |
| String interpolation | "Hello, $name!" |
| Null coalescing | $val = $arr['key'] ?? 'default'; |
| Ternary | $x = $a > 0 ? 'pos' : 'non-pos'; |
| Nullsafe operator | $city = $user?->address?->city; |
| Named arguments | array_slice(array: $arr, offset: 2); |
| Match expression | $label = match($code) { 200 => 'OK', ... }; |
| Spread operator | $merged = [...$a, ...$b]; |
| Arrow function | $double = fn($x) => $x * 2; |
| Array destructuring | [$first, $second] = $arr; |
| List by key | ['name' => $n, 'age' => $a] = $row; |
| Short closure | $fn = fn($x, $y) => $x + $y; |
| Type declaration | function add(int $a, int $b): int |
| Union types | function foo(int|string $val): void |
| Readonly property | public readonly string $name; |
| Constructor promotion | public function __construct(private string $name) |
| Enum | enum Status { case Active; case Inactive; } |
| Fibers | $fiber = new Fiber(fn() => ...); |
str_contains |
str_contains($str, 'needle') |
array_is_list |
array_is_list([1, 2, 3]) // true |
| PDO query | $stmt = $pdo->prepare("SELECT ..."); |
json_encode |
echo json_encode($data, JSON_PRETTY_PRINT); |
array_map |
$doubled = array_map(fn($x) => $x * 2, $arr); |
array_filter |
$evens = array_filter($arr, fn($x) => $x % 2 === 0); |
array_reduce |
$sum = array_reduce($arr, fn($c, $x) => $c + $x, 0); |
Variables and types
<?php
// Scalar types
$int = 42;
$float = 3.14;
$string = "Hello";
$bool = true;
$null = null;
// Type juggling — be explicit with strict_types
declare(strict_types=1);
// Type checking
gettype($int); // "integer"
is_int($int); // true
is_string($string); // true
is_null($null); // true
// Type casting
$str = (string) 42; // "42"
$num = (int) "42abc"; // 42
$bool = (bool) ""; // false
// Constants
const MAX_SIZE = 100;
define('API_URL', 'https://example.com');
PHP 8 adds union types, intersection types, and never return type:
function processId(int|string $id): void { /* ... */ }
function fail(): never {
throw new \RuntimeException("Always fails");
}
Strings
$s = "Hello, World!";
// Length and case
strlen($s); // 13
strtoupper($s); // "HELLO, WORLD!"
strtolower($s); // "hello, world!"
ucfirst("hello"); // "Hello"
ucwords("hello world"); // "Hello World"
// Search and replace
strpos($s, "World"); // 7 (false if not found)
str_contains($s, "World"); // true (PHP 8+)
str_starts_with($s, "Hello"); // true (PHP 8+)
str_ends_with($s, "World!"); // true (PHP 8+)
str_replace("World", "PHP", $s); // "Hello, PHP!"
// Extract and pad
substr($s, 7, 5); // "World"
str_pad("5", 3, "0", STR_PAD_LEFT); // "005"
trim(" hello "); // "hello"
ltrim(" hi"); // "hi"
rtrim("hi "); // "hi"
// Split and join
$parts = explode(",", "a,b,c"); // ["a","b","c"]
implode("-", $parts); // "a-b-c"
// Regex
preg_match('/\d+/', $s, $m); // $m[0] = first match
preg_match_all('/\d+/', $s, $m); // $m[0] = all matches
preg_replace('/\s+/', ' ', $s); // collapse whitespace
preg_split('/[\s,]+/', "a b,c"); // ["a","b","c"]
// Formatting
number_format(1234567.89, 2, '.', ','); // "1,234,567.89"
sprintf("%.2f", 3.14159); // "3.14"
printf("Hello, %s! You are %d.\n", "Alice", 30);
// Heredoc (interpolation) and Nowdoc (no interpolation)
$text = <<<EOT
Hello, $name!
This is a heredoc.
EOT;
$raw = <<<'EOT'
No $interpolation here.
EOT;
Arrays
PHP arrays serve as lists, maps, and everything in between.
// Indexed array
$fruits = ["apple", "banana", "cherry"];
$fruits[] = "date"; // append
count($fruits); // 4
$fruits[0]; // "apple"
// Associative array (map)
$user = ["name" => "Alice", "age" => 30];
$user["email"] = "alice@example.com";
// Multi-dimensional
$matrix = [[1, 2], [3, 4]];
$matrix[0][1]; // 2
// Destructuring
[$first, $second] = $fruits;
["name" => $name, "age" => $age] = $user;
// Common functions
in_array("apple", $fruits); // true
array_search("banana", $fruits); // 1
array_key_exists("name", $user); // true
isset($user["email"]); // true
// Add / remove
array_push($fruits, "elderberry"); // append (prefer $arr[])
array_pop($fruits); // remove last
array_unshift($fruits, "avocado"); // prepend
array_shift($fruits); // remove first
array_splice($fruits, 1, 1, ["berry"]); // replace at index
// Sort
sort($fruits); // reindex
rsort($fruits); // reverse reindex
asort($fruits); // preserve keys
arsort($fruits); // reverse, preserve keys
ksort($user); // sort by key
usort($arr, fn($a, $b) => $a - $b); // custom sort
// Transform
$doubled = array_map(fn($x) => $x * 2, [1, 2, 3]); // [2, 4, 6]
$evens = array_filter([1,2,3,4], fn($x) => $x%2===0); // [2, 4]
$sum = array_reduce([1,2,3], fn($c,$x) => $c+$x, 0); // 6
// Slice, splice, combine
$slice = array_slice($fruits, 1, 2); // 2 items from index 1
$merged = array_merge($fruits, ["fig"]);
$keys = array_keys($user); // ["name","age","email"]
$vals = array_values($user); // values reindexed
$unique = array_unique([1, 2, 2, 3]); // [1, 2, 3]
$flipped = array_flip(["a"=>1,"b"=>2]); // [1=>"a", 2=>"b"]
// Spread (PHP 8.1+: string keys too)
$a = [1, 2, 3];
$b = [4, 5, 6];
$c = [...$a, ...$b]; // [1,2,3,4,5,6]
Control flow
// if / elseif / else
if ($x > 0) {
echo "positive";
} elseif ($x < 0) {
echo "negative";
} else {
echo "zero";
}
// Match expression (PHP 8) — strict, no fall-through
$label = match(true) {
$score >= 90 => 'A',
$score >= 80 => 'B',
$score >= 70 => 'C',
default => 'F',
};
// Switch (legacy, loose comparison)
switch ($status) {
case 'active':
// ...
break;
case 'inactive':
case 'banned':
// fall-through
break;
default:
// ...
}
// Loops
for ($i = 0; $i < 10; $i++) { /* ... */ }
foreach ($fruits as $fruit) { /* ... */ }
foreach ($user as $key => $value) { /* ... */ }
while ($condition) { /* ... */ }
do { /* ... */ } while ($condition);
// Loop controls
break 2; // break out of 2 nested loops
continue; // next iteration
// Null coalescing assignment (PHP 7.4+)
$arr['key'] ??= 'default';
Functions
// Basic function with types (PHP 8)
function add(int $a, int $b): int {
return $a + $b;
}
// Default parameters
function greet(string $name, string $greeting = "Hello"): string {
return "$greeting, $name!";
}
// Variadic arguments
function sum(int ...$nums): int {
return array_sum($nums);
}
sum(1, 2, 3, 4); // 10
// Named arguments (PHP 8)
function createUser(string $name, int $age = 0, string $role = 'user'): array {
return compact('name', 'age', 'role');
}
createUser(name: 'Alice', role: 'admin'); // skip $age
// Return type: void, never, mixed, self
function logMessage(string $msg): void {
error_log($msg);
// no return value
}
// Closures
$multiply = function(int $x, int $y): int {
return $x * $y;
};
// Arrow functions (single-expression, inherit scope)
$factor = 3;
$triple = fn($x) => $x * $factor; // captures $factor automatically
$triple(5); // 15
// Closure binding — use keyword for closures
$add = function(int $n) use ($factor): int {
return $n + $factor;
};
OOP — Classes and interfaces
// Class with constructor promotion (PHP 8)
class User {
public function __construct(
public readonly int $id,
public string $name,
private string $email,
protected int $age = 0,
) {}
// Getter
public function getEmail(): string {
return $this->email;
}
// Static method
public static function fromArray(array $data): static {
return new static($data['id'], $data['name'], $data['email']);
}
// Magic methods
public function __toString(): string {
return "User({$this->name})";
}
}
$user = new User(1, 'Alice', 'alice@example.com');
echo $user->name; // Alice
echo $user->getEmail(); // alice@example.com
echo $user; // User(Alice)
// Inheritance
class Admin extends User {
public function __construct(
int $id,
string $name,
string $email,
public readonly string $role = 'admin',
) {
parent::__construct($id, $name, $email);
}
}
// Interface
interface Serializable {
public function serialize(): string;
public function unserialize(string $data): void;
}
// Abstract class
abstract class Shape {
abstract public function area(): float;
public function describe(): string {
return sprintf("%s with area %.2f", static::class, $this->area());
}
}
class Circle extends Shape {
public function __construct(private float $radius) {}
public function area(): float {
return M_PI * $this->radius ** 2;
}
}
// Traits
trait Timestamps {
private \DateTimeImmutable $createdAt;
private \DateTimeImmutable $updatedAt;
public function touch(): void {
$this->updatedAt = new \DateTimeImmutable();
}
}
class Post {
use Timestamps;
// ...
}
Enums (PHP 8.1)
// Pure enum
enum Status {
case Active;
case Inactive;
case Banned;
}
$s = Status::Active;
$s === Status::Active; // true
$s->name; // "Active"
// Backed enum (int or string)
enum Color: string {
case Red = 'red';
case Green = 'green';
case Blue = 'blue';
public function label(): string {
return ucfirst($this->value);
}
}
Color::Red->value; // "red"
Color::from('green'); // Color::Green
Color::tryFrom('purple'); // null (no exception)
Color::cases(); // [Color::Red, Color::Green, Color::Blue]
// Enums implement interfaces
enum Suit: string implements \JsonSerializable {
case Hearts = 'H';
case Diamonds = 'D';
case Clubs = 'C';
case Spades = 'S';
public function jsonSerialize(): string {
return $this->value;
}
}
Error handling
// try / catch / finally
try {
$result = riskyOperation();
} catch (\InvalidArgumentException $e) {
echo "Bad input: " . $e->getMessage();
} catch (\RuntimeException|\LogicException $e) {
echo "App error: " . $e->getMessage();
} catch (\Throwable $e) {
// Catches all exceptions AND errors (PHP 7+)
error_log($e);
throw $e; // re-throw
} finally {
// Always runs
cleanup();
}
// Custom exception
class ValidationException extends \RuntimeException {
public function __construct(
string $message,
private readonly array $errors = [],
int $code = 0,
?\Throwable $previous = null,
) {
parent::__construct($message, $code, $previous);
}
public function getErrors(): array {
return $this->errors;
}
}
// Throw as expression (PHP 8)
$value = $input ?? throw new \InvalidArgumentException("Input required");
// Error reporting
error_reporting(E_ALL);
ini_set('display_errors', '0'); // never show in production
ini_set('log_errors', '1');
ini_set('error_log', '/var/log/php/error.log');
// Set a global exception handler
set_exception_handler(function (\Throwable $e): void {
error_log("Unhandled: " . $e);
http_response_code(500);
echo json_encode(['error' => 'Server error']);
exit(1);
});
Database with PDO
Always use PDO with prepared statements — never concatenate user input into SQL.
// Connect (DSN: driver:host=...;dbname=...;charset=utf8mb4)
$pdo = new \PDO(
'mysql:host=localhost;dbname=myapp;charset=utf8mb4',
'dbuser',
'secret',
[
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => false,
]
);
// SELECT — fetch all rows
$stmt = $pdo->prepare("SELECT * FROM users WHERE active = :active");
$stmt->execute(['active' => 1]);
$users = $stmt->fetchAll(); // array of assoc arrays
// SELECT — fetch single row
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
$user = $stmt->fetch(); // assoc array | false
// Fetch as object
$stmt->setFetchMode(\PDO::FETCH_CLASS, User::class);
$user = $stmt->fetch(); // User instance
// INSERT
$stmt = $pdo->prepare(
"INSERT INTO users (name, email, created_at) VALUES (?, ?, NOW())"
);
$stmt->execute([$name, $email]);
$newId = $pdo->lastInsertId();
// UPDATE
$stmt = $pdo->prepare("UPDATE users SET name = ? WHERE id = ?");
$stmt->execute([$newName, $id]);
$rowCount = $stmt->rowCount(); // rows affected
// DELETE
$stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
$stmt->execute([$id]);
// Transaction
$pdo->beginTransaction();
try {
$pdo->prepare("UPDATE accounts SET balance = balance - ? WHERE id = ?")
->execute([$amount, $fromId]);
$pdo->prepare("UPDATE accounts SET balance = balance + ? WHERE id = ?")
->execute([$amount, $toId]);
$pdo->commit();
} catch (\Throwable $e) {
$pdo->rollBack();
throw $e;
}
Working with JSON, files, and HTTP
// JSON
$data = ['name' => 'Alice', 'scores' => [95, 87, 92]];
$json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
$decoded = json_decode($json, associative: true); // assoc array
$obj = json_decode($json); // stdClass
// Always check for errors
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \RuntimeException("JSON error: " . json_last_error_msg());
}
// Files
$content = file_get_contents('/path/to/file.txt');
file_put_contents('/path/to/out.txt', $content); // overwrite
file_put_contents('/path/to/log.txt', "line\n", FILE_APPEND);
// Read line by line (memory-efficient)
$handle = fopen('/path/to/big.csv', 'r');
while (($line = fgets($handle)) !== false) {
// process $line
}
fclose($handle);
// CSV
$rows = array_map('str_getcsv', file('/path/to/data.csv'));
// HTTP request with cURL
function httpGet(string $url, array $headers = []): string {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => $headers,
]);
$response = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($response === false || $code >= 400) {
throw new \RuntimeException("HTTP $code for $url");
}
return $response;
}
$body = httpGet('https://api.example.com/data', [
'Authorization: Bearer ' . $token,
'Accept: application/json',
]);
$data = json_decode($body, true);
Composer and autoloading
# Install Composer (one-time)
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
# Create project
composer init
composer require guzzlehttp/guzzle
composer require --dev phpunit/phpunit
# Install dependencies
composer install # installs from composer.lock
composer update # updates and rewrites composer.lock
# Autoload
composer dump-autoload
Your composer.json autoload section:
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
Then in PHP:
require_once __DIR__ . '/vendor/autoload.php';
use App\Models\User;
use App\Services\UserService;
Common mistakes
| Mistake | What happens | Fix |
|---|---|---|
== instead of === |
0 == "foo" is true in PHP 7, loose comparison surprises |
Use === for comparisons |
| Concatenating user input into SQL | SQL injection vulnerability | Always use PDO prepared statements |
mysql_* functions |
Removed in PHP 7 | Use PDO or MySQLi |
json_decode without assoc flag |
Returns stdClass, not array |
Pass associative: true |
strlen() on multibyte strings |
Counts bytes, not chars | Use mb_strlen() |
array_map ignoring keys |
Returns reindexed array | Use array_combine(array_keys($in), array_map(...)) to preserve keys |
die() / exit() in library code |
Prevents caller from handling errors | Throw exceptions instead |
PHP 8 features quick reference
| Feature | PHP version | Example |
|---|---|---|
| Named arguments | 8.0 | fn(arg: $val) |
| Match expression | 8.0 | match($x) { 1 => 'one' } |
| Nullsafe operator | 8.0 | $obj?->method() |
| Union types | 8.0 | int|string |
str_contains/starts_with/ends_with |
8.0 | Built-in string helpers |
Fibers |
8.1 | Cooperative multitasking |
| Enums | 8.1 | enum Status { case Active; } |
| Readonly properties | 8.1 | public readonly string $name; |
| Intersection types | 8.1 | Iterator&Countable |
| First-class callables | 8.1 | $fn = strlen(...) |
| Readonly classes | 8.2 | readonly class Point { } |
| DNF types | 8.2 | (A&B)|null |
#[\Override] attribute |
8.3 | Catch wrong method name override |
| Typed class constants | 8.3 | const string NAME = 'foo'; |
6 FAQ
Q: Should I use require or include?
Use require for files that must exist (it throws a fatal error if missing). Use include only when the file is truly optional. In modern PHP, prefer Composer autoloading over manual require calls entirely.
Q: What is the difference between echo and print?echo can take multiple arguments and has no return value — it's marginally faster. print always returns 1 and takes exactly one argument. In practice, use echo or just <?= $var ?> in templates.
Q: When should I use match vs switch?
Prefer match for PHP 8+. It uses strict comparison (===), has no fall-through, must be exhaustive (throws UnhandledMatchError by default), and is an expression (returns a value). Use switch only for legacy compatibility.
Q: How do I handle sessions securely?
Call session_start() at the top of every page that needs session data. Set session.cookie_httponly = 1, session.cookie_secure = 1 (HTTPS only), and session.use_strict_mode = 1 in php.ini. Regenerate the session ID after login: session_regenerate_id(true).
Q: What is the difference between static and self in PHP?self refers to the class where the method is defined. static uses late static binding — it refers to the class that was called at runtime. Use static when you expect the method to be inherited and you want polymorphic behaviour.
Q: How do I set up PHP for production?
Set display_errors = Off, log_errors = On, and point error_log to a writable path. Use opcache.enable = 1 for a 2–5× performance boost. Set expose_php = Off to hide the PHP version. Always run composer install --no-dev --optimize-autoloader in production.