DevTools

Cheatsheet PHP

Linguagem de programação web server-side

Back to languages
PHP
124 cards found
Categories:
Versions:

Basic


13 cards
Opening and Closing PHP
<?php
// PHP code here
echo "Hello World";
?>

<!-- Short tag (echo) -->
<?= "Direct output" ?>

<!-- In pure .php files, omit the ?> -->
<?php
echo "No closing tag needed";

Use <?php to open and ?> to close. The short tag <?= is equivalent to echo. In 100% PHP files, omit the final ?> to avoid accidental output.

Concatenation and Interpolation
$name = "Anna";
$age = 25;

// Concatenation with dot
echo "Hello " . $name . "!";

// Interpolation (double quotes)
echo "Hello $name, you are $age years";
echo "Hello {$name}!";  // with braces

// Single quotes (literal)
echo 'Hello $name';  // shows: Hello $name

// Heredoc (multi-line)
$text = <<<TXT
Hello $name,
You are $age years.
TXT;

// Nowdoc (no interpolation)
$raw = <<<'TXT'
No $interpolation here
TXT;

The . operator concatenates strings. Double quotes "$var" interpolate variables. {$var} for complex expressions. Heredoc allows multi-line with interpolation; Nowdoc without.

Ternary and Elvis Operator
// Full ternary
$status = ($age >= 18) ? "adult" : "minor";

// Elvis (if truthy, use the value)
$name = $input ?: "Anonymous";
// Equivalent to: $input ? $input : "Anonymous"

// Null coalescing (if non-null)
$name = $_GET['name'] ?? "Visitor";

// Chaining
$level = $score > 90 ? "A"
       : ($score > 70 ? "B" : "C");

// Ternary in HTML
<?= $active ? "Active" : "Inactive" ?>

The ternary ? : is a compact if/else. The Elvis operator ?: returns the value if truthy. ?? only checks null. Use sparingly — long ternaries are hard to read.

Magic Constants
__LINE__;    // current line number
__FILE__;    // full path of the file
__DIR__;     // folder of the file
__FUNCTION__; // function name
__CLASS__;   // class name
__METHOD__;  // Class::method
__NAMESPACE__; // current namespace

// Practical use
require __DIR__ . '/config.php';
echo "Error in " . __FILE__ . ":" . __LINE__;

// PHP 8.1: enum
__TRAIT__;   // trait name

Magic constants start and end with __. __DIR__ replaces dirname(__FILE__). __LINE__ gives the current line. Useful for debugging, includes and logging.

Variables
$name = "Cláudio";
$age = 30;
$price = 19.99;
$active = true;
$null = null;

// Dynamic variables
$field = "name";
echo $$field;  // "Cláudio"

// Constants
define("MAX", 100);
const PI = 3.14159;

// Check existence
isset($name);     // true
isset($inexist);  // false

Variables start with $. define() and const create constants. isset() checks if it exists and is not null. Types: string, int, float, bool, null.

Superglobals
$_GET['id'];       // URL parameters
$_POST['name'];    // form data
$_REQUEST['x'];    // GET + POST + COOKIE
$_SESSION['user']; // session
$_COOKIE['token']; // cookies
$_SERVER['HTTP_HOST'];  // server
$_FILES['upload'];      // upload
$_ENV['APP_KEY'];       // environment

// Practical example
$id = $_GET['id'] ?? 0;
$name = $_POST['name'] ?? '';

Superglobals are automatic arrays available everywhere. $_GET for URL, $_POST for forms, $_SESSION for session. Always sanitize before using!

Increment and Assignment
$x = 5;
$x++;    // 6 (post-increment)
++$x;    // 7 (pre-increment)
$x--;    // 6 (post-decrement)

// Compound assignment
$x += 10;   // $x = $x + 10
$x -= 3;    // $x = $x - 3
$x *= 2;    // $x = $x * 2
$x /= 4;    // $x = $x / 4
$x %= 3;    // $x = $x % 3
$x .= "!";  // $x = $x . "!" (string)
$x ??= "default";  // only if null

++$x increments before using; $x++ uses then increments. .= concatenates strings. ??= assigns only if the variable is null. +=, -=, *= are arithmetic shortcuts.

Data Types
// Scalar types
$str = "text";       // string
$num = 42;            // int
$float = 3.14;        // float
$bool = true;         // bool

// Compound types
$arr = [1, 2, 3];    // array
$obj = new stdClass;  // object

// Check type
gettype($num);        // "integer"
is_string($str);      // true
is_int($num);         // true
is_array($arr);       // true

// Casting
$str2 = (string) 42;  // "42"
$int2 = (int) "42px"; // 42
$bool2 = (bool) "";   // false

Use gettype() to see the type and is_*() to check. (int), (string), (bool) do casting. Falsy values: 0, "", null, false, [].

Include and Require
// Includes file (warning if it does not exist)
include 'header.php';

// Required (fatal error if it does not exist)
require 'config.php';

// Includes only once
require_once 'database.php';
include_once 'helpers.php';

// Path relative to the current file
require __DIR__ . '/partials/nav.php';

// Return values from files
$config = require 'config.php';

require for essential files (stops if it fails). include for optional ones. *_once avoids duplicate inclusion. __DIR__ gives the absolute path of the current folder.

Echo, Print and Output
// echo (faster, no return)
echo "Hello", " ", "World";  // multiple args
echo "Hello" . " World";     // concatenation

// print (returns 1)
print "Hello World";

// printf (formatted)
printf("%s tem %d years", "Anna", 25);

// Output buffering
ob_start();
echo "Content capturado";
$html = ob_get_clean();

// print_r for debugging
print_r($array);

echo is faster and accepts multiple arguments. print returns 1 (usable in expressions). printf() formats with placeholders. ob_start()/ob_get_clean() capture output into a variable.

Operators
// Arithmetic
$a + $b;   // addition
$a - $b;   // subtraction
$a * $b;   // multiplication
$a / $b;   // division
$a % $b;   // remainder
$a ** $b;  // power (2**3 = 8)

// Comparison
$a == $b;   // equal (value)
$a === $b;  // identical (value + type)
$a != $b;   // different
$a <=> $b;  // spaceship (-1, 0, 1)

// Logical
$a && $b;   // AND
$a || $b;   // OR
!$a;        // NOT

// Null coalescing
$val = $_GET['x'] ?? 'default';

=== compares value AND type (always prefer it). <=> (spaceship) returns -1, 0 or 1. ?? returns the first non-null value. ** is power. % is the division remainder.

Comments
// Single-line comment

# Also works (shell style)

/*
 * Multi-line
 * comment
 */

/**
 * DocBlock for functions/classes
 * @param string $name
 * @return string
 */
function greet(string $name): string {
    return "Hello $name";
}

// and # for one line. /* */ for multi-line. /** */ (DocBlock) for documentation — IDEs read @param, @return, @throws.

Type Conversion (Casting)
// Explicit casting
$str = (string) 42;      // "42"
$int = (int) "42px";     // 42
$float = (float) "3.14"; // 3.14
$bool = (bool) "";       // false
$arr = (array) $obj;     // object → array

// settype (changes the variable)
$var = "42";
settype($var, "integer");  // $var is now int

// Automatic conversion (type juggling)
"10" + 5;    // 15 (int)
"10" . 5;    // "105" (string)
true + true; // 2

// PHP 8: stricter
// "abc" + 1;  // TypeError!

Use (int), (string), (float), (bool) for casting. settype() changes the variable's type. PHP 8 is stricter: operations with non-numeric strings throw TypeError.

Strings


12 cards
Basic String Functions
$str = "  Hello World  ";

strlen($str);          // 13 (length)
trim($str);            // "Hello World"
strtoupper($str);      // "  HELLO WORLD  "
strtolower($str);      // "  hello world  "
ucfirst("hello world");  // "Hello world"
ucwords("hello world");  // "Hello World"

str_repeat("ha", 3);   // "hahaha"
str_pad("5", 3, "0", STR_PAD_LEFT);  // "005"

// Count occurrences
substr_count("banana", "na");  // 2

trim() removes spaces at the ends. strlen() gives the length. strtoupper()/strtolower() change capitalization. str_pad() pads to a size. substr_count() counts repetitions.

String Formatting
// sprintf (formatting)
sprintf("Hello %s, you are %d years old", "Anna", 25);
sprintf("Price: %.2f€", 19.9);    // "19.90€"
sprintf("ID: %05d", 42);          // "ID: 00042"
sprintf("%x", 255);               // "ff" (hex)

// number_format
number_format(1234567.891, 2, ",", ".");
// "1.234.567,89"

// money_format alternative
"R$ " . number_format(99.9, 2);

// nl2br (line breaks → <br>)
echo nl2br("Line 1\nLine 2");

// htmlspecialchars (escape HTML)
htmlspecialchars('<script>alert("x")</script>');

sprintf() formats with placeholders: %s (string), %d (int), %f (float). number_format() formats numbers with separators. htmlspecialchars() escapes HTML (prevents XSS).

Serialization
// serialize / unserialize
$data = ['name' => 'Anna', 'items' => [1,2,3]];
$str = serialize($data);
// 'a:2:{s:4:"name";s:3:"Anna";...}'

$restored = unserialize($str);

// Careful: security!
// NEVER unserialize() user data
// Use json_encode/json_decode instead

// __sleep and __wakeup
class User {
    public function __sleep(): array {
        return ['name', 'email'];  // what to serialize
    }
    public function __wakeup(): void {
        // after unserialize
    }
}

serialize() converts to string. unserialize() restores. Dangerous: never use unserialize() with user data (RCE). Prefer json_encode()/json_decode(). __sleep()/__wakeup() control serialization.

Substring and Position
$str = "Hello World PHP";

// Extract part
substr($str, 4);       // "World PHP"
substr($str, 4, 5);    // "World"
substr($str, -3);      // "PHP"

// Find position
strpos($str, "World"); // 4
strrpos($str, "P");    // 10 (last)
stripos($str, "world"); // 4 (case-insensitive)

// Check if it contains
str_contains($str, "World");  // true (PHP 8+)
str_starts_with($str, "Hello"); // true
str_ends_with($str, "PHP");   // true

substr() extracts part of the string. strpos() finds the position (or false). PHP 8+: str_contains(), str_starts_with(), str_ends_with() — more readable than strpos() !== false.

Regex with preg_*
// Test if it matches
preg_match('/^\d{9}$/', $phone);

// Extract first match
preg_match('/(\d{4})-(\d{3})/', $str, $m);
$m[0];  // full match
$m[1];  // 1st group

// All matches
preg_match_all('/\d+/', "a1 b22 c333", $all);
$all[0];  // ["1", "22", "333"]

