👑 Premium Sign in Sign up

Language lab · Beginner → Web

🐘 PHP

Concept → trace → write code → run tests → explain why → improve → mini-project. Start in the browser if you like; set up your own computer when you are ready.

💻 Runs on your computerOfficial download & docs ↗

📖 Topics with real code (16)

Every verified output below was produced by running the code with PHP 8.3.36 (cli) (built: Nov 5 2026 07:26:18) (NTS) on 5 Nov 2026. Open a topic to edit the code and run it on our isolated compile server (sign in needed).

🌱 Beginner (5)

Variables and data types

Variables start with $. Types: int, float, bool, string, array, null. var_dump shows type and value; casts like (int) convert. declare(strict_types=1) makes function type checks strict.

<?php
$age = 15;
$height = 1.62;
$isStudent = true;
$name = "Asha";
$nothing = null;
echo "$name $age $height ", var_export($isStudent, true), "\n";
var_dump($age, $height, $isStudent, $nothing);
echo gettype($name), " ", (int) "88" + 2, " ", intdiv(7, 2), " ", 7 / 2, " ", (int) 9.99, "\n";
echo PHP_INT_MAX, "\n";

✅ Verified output

Asha 15 1.62 true
int(15)
float(1.62)
bool(true)
NULL
string 90 3 3.5 9
9223372036854775807
Operators and boolean logic

Use === (same value and type) rather than ==. . joins strings, ** is power, % remainder, ?? gives a default for null, <=> compares three ways.

<?php
$a = 17; $b = 5;
echo $a + $b, " ", $a - $b, " ", $a * $b, " ", $a / $b, " ", intdiv($a, $b), " ", $a % $b, " ", $a ** 2, "\n";
var_dump(5 == "5", 5 === "5", "abc" == 0);
$hasTicket = true; $isVip = false;
echo "enter: ", var_export($hasTicket && !$isVip, true), "\n";
$nick = null;
echo $nick ?? "no nickname", " | ", 1 <=> 2, " ", 2 <=> 2, "\n";
echo "Gaya" . "Gnan", "\n";

✅ Verified output

22 12 85 3.4 3 2 289
bool(true)
bool(false)
bool(false)
enter: true
no nickname | -1 0
GayaGnan
Conditionals: if, match

if/elseif/else picks a branch. match (PHP 8) is a strict, value-returning switch with no fall-through.

<?php
$marks = 78;
if ($marks >= 90) $grade = "A+";
elseif ($marks >= 75) $grade = "A";
elseif ($marks >= 35) $grade = "Pass";
else $grade = "Fail";
echo "Grade: $grade\n";
$day = "sat";
echo match ($day) {
    "sat", "sun" => "Weekend",
    default => "School day",
}, "\n";
echo $marks >= 35 ? "passed" : "try again", "\n";

✅ Verified output

Grade: A
Weekend
passed
Loops: for, while, foreach

foreach is the usual way to walk arrays (with keys if needed). break and continue control loops.

<?php
for ($i = 1; $i <= 5; $i++) {
    if ($i === 3) continue;
    echo "for $i\n";
}
$n = 10;
while ($n > 0) {
    if ($n < 4) break;
    echo "while $n\n";
    $n -= 3;
}
foreach (["red", "green"] as $i => $colour) echo "foreach $i $colour\n";
for ($r = 1; $r <= 3; $r++) {
    for ($c = 1; $c <= 3; $c++) printf("%3d", $r * $c);
    echo "\n";
}

✅ Verified output

for 1
for 2
for 4
for 5
while 10
while 7
while 4
foreach 0 red
foreach 1 green
  1  2  3
  2  4  6
  3  6  9
Functions and type declarations

Functions can declare parameter and return types, default values, named arguments and variadic parameters.

<?php
declare(strict_types=1);

