Practice
PHP exercises
Make a change, predict what it will do, and compare the result.
- 1
PHP Basics
Print a value from a PHP script.
Adapt the lesson example. Change one value or element, predict the result, and explain why it changes.
Show starting example
PHP<?php $name = "Ada"; echo "Hello, $name\n"; - 2
PHP Arrays
Loop through an indexed array.
Adapt the lesson example. Change one value or element, predict the result, and explain why it changes.
Show starting example
PHP<?php $scores = [82, 91, 76]; foreach ($scores as $score) { echo "$score\n"; } - 3
PHP Functions
Declare typed parameters and return values.
Predict average([10, 20, 31]).
Reveal answer and reasoning
The sum is 61 and 61 / 3 is 20.333333333333, so that is what echo prints.
Show starting example
PHP<?php function average(array $values): float { return array_sum($values) / count($values); } echo average([82, 91, 76, 95]), "\n"; echo round(average([1, 2]), 1), "\n"; - 4
PHP strings, sprintf, and regular expressions
Format text with sprintf, split and join strings, and match patterns with preg functions.
What does sprintf("%03d", 5) return?
Reveal answer and reasoning
"005": the integer is zero-padded to a width of three digits.
Show starting example
PHP<?php $name = "Programonic"; echo strlen($name), " ", substr($name, 0, 7), " ", strpos($name, "m"), "\n"; echo sprintf("%05.1f|%-8s|%'*8s|%x", 3.14159, "left", "right", 255), "\n"; $parts = explode(",", "a,b,,c"); echo count($parts), " ", implode("-", $parts), "\n"; echo str_contains("haystack", "st") ? "yes" : "no", " ", ucfirst("php"), " ", strrev("abc"), "\n"; if (preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', "2024-03-15", $m)) { echo $m[1], "/", $m[2], "/", $m[3], "\n"; } echo preg_replace('/\s+/', ' ', "too many spaces"), "\n"; echo str_pad("7", 3, "0", STR_PAD_LEFT), "\n"; - 5
PHP strict types, match, and the nullsafe operator
Use union types, a match expression, constructor promotion, and ?-> to write safer code.
How does match differ from switch?
Reveal answer and reasoning
match uses strict comparison, returns a value, needs no break statements, and throws an error when no arm matches.
Show starting example
PHP<?php declare(strict_types=1); function describe(int|string|null $value): string { return match (true) { $value === null => "nothing", is_int($value) && $value < 0 => "negative int", is_int($value) => "int $value", default => "string '$value'", }; } echo describe(5), "\n", describe(-2), "\n", describe("hi"), "\n", describe(null), "\n"; class Address { public function __construct(public ?string $city = null) {} } class User { public function __construct(public string $name, public ?Address $address = null) {} } $users = [new User("Ada", new Address("London")), new User("Bo")]; foreach ($users as $user) { echo $user->name, ": ", $user->address?->city ?? "unknown", "\n"; } try { echo strlen(123); } catch (TypeError $e) { echo "TypeError caught\n"; } - 6
PHP closures, array functions, and generators
Transform arrays with map, filter, and reduce, capture variables in closures, and produce values lazily with yield.
How would you take the first 5 even squares lazily?
Reveal answer and reasoning
Write a generator that yields squares one at a time, filter inside the loop, and break after collecting 5 values.
Show starting example
PHP<?php $numbers = [1, 2, 3, 4, 5, 6]; $squares = array_map(fn($n) => $n * $n, $numbers); $evens = array_filter($squares, fn($n) => $n % 2 === 0); $sum = array_reduce($evens, fn($carry, $n) => $carry + $n, 0); echo implode(",", $squares), " | ", implode(",", $evens), " | ", $sum, "\n"; $rate = 3; $multiply = function (int $n) use ($rate) { return $n * $rate; }; echo $multiply(5), "\n"; $total = 0; array_walk($numbers, function ($n) use (&$total) { $total += $n; }); echo $total, "\n"; function fibonacci(): Generator { [$a, $b] = [0, 1]; while (true) { yield $a; [$a, $b] = [$b, $a + $b]; } } $first = []; foreach (fibonacci() as $value) { if (count($first) === 10) { break; } $first[] = $value; } echo implode(" ", $first), "\n"; - 7
PHP Classes
Hide a property and change it only through methods.
Add a decrement method.
Reveal answer and reasoning
public function decrement(): void { $this->value--; } lowers the value by one.
Show starting example
PHP<?php class Counter { private int $value = 0; public function increment(): void { $this->value++; } public function value(): int { return $this->value; } } $counter = new Counter(); $counter->increment(); $counter->increment(); echo $counter->value(), "\n"; - 8
PHP interfaces, traits, and abstract classes
Share a contract with an interface, share code with a trait, and share both with an abstract base.
Where would you declare a method that all shapes must implement?
Reveal answer and reasoning
In the Shape interface. Every class that implements it must then provide the method.
Show starting example
PHP<?php interface Shape { public function area(): float; } trait Describes { public function describe(): string { return static::class . " with area " . number_format($this->area(), 2); } } abstract class Base implements Shape { use Describes; } final class Circle extends Base { public function __construct(private float $radius) {} public function area(): float { return M_PI * $this->radius ** 2; } } final class Rect extends Base { public function __construct(private float $width, private float $height) {} public function area(): float { return $this->width * $this->height; } } foreach ([new Circle(1.5), new Rect(2, 3.5)] as $shape) { echo $shape->describe(), "\n"; } echo (new Circle(1)) instanceof Shape ? "a Circle is a Shape" : "not a Shape", "\n"; - 9
PHP Associative Arrays
Map names to values and loop over keys and values.
Remove Grace before the loop.
Reveal answer and reasoning
Call unset($scores["Grace"]); the loop then prints two lines and count gives 2.
Show starting example
PHP<?php $scores = ["Ada" => 91, "Grace" => 88]; $scores["Linus"] = 79; foreach ($scores as $name => $score) { echo "$name: $score\n"; } echo count($scores), "\n"; - 10
PHP sorting, usort, and the spaceship operator
Sort arrays by value or by custom rules, and combine several sort keys with <=>.
How would you sort the people by age descending?
Reveal answer and reasoning
Swap the operands: usort($people, fn($a, $b) => $b[1] <=> $a[1]);
Show starting example
PHP<?php $numbers = [5, 2, 9, 1, 5, 6]; sort($numbers); echo implode(" ", $numbers), "\n"; rsort($numbers); echo implode(" ", $numbers), "\n"; $people = [["Ada", 30], ["Bo", 25], ["Cy", 30], ["Di", 25]]; usort($people, fn($a, $b) => $a[1] <=> $b[1] ?: strcmp($a[0], $b[0])); echo implode(",", array_column($people, 0)), "\n"; $words = ["pear", "apple", "Fig"]; sort($words); echo implode(",", $words), "\n"; sort($words, SORT_FLAG_CASE | SORT_STRING); echo implode(",", $words), "\n"; $scores = ["Ada" => 90, "Bo" => 85, "Cy" => 90]; arsort($scores); echo implode(",", array_keys($scores)), "\n"; - 11
PHP SPL data structures: stack, queue, and heap
Use the standard SplStack, SplQueue, SplMinHeap, and SplPriorityQueue classes.
Which structure would you use for a to-do list processed in arrival order?
Reveal answer and reasoning
SplQueue: the first task added is the first one processed.
Show starting example
PHP<?php $stack = new SplStack(); $stack->push("a"); $stack->push("b"); $stack->push("c"); echo $stack->pop(), " ", $stack->top(), " ", count($stack), "\n"; $queue = new SplQueue(); $queue->enqueue(1); $queue->enqueue(2); $queue->enqueue(3); echo $queue->dequeue(), " ", $queue->dequeue(), " ", count($queue), "\n"; $heap = new SplMinHeap(); foreach ([5, 1, 8, 3] as $value) { $heap->insert($value); } echo $heap->extract(), " ", $heap->top(), "\n"; $tasks = new SplPriorityQueue(); $tasks->insert("low", 1); $tasks->insert("high", 10); $tasks->insert("medium", 5); $order = []; while (!$tasks->isEmpty()) { $order[] = $tasks->extract(); } echo implode(" > ", $order), "\n"; - 12
PHP unit tests with closures
Write a tiny test runner with named test closures and check normal values and expected errors.
What does the runner do when a test throws a TypeError?
Reveal answer and reasoning
The TypeError is not an AssertionFailure, so it is not caught by the loop and ends the script. A more robust runner would catch Throwable and report it as an error.
Show starting example
PHP<?php class AssertionFailure extends Exception {} function assertSame(mixed $expected, mixed $actual): void { if ($expected !== $actual) { throw new AssertionFailure("expected " . var_export($expected, true) . " but got " . var_export($actual, true)); } } function assertThrows(string $class, callable $code): void { try { $code(); } catch (Throwable $e) { if ($e instanceof $class) { return; } throw new AssertionFailure("expected $class but got " . get_class($e)); } throw new AssertionFailure("expected $class but nothing was thrown"); } function clamp(int $value, int $low, int $high): int { if ($low > $high) { throw new InvalidArgumentException("low > high"); } return max($low, min($high, $value)); } $tests = [ "inside the range" => fn() => assertSame(5, clamp(5, 0, 10)), "below the range" => fn() => assertSame(0, clamp(-3, 0, 10)), "above the range" => fn() => assertSame(10, clamp(99, 0, 10)), "bad bounds throw" => fn() => assertThrows(InvalidArgumentException::class, fn() => clamp(1, 5, 2)), "division by zero" => fn() => assertThrows(DivisionByZeroError::class, fn() => intdiv(1, 0)), "a failing check" => fn() => assertSame(6, clamp(7, 0, 5)), ]; $failed = 0; foreach ($tests as $name => $test) { try { $test(); echo "PASS $name\n"; } catch (AssertionFailure $e) { $failed++; echo "FAIL $name: ", $e->getMessage(), "\n"; } } echo count($tests) - $failed, " passed, $failed failed\n"; - 13
PHP password_hash, password_verify, and hash_hmac
Store passwords with bcrypt through the built-in API and sign messages with HMAC.
When should password_needs_rehash be called?
Reveal answer and reasoning
After a successful login, using the stored hash. If it returns true, hash the password again with the current settings and save the new hash.
Show starting example
PHP<?php $hash = password_hash("correct horse", PASSWORD_DEFAULT); echo "algorithm: ", password_get_info($hash)["algoName"], "\n"; echo "right password: ", password_verify("correct horse", $hash) ? "accepted" : "rejected", "\n"; echo "wrong password: ", password_verify("wrong guess", $hash) ? "accepted" : "rejected", "\n"; echo "hashes differ for equal passwords: ", password_hash("correct horse", PASSWORD_DEFAULT) !== $hash ? "yes" : "no", "\n"; $tag = hash_hmac("sha256", "what do ya want for nothing?", "Jefe"); echo $tag, "\n"; $again = hash_hmac("sha256", "what do ya want for nothing?", "Jefe"); echo "valid: ", hash_equals($tag, $again) ? "yes" : "no", "\n"; $forged = hash_hmac("sha256", "what do ya want for something?", "Jefe"); echo "forged valid: ", hash_equals($tag, $forged) ? "yes" : "no", "\n"; - 14
PHP preventing XSS with htmlspecialchars
Encode user text before printing it into HTML, and escape for the right context.
Why is the single quote encoded as '?
Reveal answer and reasoning
So that a value placed inside a single-quoted HTML attribute cannot end the attribute early and add new attributes such as onclick.
Show starting example
PHP<?php function e(string $text): string { return htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8"); } $comment = '<script>alert("x")</script>'; echo "<p>" . $comment . "</p>", "\n"; echo "<p>" . e($comment) . "</p>", "\n"; $name = "O'Brien & \"Sons\""; echo '<input value="' . e($name) . '">', "\n"; echo "q=" . urlencode("a b&c=d"), "\n"; echo json_encode(["note" => "</script>"], JSON_HEX_TAG), "\n";