Back to Articles
25 min read

Modern PHP Fundamentals: A Comprehensive Guide to Syntax, Functions, and Data Structures

Jumpstart your backend development journey with this extensive guide. We cover the entire lifecycle of a basic PHP application—from configuring local environments (XAMPP, Valet) and IDEs to mastering control structures, closures, and complex array manipulations using the latest PHP 8 standards.

Environment Setup

Installing PHP (XAMPP, WAMP, MAMP, Laravel Valet)

These are all-in-one local development stacks that bundle Apache/Nginx, MySQL, and PHP together. XAMPP is cross-platform (recommended for beginners), WAMP is Windows-only, MAMP targets macOS, and Laravel Valet is a minimalist macOS solution using Nginx that's blazing fast for Laravel development. At Google-scale thinking: pick one, don't overthink it, XAMPP for learning, Valet for serious Laravel work.

┌─────────────────────────────────────────────┐ │ Local Dev Stack Options │ ├─────────────┬─────────────┬─────────────────┤ │ XAMPP │ WAMP │ Laravel Valet │ ├─────────────┼─────────────┼─────────────────┤ │ Cross-plat │ Windows │ macOS only │ │ Apache+MySQL│ Apache+MySQL│ Nginx+MySQL │ │ Beginner │ Beginner │ Advanced │ └─────────────┴─────────────┴─────────────────┘

PHP CLI vs Web Server