// Replace with regex
preg_replace('/\s+/', ' ', $text);
preg_replace('/\d+/', 'X', "a1b2");  // "aXbX"

// Split by regex
preg_split('/[\s,]+/', "a, b  c");

preg_match() tests/finds the first match. preg_match_all() finds all of them. preg_replace() replaces with a pattern. Delimiters: /, #, ~. Modifiers: i (case-insensitive), s (dotall).

mb_* — Multibyte Strings
$str = "Hello World 🌍";

// strlen counts bytes, mb_strlen counts characters
strlen($str);      // 16 (bytes)
mb_strlen($str);   // 12 (characters)

// Multibyte substring
mb_substr($str, 0, 3);   // "Hello"
mb_strtoupper($str);     // "HELLO WORLD 🌍"
mb_strtolower($str);     // "hello world 🌍"

// Encoding
mb_detect_encoding($str);        // "UTF-8"
mb_convert_encoding($str, 'ISO-8859-1');

// Internal encoding
mb_internal_encoding('UTF-8');

mb_* functions handle UTF-8 correctly. strlen() counts bytes, mb_strlen() counts characters. Use mb_substr(), mb_strtoupper() for accents and emojis. Set mb_internal_encoding('UTF-8') at the start.

Replacement
// Replace text
str_replace("world", "PHP", "Hello world");
// "Hello PHP"

// Replace with count
str_replace("a", "o", "banana", $count);
// "bonono" ($count = 3)

// Replace with arrays
$search = ["foo", "bar"];
$replace = ["baz", "qux"];
str_replace($search, $replace, "foo bar");

// Replace by position
substr_replace("Hello World", "PHP", 4, 5);
// "Hello PHP"

str_replace() replaces all occurrences. The 4th parameter (by reference) counts how many replacements it made. substr_replace() replaces by position. For regex, use preg_replace().

Comparison and Encoding
// Comparison
strcmp("abc", "abc");   // 0 (equal)
strcasecmp("ABC", "abc"); // 0 (case-insensitive)
strncmp("abcdef", "abcxyz", 3); // 0 (3 chars)

// Encoding
mb_strlen("café");      // 4 (correct UTF-8)
mb_strtoupper("café");  // "CAFÉ"
mb_substr("Hello World", 0, 3);  // "Hello"
mb_detect_encoding($str);

// Convert encoding
mb_convert_encoding($str, "UTF-8", "ISO-8859-1");
utf8_encode($str);  // ISO → UTF-8
utf8_decode($str);  // UTF-8 → ISO

For strings with accents/emoji, use mb_* (multibyte) functions. mb_strlen() counts characters correctly. strcmp() compares strings (returns 0 if equal). Always use UTF-8 in your project.

sprintf and printf
// printf: direct output
printf("Name: %s, Age: %d", "Anna", 25);

// sprintf: returns string
$msg = sprintf("Total: %.2f EUR", 19.9);
// "Total: 19.90 EUR"

// Placeholders
sprintf("%05d", 42);        // "00042" (padding)
sprintf("%-10s|", "Esq");   // "Esq       |" (left)
sprintf("%+d", 42);         // "+42" (sign)
sprintf("%x", 255);         // "ff" (hex)
sprintf("%b", 10);          // "1010" (binary)

// Positional argument
sprintf("%2\$s %1\$s", "World", "Hello");
// "Hello World"

sprintf() formats and returns a string. printf() outputs directly. Placeholders: %s string, %d int, %f float, %x hex. Modifiers: %05d padding, %.2f decimals, %-10s alignment.

Split and Join
// Split string into array
$parts = explode(",", "a,b,c");
// ["a", "b", "c"]

$words = explode(" ", "Hello World PHP", 2);
// ["Hello", "World PHP"] (limit 2)

// Join array into string
$str = implode(", ", ["a", "b", "c"]);
// "a, b, c"

$url = implode("/", ["api", "users", "1"]);
// "api/users/1"

// Split into characters
$chars = str_split("ABC");
// ["A", "B", "C"]

explode() splits by delimiter. The 3rd parameter limits the number of parts. implode() joins an array with a separator. str_split() splits into individual characters.

Advanced str_replace
// Multiple replacements
$search = ['foo', 'bar', 'baz'];
$replace = ['FOO', 'BAR', 'BAZ'];
str_replace($search, $replace, 'foo bar baz');

// strtr (more efficient for multiple)
strtr('foo bar', ['foo' => 'FOO', 'bar' => 'BAR']);

// Replace only 1st occurrence
$pos = strpos($str, 'needle');
if ($pos !== false) {
    $str = substr_replace($str, 'new', $pos, 6);
}

// Remove accents
function removeAccents(string $s): string {
    return strtr($s, 'áàãâéêíóôõúüç',
                    'aaaaeeiooouuc');
}

str_replace() accepts arrays for multiple replacements. strtr() is more efficient for key-value pairs. substr_replace() replaces by position. For complex patterns, use preg_replace().

Advanced Trim and Padding
$str = "  Hello World  ";
trim($str);       // "Hello World"
ltrim($str);      // "Hello World  "
rtrim($str);      // "  Hello World"

// Trim with custom characters
trim("---text---", "-");     // "text"
trim("00042", "0");           // "42"
trim("\t\n text \r\n");     // "text"

// Padding
str_pad("5", 3, "0", STR_PAD_LEFT);   // "005"
str_pad("Hi", 10, "-", STR_PAD_BOTH); // "----Hi----"
str_pad("OK", 6, ".", STR_PAD_RIGHT); // "OK...."

// Wordwrap
wordwrap("Text long here", 10, "\n");

trim() removes spaces (or given characters) from the ends. ltrim()/rtrim() only left/right. str_pad() pads to the desired size. wordwrap() wraps text into fixed-size lines.

Arrays


13 cards
Creating Arrays
// Indexed array
$fruits = ["apple", "banana", "pear"];

// Associative array
$user = [
    "name" => "Anna",
    "age" => 30,
    "active" => true,
];

// Multidimensional array
$matrix = [
    [1, 2, 3],
    [4, 5, 6],
];

// Create with range
$numbers = range(1, 10);
$letters = range('a', 'f');

// Empty array
$empty = [];

Short syntax [] (PHP 5.4+). Arrays can be indexed (0, 1, 2...) or associative (key => value). range() creates sequences. Arrays in PHP are ordered maps (they accept any key type).

array_reduce and array_keys
$numbers = [1, 2, 3, 4, 5];

// reduce: accumulate into a single value
$sum = array_reduce($numbers, fn($acc, $n) => $acc + $n, 0);
// 15

$concat = array_reduce(["a","b","c"], fn($acc, $s) => $acc . $s, "");
// "abc"

// keys and values
$arr = ["name" => "Anna", "age" => 30];
array_keys($arr);    // ["name", "age"]
array_values($arr);  // ["Anna", 30]

// in_array (check existence)
in_array("Anna", $arr);           // true
in_array("Anna", $arr, true);     // true (strict)
array_key_exists("name", $arr);  // true

array_reduce() accumulates all elements into a single value. array_keys()/array_values() extract keys/values. in_array() checks if a value exists. array_key_exists() checks a key.

array_column and array_multisort
$users = [
    ['name' => 'Anna', 'age' => 30],
    ['name' => 'Ray', 'age' => 25],
    ['name' => 'Eve', 'age' => 35],
];

// Extract column
$names = array_column($users, 'name');
// ['Anna', 'Ray', 'Eve']

// Reindex by column
$byName = array_column($users, null, 'name');
// ['Anna' => [...], 'Ray' => [...], ...]

// Sort by column
array_multisort(
    array_column($users, 'age'), SORT_ASC,
    $users
);

array_column() extracts a column from a multidimensional array. With null the the 2nd arg, it reindexes by the column. array_multisort() sorts the original array by a specific column.

array_flip and array_search
// array_flip: swaps keys ↔ values
$map = ['a' => 1, 'b' => 2, 'c' => 3];
array_flip($map);  // [1 => 'a', 2 => 'b', 3 => 'c']

// array_search: finds key by value
$fruits = ['apple', 'banana', 'cherry'];
array_search('banana', $fruits);  // 1
array_search('x', $fruits);       // false

// array_key_exists vs isset
$arr = ['name' => null];
array_key_exists('name', $arr);  // true
isset($arr['name']);             // false (it is null!)

// array_rand: random key(s)
array_rand($fruits);       // 0, 1 or 2
array_rand($fruits, 2);    // [0, 2] (2 keys)

array_flip() swaps keys and values. array_search() returns the key of the found value (or false). array_key_exists() checks a key even with a null value (unlike isset()). array_rand() returns random key(s).

Accessing and Modifying
$arr = ["a" => 1, "b" => 2, "c" => 3];

// Access
$arr["a"];       // 1
$arr["b"] ?? 0;  // 2 (with default)

// Modify
$arr["d"] = 4;   // add
$arr["a"] = 10;  // update
unset($arr["b"]); // remove

// Add to end/start
$arr[] = 5;              // end (push)
array_push($arr, 6, 7);  // end (multiple)
array_unshift($arr, 0);  // start

// Remove from end/start
$last = array_pop($arr);
$first = array_shift($arr);

$arr[] adds to the end. unset() removes an element. array_push()/array_pop() for the end. array_unshift()/array_shift() for the start. ?? avoids an error if the key does not exist.

Sorting
$numbers = [3, 1, 4, 1, 5];

sort($numbers);     // [1, 1, 3, 4, 5] (loses keys)
rsort($numbers);    // [5, 4, 3, 1, 1] (reverse)

// Preserving keys
asort($numbers);    // sorts by value
arsort($numbers);   // reverse by value
ksort($numbers);    // sorts by key
krsort($numbers);   // reverse by key

// Custom sorting
$users = [["name" => "B"], ["name" => "A"]];
usort($users, fn($a, $b) => strcmp($a["name"], $b["name"]));

// Sort by column
array_multisort(array_column($users, "name"), SORT_ASC, $users);

sort() sorts values (loses keys). asort() preserves keys. usort() accepts a custom callback. array_multisort() sorts by a column of a multidimensional array. Prefix r = reverse, k = by key.

array_intersect and array_diff
$a = [1, 2, 3, 4, 5];
$b = [3, 4, 5, 6, 7];

// Intersection (common)
array_intersect($a, $b);  // [3, 4, 5]

// Difference (in $a but not in $b)
array_diff($a, $b);  // [1, 2]

// Associative
$x = ['a' => 1, 'b' => 2];
$y = ['b' => 2, 'c' => 3];
array_intersect_assoc($x, $y);  // ['b' => 2]
array_diff_key($x, $y);         // ['a' => 1]

// Combine with spread
$common = [...array_intersect($a, $b)];