function area(int $length, int $width = 1): int {
    return $length * $width;
}
function average(int ...$nums): float {
    return array_sum($nums) / count($nums);
}
function greet(string $name, string $greeting = "Namaste"): string {
    return "$greeting, $name!";
}
echo area(5, 3), " ", area(7), " ", average(4, 9, 2, 7), "\n";
echo greet(greeting: "Hello", name: "Meena"), "\n";
try {
    area("5", 3);
} catch (TypeError $e) {
    echo "TypeError with strict types\n";
}

✅ Verified output

15 7 5.5
Hello, Meena!
TypeError with strict types

🌿 Intermediate (7)

Indexed arrays

PHP arrays are ordered maps. Indexed arrays use 0,1,2... array_map, array_filter and sort transform them.

<?php
$marks = [72, 95, 64, 88];
$marks[] = 91;
sort($marks);
echo implode(", ", $marks), " | first {$marks[0]} last ", end($marks), " count ", count($marks), "\n";
$odd = array_filter(range(1, 5), fn($x) => $x % 2 === 1);
$squares = array_map(fn($x) => $x * $x, $odd);
echo implode(" ", $squares), " sum ", array_sum($squares), "\n";
[$first, $second] = $marks;
echo "$first $second\n";
print_r(array_slice($marks, -2));

✅ Verified output

64, 72, 88, 91, 95 | first 64 last 95 count 5
1 9 25 sum 35
64 72
Array
(
    [0] => 91
    [1] => 95
)
Strings

Double quotes interpolate variables; single quotes do not. Useful functions: strlen, strtoupper, str_replace, explode, implode, sprintf, str_contains. mb_ functions handle Unicode correctly.

<?php
$t = trim("  GayaGnan learning  ");
echo strtoupper($t), " | ", str_replace("learning", "labs", $t), " | ", strlen($t), "\n";
$words = explode(" ", $t);
echo implode("-", $words), " contains Gnan: ", var_export(str_contains($t, "Gnan"), true), "\n";
echo sprintf("Total: ₹%.2f for %d items", 49.5 * 3, 3), "\n";
$s = "నమస్తే";
echo "bytes ", strlen($s), " characters ", mb_strlen($s), "\n";
echo 'single: $t', " | palindrome: ", var_export("level" === strrev("level"), true), "\n";

✅ Verified output

GAYAGNAN LEARNING | GayaGnan labs | 17
GayaGnan-learning contains Gnan: true
Total: ₹148.50 for 3 items
bytes 18 characters 6
single: $t | palindrome: true
Associative arrays

Keys can be strings. ksort sorts by key; array_key_exists checks a key; array_count_values counts items.

<?php
$stock = ["pen" => 40, "book" => 12];
$stock["eraser"] = 25;
$stock["pen"] -= 5;
ksort($stock);
foreach ($stock as $item => $count) echo "$item $count\n";
echo "has ruler? ", var_export(array_key_exists("ruler", $stock), true), "\n";
$freq = array_count_values(explode(" ", "the cat and the hat and the bat"));
ksort($freq);
print_r($freq);
echo json_encode(["name" => "Asha", "marks" => [88, 92]]), "\n";

✅ Verified output

book 12
eraser 25
pen 35
has ruler? false
Array
(
    [and] => 2
    [bat] => 1
    [cat] => 1
    [hat] => 1
    [the] => 3
)
{"name":"Asha","marks":[88,92]}
Classes and constructor promotion

Classes have typed properties and methods. Constructor promotion (PHP 8) declares and assigns properties in the constructor signature; readonly fields cannot change.

<?php
class BankAccount {
    public function __construct(public readonly string $owner, private float $balance = 0) {}
    public function deposit(float $amount): void {
        if ($amount <= 0) throw new InvalidArgumentException("amount must be positive");
        $this->balance += $amount;
    }
    public function balance(): float { return $this->balance; }
}
$acc = new BankAccount("Asha", 500);
$acc->deposit(250);
echo $acc->owner, " ", $acc->balance(), "\n";
try {
    $acc->owner = "x";
} catch (Error $e) {
    echo "readonly: ", get_class($e), "\n";
}