PHP CLI (Command Line Interface) executes scripts directly from terminal without a web server, ideal for cron jobs, queue workers, and build scripts. Web Server mode (via Apache's mod_php or PHP-FPM with Nginx) handles HTTP requests. In production, always use PHP-FPM for better process management and resource isolation.

# CLI Mode $ php script.php $ php -r "echo 'Hello';" # Check PHP version $ php -v # Start built-in web server (development only!) $ php -S localhost:8000

mod_php

mod_php is an Apache module that embeds the PHP interpreter directly into the Apache worker process. When Apache receives a request for a .php file, the same process that handles the HTTP connection also parses and executes the script.

  • Pros

    • Simple to set up on Apache; just enable the module.
    • Low latency for single‑process setups because no extra IPC is needed.
  • Cons

    • All Apache workers share the same PHP runtime, so a misbehaving script can affect every request.
    • Scaling is limited: each Apache process consumes memory for both the web server and PHP, leading to higher resource usage under load.
    • Difficult to isolate different PHP versions or extensions per site.

PHP‑FPM

PHP‑FPM (FastCGI Process Manager) (Common Gateway Interface) runs PHP as a separate pool of worker processes that communicate with the web server via the FastCGI protocol. Nginx (and Apache with mod_proxy_fcgi) forwards HTTP requests to these workers.

  • Pros

    • Process isolation – each pool can have its own user, PHP version, and configuration.
    • Dynamic scaling – the manager can spawn or kill workers based on load, keeping memory usage efficient.
    • Better security – workers run under non‑privileged users, reducing the impact of a compromised script.
    • Works well with both Nginx and Apache (via mod_proxy_fcgi).
  • Cons

    • Slightly more complex to configure (requires a FastCGI socket or TCP port).
    • Adds an extra network hop, though the overhead is minimal.

Comparison

Featuremod_php (Apache)PHP‑FPM (FastCGI)
IntegrationBuilt‑in Apache moduleSeparate process pool, accessed via FastCGI
Process isolationShared across all Apache workersIsolated per pool (user, version)
ScalingLimited; each Apache worker holds PHPDynamic worker count, lower memory per request
SecurityLess granular (all workers share same privileges)Per‑pool user IDs, better sandboxing
Typical use caseSmall sites, simple Apache setupsProduction, high‑traffic, multi‑site environments

In production, PHP‑FPM is generally preferred because it offers superior process management, resource isolation, and flexibility, especially when paired with Nginx or Apache’s mod_proxy_fcgi.


php.ini Configuration

The php.ini file is PHP's master configuration controlling memory limits, execution time, error reporting, extensions, and upload sizes. Locate it with php --ini. Critical production settings include disabling display_errors, setting appropriate memory_limit, and configuring opcache for performance.

; Key php.ini settings memory_limit = 256M max_execution_time = 30 upload_max_filesize = 50M post_max_size = 50M display_errors = Off ; Off in production! error_reporting = E_ALL date.timezone = UTC opcache.enable = 1

IDE Setup (PHPStorm, VS Code)

PHPStorm is the gold standard, paid but worth every penny with built-in debugging, refactoring, database tools, and framework support. VS Code is free and excellent with extensions: PHP Intelephense (intellisense), PHP Debug (Xdebug), and PHP CS Fixer. Configure Xdebug for step-debugging regardless of IDE choice.

VS Code Essential Extensions: ├── PHP Intelephense (bmewburn) ├── PHP Debug (xdebug) ├── PHP CS Fixer (junstyle) ├── PHPDoc Generator └── DotENV

First PHP Script

Every PHP file starts with <?php and code executes top-to-bottom. Save as .php extension, place in your web server's document root (htdocs for XAMPP), and access via browser or run via CLI.

<?php // first.php - Your first PHP script echo "Hello, World!"; // Variables $name = "Developer"; $year = date("Y"); echo "Welcome, $name! It's $year.";

Basic Syntax

PHP Tags and Embedding in HTML

PHP code lives between <?php ?> tags and can be embedded directly in HTML files. Short echo syntax <?= ?> outputs values directly. Avoid short open tags <? for portability. Files containing only PHP should omit the closing ?> to prevent accidental whitespace issues.

<!DOCTYPE html> <html> <body> <h1><?php echo "Dynamic Title"; ?></h1> <p>Current time: <?= date("H:i:s") ?></p> <?php $items = ['Apple', 'Banana', 'Orange']; foreach ($items as $item) { echo "<li>$item</li>"; } ?> </body> </html>

Comments (Single-line, Multi-line, DocBlocks)

Comments are ignored by the interpreter. Use // or # for single-line, /* */ for multi-line blocks, and /** */ DocBlocks for documentation that tools like PHPDoc can parse. DocBlocks are essential for IDE intellisense and auto-generated API documentation.

<?php // Single-line comment # Also single-line (shell-style) /* Multi-line comment spans multiple lines */ /** * DocBlock - parsed by documentation tools * * @param string $name User's name * @param int $age User's age * @return bool Success status */ function registerUser(string $name, int $age): bool { return true; }

Variables and Variable Scope

Variables start with $, are case-sensitive, and don't require declaration. PHP has function-level scope, variables inside functions are local by default. Use global keyword or $GLOBALS array to access global variables inside functions. Static variables persist across function calls.

<?php $global = "I'm global"; function demo() { $local = "I'm local"; global $global; // Access global static $count = 0; // Persists across calls $count++; echo $global; // Works echo $GLOBALS['global']; // Alternative } demo(); // $count = 1 demo(); // $count = 2

Constants and Magic Constants

Constants are immutable values defined via define() or const. Magic constants are predefined and change based on context: __FILE__, __DIR__, __LINE__, __CLASS__, __FUNCTION__, __METHOD__, __NAMESPACE__. Constants are globally accessible and conventionally UPPERCASE.

<?php // Defining constants define('API_KEY', 'abc123'); const MAX_USERS = 100; // Magic constants (context-aware) echo __FILE__; // /var/www/app/index.php echo __DIR__; // /var/www/app echo __LINE__; // Current line number echo __FUNCTION__; // Current function name echo __CLASS__; // Current class name echo __METHOD__; // Class::method echo __NAMESPACE__; // Current namespace

Data Types (String, Integer, Float, Boolean, Array, Object, Resource, NULL)

PHP has 8 basic data types (often called "primitive" or "basic types" in the community, though PHP's documentation avoids the term "primitive" formally). They are:

  • 4 Scalar Types (hold single values): int (integer), float (floating-point), string (text), bool (boolean).

  • 2 Compound Types (hold multiple values): array (ordered/indexed or associative), object (instance of a class).

  • 2 Special Types: resource (handle to external resources, e.g., database connections), null (absence of a value).

Use gettype() to check type at runtime, var_dump() for debugging. PHP 7+ added type declarations for stricter code.

<?php $string = "Hello"; // string $integer = 42; // int $float = 3.14; // float $boolean = true; // bool $array = [1, 2, 3]; // array $object = new stdClass(); // object $nothing = null; // NULL var_dump($integer); // int(42) // Type declarations (PHP 7+) function add(int $a, int $b): int { return $a + $b; }

String

In PHP both single quotes (') and double quotes (") are used to delimit string literals, but they behave differently:

AspectSingle‑quoted string ('...')Double‑quoted string ("...")
Variable interpolationNo interpolation; $var is treated as literal text.Variables are expanded, e.g., "Hello $name" inserts the value of $name.
Escape sequencesOnly \\ (backslash) and \' (single‑quote) are recognized.Recognizes many escapes: \\, \", \n, \r, \t, \v, \f, \$, and octal/hex (\0, \x41).
PerformanceSlightly faster because the parser does less work.Slightly slower due to interpolation and more escape processing.
Typical useWhen you need a literal string with no variable substitution.When you want to embed variables or special characters directly.
$name = 'Alice'; // Single‑quoted echo 'Hello $name\n'; // outputs: Hello $name\n // Double‑quoted echo "Hello $name\n"; // outputs: Hello Alice (followed by a newline)

Type Juggling and Type Casting

PHP automatically converts types based on context (type juggling), convenient but dangerous. Explicit casting uses (type) syntax. PHP 8 introduced stricter comparisons. Always use === over == to avoid juggling surprises. Enable strict_types for type-safe code.

<?php // Type juggling (automatic) $result = "5" + 3; // int(8) - string becomes int $test = "10 apples"; echo $test + 5; // 15 (takes leading number) // Explicit casting $str = "123"; $int = (int) $str; // 123 $flt = (float) "3.14"; // 3.14 $bool = (bool) 1; // true $arr = (array) $obj; // object to array // Strict mode declare(strict_types=1);

echo vs print

Both output strings, but echo is marginally faster, accepts multiple arguments, and returns nothing. print returns 1 (usable in expressions) and accepts only one argument. In practice, use echo—it's the convention and slightly more performant.

<?php // echo - faster, multiple args, no return echo "Hello"; echo "Hello", " ", "World"; // Multiple args echo("Parentheses optional"); // print - returns 1, single arg only print "Hello"; $result = print "Hi"; // $result = 1 // print in expression (rare use case) $success = ($value > 0) && print "Positive!";

Heredoc and Nowdoc Syntax

Heredoc (<<<IDENTIFIER) allows multi-line strings with variable interpolation—great for HTML templates and SQL. Nowdoc (<<<'IDENTIFIER') is like single quotes—no parsing. Closing identifier must be on its own line with no leading whitespace (PHP 7.3+ relaxed this rule).

<?php $name = "John"; $price = 99.99; // Heredoc - variables ARE parsed $html = <<<HTML <div class="card"> <h1>Hello, $name!</h1> <p>Price: \${$price}</p> </div> HTML; // Nowdoc - variables NOT parsed $template = <<<'CODE' $variable = "not parsed"; echo $name; // Literal: $name CODE;

Operators

Arithmetic Operators

Standard mathematical operations: + (add), - (subtract), * (multiply), / (divide), % (modulus), ** (exponentiation, PHP 5.6+). Division always returns float if not evenly divisible. Use intdiv() for integer division.

<?php $a = 10; $b = 3; echo $a + $b; // 13 echo $a - $b; // 7 echo $a * $b; // 30 echo $a / $b; // 3.333... echo $a % $b; // 1 (modulus) echo $a ** $b; // 1000 (power) echo intdiv(10, 3); // 3 (integer division)

Assignment Operators

Basic assignment = plus compound operators that combine arithmetic/string operations with assignment: +=, -=, *=, /=, %=, **=, .=. These modify the variable in place, improving readability and slight performance.

<?php $x = 10; $x += 5; // $x = $x + 5 → 15 $x -= 3; // $x = $x - 3 → 12 $x *= 2; // $x = $x * 2 → 24 $x /= 4; // $x = $x / 4 → 6 $x %= 4; // $x = $x % 4 → 2 $x **= 3; // $x = $x ** 3 → 8 $str = "Hello"; $str .= " World"; // "Hello World"

Comparison Operators

== (equal with juggling), === (identical—type + value), !=/<> (not equal), !== (not identical), <, >, <=, >=. Always prefer === to avoid type juggling bugs. PHP 8 made comparisons stricter for strings and numbers.

<?php $a = 5; $b = "5"; var_dump($a == $b); // true (value equal) var_dump($a === $b); // false (type differs) var_dump($a != $b); // false var_dump($a !== $b); // true // PHP 8: "0" == false is NOW false (was true!) // Comparison table // ┌─────────┬────────┬─────────┐ // │ Expr │ == │ === │ // ├─────────┼────────┼─────────┤ // │ 1=="1" │ true │ false │ // │ 0==null │ true │ false │ // │ 0==[] │ false │ false │ // └─────────┴────────┴─────────┘

Increment/Decrement Operators

++$x (pre-increment), $x++ (post-increment), --$x (pre-decrement), $x-- (post-decrement). Pre returns the new value, post returns original then changes. Common in loops but be cautious about readability in complex expressions.

<?php $a = 5; echo $a++; // Outputs 5, then $a becomes 6 echo ++$a; // $a becomes 7, outputs 7 echo $a--; // Outputs 7, then $a becomes 6 echo --$a; // $a becomes 5, outputs 5 // Practical loop example for ($i = 0; $i < 5; $i++) { echo $i; // 0, 1, 2, 3, 4 }

Logical Operators

&&/and (AND), ||/or (OR), ! (NOT), xor (exclusive OR). Symbolic versions (&&, ||) have higher precedence than word versions (and, or). Short-circuit evaluation: second operand not evaluated if result determined by first.

<?php $a = true; $b = false; var_dump($a && $b); // false var_dump($a || $b); // true var_dump(!$a); // false var_dump($a xor $b); // true (one true, not both) // Short-circuit evaluation $result = false && expensive_function(); // Never called! // Precedence trap! $x = true || false && false; // true (&& first) $y = (true || false) && false; // false

String Operators

Concatenation . joins strings, .= appends to existing string. Unlike JavaScript's +, PHP strictly uses . for strings to avoid ambiguity with arithmetic. Variables can be interpolated directly in double-quoted strings.

<?php $first = "Hello"; $last = "World"; // Concatenation $full = $first . " " . $last; // "Hello World" // Append $str = "Hello"; $str .= " World"; // "Hello World" // Interpolation (double quotes) $name = "John"; echo "Hello, $name!"; // Hello, John! echo "Hello, {$name}!"; // Explicit syntax

Array Operators

+ (union—preserves first array keys), == (equal values), === (identical—same order and types), !=/<> (not equal), !== (not identical). Union differs from array_merge()—it doesn't renumber numeric keys.

<?php $a = ['a' => 1, 'b' => 2]; $b = ['b' => 3, 'c' => 4]; // Union: $a takes priority on collisions $union = $a + $b; // ['a' => 1, 'b' => 2, 'c' => 4] // vs array_merge(): $b overwrites $merged = array_merge($a, $b); // ['a' => 1, 'b' => 3, 'c' => 4] var_dump($a == $b); // false var_dump($a === $b); // false

Null Coalescing Operator (??)

Returns the left operand if it exists and is not null, otherwise returns the right. Chainable. Perfect for default values without triggering "undefined" notices. ??= (PHP 7.4+) assigns only if null/undefined.

<?php // Instead of: $name = isset($_GET['name']) ? $_GET['name'] : 'Guest'; // Use: $name = $_GET['name'] ?? 'Guest'; // Chainable $value = $a ?? $b ?? $c ?? 'default'; // Null coalescing assignment (PHP 7.4+) $config['timeout'] ??= 30; // Sets only if null/undefined // Works with nested arrays $city = $user['address']['city'] ?? 'Unknown';

Nullsafe Operator (?->)

PHP 8.0 addition. Short-circuits to null if the object is null instead of throwing an error. Chainable. Eliminates verbose null checking chains common in nested object access.

<?php // Before PHP 8: $country = null; if ($user !== null && $user->address !== null) { $country = $user->address->country; } // PHP 8+ with nullsafe: $country = $user?->address?->country; // Chainable with methods $name = $user?->getProfile()?->getName() ?? 'Anonymous'; // Note: Only for objects, not arrays // Use ?? for arrays: $arr['key'] ?? null

Spaceship Operator (<=>)

Combined comparison operator returning -1, 0, or 1. Perfect for sorting callbacks. Compares left to right: returns -1 if left is smaller, 0 if equal, 1 if larger. Works with all comparable types.

<?php echo 1 <=> 2; // -1 (1 < 2) echo 2 <=> 2; // 0 (equal) echo 3 <=> 2; // 1 (3 > 2) // Perfect for usort $users = [ ['name' => 'Bob', 'age' => 30], ['name' => 'Alice', 'age' => 25], ]; usort($users, fn($a, $b) => $a['age'] <=> $b['age']); // Sorted by age ascending // Descending: swap operands usort($users, fn($a, $b) => $b['age'] <=> $a['age']);

Spread Operator (...)

Unpacks arrays or Traversable objects into individual elements. Used in function calls to expand arrays as arguments, in array literals to merge arrays (PHP 7.4+), and in function definitions for variadic parameters.

<?php // Variadic function definition function sum(...$numbers) { return array_sum($numbers); } echo sum(1, 2, 3, 4); // 10 // Unpacking in function calls $args = [1, 2, 3]; echo sum(...$args); // 6 // Array merging (PHP 7.4+) $arr1 = [1, 2, 3]; $arr2 = [4, 5, 6]; $merged = [...$arr1, ...$arr2]; // [1,2,3,4,5,6] // Named arguments + spread (PHP 8+) $params = ['b' => 2, 'c' => 3]; myFunc(a: 1, ...$params);

Control Structures

if, else, elseif

Standard conditional branching. Expressions evaluated as boolean (falsy: false, 0, 0.0, "", "0", [], null). Use strict comparisons to avoid type juggling. elseif can be written as one or two words.

<?php $score = 85; if ($score >= 90) { $grade = 'A'; } elseif ($score >= 80) { $grade = 'B'; } elseif ($score >= 70) { $grade = 'C'; } else { $grade = 'F'; } // Ternary shorthand $grade = $score >= 60 ? 'Pass' : 'Fail'; // Null coalescing ternary $name = $user['name'] ?? 'Guest';

switch Statement

Multi-branch comparison using loose equality (==). Fall-through behavior requires explicit break. Use default for unmatched cases. Better for comparing one variable against multiple discrete values, but consider match in PHP 8+.

<?php $day = 'Monday'; switch ($day) { case 'Monday': case 'Tuesday': // Fall-through echo "Weekday"; break; case 'Saturday': case 'Sunday': echo "Weekend"; break; default: echo "Unknown"; } // ⚠️ Warning: Uses == (loose comparison) // switch(0) matches case "string"!

match Expression (PHP 8+)

Modern replacement for switch: expression-based (returns value), uses strict comparison (===), no fall-through, throws UnhandledMatchError if no match. More concise and safer than switch.

When using match(true), each arm’s condition must be a boolean expression, the first true condition wins.

<?php $status = 200; // match is an expression (returns value) $message = match($status) { 200, 201 => 'Success', 400 => 'Bad Request', 404 => 'Not Found', 500 => 'Server Error', default => 'Unknown', }; $age = 27; // Uses strict comparison (===) $result = match(true) { $age < 18 => 'Minor', $age < 65 => 'Adult', default => 'Senior', }; echo "Age $age is $result\n";

while Loop

Evaluates condition before each iteration—may execute zero times. Use for unknown iteration counts. Beware infinite loops; always ensure termination condition changes.

<?php $i = 0; while ($i < 5) { echo $i; // 0, 1, 2, 3, 4 $i++; } // Reading file lines $handle = fopen('file.txt', 'r'); while (($line = fgets($handle)) !== false) { echo $line; } fclose($handle);

do-while Loop

Evaluates condition after each iteration—guarantees at least one execution. Useful when you need to run code before checking condition (e.g., user input validation, menu systems).

<?php $i = 0; do { echo $i; // 0, 1, 2, 3, 4 $i++; } while ($i < 5); // Always runs at least once $j = 100; do { echo "Runs once!"; // Still executes } while ($j < 5); // Practical: retry logic do { $result = attemptConnection(); $tries++; } while (!$result && $tries < 3);

for Loop

Three-part loop: initialization, condition, increment—all optional. Classic counter-based iteration. Variables declared in init are scoped to the loop block (unlike JavaScript's pre-ES6 var).

<?php for ($i = 0; $i < 5; $i++) { echo $i; // 0, 1, 2, 3, 4 } // Multiple expressions for ($i = 0, $j = 10; $i < $j; $i++, $j--) { echo "$i-$j "; // 0-10 1-9 2-8 3-7 4-6 } // Infinite loop (careful!) for (;;) { // break required to exit } // Pre-calculate array length for ($i = 0, $len = count($arr); $i < $len; $i++) { echo $arr[$i]; }

foreach Loop

Iterates over arrays and objects. $value is a copy by default; use &$value for reference (modify in place). Always unset() references after loop to avoid bugs. Most idiomatic way to iterate in PHP.

<?php $fruits = ['apple', 'banana', 'orange']; // Value only foreach ($fruits as $fruit) { echo $fruit; } // Key-value foreach ($fruits as $index => $fruit) { echo "$index: $fruit"; } // By reference (modify in place) foreach ($fruits as &$fruit) { $fruit = strtoupper($fruit); } unset($fruit); // ⚠️ IMPORTANT: unset reference! // Associative array $user = ['name' => 'John', 'age' => 30]; foreach ($user as $key => $value) { echo "$key: $value\n"; }

break and continue

break exits the loop entirely, continue skips to next iteration. Both accept optional numeric argument for nested loops (e.g., break 2 exits two levels). Use sparingly—complex break/continue often signals refactoring opportunity.

<?php // break - exit loop for ($i = 0; $i < 10; $i++) { if ($i === 5) break; echo $i; // 0, 1, 2, 3, 4 } // continue - skip iteration for ($i = 0; $i < 5; $i++) { if ($i === 2) continue; echo $i; // 0, 1, 3, 4 } // Breaking nested loops foreach ($matrix as $row) { foreach ($row as $cell) { if ($cell === 'target') { break 2; // Exit both loops } } }

goto Statement

Jumps to a labeled statement within the same file and function scope. Cannot jump into loops or switch. Universally considered bad practice—creates spaghetti code. PHP includes it for legacy compatibility; modern code should never use it.

<?php // ⚠️ DON'T USE THIS IN REAL CODE goto end; echo "This is skipped"; end: echo "Jumped here"; // Only valid use case: breaking deeply nested // structures (but refactoring is better) // Restrictions: // - Cannot jump INTO loops/switch // - Cannot jump between functions // - Cannot jump into different files

Alternative Syntax for Control Structures

PHP offers colon-based syntax for templating: if(): / endif;, foreach(): / endforeach;, etc. Improves readability when mixing PHP with HTML. Never mix curly braces with alternative syntax in the same block.

<?php $isAdmin = true; ?> <?php if ($isAdmin): ?> <div class="admin-panel"> <h1>Welcome, Admin!</h1> </div> <?php else: ?> <p>Access denied</p> <?php endif; ?> <?php foreach ($items as $item): ?> <li><?= htmlspecialchars($item) ?></li> <?php endforeach; ?> <?php // Available alternatives: // if/endif, while/endwhile, for/endfor // foreach/endforeach, switch/endswitch ?>

┌────────────────────────────────────────────────────────┐ │ PHP Control Flow Summary │ ├──────────────────┬─────────────────────────────────────┤ │ Conditionals │ if, else, elseif, switch, match │ │ Counted Loops │ for │ │ Conditional Loops│ while, do-while │ │ Collection Loops │ foreach │ │ Jump Statements │ break, continue, return, goto │ └──────────────────┴─────────────────────────────────────┘

Functions

Defining Functions

Functions in PHP are declared using the function keyword, followed by a name, parentheses for parameters, and curly braces containing the code block. Function names are case-insensitive (though following conventions is recommended) and must start with a letter or underscore.

function greet() { echo "Hello, World!"; } greet(); // Output: Hello, World!

Function Parameters

Parameters are variables passed into functions, allowing you to inject data for processing. They act as local variables within the function scope and are passed by value by default (use & prefix for pass-by-reference).

function add($a, $b) { return $a + $b; } echo add(5, 3); // Output: 8 // Pass by reference function increment(&$value) { $value++; } $num = 10; increment($num); echo $num; // Output: 11

Default Parameter Values

Default values allow parameters to be optional; if no argument is provided, the default is used. Default parameters must be placed after required parameters in the function signature to avoid ambiguity.

function greet($name = "Guest") { return "Hello, $name!"; } echo greet(); // Output: Hello, Guest! echo greet("Alice"); // Output: Hello, Alice!

Named Arguments (PHP 8+)

Named arguments allow passing values by parameter name rather than position, improving readability and allowing you to skip optional parameters. This is especially useful for functions with many optional parameters.

function createUser($name, $email, $role = 'user', $active = true) { return compact('name', 'email', 'role', 'active'); } // Skip $role, set $active directly $user = createUser( name: "John", email: "john@example.com", active: false );

Return Values

Functions can return data using the return statement, which immediately exits the function and passes the value back to the caller. Functions without an explicit return statement return null by default.

function square($n) { return $n * $n; } $result = square(4); // $result = 16 function multipleReturns($n) { return [$n, $n * 2, $n * 3]; // Return array for multiple values } [$a, $b, $c] = multipleReturns(5); // $a=5, $b=10, $c=15

Return Type Declarations

Return type declarations enforce the type of value a function must return, enabling strict typing and better IDE support. Use : type after the parameter list, and declare(strict_types=1) for strict mode.

declare(strict_types=1); function divide(float $a, float $b): float { return $a / $b; } function getUser(): array { return ['id' => 1, 'name' => 'John']; } function process(): void { // void = no return value echo "Processing..."; }

Parameter Type Declarations

Type declarations (type hints) enforce the expected type of arguments, catching type errors early. PHP supports scalar types (int, float, string, bool), compound types (array, object, callable, iterable), and class/interface names.

declare(strict_types=1); function processOrder(int $id, string $product, float $price): string { return "Order #$id: $product at $$price"; } function handleItems(array $items, callable $callback): void { foreach ($items as $item) { $callback($item); } }
┌─────────────────────────────────────────┐ │ PHP Type Declarations │ ├─────────────────────────────────────────┤ │ Scalar: int, float, string, bool │ │ Compound: array, object, callable │ │ Special: iterable, mixed, void, never │ │ Classes: ClassName, InterfaceName │ └─────────────────────────────────────────┘

Nullable Types

Nullable types allow a parameter or return value to accept either the specified type or null, declared with a ? prefix. This is useful when a value might be absent or for optional return values.

function findUser(int $id): ?array { $users = [1 => ['name' => 'John']]; return $users[$id] ?? null; // Returns array or null } function greet(?string $name): string { return "Hello, " . ($name ?? "Guest"); } echo greet(null); // Output: Hello, Guest echo greet("Alice"); // Output: Hello, Alice

Union Types (PHP 8+)

Union types allow a parameter or return value to accept multiple types, specified using the | operator. This provides flexibility while maintaining type safety, replacing the need for mixed type in many cases.

function formatId(int|string $id): string { return "ID: $id"; } echo formatId(123); // Output: ID: 123 echo formatId("ABC"); // Output: ID: ABC function parse(string $input): int|float|false { if (!is_numeric($input)) return false; return strpos($input, '.') ? (float)$input : (int)$input; }

Mixed Type

The mixed type (PHP 8+) explicitly indicates that a parameter accepts any type, equivalent to string|int|float|bool|null|array|object|callable|resource. Use it sparingly—prefer specific types when possible for better code clarity.

function dump(mixed $value): void { var_dump($value); } function process(mixed $input): mixed { // Can accept and return anything return is_array($input) ? count($input) : $input; } dump("string"); // string(6) "string" dump(123); // int(123) dump([1, 2, 3]); // array(3) {...}

Variable-length Arguments (Variadic Functions)

Variadic functions accept an unlimited number of arguments using the ... spread operator, which collects all remaining arguments into an array. You can also use ... to unpack arrays when calling functions.

function sum(int ...$numbers): int { return array_sum($numbers); } echo sum(1, 2, 3, 4, 5); // Output: 15 // Unpacking arrays into arguments $values = [10, 20, 30]; echo sum(...$values); // Output: 60 function log(string $level, string ...$messages): void { foreach ($messages as $msg) { echo "[$level] $msg\n"; } }

Anonymous Functions (Closures)

Anonymous functions (closures) are functions without names, useful for callbacks and functional programming. They can capture variables from the parent scope using the use keyword and are instances of the Closure class.

// Basic closure $greet = function($name) { return "Hello, $name!"; }; echo $greet("World"); // Output: Hello, World! // Capturing variables with 'use' $multiplier = 3; $multiply = function($n) use ($multiplier) { return $n * $multiplier; }; echo $multiply(5); // Output: 15 // As callback $numbers = [1, 2, 3, 4]; $squared = array_map(function($n) { return $n * $n; }, $numbers); // $squared = [1, 4, 9, 16]

Arrow Functions

Arrow functions (PHP 7.4+) provide a concise syntax for simple closures using fn keyword. They automatically capture variables from parent scope by value (no use needed) and can only contain a single expression that is implicitly returned.

// Traditional closure $double = function($n) { return $n * 2; }; // Arrow function equivalent $double = fn($n) => $n * 2; echo $double(5); // Output: 10 // Auto-captures parent scope $factor = 3; $multiply = fn($n) => $n * $factor; // With array functions $numbers = [1, 2, 3, 4, 5]; $evens = array_filter($numbers, fn($n) => $n % 2 === 0); // [2, 4] $doubled = array_map(fn($n) => $n * 2, $numbers); // [2,4,6,8,10]

First-class Callable Syntax (PHP 8.1+)

PHP 8.1 introduced first-class callable syntax using ... to create closure references to functions and methods, providing a cleaner alternative to string-based callables. This enables better static analysis and IDE support.

function double(int $n): int { return $n * 2; } // Old way (string-based) $callback = 'double'; // New way (first-class callable) $callback = double(...); echo $callback(5); // Output: 10 // Works with methods too class Calculator { public function add(int $a, int $b): int { return $a + $b; } public static function multiply(int $a, int $b): int { return $a * $b; } } $calc = new Calculator(); $adder = $calc->add(...); $multiplier = Calculator::multiply(...); echo $adder(2, 3); // Output: 5 echo $multiplier(4, 5); // Output: 20

Built-in Functions Overview

PHP provides thousands of built-in functions organized by extension/category, covering everything from string manipulation to database access. Use get_defined_functions() to list all available functions and the PHP manual for comprehensive documentation.

// String functions strlen("hello"); // 5 strtoupper("hello"); // "HELLO" str_replace("a", "b", "cat"); // "cbt" // Array functions count([1, 2, 3]); // 3 array_merge([1], [2]); // [1, 2] in_array(2, [1, 2, 3]); // true // Math functions abs(-5); // 5 round(3.7); // 4 rand(1, 100); // Random 1-100 // Date/Time date("Y-m-d"); // "2024-01-15" time(); // Unix timestamp strtotime("+1 week"); // Timestamp // File functions file_get_contents("file.txt"); file_exists("file.txt"); // JSON json_encode(['a' => 1]); // '{"a":1}' json_decode('{"a":1}'); // object
┌────────────────────────────────────────────────┐ │ PHP Built-in Function Categories │ ├────────────────────────────────────────────────┤ │ String │ strlen, substr, strpos, explode │ │ Array │ sort, array_map, array_filter │ │ Math │ abs, round, rand, max, min │ │ Date/Time │ date, time, strtotime, mktime │ │ File │ fopen, fread, file_get_contents │ │ JSON │ json_encode, json_decode │ │ Variable │ isset, empty, gettype, var_dump │ │ Output │ echo, print, printf, print_r │ └────────────────────────────────────────────────┘

Arrays

Indexed Arrays

Indexed arrays use sequential numeric keys starting from 0 (by default). They're PHP's equivalent of lists and are created with [] syntax or the array() construct, with elements accessed by their numeric index.

// Creating indexed arrays $fruits = ["apple", "banana", "cherry"]; $numbers = array(1, 2, 3, 4, 5); // Old syntax // Accessing elements echo $fruits[0]; // "apple" echo $fruits[-1]; // Error! (unlike Python) echo $fruits[count($fruits) - 1]; // "cherry" (last element) // Modifying $fruits[1] = "blueberry"; $fruits[] = "date"; // Append to end // Iterating foreach ($fruits as $fruit) { echo "$fruit\n"; } foreach ($fruits as $index => $fruit) { echo "$index: $fruit\n"; } // Array structure print_r($fruits); /* Array ( [0] => apple [1] => blueberry [2] => cherry [3] => date ) */
┌─────────────────────────────────────┐ │ Indexed Array: $fruits │ ├───────┬─────────────────────────────┤ │ Index │ Value │ ├───────┼─────────────────────────────┤ │ 0 │ "apple" │ │ 1 │ "banana" │ │ 2 │ "cherry" │ └───────┴─────────────────────────────┘

Associative Arrays

Associative arrays use named string keys instead of numeric indices, functioning like dictionaries or hash maps. They maintain insertion order (since PHP 7) and are ideal for structured data with meaningful key names.

// Creating associative arrays $user = [ "name" => "John", "email" => "john@example.com", "age" => 30 ]; // Accessing elements echo $user["name"]; // "John" echo $user["missing"] ?? "default"; // "default" (null coalescing) // Modifying $user["age"] = 31; $user["role"] = "admin"; // Add new key // Check key exists if (array_key_exists("name", $user)) { echo "Name exists"; } if (isset($user["name"])) { echo "Name is set and not null"; } // Iterating foreach ($user as $key => $value) { echo "$key: $value\n"; } // Get keys and values $keys = array_keys($user); // ["name", "email", "age", "role"] $values = array_values($user); // ["John", "john@...", 31, "admin"] // Remove element unset($user["role"]);
┌──────────────────────────────────────────┐ │ Associative Array: $user │ ├────────────┬─────────────────────────────┤ │ Key │ Value │ ├────────────┼─────────────────────────────┤ │ "name" │ "John" │ │ "email" │ "john@example.com" │ │ "age" │ 30 │ └────────────┴─────────────────────────────┘

Multidimensional Arrays

Multidimensional arrays are arrays containing other arrays, enabling complex data structures like matrices, tables, or nested objects. Access nested elements by chaining brackets, and iterate using nested loops.

// 2D array (matrix) $matrix = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]; echo $matrix[1][2]; // 6 (row 1, col 2) // Array of associative arrays $users = [ ["name" => "Alice", "role" => "admin"], ["name" => "Bob", "role" => "user"], ["name" => "Charlie", "role" => "user"] ]; echo $users[0]["name"]; // "Alice" // Deeply nested $company = [ "name" => "TechCorp", "departments" => [ "engineering" => [ "head" => "Jane", "employees" => ["John", "Mike", "Sara"] ], "sales" => [ "head" => "Tom", "employees" => ["Amy", "Bob"] ] ] ]; echo $company["departments"]["engineering"]["employees"][0]; // "John" // Iterating nested arrays foreach ($users as $user) { echo "{$user['name']} is a {$user['role']}\n"; }
┌─────────────────────────────────────────┐ │ $matrix (3x3) │ ├─────────┬───────┬───────┬───────────────┤ │ │ [0] │ [1] │ [2] │ ├─────────┼───────┼───────┼───────────────┤ │ [0] │ 1 │ 2 │ 3 │ │ [1] │ 4 │ 5 │ 6 │ │ [2] │ 7 │ 8 │ 9 │ └─────────┴───────┴───────┴───────────────┘

Array Functions (sort, array_map, array_filter, array_reduce)

PHP provides powerful array functions for transformation and manipulation. sort/usort modify arrays in-place, while array_map, array_filter, and array_reduce enable functional programming patterns by returning new arrays.

$numbers = [3, 1, 4, 1, 5, 9, 2, 6]; // Sorting (modifies original) sort($numbers); // [1, 1, 2, 3, 4, 5, 6, 9] rsort($numbers); // Reverse sort asort($numbers); // Sort, preserve keys ksort($numbers); // Sort by keys // Custom sort usort($numbers, fn($a, $b) => $b - $a); // Descending // array_map - transform each element $doubled = array_map(fn($n) => $n * 2, $numbers); // array_filter - keep matching elements $evens = array_filter($numbers, fn($n) => $n % 2 === 0); // array_reduce - reduce to single value $sum = array_reduce($numbers, fn($carry, $n) => $carry + $n, 0); // Chaining operations $result = array_reduce( array_filter( array_map(fn($n) => $n * 2, [1, 2, 3, 4, 5]), fn($n) => $n > 4 ), fn($sum, $n) => $sum + $n, 0 ); // Double, filter >4, sum = 6+8+10 = 24 // Other useful functions array_merge([1, 2], [3, 4]); // [1, 2, 3, 4] array_unique([1, 2, 2, 3]); // [1, 2, 3] array_reverse([1, 2, 3]); // [3, 2, 1] array_slice([1,2,3,4], 1, 2); // [2, 3] array_search(3, [1,2,3]); // 2 (index) in_array(3, [1,2,3]); // true

Array Destructuring

Array destructuring (using list() or short [] syntax) allows extracting array elements into individual variables in a single statement. This works with both indexed and associative arrays (PHP 7.1+ for associative keys).

// Basic destructuring (indexed) $coords = [10, 20, 30]; [$x, $y, $z] = $coords; echo "$x, $y, $z"; // "10, 20, 30" // Skip elements [$first, , $third] = [1, 2, 3]; // $first=1, $third=3 // With list() syntax (equivalent) list($a, $b) = [1, 2]; // Nested destructuring $nested = [[1, 2], [3, 4]]; [[$a, $b], [$c, $d]] = $nested; // Associative array destructuring (PHP 7.1+) $user = ["name" => "John", "email" => "j@example.com", "age" => 30]; ["name" => $name, "email" => $email] = $user; echo "$name, $email"; // "John, j@example.com" // In foreach $users = [ ["name" => "Alice", "role" => "admin"], ["name" => "Bob", "role" => "user"] ]; foreach ($users as ["name" => $name, "role" => $role]) { echo "$name is $role\n"; } // Swap variables [$a, $b] = [$b, $a];

Array Unpacking

Array unpacking uses the splat operator (...) to expand array elements in function calls or merge arrays (PHP 7.4+). This provides a concise syntax for spreading array contents into individual values.

// Unpacking in function calls function sum(int $a, int $b, int $c): int { return $a + $b + $c; } $numbers = [1, 2, 3]; echo sum(...$numbers); // 6 // Array merging with spread operator (PHP 7.4+) $arr1 = [1, 2, 3]; $arr2 = [4, 5, 6]; $merged = [...$arr1, ...$arr2]; // [1, 2, 3, 4, 5, 6] // Insert elements $middle = [3, 4]; $all = [1, 2, ...$middle, 5, 6]; // [1, 2, 3, 4, 5, 6] // Works with any iterable $generator = function() { yield 1; yield 2; }; $arr = [...$generator(), 3, 4]; // [1, 2, 3, 4] // Unpacking associative arrays (PHP 8.1+) $defaults = ["color" => "blue", "size" => "medium"]; $custom = ["color" => "red"]; $merged = [...$defaults, ...$custom]; // ["color" => "red", "size" => "medium"] // Combine with named arguments function createUser($name, $email, $role = 'user') {} $data = ['name' => 'John', 'email' => 'j@test.com']; createUser(...$data); // PHP 8+
┌─────────────────────────────────────────────────────┐ │ Array Unpacking Examples │ ├─────────────────────────────────────────────────────┤ │ $a = [1, 2]; │ │ $b = [3, 4]; │ │ │ │ [...$a, ...$b] → [1, 2, 3, 4] │ │ [0, ...$a, ...$b] → [0, 1, 2, 3, 4] │ │ sum(...$a) → sum(1, 2) │ └─────────────────────────────────────────────────────┘

PHP Superglobals

PHP defines a set of built‑in variables that are automatically available in every scope, functions, classes, included files, etc. These are called superglobals. They are populated by the PHP engine from the request, server environment, or internal state.

PHP Superglobals

PHP defines a set of built‑in variables that are automatically available in every scope—functions, classes, included files, etc. These are called superglobals. They are populated by the PHP engine from the request, server environment, or internal state.

List of Superglobals

SuperglobalTypical SourceCommon Use
$_GETURL query string (?key=value)Read‑only request parameters
$_POSTHTTP POST body (form data, JSON, etc.)Retrieve submitted form values
$_REQUESTMerges $_GET, $_POST, and $_COOKIE (order defined by request_order/variables_order)Quick access to any client‑supplied value (use with caution)
$_SERVERServer and execution environment (headers, script name, IP, etc.)Access request metadata, paths, protocol
$_ENVEnvironment variables (OS)Configuration, secrets (when not using other methods)
$_FILESUploaded files (multipart/form‑data)Handle file uploads
$_COOKIEHTTP cookies sent by the clientRead/write user‑specific data
$_SESSIONSession data stored on the server (via session_start())Persist user state across requests
$_GLOBALSReference to all global variables (including superglobals)Rarely used; can create variable variables

Brief Description of Each

  • $_GET – associative array of query‑string parameters. Values are strings; multiple values for the same key become an array when the key ends with [].
  • $_POST – associative array of data sent in the request body with application/x-www-form-urlencoded or multipart/form-data. For raw JSON you must read php://input.
  • $_REQUEST – convenience array that merges $_GET, $_POST, and $_COOKIE. Order is configurable; relying on it can lead to ambiguous data sources.
  • $_SERVER – contains server‑ and execution‑level information such as $_SERVER['REQUEST_METHOD'], $_SERVER['REMOTE_ADDR'], $_SERVER['SCRIPT_FILENAME'], and all HTTP headers prefixed with HTTP_.
  • $_ENV – mirrors the process environment. Populated from the OS environment or from variables_order/auto_globals_jit settings.
  • $_FILES – structured array for each uploaded file: name, type, tmp_name, error, size. Use move_uploaded_file() to store safely.
  • $_COOKIE – key/value pairs of cookies sent by the client. Set cookies with setcookie() or setrawcookie().
  • $_SESSION – after session_start(), holds per‑user data persisted between requests. Backed by files, Redis, etc., depending on session handler.
  • $_GLOBALS – an associative array referencing all global variables, including the superglobals themselves. Accessing variables via $_GLOBALS['varName'] is equivalent to $GLOBALS['varName']. Generally discouraged due to readability concerns.

Quick Reference Cheat Sheet

┌─────────────────────────────────────────────────────────────────────┐ │ PHP FUNCTIONS/STRINGS/ARRAYS │ ├─────────────────────────────────────────────────────────────────────┤ │ FUNCTIONS │ │ function name($param): type { } │ Basic function │ │ fn($x) => $x * 2 │ Arrow function │ │ function($x) use ($y) { } │ Closure with captured var │ │ func(...$args) │ Variadic parameters │ │ name: value │ Named arguments (PHP 8) │ │ ?type, type1|type2 │ Nullable, Union types │ ├─────────────────────────────────────────────────────────────────────┤ │ STRINGS │ │ "Hello $name" │ Interpolation │ │ strlen(), substr(), strpos() │ Core string functions │ │ sprintf("%s is %d", $a, $b) │ Formatting │ │ preg_match('/pattern/', $str) │ Regex matching │ │ mb_strlen(), mb_substr() │ Multibyte safe │ ├─────────────────────────────────────────────────────────────────────┤ │ ARRAYS │ │ [1, 2, 3] │ Indexed array │ │ ["key" => "value"] │ Associative array │ │ [$a, $b] = $arr │ Destructuring │ │ [...$arr1, ...$arr2] │ Unpacking/spreading │ │ array_map(fn, $arr) │ Transform elements │ │ array_filter($arr, fn) │ Filter elements │ │ array_reduce($arr, fn, init) │ Reduce to single value │ └─────────────────────────────────────────────────────────────────────┘