array_intersect() returns common values. array_diff() returns values that are in the 1st but not in the others. Suffixes: _key compares keys, _assoc compares both. They preserve the keys of the 1st array.

Iterating Arrays
$fruits = ["apple", "banana", "pear"];

// Simple foreach
foreach ($fruits as $fruit) {
    echo $fruit;
}

// foreach with key
foreach ($fruits as $i => $fruit) {
    echo "$i: $fruit";
}

// Traditional for
for ($i = 0; $i < count($fruits); $i++) {
    echo $fruits[$i];
}

// while with each (old)
// array_walk (callback)
array_walk($fruits, function($val, $key) {
    echo "$key => $val\n";
});

foreach is the preferred way to iterate. $i => $val gives key and value. count() returns the size. array_walk() applies a callback to each element. Avoid modifying the array during iteration.

Merge, Slice and Chunk
$a = [1, 2, 3];
$b = [3, 4, 5];

// Merge
$c = array_merge($a, $b);    // [1,2,3,3,4,5]
$d = [...$a, ...$b];         // spread (PHP 7.4+)

// Associative: last key wins
$x = ["a" => 1];
$y = ["a" => 2, "b" => 3];
array_merge($x, $y);  // ["a" => 2, "b" => 3]

// Slice (extract part)
array_slice([1,2,3,4,5], 1, 3);  // [2,3,4]

// Chunk (split into groups)
array_chunk([1,2,3,4,5], 2);
// [[1,2], [3,4], [5]]

// Unique
array_unique([1, 2, 2, 3, 3]);  // [1, 2, 3]

array_merge() combines arrays. Spread [...$a, ...$b] is more modern. array_slice() extracts a part. array_chunk() splits into groups. array_unique() removes duplicates.

array_walk and array_walk_recursive
$fruits = ['apple', 'banana', 'cherry'];

// array_walk: modifies by reference
array_walk($fruits, function (&$item, $key) {
    $item = ucfirst($item);
});
// ['Apple', 'Banana', 'Cherry']

// With extra parameter
$prefix = 'Fruit: ';
array_walk($fruits, function (&$item) use ($prefix) {
    $item = $prefix . $item;
});

// Recursive (nested arrays)
$nested = ['a' => [1, 2], 'b' => [3, 4]];
array_walk_recursive($nested, function (&$val) {
    $val *= 2;
});
// ['a' => [2, 4], 'b' => [6, 8]]

array_walk() applies a callback to each element (by reference). Unlike array_map(): it modifies the original and passes the key the the 2nd argument. array_walk_recursive() enters nested arrays.

array_map and array_filter
$numbers = [1, 2, 3, 4, 5];

// map: transform each element
$double = array_map(fn($n) => $n * 2, $numbers);
// [2, 4, 6, 8, 10]

// filter: keep those that pass
$even = array_filter($numbers, fn($n) => $n % 2 === 0);
// [2 => 2, 4 => 4]

// filter without callback (removes falsy)
$clean = array_filter([0, 1, "", "a", null, 2]);
// [1 => 1, 3 => "a", 5 => 2]

// map with keys preserved
$upper = array_map('strtoupper', ["ana", "rui"]);
// ["ANA", "RUI"]

array_map() transforms each element and returns a new array. array_filter() keeps only those that pass the test. Without a callback, array_filter() removes falsy values. Both preserve the original keys.

Destructuring and Spread
// Destructuring (list)
[$a, $b, $c] = [1, 2, 3];
echo $a;  // 1

// With keys
["name" => $name, "age" => $age] = [
    "name" => "Anna", "age" => 30
];

// Skip elements
[, , $third] = [1, 2, 3];

// Spread in calls
function sum($a, $b, $c) { return $a+$b+$c; }
$args = [1, 2, 3];
sum(...$args);  // 6

// Spread to create arrays
$base = [1, 2];
$extra = [...$base, 3, 4];  // [1,2,3,4]

Destructuring [$a, $b] = [1, 2] extracts values. With keys: ["name" => $n]. Spread ... expands arrays into arguments or into another array. A lone comma [, , $x] skips positions.

array_unique and Counting
$arr = [1, 2, 2, 3, 3, 3];

// Remove duplicates
array_unique($arr);       // [1, 2, 3]
array_values(array_unique($arr));  // reindex

// Counting
count($arr);              // 6
sizeof($arr);             // 6 (alias)

// Recursive counting
$nested = ['a' => [1, 2], 'b' => [3]];
count($nested, COUNT_RECURSIVE);  // 5

// Count values
array_count_values(['a','b','a','c','a']);
// ['a' => 3, 'b' => 1, 'c' => 1]

// Check existence
in_array('banana', $fruits);         // true/false
in_array('5', [5, 10], true);        // false (strict)

array_unique() removes duplicates (preserves keys — use array_values() to reindex). count() counts elements. array_count_values() counts occurrences. in_array() with 3rd arg true does a strict comparison.

Flow Control


10 cards
If / Else / ElseIf
$age = 20;

if ($age >= 18) {
    echo "Adult";
} elseif ($age >= 13) {
    echo "Teenager";
} else {
    echo "Child";
}

// Alternative syntax (templates)
<?php if ($active): ?>
    <p>Active</p>
<?php else: ?>
    <p>Inactive</p>
<?php endif; ?>

// Null coalescing chain
$val = $a ?? $b ?? $c ?? "default";

if/elseif/else for conditions. The alternative syntax if: endif; is ideal in HTML templates. Chained ?? checks multiple variables. Use === in comparisons to avoid type coercion.

Comparison Operators
// Loose vs Strict
0 == "0";     // true  (compares value)
0 === "0";    // false (compares value + type)
null == false;  // true
null === false; // false

// Spaceship
1 <=> 2;   // -1
2 <=> 2;   // 0
3 <=> 2;   // 1

// Useful in usort
$nums = [3, 1, 2];
usort($nums, fn($a, $b) => $a <=> $b);

// Null coalescing vs ternary
$val = $x ?? "default";     // null only
$val = $x ?: "default";     // any falsy

Always prefer === (strict) over == (loose). <=> (spaceship) returns -1, 0 or 1 — ideal for usort(). ?? only triggers on null; ?: on any falsy value.

Nested Loops and Labels
// Nested loop with break/continue by level
for ($i = 1; $i <= 3; $i++) {
    for ($j = 1; $j <= 3; $j++) {
        if ($i === 2 && $j === 2) break 2;
        echo "$i,$j ";
    }
}
// 1,1 1,2 1,3 2,1

// Nested foreach
foreach ($categories as $cat) {
    foreach ($cat->products as $prod) {
        if ($prod->stock === 0) continue 2;
        echo $prod->name;
    }
}

// while with multiple conditions
$x = 0; $y = 10;
while ($x < 5 && $y > 3) {
    $x++; $y--;
}

break 2 and continue 2 act on the outer loop. The number indicates how many levels. In nested loops, use continue 2 to skip the parent loop's iteration. Combine conditions with && or ||.

Switch and Match
// Classic switch
$day = "Monday";
switch ($day) {
    case "Monday":
    case "Tuesday":
        echo "Start";
        break;
    case "Friday":
        echo "End";
        break;
    default:
        echo "Middle";
}

// Match (PHP 8+) — safer
$result = match($day) {
    "Monday", "Tuesday" => "Start",
    "Friday" => "End",
    default => "Middle",
};

switch compares with == (loose). match (PHP 8+) compares with === (strict) and returns a value. match does not need break and throws an error if no case matches (without default).

Alternative Syntax (Templates)
<?php if ($loggedIn): ?>
    <p>Welcome, <?= $name ?></p>
<?php else: ?>
    <a href="/login">Enter</a>
<?php endif; ?>

<?php foreach ($items as $item): ?>
    <li><?= htmlspecialchars($item) ?></li>
<?php endforeach; ?>

<?php while ($row = $stmt->fetch()): ?>
    <p><?= $row['name'] ?></p>
<?php endwhile; ?>

The alternative syntax if: endif;, foreach: endforeach;, while: endwhile; is ideal in HTML templates. More readable than braces when mixed with HTML. Use <?= for escaped output.

Match Expression (PHP 8) 8.0
// Switch: verbose, loose comparison
switch ($status) {
    case 'active':
        $label = 'Active';
        break;
    case 'inactive':
        $label = 'Inactive';
        break;
    default:
        $label = 'Unknown';
}

// Match: expression, strict, no break
$label = match($status) {
    'active' => 'Active',
    'inactive', 'suspended' => 'Blocked',
    default => throw new Exception("Status invalid"),
};

match (PHP 8+) is an expression that returns a value. It compares with === (strict). No break needed. Multiple cases with commas. Without default, it throws UnhandledMatchError if nothing matches.

For and Foreach
// Traditional for
for ($i = 0; $i < 10; $i++) {
    echo $i;
}

// for with multiple variables
for ($i = 0, $j = 10; $i < $j; $i++, $j--) {
    echo "$i $j";
}

// foreach (arrays)
foreach ([1, 2, 3] as $val) {
    echo $val;
}

// foreach by reference
$arr = [1, 2, 3];
foreach ($arr as &$val) {
    $val *= 2;  // modifies the original
}
unset($val);  // important!

for when you know the number of iterations. foreach for arrays/objects. &$val iterates by reference (modifies the original) — do unset($val) afterwards to avoid bugs.

Break, Continue and Goto
// break with level (nested loops)
for ($i = 0; $i < 10; $i++) {
    for ($j = 0; $j < 10; $j++) {
        if ($i * $j > 20) break 2;  // exits both
        echo "$i x $j = " . ($i*$j);
    }
}

// continue with level
for ($i = 0; $i < 5; $i++) {
    for ($j = 0; $j < 5; $j++) {
        if ($j === 2) continue 2;  // skips the outer for
    }
}

// goto (avoid!)
goto end;
echo "This does not run";
end:
echo "Jumped to here";

break 2 exits 2 nested loops. continue 2 skips to the next iteration of the outer loop. goto exists but avoid it — it makes code hard to follow. Prefer functions and clear conditions.

While and Do-While
// while
$n = 0;
while ($n < 5) {
    echo $n++;
}

// do-while (runs at least once)
do {
    $input = readline("Name: ");
} while (empty($input));

// Infinite loop with break
$i = 0;
while (true) {
    if ($i >= 10) break;
    echo $i++;
}

// continue (skip iteration)
for ($i = 0; $i < 10; $i++) {
    if ($i % 2 === 0) continue;
    echo $i;  // only odd numbers
}

while repeats while the condition is true. do-while runs at least once. break exits the loop. continue skips to the next iteration. Beware of infinite loops!

declare and Ticks
// strict_types: forces exact types
declare(strict_types=1);