✅ Verified output

Asha 750
readonly: Error
Interfaces, inheritance and polymorphism

An interface is a contract; abstract classes share code; subclasses override methods. instanceof checks the type.

<?php
interface Shape { public function area(): float; }
abstract class Base implements Shape {
    public function describe(): string { return static::class . " " . number_format($this->area(), 2); }
}
class Circle extends Base { public function __construct(private float $r) {} public function area(): float { return M_PI * $this->r ** 2; } }
class Rect extends Base { public function __construct(private float $w, private float $h) {} public function area(): float { return $this->w * $this->h; } }
foreach ([new Circle(1), new Rect(3, 4)] as $s) echo $s->describe(), " ", var_export($s instanceof Shape, true), "\n";

✅ Verified output

Circle 3.14 true
Rect 12.00 true
Exceptions

try/catch/finally handles errors. Catch several types with |. Custom exceptions extend Exception.

<?php
class InsufficientFunds extends Exception {}
function withdraw(float $balance, float $amount): float {
    if ($amount > $balance) throw new InsufficientFunds("need " . ($amount - $balance) . " more");
    return $balance - $amount;
}
try {
    echo intdiv(10, 0);
} catch (DivisionByZeroError $e) {
    echo "DivisionByZeroError: ", $e->getMessage(), "\n";
} finally {
    echo "finally always runs\n";
}
try {
    withdraw(100, 150);
} catch (InsufficientFunds | TypeError $e) {
    echo get_class($e), ": ", $e->getMessage(), "\n";
}

✅ Verified output

DivisionByZeroError: Division by zero
finally always runs
InsufficientFunds: need 50 more
Files and CSV

file_put_contents and file_get_contents handle whole files; fgetcsv reads CSV rows. sys_get_temp_dir gives the temp folder.

<?php
$path = tempnam(sys_get_temp_dir(), "marks");
file_put_contents($path, "name,marks\nAsha,88\nRavi,95\n");
$f = fopen($path, "r");
fgetcsv($f, escape: "\\");
$total = 0; $count = 0;
while (($row = fgetcsv($f, escape: "\\")) !== false) {
    echo "$row[0] -> $row[1]\n";
    $total += (int) $row[1];
    $count++;
}
fclose($f);
unlink($path);
echo "average ", $total / $count, "\n";

✅ Verified output

Asha -> 88
Ravi -> 95
average 91.5

🌳 Advanced (4)

Recursion and memoization

A static array inside the function keeps the cache between calls. PHP ints are 64-bit, enough for fib(80).

<?php
function fib(int $n): int {
    static $memo = [];
    if ($n < 2) return $n;
    return $memo[$n] ??= fib($n - 1) + fib($n - 2);
}
function binarySearch(array $a, int $t, int $lo, int $hi): int {
    if ($lo > $hi) return -1;
    $mid = intdiv($lo + $hi, 2);
    if ($a[$mid] === $t) return $mid;
    return $a[$mid] < $t ? binarySearch($a, $t, $mid + 1, $hi) : binarySearch($a, $t, $lo, $mid - 1);
}
echo implode(" ", array_map('fib', range(0, 9))), " | fib(80) = ", fib(80), "\n";
$a = [2, 5, 8, 12, 16, 23, 38];
echo "index of 23: ", binarySearch($a, 23, 0, count($a) - 1), "\n";

✅ Verified output

0 1 1 2 3 5 8 13 21 34 | fib(80) = 23416728348467685
index of 23: 5
Closures, arrow functions and callables

Closures can capture variables with use (by value or &reference). Arrow functions fn() capture automatically. usort takes a comparison callable.

<?php
function makeCounter(): Closure {
    $n = 0;
    return function () use (&$n) { return ++$n; };
}
$c = makeCounter();
$c(); $c();
echo "count ", $c(), "\n";
$applyTwice = fn(callable $f, $x) => $f($f($x));
echo $applyTwice(fn($n) => $n * 3, 2), " ", $applyTwice('strtoupper', "hi"), "\n";
$students = [["Asha", 88], ["Ravi", 95], ["Meena", 72]];
usort($students, fn($a, $b) => $b[1] <=> $a[1]);
echo implode(", ", array_column($students, 0)), "\n";

