PHP essentials: 50 cards on modern PHP 8, arrays, enums and PDO
Modern PHP 8 with short snippets: strict types, arrays, match, nullsafe and named arguments, enums, readonly, Composer, PDO and password_hash.
Practice now
Study without an account
declare(strict_types=1); do?Scheduled with the default Askesis timings. Progress is saved in this browser.
All 50 cards
- 1What does
declare(strict_types=1);do?Makes scalar type declarations strict for calls made from that file: passing"5"to anintparameter throws aTypeErrorinstead of being coerced to5It must be the first statement in the file, and it applies per file, to the calling code. - 2Add parameter and return types to a functionphp
function add(int $a, int $b): int { return $a + $b; } - 3Nullable and union types
?stringaccepts a string ornull(the same asstring|null); union types likeint|float(PHP 8.0) accept any of the listed types - 4
==vs=====compares after type juggling;===requires the same type and value. Prefer==="1" == "01"is true, because numeric strings compare as numbers;"1" === "01"is false. - 5What is
0 == "a"in PHP 8?false. Since PHP 8.0, comparing a number with a non-numeric string compares them as stringsIn PHP 7 the string was cast to0, so the result wastrue, a classic source of bugs. - 6What is
"10" == "1e1"?true: both strings are numeric, so==compares them as numbers (10 and 10.0) - 7
voidvsneverreturn typesvoid: the function returns without a value.never(PHP 8.1): it never returns normally, because it always throws or exits - 8Single vs double quotesDouble quotes interpolate variables (
"Hi $name","Hi {$user->name}") and escapes like\n; single quotes are literal. Concatenate strings with. - 9What does
7 / 2return in PHP?3.5:/returns a float unless the division is exact (6 / 2isint(3))Useintdiv(7, 2)for integer division (3). - 10What is a PHP array?An ordered map: one type serves as both list and dictionary, with integer or string keys, and it remembers insertion order
- 11What does this print?php
$a = ["1" => "x", 1 => "y"]; echo count($a);1: the string key"1"is cast to the integer1, so the second entry overwrites the firstKeys liketrueare cast too (to1), but"01"stays a string. - 12Append to an arrayphp
$list = [1, 2]; $list[] = 3; // [1, 2, 3]array_push($list, 3)does the same. - 13What does
array_filter([1, 0, 2])return?[0 => 1, 2 => 2]: it drops falsy values but keeps the original keys, leaving a gapWrap it inarray_values(…)to reindex. Mind the argument order:array_filter($array, $fn)butarray_map($fn, $array). - 14Destructure an arrayphp
[$first, $second] = [1, 2]; ['id' => $id, 'name' => $name] = $row; [$a, $b] = [$b, $a]; // swap - 15Merge arrays with the spread operatorphp
$all = [...$defaults, ...$overrides];Since PHP 8.1 this works with string keys too, where later keys win. - 16What does this print?php
$a = [1, 2, 3]; foreach ($a as &$v) {} foreach ($a as $v) {} print_r($a);[1, 2, 2]: after the first loop$vis still a reference to$a[2], so the second loop overwrites that element on every passCallunset($v)after a by-referenceforeach. - 17Are arrays passed by value or by reference?By value: assigning or passing an array gives an independent copy (made lazily, on write). Objects, by contrast, are passed as handles to the same object
- 18
isset($a['k'])vsarray_key_exists('k', $a)issetreturnsfalsewhen the key exists but its value isnull;array_key_existschecks only for the key - 19Read a request parameter with a fallbackphp
$page = $_GET['page'] ?? 1;??doesn’t warn about undefined keys.$config['debug'] ??= false;assigns only when the key is unset or null. - 20A
matchexpressionphp$label = match ($status) { 200, 201 => 'OK', 404 => 'Not found', default => 'Error', };PHP 8.0+. - 21
matchvsswitchmatchis an expression that returns a value, compares with===, never falls through, and throwsUnhandledMatchErrorwhen no arm matches;switchcompares with==and falls through withoutbreak - 22Get a nested value when any link may be nullphp
$country = $session?->user?->getAddress()?->country;The nullsafe operator?->(PHP 8.0) short-circuits the rest of the chain tonull. - 23What are named arguments?Passing arguments by parameter name (PHP 8.0), so you can skip optional parameters and the order doesn’t matterphp
htmlspecialchars($text, double_encode: false); - 24Declare and assign properties in the constructor signaturephp
class Point { public function __construct( public readonly int $x = 0, public readonly int $y = 0, ) {} }Constructor property promotion, PHP 8.0. - 25What does
readonlydo on a property?The property can be initialized once, from inside the class, and any later write throws anError(PHP 8.1). It must be typedPHP 8.2 addedreadonly class, which makes every property readonly. - 26A backed enumphp
enum Suit: string { case Hearts = 'H'; case Spades = 'S'; } Suit::from('H'); // Suit::Hearts Suit::tryFrom('X'); // nullEnums arrived in PHP 8.1.fromthrows aValueErrorfor an unknown value. - 27What can PHP enums do?Cases are singleton objects compared with
===;->namegives the case name and->valuethe backing value;Suit::cases()lists them; enums can have methods and constants and implement interfaces - 28Arrow functions vs anonymous functions
fn($x) => $x * $factorcaptures outer variables by value automatically;function ($x) use ($factor) { … }must list them inuseArrow functions (PHP 7.4) hold a single expression. - 29Get a callable for a function or methodphp
$len = strlen(...); $save = $repo->save(...);First-class callable syntax, PHP 8.1. - 30Check whether a string contains a substringphp
if (str_contains($haystack, 'needle')) { /* … */ }str_contains,str_starts_withandstr_ends_witharrived in PHP 8.0. - 31Why is this check buggy?php
if (strpos('apple', 'a')) { echo 'found'; }strposreturns0when the match is at the start, and0is falsy. Compare with!== false, or usestr_contains - 32Throw if a lookup returns null, in one expressionphp
$user = $repo->find($id) ?? throw new NotFoundException();throwbecame an expression in PHP 8.0. - 33A computed property with a property hookphp
class User { public function __construct( public string $first, public string $last, ) {} public string $fullName { get => "{$this->first} {$this->last}"; } }Property hooks arrived in PHP 8.4. - 34What is a trait?A reusable set of methods (and properties) copied into a class with
use, giving code reuse despite single inheritancephptrait Greets { public function greet(): string { return 'Hello from ' . static::class; } } class User { use Greets; } - 35How does PSR-4 autoloading work?Composer maps a namespace prefix to a directory, so
App\Models\Useris loaded fromsrc/Models/User.phpthe first time the class is usedConfigure it underautoload.psr-4incomposer.json, e.g."App\\": "src/". - 36PHP’s exception hierarchyEverything thrown implements
Throwable, which has two branches:Exception(application errors) andError(engine errors likeTypeError,ValueErrorandDivisionByZeroError)catch (Exception $e)doesn’t catchErrors; catchThrowableto get both. - 37Add a package with Composerbash
composer require monolog/monologThis updatescomposer.jsonandcomposer.lockand installs the package intovendor/. - 38
composer installvscomposer updateinstallinstalls the exact versions recorded incomposer.lock;updateresolves the newest versions allowed bycomposer.jsonand rewrites the lock fileCommitcomposer.lockfor applications, so every machine gets the same versions. - 39Load Composer’s autoloaderphp
require __DIR__ . '/vendor/autoload.php'; - 40What does the version constraint
^2.3allow?Any version from2.3.0up to, but not including,3.0.0: the updates that shouldn’t break compatibility under semantic versioning~2.3also means>=2.3.0 <3.0.0, but~2.3.1means>=2.3.1 <2.4.0. - 41Query a database safely with PDOphp
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?'); $stmt->execute([$email]); $user = $stmt->fetch();Prepared statements send values separately from the SQL, which prevents SQL injection. Never interpolate input into SQL. - 42Use named placeholders with PDOphp
$stmt = $pdo->prepare('UPDATE users SET name = :name WHERE id = :id'); $stmt->execute(['name' => $name, 'id' => $id]); - 43How do PDO errors surface in PHP 8?As
PDOExceptions:PDO::ERRMODE_EXCEPTIONis the default since PHP 8.0. Before that the default was silent mode, so a failed query just returnedfalsephp$pdo = new PDO($dsn, $user, $pass, [ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]); - 44Hash and check a passwordphp
$hash = password_hash($password, PASSWORD_DEFAULT); if (password_verify($input, $hash)) { /* logged in */ }The hash string includes the algorithm, cost and salt; store it in aVARCHAR(255)column. PHP 8.4 raised the default bcrypt cost from 10 to 12. - 45Why not store passwords with
md5()orsha1()?They are fast, unsalted hashes, so leaked hashes can be brute-forced or looked up quickly.password_hashuses a slow, salted algorithm (bcrypt by default, or Argon2) - 46Escape user input when printing it into HTMLphp
echo htmlspecialchars($comment, ENT_QUOTES, 'UTF-8');This prevents XSS. Since PHP 8.1,ENT_QUOTESis part of the default flags. - 47
echovsprint_rvsvar_dumpechooutputs strings;print_rprints arrays and objects readably;var_dumpalso shows types and lengths, e.g.string(3) "abc" - 48Common superglobals
$_GET(query string),$_POST(form body),$_SERVER(request and server info),$_SESSION(session data, aftersession_start()),$_COOKIEand$_FILES - 49Chain function calls with the pipe operatorphp
$slug = $title |> trim(...) |> strtolower(...) |> (fn($s) => str_replace(' ', '-', $s));The pipe operator|>arrived in PHP 8.5. Each step must be a callable that takes one argument; arrow functions need parentheses. - 50Serve the current directory for local developmentbash
php -S localhost:8000The built-in web server is meant for development only, not production.
Frequently asked questions
Which PHP version does the deck use?
PHP 8. Newer features are marked with the version that introduced them, from match and named arguments (8.0) and enums (8.1) to property hooks (8.4) and the pipe operator (8.5).
Does it cover Laravel or Symfony?
No. It covers the language, Composer and PDO, which both frameworks build on.
How do I prevent SQL injection in PHP?
Use prepared statements (PDO or mysqli) with placeholders and pass the values separately. Never build SQL strings from user input.
More in Programming
JavaScript
Master the JavaScript every developer uses daily: scope, closures, equality, array methods, promises, async/await and the event loop.
50 cardsAdvanced JS
Fifty advanced JavaScript cards: event-loop ordering puzzles, generators and async iteration, this binding, Proxy and Reflect, modules, AbortController and coercion edge cases.
50 cardsTypeScript
Learn the TypeScript you use every day: narrowing, generics, keyof, mapped and conditional types, utility types, satisfies and strict mode, with code on every card.
50 cardsPython
Learn idiomatic Python fast: comprehensions, f-strings, args and kwargs, generators, decorators, context managers and the standard library.
50 cardsGit & terminal
The Git commands and shell skills developers use every day: branching, rebasing, undoing mistakes, pipes, redirects, permissions and processes.
50 cardsJava
Core Java with short snippets: equals vs ==, records, sealed types, collections, generics, exceptions, streams, lambdas and JVM basics.
50 cardsC#
C# and .NET fundamentals with short snippets: value vs reference types, properties, records, nullable reference types, LINQ, async/await and DI.
50 cardsKotlin
Kotlin fundamentals with short snippets: null safety, val vs var, data and sealed classes, extension functions, scope functions and coroutines.
50 cardsSwift
Swift fundamentals with short snippets: optionals, structs vs classes, protocols, enums with associated values, closures, ARC, async/await and SwiftUI.
50 cardsFlutter
Flutter and Dart basics: null safety, futures and streams, stateless vs stateful widgets, setState, hot reload, keys, layout and state management.
50 cardsRuby
Ruby fundamentals with short snippets: blocks, procs and lambdas, symbols, Enumerable, mixins, truthiness, method_missing, gems and Bundler.
50 cardsGo
Go (Golang) fundamentals with short, idiomatic snippets: slices vs arrays, maps, defer, error wrapping, interfaces, goroutines, channels, select and context.
50 cardsRust
Learn the Rust that the compiler keeps asking about: ownership, borrowing, lifetimes, Option and Result, traits, closures, smart pointers and async.
50 cardsC++
Modern C++ in short snippets: references vs pointers, RAII, smart pointers, move semantics, STL containers and algorithms, const, templates and UB.
50 cardsHaskell
Haskell fundamentals with short code: types and type classes, pattern matching, laziness, Maybe and Either, folds, Functor, Applicative and Monad.
50 cardsElixir
Elixir fundamentals with short snippets: immutability, pattern matching, the pipe operator, Enum vs Stream, processes, GenServer, supervisors and mix.
50 cardsx86-64 assembly
x86-64 assembly in Intel syntax: registers and sub-registers, flags, mov and lea, jumps, the stack frame, the System V calling convention and Linux syscalls.
50 cardsRegex
Learn regular expressions for good: anchors, classes, greedy vs lazy quantifiers, groups, lookarounds, flags, replacements and the backtracking trap.
50 cards