function sum(int $a, int $b): int {
    return $a + $b;
}
sum(1, "2");  // TypeError!

// ticks: runs every N statements
declare(ticks=1);
register_tick_function(function () {
    echo "Tick executed\n";
});

// encoding (PHP 8.3+)
// declare(encoding='UTF-8');

declare(strict_types=1) must be the 1st line of the file. It forces exact type hints (no coercion). ticks runs a callback every N statements (useful for profiling). strict_types is per file, not global.

Functions


12 cards
Defining Functions
// Basic function
function greet(string $name): string {
    return "Hello, $name!";
}

// Parameter with default
function power(int $base, int $exp = 2): int {
    return $base ** $exp;
}

// No return (void)
function log(string $msg): void {
    file_put_contents('app.log', $msg . PHP_EOL, FILE_APPEND);
}

// Nullable return
function find(int $id): ?string {
    return $id === 1 ? "Anna" : null;
}

Use type hints on parameters and return. ?string allows null. void indicates no return. Defaults with = value. Function names are case-insensitive.

Recursion and Memoization
// Recursive factorial
function factorial(int $n): int {
    return $n <= 1 ? 1 : $n * factorial($n - 1);
}

// Fibonacci with memoization
function fib(int $n, array &$cache = []): int {
    if ($n <= 1) return $n;
    return $cache[$n] ??= fib($n-1, $cache) + fib($n-2, $cache);
}

// Recursion with array
function flatten(array $arr): array {
    $result = [];
    foreach ($arr as $item) {
        is_array($item)
            ? $result = [...$result, ...flatten($item)]
            : $result[] = $item;
    }
    return $result;
}

Recursion: a function that calls itself. Always have a base case to stop. &$cache by reference for memoization. ??= computes and stores only if it does not exist.

Variable and Dynamic Functions
// Variable function
$fn = 'strtoupper';
echo $fn('hello');  // "HELLO"

// Dynamic name
$action = $_GET['action'] ?? 'list';
$functions = ['list' => 'listItems', 'create' => 'createItem'];
if (isset($functions[$action])) {
    $functions[$action]();
}

// call_user_func
call_user_func('strtoupper', 'hello');
call_user_func_array('array_merge', [[1], [2]]);

// function_exists
if (function_exists('array_is_list')) {
    // PHP 8.1+
}

Variable functions: store the name in a variable and call with $fn(). call_user_func() calls by name. call_user_func_array() passes args the an array. function_exists() checks availability. Useful for dispatch patterns.

Arrow Functions and Closures
// Arrow function (PHP 7.4+)
$double = fn($x) => $x * 2;
$sum = fn($a, $b) => $a + $b;

// Automatic scope capture
$factor = 3;
$multiply = fn($x) => $x * $factor;

// Traditional closure
$counter = function() {
    static $count = 0;
    return ++$count;
};

// Closure with use
$prefix = "Dr.";
$greet = function($name) use ($prefix) {
    return "$prefix $name";
};

fn() => is an arrow function (captures variables automatically). function() use ($var) is a closure that imports variables from the scope. static preserves the value between calls.

Advanced Type Hints
// Union types (PHP 8+)
function format(int|float $num): string {
    return number_format($num, 2);
}

// Nullable
function fetch(int $id): ?User {
    return $id ? new User() : null;
}

// mixed (PHP 8+)
function dump(mixed $val): void {
    var_dump($val);
}

// never (PHP 8.1+)
function abort(string $msg): never {
    throw new RuntimeException($msg);
}

// strict mode
declare(strict_types=1);
function sum(int $a, int $b): int {
    return $a + $b;
}
// sum(1, "2");  // TypeError!

declare(strict_types=1) disables automatic coercion. Union types int|float accept multiple types. ?Type allows null. mixed accepts anything. never indicates it never returns.

Named Arguments (PHP 8) 8.0
// Before: order matters
array_slice($arr, 0, 5, true);

// Now: by name
array_slice(
    array: $arr,
    offset: 0,
    length: 5,
    preserve_keys: true
);

// Skip parameters with default
htmlspecialchars($input, double_encode: false);

// Combine positional + named
str_pad('5', length: 3, pad_string: '0');

Named arguments (PHP 8+) allow passing parameters by name in any order. Ideal for functions with many optional parameters. You can skip defaults. After a named, all must be named.

Variadic Parameters and Spread
// Variadic (...args)
function sum(...$nums): int {
    return array_sum($nums);
}
sum(1, 2, 3);  // 6

// Spread to call
$args = [1, 2, 3];
sum(...$args);  // 6

// Named arguments (PHP 8+)
function createUser(string $name, int $age = 0, bool $active = true) {}
createUser(name: "Anna", active: false);

// First-class callable (PHP 8.1+)
$strlen = strlen(...);
echo $strlen("hello");  // 3

...$args accepts a variable number of arguments. ... (spread) expands arrays. Named arguments name: "Anna" (PHP 8+) allow skipping parameters. strlen(...) creates a first-class callable.

Anonymous Functions and Closures
// Closure with state
function counter(): Closure {
    $count = 0;
    return function() use (&$count) {
        return ++$count;
    };
}
$next = counter();
$next();  // 1
$next();  // 2

// IIFE (Immediately Invoked)
$result = (function() {
    $x = calculate();
    $y = process($x);
    return $y;
})();

// Closure::fromCallable (PHP 7.1+)
$upper = Closure::fromCallable('strtoupper');
echo $upper('hello');  // "HELLO"

Closures capture variables with use. &$var captures by reference (modifies the original). IIFE runs immediately. Closure::fromCallable() converts functions into closures. Useful for callbacks and factories.

Union Types and Mixed (PHP 8) 8.0
// Union types
function format(int|float $num): string {
    return number_format($num, 2);
}

// Nullable shorthand
function find(int $id): ?User {
    // returns User or null
}

// mixed (any type)
function debug(mixed $value): void {
    var_dump($value);
}

// never (never returns)
function abort(string $msg): never {
    throw new RuntimeException($msg);
}

Union types int|float accept multiple types. ?Type is shorthand for Type|null. mixed accepts any type. never (PHP 8.1) indicates the function never returns (throws an exception or terminates).

Callbacks and Higher-Order
// Function the argument
function apply(callable $fn, array $arr): array {
    return array_map($fn, $arr);
}
$result = apply(fn($x) => $x * 2, [1, 2, 3]);

// Return function
function multiplier(int $factor): Closure {
    return fn($x) => $x * $factor;
}
$triple = multiplier(3);
echo $triple(5);  // 15

// call_user_func
call_user_func('strtoupper', 'hello');  // "HELLO"
call_user_func_array('max', [1, 5, 3]);  // 5

callable is the type hint for functions/callbacks. array_map(), array_filter(), usort() accept callbacks. call_user_func() calls dynamically. Functions that return functions = higher-order.

First-Class Callable Syntax (PHP 8.1)
// Before: Closure::fromCallable or string
$fn = Closure::fromCallable('strlen');
$fn2 = 'strtoupper';

// PHP 8.1+: syntax (...)
$len = strlen(...);
echo $len('Hello');  // 3

$upper = strtoupper(...);
$nums = array_map(abs(...), [-1, -2, 3]);
// [1, 2, 3]

// Object methods
$obj = new MyClass();
$method = $obj->doSomething(...);
$method('arg');

// Static methods
$static = MyClass::create(...);

The syntax function(...) (PHP 8.1+) creates a Closure from any callable. Replaces Closure::fromCallable(). Works with functions, object methods ($obj->method(...)) and static methods (Class::method(...)).

Fibers and Generators 8.0
// Generator (lazy evaluation)
function infinite_range(): Generator {
    $i = 0;
    while (true) {
        yield $i++;
    }
}

foreach (infinite_range() as $n) {
    if ($n > 5) break;
    echo $n;  // 0, 1, 2, 3, 4, 5
}

// Fiber (PHP 8.1+)
$fiber = new Fiber(function (): void {
    $value = Fiber::suspend('pause');
    echo "Received: $value";
});

$result = $fiber->start();  // 'pause'
$fiber->resume('data');       // "Received: data"

Generator with yield is lazy — it only computes when requested. Ideal for infinite sequences and large files. Fiber (PHP 8.1+) allows pausing/resuming execution. Foundation of asynchronous systems.

OOP


17 cards
Classes and Objects
class User {
    public string $name;
    private int $age;
    protected float $balance = 0;

    public function __construct(
        string $name,
        int $age
    ) {
        $this->name = $name;
        $this->age = $age;
    }

    public function greet(): string {
        return "Hello, {$this->name}";
    }
}

$user = new User("Anna", 30);
echo $user->greet();

public accessible from outside. private only within the class. protected class + children. $this references the instance. __construct() is the constructor. new creates instances.

Constructor Promotion (PHP 8)
// Before (PHP 7)
class UserOld {
    private string $name;
    private int $age;

    public function __construct(string $name, int $age) {
        $this->name = $name;
        $this->age = $age;
    }
}

// Now (PHP 8+)
class User {
    public function __construct(
        private string $name,
        private int $age,
        private bool $active = true,
    ) {}

    public function getName(): string {
        return $this->name;
    }
}

Constructor Promotion (PHP 8+) declares and assigns properties directly in the constructor. private string $name in the parameter creates the property automatically. Significantly reduces boilerplate.

Namespaces and Autoload
// src/Models/User.php
namespace App\Models;

class User {
    public function __construct(
        private string $name
    ) {}
}

// index.php
require 'vendor/autoload.php';

use App\Models\User;
use App\Services\{EmailService, LogService};

$user = new User("Anna");

// Alias
use App\Models\User as AppUser;
use function App\Helpers\format;
use const App\Config\MAX_SIZE;

namespace organizes classes intthe packages. use imports classes. autoload (Composer) loads classes automatically. use function and use const import functions/constants. PSR-4: 1 class = 1 file.

Final, Visibility and Constants
class Base {
    final public function fixo(): void {}
    // Cannot be overridden
}

final class Immutable {
    // Cannot be extended
}

class Config {
    // Visibility on constants (PHP 7.1+)
    public const VERSION = '2.0';
    protected const DB_HOST = 'localhost';
    private const SECRET = 'abc123';

    // Constant with expression (PHP 8.3+)
    // const TOTAL = self::PRICE * 1.23;
}

// Access
echo Config::VERSION;  // "2.0"

final prevents method override or class extension. Class constants can have visibility (public, protected, private) since PHP 7.1. Access with Class::CONST. PHP 8.3 allows final on constants.

Intersection Types (PHP 8.1) 8.0
interface HasName {
    public function getName(): string;
}

interface HasEmail {
    public function getEmail(): string;
}