✅ Verified output

count 3
18 HI
Ravi, Asha, Meena
Generators and Fibers

A generator yields values one at a time, so a large sequence never sits in memory. Fibers (PHP 8.1) pause and resume code: the base for async libraries.

<?php
function evens(int $limit): Generator {
    for ($i = 0; $i <= $limit; $i += 2) yield $i;
}
$sum = 0;
foreach (evens(1_000_000) as $e) $sum += $e;
echo "sum of evens ", $sum, ", memory stays small\n";

$fiber = new Fiber(function (string $task): string {
    echo "start $task\n";
    $got = Fiber::suspend("paused at step 1");
    echo "resumed with $got\n";
    return "done $task";
});
echo $fiber->start("download"), "\n";
$fiber->resume("data");
echo $fiber->getReturn(), "\n";

✅ Verified output

sum of evens 250000500000, memory stays small
start download
paused at step 1
resumed with data
done download
Copy-on-write and references

Arrays are copied on assignment (copy-on-write keeps it cheap); objects are handles, so two variables can share one object. & makes a reference to the same variable.

<?php
$a = [1, 2];
$b = $a;          // copy
$b[] = 3;
echo "a ", implode(",", $a), " b ", implode(",", $b), "\n";
$o1 = new stdClass();
$o1->x = 1;
$o2 = $o1;        // same object
$o2->x = 99;
echo "objects share: ", $o1->x, "\n";
$o3 = clone $o1;
$o3->x = 5;
echo "clone is separate: ", $o1->x, " ", $o3->x, "\n";
function addMark(array &$list): void { $list[] = 100; }
addMark($a);
echo "by reference: ", implode(",", $a), "\n";

✅ Verified output

a 1,2 b 1,2,3
objects share: 99
clone is separate: 99 5
by reference: 1,2,100

🛠️ Set up and verify

  1. Install
    winget install PHP.PHP.8.3
    brew install php
    sudo apt install php-cli php-sqlite3
  2. Editor: Visual Studio Code with PHP Intelephense, PHP Debug.
  3. Create hello.php
    <?php
    echo "Hello, GayaGnan!\n";
  4. Run it and check the version
    php hello.php
    php --version
  5. Health check: paste what your terminal printed.

Install one language at a time. These commands only install or print versions; nothing here deletes files. Versions change: check the official page and note the version you installed.

🧭 The 14-lab path

  • 00 · Setup and verify — install the runtime and editor, run hello world, print the version, fix PATH
  • 01 · Syntax and trace — follow each line and the value of every variable
  • 02 · Logic — conditionals and decision trees
  • 03 · Loops — trace tables and loop challenges
  • 04 · Functions — break problems down and test each piece
  • 05 · Data structures — lists, maps, stacks, queues and trees you can see
  • 06 · Debugging — bug cases with a hint ladder
  • 07 · Algorithms — search, sort and Big-O comparison
  • 08 · Files and data — read and write fixture files
  • 09 · API or database — talk to a mock API or a sandbox database
  • 10 · Testing — write tests and read failing assertions
  • 11 · Security — secure coding and dependency hygiene for this language
  • 12 · Project — one portfolio mini-project
  • 13 · Assessment — quiz, coding test, rubric and an explain-your-answer prompt

📚 Modules

  1. syntax
  2. arrays
  3. functions
  4. forms
  5. validation
  6. sessions
  7. PDO
  8. SQL safety
  9. Composer
  10. MVC concepts
  11. REST
  12. tests
  13. environment config

🏗️ Mini-projects

  • Course enrolment mock portal
  • Catalogue API
  • Login flow with local test identities (never real credentials)

Projects use synthetic data only.