class Contact implements HasName, HasEmail {
    public function getName(): string { return 'Anna'; }
    public function getEmail(): string { return 'ana@mail.com'; }
}

// Intersection: must implement BOTH
function notify(HasName&HasEmail $c): void {
    echo "{$c->getName()}: {$c->getEmail()}";
}

notify(new Contact());  // OK

// DNF types (PHP 8.2): union + intersection combination
function process((HasName&HasEmail)|null $c): void {}

Intersection types A&B require the value to implement ALL interfaces. Different from union A|B (one or the other). PHP 8.2 introduces DNF types: (A&B)|C combines intersection inside union with parentheses.

Inheritance and Polymorphism
class Animal {
    public function __construct(
        protected string $name
    ) {}

    public function sound(): string {
        return "...";
    }
}

class Dog extends Animal {
    public function sound(): string {
        return "{$this->name}: Au au!";
    }
}

class Cat extends Animal {
    public function sound(): string {
        return "{$this->name}: Miau!";
    }
}

// Polymorphism
$animals = [new Dog("Rex"), new Cat("Felix")];
foreach ($animals as $a) {
    echo $a->sound();  // calls the correct method
}

extends creates inheritance. The child class inherits methods and can override them (override). parent::method() calls the parent class method. Polymorphism: same interface, different behaviors.

Enums (PHP 8.1+)
enum Status: string {
    case Active = 'active';
    case Inactive = 'inactive';
    case Pending = 'pending';

    public function label(): string {
        return match($this) {
            self::Active => '✅ Active',
            self::Inactive => '❌ Inactive',
            self::Pending => '⏳ Pending',
        };
    }
}

$status = Status::Active;
echo $status->value;  // "active"
echo $status->label();  // "✅ Active"

// Comparison
$status === Status::Active;  // true

enum (PHP 8.1+) defines fixed sets of values. : string or : int for backed enums. ->value gives the value. They can have methods. Ideal for states, types and fixed categories.

Dependency Injection
// Without DI (coupled)
class OrderService {
    private $db = new Database();  // bad!
}

// With DI (decoupled)
class OrderService {
    public function __construct(
        private DatabaseInterface $db,
        private LoggerInterface $logger,
    ) {}

    public function create(Order $order): void {
        $this->db->save($order);
        $this->logger->info("Order created");
    }
}

// Manual injection
$service = new OrderService(
    new MySQLDatabase(),
    new FileLogger()
);

Dependency Injection: receive dependencies in the constructor instead of creating them inside. Program to interfaces, not implementations. Makes testing (mocks) and swapping implementations easier. Foundation of SOLID.

Readonly Properties (PHP 8.1+) 8.0
class Point {
    public function __construct(
        public readonly float $x,
        public readonly float $y,
    ) {}
}

$p = new Point(1.5, 2.5);
echo $p->x;    // 1.5
$p->x = 3.0;   // Error! Cannot modify readonly

// Readonly class (PHP 8.2+)
readonly class Config {
    public function __construct(
        public string $host,
        public int $port,
    ) {}
}

readonly (PHP 8.1+) allows writing only once. After assignment, it cannot be modified. Ideal for Value Objects and DTOs. readonly class (PHP 8.2+) applies it to all properties.

Interfaces and Abstract
interface Payable {
    public function pay(float $value): bool;
    public function getMethod(): string;
}

abstract class PaymentBase implements Payable {
    abstract protected function validate(): bool;

    public function pay(float $value): bool {
        if (!$this->validate()) return false;
        // process...
        return true;
    }
}

class CreditCard extends PaymentBase {
    protected function validate(): bool {
        return true;
    }
    public function getMethod(): string {
        return "Card";
    }
}

interface defines a contract (signatures only). implements fulfills the contract. abstract class can have methods with a body. abstract method forces implementation in the child. A class can implement multiple interfaces.

Static and Constants
class Config {
    const MAX_UPLOAD = 10_485_760;  // 10MB
    private static array $cache = [];

    public static function get(string $key): mixed {
        return self::$cache[$key] ?? null;
    }

    public static function set(string $key, mixed $val): void {
        self::$cache[$key] = $val;
    }
}

// Usage without instance
Config::set('debug', true);
Config::get('debug');  // true
Config::MAX_UPLOAD;    // 10485760

static belongs to the class (not the instance). Access with Class::method(). self:: references the class itself. const defines class constants. Useful for singletons, factories and utilities.

Late Static Binding
class Animal {
    public static function create(): static {
        return new static();  // called class
    }

    public function type(): string {
        return static::class;  // real class
    }
}

class Dog extends Animal {}

$dog = Dog::create();
echo get_class($dog);  // "Dog" (not "Animal")
echo $dog->type();     // "Dog"

// self vs static
// self:: → class where it is defined
// static:: → class that was called

static:: (Late Static Binding) refers to the called class, not the defining one. self:: always refers to the class where the method lives. new static() creates an instance of the child class. Essential for factories and inheritance.

Attributes (PHP 8) 8.0
// Define attribute
#[Attribute(Attribute::TARGET_CLASS)]
class Route {
    public function __construct(
        public string $path,
        public string $method = 'GET',
    ) {}
}

// Use
#[Route('/users', method: 'POST')]
class UserController {
    public function store() {}
}

// Read via Reflection
$ref = new ReflectionClass(UserController::class);
$attrs = $ref->getAttributes(Route::class);
$route = $attrs[0]->newInstance();
echo $route->path;  // "/users"

#[Attribute] (PHP 8+) replaces comment annotations. Native metadata readable via Reflection. Used in frameworks (Symfony, Laravel) for routes, validation, dependency injection.

Traits
trait Loggable {
    public function log(string $msg): void {
        echo "[" . static::class . "] $msg\n";
    }
}

trait Timestampable {
    public ?DateTime $createdAt = null;

    public function markCreated(): void {
        $this->createdAt = new DateTime();
    }
}

class Post {
    use Loggable, Timestampable;

    public function __construct(
        public string $title
    ) {
        $this->markCreated();
        $this->log("Post created");
    }
}

trait shares code between classes without inheritance. use TraitName inside the class. Multiple traits with use A, B. Solves PHP's "multiple inheritance". static::class gives the current class name.

Magic Methods
class Product {
    private array $data = [];

    public function __get(string $key): mixed {
        return $this->data[$key] ?? null;
    }

    public function __set(string $key, mixed $val): void {
        $this->data[$key] = $val;
    }

    public function __isset(string $key): bool {
        return isset($this->data[$key]);
    }

    public function __toString(): string {
        return json_encode($this->data);
    }

    public function __invoke(): void {
        echo "Called the function";
    }
}

$p = new Product();
$p->name = "Keyboard";  // __set
echo $p->name;          // __get
echo $p;                // __toString

Magic methods start with __. __get()/__set() for dynamic properties. __toString() when used the a string. __invoke() when called the a function. __construct(), __destruct() for lifecycle.

Object Cloning and __clone
class Cart {
    public array $items = [];
    public function __construct(
        public string $client
    ) {}

    public function __clone(): void {
        // Deep copy of items
        $this->items = array_map(
            fn($i) => clone $i,
            $this->items
        );
    }
}

$original = new Cart('Anna');
$original->items = [new Item('Book')];

$copy = clone $original;
$copy->client = 'Brian';
// $original->client stays "Anna"

clone creates a shallow copy of the object. __clone() is called after the copy — use it for deep copying internal objects. Without clone, the assignment $b = $a copies the reference, not the object.

Advanced Enums (PHP 8.1) 8.0
// Backed enum (with values)
enum Status: string {
    case Active = 'active';
    case Inactive = 'inactive';
    case Suspended = 'suspended';

    public function label(): string {
        return match($this) {
            self::Active => 'Active ✅',
            self::Inactive => 'Inactive ❌',
            self::Suspended => 'Suspended ⏸',
        };
    }

    public function canEdit(): bool {
        return $this === self::Active;
    }
}

$status = Status::from('active');
$status->value;   // 'active'
$status->name;    // 'Active'
$status->label(); // 'Active ✅'
Status::tryFrom('x');  // null (no error)

Enums with : string or : int are backed (they have a value). from() converts from a value (throws on invalid). tryFrom() returns null. They can have methods and use match. They replace magic constants.

Files and JSON


9 cards
Reading and Writing Files
// Read everything at once
$content = file_get_contents('data.txt');

// Write (overwrites)
file_put_contents('output.txt', 'Hello!');

// Append (add to the end)
file_put_contents('log.txt', "line\n", FILE_APPEND);

// Read the array of lines
$lines = file('data.txt', FILE_IGNORE_NEW_LINES);

// Check existence
if (file_exists('config.ini')) {
    $ini = parse_ini_file('config.ini');
}

file_get_contents() reads everything. file_put_contents() writes. FILE_APPEND adds without overwriting. file() returns an array of lines. file_exists() checks before reading.

SplFileObject and Streams
// Read line by line (efficient)
$file = new SplFileObject('big.csv');
$file->setFlags(SplFileObject::READ_CSV);

foreach ($file as $line) {
    [$name, $email] = $line;
    echo "$name: $email\n";
}

// fopen/fread/fwrite
$fp = fopen('data.txt', 'r');
while (!feof($fp)) {
    $line = fgets($fp);
}
fclose($fp);

// php://input (raw body)
$body = file_get_contents('php://input');

SplFileObject is object-oriented and efficient for large files. fopen()/fgets() for manual reading. php://input reads the raw request body. Always close with fclose().

File Locking and Atomicity
// Exclusive lock (write)
$fp = fopen('counter.txt', 'c+');
if (flock($fp, LOCK_EX)) {
    $val = (int) stream_get_contents($fp);
    ftruncate($fp, 0);
    rewind($fp);
    fwrite($fp, (string)($val + 1));
    fflush($fp);
    flock($fp, LOCK_UN);
}
fclose($fp);

// Shared lock (read)
$fp = fopen('data.txt', 'r');
flock($fp, LOCK_SH);
$content = stream_get_contents($fp);
flock($fp, LOCK_UN);
fclose($fp);

// Atomic write with rename
file_put_contents('tmp_' . $f, $data);
rename('tmp_' . $f, $f);  // atomic

flock() prevents race conditions. LOCK_EX = exclusive (write), LOCK_SH = shared (read). Always do fflush() before LOCK_UN. rename() is atomic on the same filesystem — ideal for safe writing.

JSON
// Array → JSON
$data = ['name' => 'Anna', 'age' => 30];
$json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

// JSON → Array
$arr = json_decode($json, true);  // true = array
$obj = json_decode($json);       // stdClass

// Handle errors
if (json_last_error() !== JSON_ERROR_NONE) {
    throw new Exception(json_last_error_msg());
}

// PHP 8+: named + flags
json_encode($data, flags: JSON_THROW_ON_ERROR);

json_encode() converts to JSON. json_decode($json, true) to an associative array. JSON_PRETTY_PRINT formats. JSON_UNESCAPED_UNICODE preserves accents. JSON_THROW_ON_ERROR throws an exception on error.

CSV and Large Files
// Write CSV
$fp = fopen('data.csv', 'w');
fputcsv($fp, ['Name', 'Email']);
fputcsv($fp, ['Anna', 'ana@mail.com']);
fclose($fp);

// Read CSV
$fp = fopen('data.csv', 'r');
$headers = fgetcsv($fp);
while (($row = fgetcsv($fp)) !== false) {
    $data[] = array_combine($headers, $row);
}
fclose($fp);

// Large file (line by line)
$handle = fopen('gigante.log', 'r');
while (($line = fgets($handle)) !== false) {
    process($line);  // constant memory
}
fclose($handle);

fputcsv() writes CSV. fgetcsv() reads line by line. array_combine() maps headers to values. For large files, use fgets() in a loop — constant memory regardless of size.

File Manipulation
// Create folder
mkdir('uploads/2024', 0755, true);  // recursive

// Copy, move, delete
copy('a.txt', 'b.txt');
rename('b.txt', 'c.txt');  // move/rename
unlink('c.txt');           // delete file
rmdir('folder-empty');      // delete folder

// Information
filesize('data.txt');     // bytes
filemtime('data.txt');    // last modification
pathinfo('photo.jpg', PATHINFO_EXTENSION);  // "jpg"

// List folder
$files = glob('uploads/*.jpg');
$all = scandir('.');

mkdir() with true creates recursively. rename() moves/renames. unlink() deletes files. glob() finds by pattern. pathinfo() extracts extension, name, folder.

Pathinfo and Path Manipulation
$path = '/var/www/uploads/photo.jpg';

pathinfo($path, PATHINFO_DIRNAME);   // /var/www/uploads
pathinfo($path, PATHINFO_BASENAME);  // photo.jpg
pathinfo($path, PATHINFO_FILENAME);  // photo
pathinfo($path, PATHINFO_EXTENSION); // jpg

// Build paths
$destination = __DIR__ . '/uploads/' . $filename;

// Normalize
realpath('./../file.txt');
basename('/path/to/file.php');  // file.php
dirname('/path/to/file.php');   // /path/to

// Temp
$tmp = tempnam(sys_get_temp_dir(), 'prefix_');

pathinfo() extracts parts of the path. realpath() resolves . and ... basename() gives the file name. dirname() gives the folder. tempnam() creates a unique temporary file.

File Upload
// HTML: <form enctype="multipart/form-data">
// <input type="file" name="photo">

if (isset($_FILES['photo'])) {
    $f = $_FILES['photo'];

    if ($f['error'] !== UPLOAD_ERR_OK) {
        die("Error: " . $f['error']);
    }

    $ext = pathinfo($f['name'], PATHINFO_EXTENSION);
    $permitidas = ['jpg', 'png', 'webp'];

    if (!in_array(strtolower($ext), $permitidas)) {
        die("Type not allowed");
    }

    $destination = 'uploads/' . uniqid() . '.' . $ext;
    move_uploaded_file($f['tmp_name'], $destination);
}

$_FILES contains the upload data. move_uploaded_file() moves it to the final destination. Always validate: error, extension, size. Use uniqid() for unique names. Never trust the original name.

DirectoryIterator and Glob
// glob: find files by pattern
$photos = glob('uploads/*.{jpg,png,webp}', GLOB_BRACE);
$phpFiles = glob(__DIR__ . '/*.php');

// DirectoryIterator (OOP)
$dir = new DirectoryIterator('/var/www');
foreach ($dir as $item) {
    if ($item->isDot()) continue;
    echo $item->getFilename();
    echo $item->getSize();
    echo $item->isDir() ? ' [DIR]' : ' [FILE]';
}

// RecursiveDirectoryIterator
$it = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator('/src')
);
foreach ($it as $file) {
    if ($file->getExtension() === 'php') {
        echo $file->getPathname();
    }
}

glob() finds files by pattern (supports *, ?, {a,b}). DirectoryIterator is the OOP way to list directories. RecursiveDirectoryIterator traverses subdirectories recursively.

Database and Security


9 cards
PDO — Connection
try {
    $pdo = new PDO(
        'mysql:host=localhost;dbname=app;charset=utf8mb4',
        'root',
        'password',
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false,
        ]
    );
} catch (PDOException $e) {
    die("Error na connection: " . $e->getMessage());
}

PDO supports MySQL, PostgreSQL, SQLite, etc. ERRMODE_EXCEPTION throws exceptions on errors. FETCH_ASSOC returns associative arrays. charset=utf8mb4 for full Unicode.

Sessions and Cookies
// Start session
session_start();

// Store/read data
$_SESSION['user_id'] = 42;
$_SESSION['name'] = 'Anna';
$userId = $_SESSION['user_id'] ?? null;

// Destroy session
session_destroy();
$_SESSION = [];

// Cookies
setcookie('token', $value, [
    'expires' => time() + 86400,
    'path' => '/',
    'httponly' => true,
    'secure' => true,
    'samesite' => 'Strict',
]);
$token = $_COOKIE['token'] ?? null;

session_start() starts the session (before any output). $_SESSION stores data on the server. setcookie() creates cookies on the client. httponly prevents access via JS. secure only over HTTPS.

SQLite and Advanced PDO
// SQLite (no server)
$pdo = new PDO('sqlite:data.db');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// Last inserted ID
$pdo->prepare("INSERT INTO users (name) VALUES (?)")
    ->execute(['Anna']);
$id = $pdo->lastInsertId();

// Multiple rows
$stmt = $pdo->query("SELECT * FROM users");
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);

// Fetch modes
$stmt->fetch(PDO::FETCH_OBJ);    // stdClass
$stmt->fetch(PDO::FETCH_COLUMN); // 1st column
$stmt->fetchAll(PDO::FETCH_KEY_PAIR); // id => name

// quote (avoid — use prepared)
$pdo->quote("O'Brien");  // "O''Brien"

sqlite: creates a file-based DB (no server). lastInsertId() returns the generated ID. fetchAll() returns all rows. FETCH_KEY_PAIR creates a key→value map. quote() escapes but always prefer prepared statements.

Prepared Statements
// Safe INSERT
$stmt = $pdo->prepare(
    "INSERT INTO users (name, email) VALUES (:name, :email)"
);
$stmt->execute([
    ':name' => $name,
    ':email' => $email,
]);
$id = $pdo->lastInsertId();

// SELECT with parameters
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
$user = $stmt->fetch();

// Multiple results
$stmt = $pdo->query("SELECT * FROM users");
$users = $stmt->fetchAll();

prepare() + execute() prevents SQL Injection. Use placeholders :name or ?. fetch() returns 1 row. fetchAll() returns all. NEVER concatenate variables into the query!

PDO — UPDATE and DELETE
// UPDATE
$stmt = $pdo->prepare(
    "UPDATE users SET name = :name, email = :email WHERE id = :id"
);
$stmt->execute([
    ':name' => $novoNome,
    ':email' => $novoEmail,
    ':id' => $userId,
]);
echo $stmt->rowCount();  // affected rows

// DELETE
$stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
$stmt->execute([$userId]);

// Transaction
$pdo->beginTransaction();
try {
    $pdo->exec("UPDATE contas SET balance = balance - 100 WHERE id = 1");
    $pdo->exec("UPDATE contas SET balance = balance + 100 WHERE id = 2");
    $pdo->commit();
} catch (Exception $e) {
    $pdo->rollBack();
}

rowCount() returns affected rows. beginTransaction()/commit()/rollBack() for atomic transactions. If something fails, rollBack() undoes everything. Essential for operations that depend on each other.

Security — Hash and Escape
// Password hashing
$hash = password_hash($password, PASSWORD_DEFAULT);

// Verify password
if (password_verify($input, $hash)) {
    echo "Password correta";
}

// Rehash if needed
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
    $novoHash = password_hash($password, PASSWORD_DEFAULT);
}

// Escape output (XSS)
echo htmlspecialchars($input, ENT_QUOTES, 'UTF-8');

// CSRF token
$token = bin2hex(random_bytes(32));
$_SESSION['csrf'] = $token;

password_hash() creates a secure hash (bcrypt). password_verify() compares. htmlspecialchars() prevents XSS. random_bytes() generates secure tokens. Never use md5() or sha1() for passwords!

Security Headers
// HTTP security headers
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block');
header('Content-Security-Policy: default-src \'self\'');
header('Strict-Transport-Security: max-age=31536000');
header('Referrer-Policy: strict-origin-when-cross-origin');

// Cache
header('Cache-Control: no-store, no-cache');
header('Pragma: no-cache');

// Content type
header('Content-Type: application/json; charset=utf-8');
echo json_encode($data);

Security headers: X-Frame-Options prevents clickjacking. CSP controls allowed resources. HSTS forces HTTPS. X-Content-Type-Options prevents MIME sniffing. Always set the correct Content-Type.

Sanitization and Validation
// filter_var
$email = filter_var($input, FILTER_SANITIZE_EMAIL);
$url = filter_var($input, FILTER_SANITIZE_URL);
$int = filter_var($input, FILTER_VALIDATE_INT);
$emailOk = filter_var($input, FILTER_VALIDATE_EMAIL);

// filter_input (straight from the request)
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);

// Manual validation
$name = trim($_POST['name'] ?? '');
if (mb_strlen($name) < 2 || mb_strlen($name) > 100) {
    $errors[] = "Name invalid";
}

// htmlspecialchars on output
echo htmlspecialchars($name, ENT_QUOTES, 'UTF-8');

filter_var() sanitizes/validates data. FILTER_VALIDATE_* returns false if invalid. FILTER_SANITIZE_* cleans characters. Always validate on the server — never trust the client.

PDO Transactions
try {
    $pdo->beginTransaction();

    $pdo->prepare("UPDATE contas SET balance = balance - ? WHERE id = ?")
        ->execute([100, $deOrigem]);

    $pdo->prepare("UPDATE contas SET balance = balance + ? WHERE id = ?")
        ->execute([100, $paraDestino]);

    $pdo->prepare("INSERT INTO transfers (de, to, value) VALUES (?, ?, ?)")
        ->execute([$deOrigem, $paraDestino, 100]);

    $pdo->commit();
} catch (Exception $e) {
    $pdo->rollBack();
    throw $e;
}

beginTransaction() starts a transaction. commit() confirms everything. rollBack() undoes everything if something fails. Essential for operations that depend on each other (transfers, orders). Always use try/catch with rollBack().

Numbers and Dates


9 cards
Math Functions
abs(-5);          // 5
max(1, 5, 3);     // 5
min(1, 5, 3);     // 1
round(3.7);       // 4
floor(3.7);       // 3
ceil(3.2);        // 4
round(3.14159, 2); // 3.14

rand(1, 100);     // random 1-100
random_int(1, 100); // cryptographically secure

pow(2, 10);       // 1024
sqrt(144);        // 12
fmod(10, 3);      // 1 (float remainder)
intdiv(10, 3);    // 3 (integer division)

round() rounds. floor() rounds down, ceil() rounds up. random_int() is cryptographically secure (prefer it over rand()). intdiv() does integer division. fmod() is the remainder for floats.

date() and time() Functions
// date() — quick formatting
echo date('d/m/Y');        // 25/12/2024
echo date('H:i:s');        // 14:30:00
echo date('Y-m-d H:i:s');  // 2024-12-25 14:30:00
echo date('l');            // Wednesday
echo date('F');            // December

// time() — current timestamp
$ts = time();
echo date('d/m/Y', $ts + 86400);  // tomorrow

// strtotime() — string → timestamp
$ts = strtotime('+1 week');
$ts = strtotime('next monday');
$ts = strtotime('2024-12-25');

// Timezone
date_default_timezone_set('Europe/Lisbon');

date() formats timestamps. time() gives the current timestamp. strtotime() converts strings into timestamps. date_default_timezone_set() sets the timezone. Formats: d (day), m (month), Y (year), H (hour).

Base Conversion and Hex
// Decimal → other bases
decbin(42);     // "101010"
decoct(42);     // "52"
dechex(255);    // "ff"

// Other bases → decimal
bindec('101010');   // 42
octdec('52');       // 42
hexdec('ff');       // 255

// base_convert (any base 2-36)
base_convert('ff', 16, 2);   // "11111111"
base_convert('255', 10, 16); // "ff"

// Literals in code
$bin = 0b1010;   // 10
$oct = 0755;     // 493
$hex = 0xFF;     // 255

// Colors
$color = dechex(16711680);  // "ff0000"

decbin(), decoct(), dechex() convert decimal to binary/octal/hex. bindec(), octdec(), hexdec() do the reverse. base_convert() converts between bases 2-36. Literals: 0b binary, 0x hex, 0 octal.

Number Formatting
// number_format
number_format(1234567.891, 2, ',', '.');
// "1.234.567,89"

// sprintf
sprintf("%.2f", 19.9);   // "19.90"
sprintf("%05d", 42);     // "00042"
sprintf("%x", 255);      // "ff" (hex)
sprintf("%b", 10);       // "1010" (binary)

// Conversions
intval("42px");    // 42
floatval("3.14");  // 3.14
(int) "42";        // 42
(float) "3.14";    // 3.14

// Check
is_numeric("42");   // true
is_nan(acos(2));    // true
is_infinite(INF);   // true

number_format() formats with separators. sprintf() for advanced formatting. intval()/floatval() convert. is_numeric() checks whether it is a number or a numeric string.

Carbon-like Without Dependencies
// Useful date helpers
function today(): string {
    return date('Y-m-d');
}

function tomorrow(): string {
    return date('Y-m-d', strtotime('+1 day'));
}

function daysBetween(string $from, string $to): int {
    $d1 = new DateTime($from);
    $d2 = new DateTime($to);
    return (int) $d1->diff($d2)->format('%r%a');
}

function formatPt(DateTime $dt): string {
    $months = ['', 'January', 'February', 'March',
              'April', 'May', 'June', 'July',
              'August', 'September', 'October',
              'November', 'December'];
    return $dt->format('d') . ' de ' .
           $months[(int)$dt->format('n')] .
           ' de ' . $dt->format('Y');
}

Utility date functions without external dependencies. strtotime() for quick calculations. DateTime::diff() for precise differences. %r%a gives signed days. For real projects, consider the Carbon library.

DateTime
// Current date
$now = new DateTime();
echo $now->format('d/m/Y H:i:s');

// Specific date
$natal = new DateTime('2024-12-25');
$custom = DateTime::createFromFormat('d/m/Y', '25/12/2024');

// Modify
$now->modify('+1 week');
$now->add(new DateInterval('P1M'));  // +1 month
$now->sub(new DateInterval('P2D'));  // -2 days

// Compare
if ($natal > $now) {
    echo "Natal still not chegou";
}

// Timestamp
$ts = $now->getTimestamp();
$dt = (new DateTime())->setTimestamp($ts);

DateTime is the modern way to work with dates. format() formats the output. modify() accepts strings like +1 week. DateInterval for precise intervals. P1M = 1 month, P2D = 2 days.

Random Numbers and UUID
// Secure randoms
random_int(1, 100);           // secure int
bin2hex(random_bytes(16));    // hex 32 chars
base64_encode(random_bytes(32));

// UUID v4 (no dependencies)
function uuid4(): string {
    $data = random_bytes(16);
    $data[6] = chr(ord($data[6]) & 0x0f | 0x40);
    $data[8] = chr(ord($data[8]) & 0x3f | 0x80);
    return vsprintf('%s%s-%s-%s-%s-%s%s%s',
        str_split(bin2hex($data), 4));
}

echo uuid4();
// "550e8400-e29b-41d4-a716-446655440000"

random_int() and random_bytes() are cryptographically secure. rand() and mt_rand() are NOT secure for tokens. UUID v4 can be generated with random_bytes() and bit manipulation.

Difference Between Dates
$start = new DateTime('2024-01-01');
$end = new DateTime('2024-12-25');

$diff = $start->diff($end);
echo $diff->days;     // 359 (total days)
echo $diff->m;        // 11 (months)
echo $diff->format('%m months e %d days');

// Age
$nascimento = new DateTime('1994-05-15');
$age = $nascimento->diff(new DateTime())->y;

// Carbon-like (no dependencies)
function diasAte(DateTime $target): int {
    return (int) (new DateTime())->diff($target)->format('%r%a');
}

diff() returns a DateInterval. ->days gives the total days. ->y, ->m, ->d give years, months, days. %r in format shows a negative sign if the date has already passed.

BCMath — Arbitrary Precision
// Float has precision problems
0.1 + 0.2 === 0.3;  // false!

// BCMath: strings with exact precision
bcadd('0.1', '0.2', 10);       // '0.3'
bcsub('10', '3.5', 2);         // '6.50'
bcmul('1.5', '2.3', 4);        // '3.4500'
bcdiv('10', '3', 20);          // '3.33333333333333333333'
bcmod('10', '3');              // '1'
bccomp('1.001', '1.0', 3);     // 1 (greater)
bcpow('2', '10');              // '1024'
bcsqrt('144', 2);              // '12.00'

// Global scale
bcscale(4);
echo bcdiv('1', '3');  // '0.3333'

BCMath does arbitrary-precision arithmetic via strings. Essential for money and scientific calculations. bcscale() sets global decimals. Never use float for monetary values — use bcadd(), bcmul(), etc.

Errors and Debug


8 cards
Try / Catch / Finally
try {
    $result = 10 / 0;
} catch (DivisionByZeroError $e) {
    echo "Error: " . $e->getMessage();
} catch (Exception $e) {
    echo "Generic: " . $e->getMessage();
} finally {
    echo "Always executa";
}

// Multiple types
try {
    // ...
} catch (InvalidArgumentException | RuntimeException $e) {
    echo $e->getMessage();
}

// PHP 8: non-capturing catch
try {
    // ...
} catch (Exception) {
    echo "Algo failed";
}

try runs risky code. catch captures specific exceptions. finally always runs. PHP 8: catch (Exception) without a variable. Order from most specific to most generic.

Logging and Error Reporting
// error_log to file
error_log("Error: " . $e->getMessage(), 3, '/var/log/app.log');

// error_log with context
error_log(json_encode([
    'level' => 'error',
    'message' => $msg,
    'context' => ['user_id' => $userId],
    'time' => date('c'),
]));

// Per-environment configuration
// Development:
ini_set('display_errors', '1');
error_reporting(E_ALL);

// Production:
ini_set('display_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', '/var/log/php-errors.log');

error_log() writes to the system log or a file. In development: display_errors = 1. In production: display_errors = 0 + log_errors = 1. Never show errors to the user in production.

Throwing Exceptions
// Throw exception
function divide(int $a, int $b): float {
    if ($b === 0) {
        throw new InvalidArgumentException(
            "Divisor cannot be zero"
        );
    }
    return $a / $b;
}

// Custom exception
class InsufficientBalanceException extends Exception {
    public function __construct(
        private float $balance,
        private float $value
    ) {
        parent::__construct(
            "Balance {$this->balance} < {$this->value}"
        );
    }
}

throw new Exception() throws exceptions. Create classes with extends Exception for specific errors. Include clear messages. The exception propagates until it finds a catch.

Exceptions with Context
// Chain exceptions (exception chaining)
try {
    $data = $api->fetch($url);
} catch (HttpException $e) {
    throw new ServiceException(
        "Failure fetching data from the API",
        previous: $e  // PHP 8: named arg
    );
}

// Exception with extra data
class ValidationException extends Exception {
    public function __construct(
        private array $errors,
        string $message = "Validation failed"
    ) {
        parent::__construct($message);
    }

    public function getErrors(): array {
        return $this->errors;
    }
}

Exception chaining: pass the original exception the previous. getPrevious() accesses the cause. Create exceptions with extra data (e.g. $errors). Makes debugging and structured logging easier.

Error Handling
// Global handler
set_exception_handler(function (Throwable $e) {
    error_log($e->getMessage());
    echo "Error internal. Tente again.";
});

// Custom error handler
set_error_handler(function ($severity, $message, $file, $line) {
    throw new ErrorException($message, 0, $severity, $file, $line);
});

// Suppression (avoid!)
$result = @file_get_contents('nonexistent.txt');

// error_reporting
error_reporting(E_ALL);
ini_set('display_errors', '0');  // production

set_exception_handler() captures unhandled exceptions. set_error_handler() converts errors into exceptions. @ suppresses errors (avoid!). error_reporting(E_ALL) shows all. In production: log, don't display.

Custom Exception Classes
class AppException extends Exception {
    public function __construct(
        string $message,
        private int $httpCode = 500,
        ?Throwable $previous = null
    ) {
        parent::__construct($message, 0, $previous);
    }

    public function getHttpCode(): int {
        return $this->httpCode;
    }
}

class NotFoundException extends AppException {
    public function __construct(string $resource, int $id) {
        parent::__construct(
            "$resource #$id not found",
            httpCode: 404
        );
    }
}

throw new NotFoundException('User', 42);

Create exception hierarchies with extends Exception. Add context (HTTP code, resource, etc.). Makes selective catch and proper HTTP responses easier. Pattern: one base exception per module + specific ones per case.

Debug and Var Dump
// var_dump (type + value)
var_dump([1, "a", null, true]);

// print_r (readable)
print_r($array);

// var_export (valid PHP code)
echo var_export($config, true);

// debug_backtrace
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
echo $trace[0]['file'] . ':' . $trace[0]['line'];

// error_log (writes to the log)
error_log("Debug: " . print_r($data, true));

// dd() (Laravel/Symfony)
// dd($variable);  // dump + die

var_dump() shows type and value. print_r() is more readable for arrays. var_export() generates valid PHP code. debug_backtrace() shows the call stack. error_log() writes to the log without output.

set_error_handler and Shutdown
// Convert warnings into exceptions
set_error_handler(function (
    int $severity,
    string $message,
    string $file,
    int $line
): bool {
    throw new ErrorException(
        $message, 0, $severity, $file, $line
    );
});

// Shutdown: captures fatal errors
register_shutdown_function(function () {
    $error = error_get_last();
    if ($error && in_array($error['type'],
        [E_ERROR, E_PARSE, E_CORE_ERROR]
    )) {
        error_log(json_encode($error));
        http_response_code(500);
    }
});

// Restore handler
restore_error_handler();

set_error_handler() converts warnings/notices into exceptions. register_shutdown_function() runs at the end — captures fatal errors via error_get_last(). restore_error_handler() restores the previous handler.

Tricks and Tips


12 cards
Null Coalescing Operator (??)
// Basic
$name = $_GET['name'] ?? 'Visitor';

// Chaining
$val = $a ?? $b ?? $c ?? 'default';

// Null coalescing assignment (PHP 7.4+)
$config['debug'] ??= false;
// Only assigns if it does not exist or is null

// In nested arrays
$city = $user['address']['city'] ?? 'N/D';
// No error even if $user['address'] does not exist!

// Combine with isset
$items = $_SESSION['cart'] ?? [];

?? returns the first non-null value. It does not throw an error on missing keys. ??= assigns only if null. Replaces patterns like isset($x) ? $x : default. Chain it for multiple fallbacks.

Bitwise and Ternary Operators
// Bitwise
$a & $b;   // AND
$a | $b;   // OR
$a ^ $b;   // XOR
~$a;       // NOT
$a << 2;   // shift left (×4)
$a >> 1;   // shift right (÷2)

// Flags with bitwise
define('READ', 1);    // 001
define('WRITE', 2);   // 010
define('EXEC', 4);    // 100

$perms = READ | WRITE;  // 3 (011)
$perms & READ;   // 1 (has READ)
$perms & EXEC;   // 0 (no EXEC)

// Nested ternary (avoid!)
$x = $a ?: $b ?? $c;  // confusing

Bitwise: & (AND), | (OR), ^ (XOR), ~ (NOT). << multiplies by 2, >> divides. Useful for flags/permissions. Combine with |, test with &.

Null-Safe Operator (?->)
// Without null-safe: chained checks
$city = null;
if ($user !== null) {
    $addr = $user->getAddress();
    if ($addr !== null) {
        $city = $addr->getCity();
    }
}

// With null-safe (PHP 8+)
$city = $user?->getAddress()?->getCity();

// If any link is null, returns null
$name = $user?->profile?->name ?? 'Anonymous';

// Works with methods and properties
$len = $user?->getName()?->length();

The ?-> operator (PHP 8+) returns null if the object is null, without error. Replaces chains of if !== null. Combine with ?? for a default. Works with chained properties and methods.

Spread and Unpacking
// Merge arrays
$a = [1, 2];
$b = [3, 4];
$c = [...$a, ...$b];  // [1, 2, 3, 4]

// Associative (PHP 8.1+)
$defaults = ['color' => 'blue', 'size' => 'M'];
$custom = [...$defaults, 'color' => 'red'];
// ['color' => 'red', 'size' => 'M']

// In function calls
function sum($a, $b, $c) { return $a + $b + $c; }
$args = [1, 2, 3];
sum(...$args);  // 6

// array_push with spread
$arr = [1, 2];
array_push($arr, ...[3, 4, 5]);

Spread ... expands arrays. In associative arrays (PHP 8.1+), the last key wins. Replaces array_merge() with cleaner syntax. Works in function calls and array_push().

list() and Advanced Destructuring
// Classic list()
list($a, $b) = [1, 2];

// Short syntax
[$x, $y] = [10, 20];

// In foreach
$users = [['Anna', 30], ['Ray', 25]];
foreach ($users as [$name, $age]) {
    echo "$name tem $age";
}

// Ignore elements
[, , $third] = [1, 2, 3, 4];

// Associative (PHP 7.1+)
['name' => $name, 'age' => $age] = $user;

// Swap
[$a, $b] = [$b, $a];

list() or [] for destructuring. Works in foreach for multidimensional arrays. A lone comma ignores positions. Associative destructuring with ['key' => $var]. Elegant swap: [$a, $b] = [$b, $a].

Short Ternary and Advanced Elvis
// Elvis operator (?:)
$name = $_POST['name'] ?: 'Anonymous';
// Equivalent to: $_POST['name'] ? $_POST['name'] : 'Anonymous'

// vs Null Coalescing (??)
$name = $_POST['name'] ?? 'Anonymous';
// ?? only checks null; ?: checks falsy

// Practical difference
$value = 0;
$value ?: 10;   // 10 (0 is falsy)
$value ?? 10;   // 0  (0 is not null)

// Ternary in expressions
$class = $active ? 'active' : 'inactive';
$msg = $errors ? implode(', ', $errors) : 'OK';

// Nested ternary (avoid!)
// $x = $a ? 'a' : ($b ? 'b' : 'c');

?: (Elvis) returns the value if it is truthy, otherwise the default. ?? only checks null. Difference: 0 ?: 10 gives 10, but 0 ?? 10 gives 0. Use ?? for optional values, ?: for truthy/falsy.

Array Tricks
// Last element
$last = end($arr);
$last = $arr[array_key_last($arr)];

// Remove duplicates
$unico = array_unique([1, 2, 2, 3, 3]);

// Flatten array
$flat = array_merge(...[[1,2], [3,4]]);
// or
$flat = [...[1,2], ...[3,4]];

// array_combine (keys + values)
$keys = ['a', 'b', 'c'];
$vals = [1, 2, 3];
$arr = array_combine($keys, $vals);

// array_flip (swap key ↔ value)
$arr = ['a' => 1, 'b' => 2];
$flip = array_flip($arr);  // [1 => 'a', 2 => 'b']

array_key_last() gives the last key. array_unique() removes duplicates. array_combine() creates an array from 2 arrays (keys + values). array_flip() swaps keys with values. Spread ... flattens arrays.

The @ Operator and Suppression
// Error suppression (avoid!)
$value = @$arr['key_nonexistent'];
$fp = @fopen('nonexistent.txt', 'r');

// Better alternatives:
$value = $arr['key'] ?? null;

if (file_exists('file.txt')) {
    $fp = fopen('file.txt', 'r');
}

// Temporary error_reporting(0)
$old = error_reporting(0);
// ... code that may raise a warning ...
error_reporting($old);

The @ operator suppresses errors — avoid it! It makes debugging hard and hides bugs. Prefer ?? for missing keys, file_exists() before opening, or try/catch. PHP 8: @ does not suppress fatal errors.

New PHP 8.x Functions 8.0
// str_contains, starts_with, ends_with
str_contains('Hello World', 'World');
str_starts_with('Hello', 'Ol');
str_ends_with('test.php', '.php');

// array_is_list (PHP 8.1)
array_is_list([1, 2, 3]);     // true
array_is_list(['a' => 1]);    // false

// enum_exists (PHP 8.1)
enum_exists(Status::class);

// fdiv (division without warning)
fdiv(1, 0);  // INF (no error!)

// get_debug_type (PHP 8)
get_debug_type(null);   // "null"
get_debug_type([]);     // "array"
get_debug_type(1.5);    // "float"

PHP 8+: str_contains(), str_starts_with(), str_ends_with(). PHP 8.1: array_is_list(), enum_exists(), fdiv(). PHP 8: get_debug_type() shows the type in a readable way.

Strings — Tricks
// str_contains, starts_with, ends_with (PHP 8+)
str_contains('Hello World', 'World');  // true
str_starts_with('Hello', 'Ol');        // true
str_ends_with('file.php', '.php'); // true

// str_pad
str_pad('42', 5, '0', STR_PAD_LEFT);  // "00042"

// strtr (multiple replacements)
strtr('Hello World', ['Hello' => 'Hi', 'World' => 'World']);

// sscanf (extract with format)
sscanf('2024-12-25', '%d-%d-%d', $y, $m, $d);

// wordwrap
echo wordwrap($text, 80, "\n");

PHP 8+: str_contains(), str_starts_with(), str_ends_with() — more readable than strpos(). str_pad() pads. strtr() does multiple replacements at once. sscanf() extracts with a format.

Compact and Extract
// compact: variables → array
$name = 'Anna';
$age = 30;
$email = 'ana@mail.com';

$user = compact('name', 'age', 'email');
// ['name' => 'Anna', 'age' => 30, ...]

// extract: array → variables (careful!)
$data = ['title' => 'Post', 'author' => 'Anna'];
extract($data);
echo $title;  // 'Post'
echo $author;   // 'Anna'

// extract with prefix (safer)
extract($data, EXTR_PREFIX_ALL, 'd');
echo $d_title;  // 'Post'

compact() creates an array from variables. extract() does the reverse — careful: it overwrites existing variables! Use EXTR_PREFIX_ALL for safety. Prefer destructuring with [...] over extract().

PHP 8.2 and 8.3 New Features 8.0
// PHP 8.2: readonly classes
readonly class Point {
    public function __construct(
        public float $x,
        public float $y,
    ) {}
}

// PHP 8.2: constants in traits
trait HasId {
    public const TABLE = 'items';
}

// PHP 8.3: typed class constants
class Config {
    final const int MAX = 100;
    final const string APP = 'MyApp';
}

// PHP 8.3: json_validate()
json_validate('{"ok":true}');  // true
json_validate('{invalid}');    // false

// PHP 8.3: Randomizer
$rng = new Random\Randomizer();
$rng->getInt(1, 100);
$rng->shuffleArray([1, 2, 3]);

PHP 8.2: readonly class, constants in traits, DNF types. PHP 8.3: final and types on constants, json_validate() without decode, Random\Randomizer for OOP randomness, #[\Override] attribute.