dev.rean.me
โญ FEATURED POSTphp

Complete PHP References & Tips with Code Examples

Comprehensive PHP reference guide covering variables, arrays, functions, OOP, MySQL, APIs, security, and practical tips with code examples and related tutorials.

2026-09-19 ยท 30 min read

Share:

Hello my friend! Welcome to the Complete PHP References & Tips guide on dev.rean.me!

PHP is the most widely-used server-side scripting language โ€” powering over 77% of all websites including WordPress, Wikipedia, and Facebook (originally). In this guide, we cover all essential PHP syntax โ€” variables, arrays, functions, OOP, MySQL, REST APIs, security patterns, and best practices โ€” grouped by category with clear code examples and related tutorial links. Bookmark this page for quick daily reference! ๐Ÿ˜


1. Setup & Environment

# After installing XAMPP, start Apache & MySQL from XAMPP Control Panel
# Place PHP files in: C:/xampp/htdocs/my-project/

# Then visit in browser:
http://localhost/my-project/index.php

PHP File Structure

<?php
// All PHP code must be inside <?php ... ?> tags
// PHP files typically use .php extension

echo "Hello, World!";   // Output text
phpinfo();               // Display PHP configuration info (dev only!)
echo PHP_EOL;            // Platform-independent newline
echo PHP_VERSION;        // e.g. "8.3.0"
?>

Useful CLI Commands

php -v                    # Check PHP version
php -S localhost:8000     # Start built-in dev server
php index.php             # Run PHP file from terminal
php -l index.php          # Syntax check (lint) a file
composer install          # Install Composer dependencies
composer require vendor/package  # Add a package

๐Ÿ’ก Tip: Use php -S localhost:8000 for quick local testing without installing XAMPP! Just run the command inside your project folder and open http://localhost:8000 in your browser.


2. Variables & Data Types

PHP is a dynamically typed language โ€” variable types are determined at runtime.

TypeExampleDescription
string"Hello" 'World'Text value
integer42 -10Whole number
float3.14 -0.5Decimal number
booleantrue falseTrue or False
array[1, 2, 3]Ordered collection
objectnew MyClass()Instance of a class
nullnullNo value / empty
resourcefopen(...)External resource (file, DB)
<?php
// โ”€โ”€ VARIABLE RULES โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// Variables start with $ sign โ€” always!
$name   = "Sok Dara";       // string
$age    = 25;               // integer
$price  = 19.99;            // float
$isActive = true;           // boolean
$nothing = null;            // null

// โ”€โ”€ TYPE CHECKING โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
var_dump($name);             // string(8) "Sok Dara"
var_dump($age);              // int(25)
var_dump($isActive);         // bool(true)

echo gettype($price);        // "double" (float)
echo is_string($name);       // 1 (true)
echo is_int($age);           // 1 (true)
echo is_null($nothing);      // 1 (true)
echo isset($name);           // 1 (true โ€” variable exists and not null)
echo empty($nothing);        // 1 (true โ€” null is empty)

// โ”€โ”€ TYPE CASTING โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
$strNum = "42";
$intNum = (int) $strNum;     // 42 as integer
$floatNum = (float) $strNum; // 42.0 as float

// โ”€โ”€ TYPE JUGGLING (automatic coercion) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
echo "5" + 3;    // 8 (string "5" becomes int 5)
echo "5" . 3;    // "53" (. is string concatenation!)

// โ”€โ”€ CONSTANTS (immutable values) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
define("MAX_SIZE", 100);
const APP_NAME = "MyApp";    // Class-level or top-level only
echo MAX_SIZE;               // 100
echo APP_NAME;               // "MyApp"

๐Ÿ’ก Tip: Always use === (strict equality) instead of == (loose equality) in PHP! "0" == false evaluates to true with loose comparison, which causes hard-to-find bugs. "0" === false correctly returns false.


3. Strings โ€” Complete Method Reference

FunctionReturnsDescription
strlen($str)intLength of string
strtolower($str)stringConvert to lowercase
strtoupper($str)stringConvert to UPPERCASE
trim($str)stringRemove leading/trailing whitespace
ltrim($str)stringRemove left whitespace
rtrim($str)stringRemove right whitespace
str_contains($str, $needle)boolCheck if substring exists (PHP 8+)
str_starts_with($str, $prefix)boolCheck prefix (PHP 8+)
str_ends_with($str, $suffix)boolCheck suffix (PHP 8+)
strpos($str, $needle)int|falsePosition of first match
strrpos($str, $needle)int|falsePosition of last match
str_replace($find, $replace, $str)stringReplace all occurrences
str_ireplace($find, $replace, $str)stringCase-insensitive replace
substr($str, $start, $len)stringExtract substring
str_pad($str, $len, $pad)stringPad string to length
str_repeat($str, $n)stringRepeat string n times
str_split($str, $len)arraySplit into chunks
explode($sep, $str)arraySplit by delimiter
implode($sep, $arr)stringJoin array into string
sprintf($format, ...$args)stringFormat string
number_format($n, $dec)stringFormat number with commas
nl2br($str)stringConvert newlines to <br>
htmlspecialchars($str)stringEscape HTML entities (security!)
strip_tags($str)stringRemove all HTML/PHP tags
wordwrap($str, $width)stringWrap long strings
ucfirst($str)stringCapitalize first letter
ucwords($str)stringCapitalize each word
md5($str)stringMD5 hash (not for passwords!)
sha1($str)stringSHA1 hash (not for passwords!)
<?php
$name = "  Hello, PHP World!  ";

// Clean & transform
echo trim($name);                      // "Hello, PHP World!"
echo strtoupper(trim($name));          // "HELLO, PHP WORLD!"
echo str_replace("PHP", "Dev", $name); // "  Hello, Dev World!  "

// PHP 8+ string helpers (preferred!)
echo str_contains($name, "PHP");       // true (1)
echo str_starts_with(trim($name), "Hello"); // true (1)
echo str_ends_with(trim($name), "!");  // true (1)

// Splitting and joining
$csv = "apple,banana,mango,orange";
$fruits = explode(",", $csv);          // ["apple","banana","mango","orange"]
echo implode(" | ", $fruits);          // "apple | banana | mango | orange"

// sprintf โ€” formatted output
$price = 1234.5;
$label = sprintf("Price: $%.2f", $price); // "Price: $1234.50"
echo number_format($price, 2, ".", ",");   // "1,234.50"

// Heredoc (multi-line string with variable interpolation)
$user = "Dara";
$html = <<<HTML
  <div>
    <h1>Welcome, $user!</h1>
  </div>
HTML;

// Nowdoc (like heredoc but NO variable interpolation โ€” like single quotes)
$raw = <<<'TEXT'
  No $variable interpolation here.
TEXT;

// โš ๏ธ Security: ALWAYS escape output to prevent XSS!
$userInput = "<script>alert('XSS')</script>";
echo htmlspecialchars($userInput, ENT_QUOTES, "UTF-8");
// Outputs: &lt;script&gt;alert(&#039;XSS&#039;)&lt;/script&gt;

๐Ÿ’ก Tip: Use htmlspecialchars($str, ENT_QUOTES, 'UTF-8') every time you output user-provided data in HTML. This is one of the most important PHP security habits โ€” it prevents XSS (Cross-Site Scripting) attacks!


4. Numbers & Math Reference

<?php
// โ”€โ”€ MATH FUNCTIONS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
echo abs(-15);          // 15
echo round(4.567, 2);   // 4.57
echo floor(4.9);        // 4
echo ceil(4.1);         // 5
echo max(3, 7, 1, 9);  // 9
echo min(3, 7, 1, 9);  // 1
echo pow(2, 10);        // 1024
echo sqrt(144);         // 12
echo pi();              // 3.14159...
echo fmod(10, 3);       // 1 (float modulo)
echo intdiv(10, 3);     // 3 (integer division, PHP 7+)

// โ”€โ”€ RANDOM NUMBERS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
echo rand(1, 100);      // Random int 1โ€“100
echo mt_rand(1, 100);   // Faster Mersenne Twister random
// Cryptographically secure random int (PHP 7+)
echo random_int(1, 100);

// โ”€โ”€ NUMBER FORMATTING โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
echo number_format(1234567.891, 2);         // "1,234,567.89"
echo number_format(1234567.891, 2, ".", ","); // "1,234,567.89"
echo number_format(1234567.891, 2, ",", "."); // "1.234.567,89" (European)

// โ”€โ”€ INTEGER LIMITS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
echo PHP_INT_MAX;   // 9223372036854775807 (64-bit)
echo PHP_INT_MIN;   // -9223372036854775808
echo PHP_FLOAT_MAX; // 1.7976931348623E+308

// โ”€โ”€ BASE CONVERSION โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
echo decbin(255);        // "11111111" (decimal โ†’ binary)
echo decoct(255);        // "377" (decimal โ†’ octal)
echo dechex(255);        // "ff" (decimal โ†’ hex)
echo bindec("11111111"); // 255 (binary โ†’ decimal)
echo hexdec("ff");       // 255 (hex โ†’ decimal)

๐Ÿ’ก Tip: Use random_int() instead of rand() when you need cryptographically secure random numbers โ€” for example generating tokens, passwords, or OTPs. rand() is predictable and not secure for cryptographic uses!


5. Arrays โ€” Complete Reference

Array Types

TypeSyntaxExample
Indexed[val1, val2]["Apple", "Banana"]
Associative["key" => "val"]["name" => "Rean"]
MultidimensionalNested arrays[["id"=>1], ["id"=>2]]
<?php
// โ”€โ”€ INDEXED ARRAY โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
$fruits = ["Apple", "Banana", "Mango", "Orange"];
echo $fruits[0];           // "Apple"
echo count($fruits);       // 4

// โ”€โ”€ ASSOCIATIVE ARRAY โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
$user = [
    "name"  => "Sok Dara",
    "age"   => 25,
    "role"  => "Developer",
    "email" => "dara@example.com",
];
echo $user["name"];        // "Sok Dara"
echo $user["role"];        // "Developer"

// โ”€โ”€ ADD / UPDATE / DELETE โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
$fruits[] = "Grape";                // Append to end
$fruits[0] = "Pineapple";          // Update index 0
unset($fruits[1]);                  // Remove element (preserves keys!)
$fruits = array_values($fruits);    // Re-index after unset

// โ”€โ”€ ARRAY FUNCTIONS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// Sorting
sort($fruits);                      // Sort values (re-index)
rsort($fruits);                     // Sort reverse (re-index)
asort($user);                       // Sort values, preserve keys
arsort($user);                      // Sort values reverse, preserve keys
ksort($user);                       // Sort by key
krsort($user);                      // Sort by key reverse

// Searching
echo in_array("Mango", $fruits);    // 1 (true)
echo array_search("Mango", $fruits); // index (or false)
echo array_key_exists("name", $user); // 1 (true)
echo isset($user["email"]);         // 1 (true)

// Manipulation
$nums = [3, 1, 4, 1, 5, 9, 2, 6];
$unique = array_unique($nums);      // Remove duplicates
$reversed = array_reverse($nums);   // Reverse order
$sliced = array_slice($nums, 1, 3); // Extract portion [1,4,1]
$sum = array_sum($nums);            // 31
$min = min($nums);                  // 1
$max = max($nums);                  // 9

// Stack & Queue operations
array_push($fruits, "Kiwi");       // Add to end (same as $arr[])
$last = array_pop($fruits);        // Remove & return last item
array_unshift($fruits, "Lemon");   // Add to beginning
$first = array_shift($fruits);     // Remove & return first item

// Merging & combining
$merged = array_merge($fruits, ["Durian", "Jackfruit"]);
$combined = array_combine(["a","b","c"], [1, 2, 3]);
// ["a"=>1, "b"=>2, "c"=>3]

// Flipping (swap keys โ†” values)
$flipped = array_flip(["a"=>1, "b"=>2]); // [1=>"a", 2=>"b"]

// Chunking
$chunks = array_chunk($nums, 3);    // [[3,1,4],[1,5,9],[2,6]]

// Column extraction from 2D array
$users = [
    ["id"=>1, "name"=>"Dara"],
    ["id"=>2, "name"=>"Bopha"],
];
$names = array_column($users, "name"); // ["Dara","Bopha"]
$byId  = array_column($users, null, "id"); // Keyed by id

// โ”€โ”€ FUNCTIONAL ARRAY METHODS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// array_map โ€” transform every item
$doubled = array_map(fn($n) => $n * 2, [1, 2, 3, 4]);
// [2, 4, 6, 8]

// array_filter โ€” keep items matching condition
$evens = array_filter([1,2,3,4,5], fn($n) => $n % 2 === 0);
// [2, 4] (preserves keys!)
$evens = array_values($evens); // Re-index: [2, 4]

// array_reduce โ€” accumulate items
$total = array_reduce([1,2,3,4,5], fn($carry, $item) => $carry + $item, 0);
// 15

// usort โ€” custom sort
$users = [["name"=>"Zara"],["name"=>"Alice"],["name"=>"Mike"]];
usort($users, fn($a, $b) => strcmp($a["name"], $b["name"]));
// Sorted alphabetically by name

๐Ÿ’ก Tip: After using unset() on an array element, the numeric keys have gaps. Always call array_values() afterwards to re-index from 0 if you need sequential keys!


6. Control Flow (if, switch, match, loops)

<?php
// โ”€โ”€ IF / ELSEIF / ELSE โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
$score = 85;

if ($score >= 90) {
    echo "Grade: A";
} elseif ($score >= 80) {
    echo "Grade: B";
} elseif ($score >= 70) {
    echo "Grade: C";
} else {
    echo "Grade: F";
}

// Ternary
$status = $score >= 60 ? "Pass" : "Fail";

// Null coalescing (PHP 7+)
$username = $_GET["user"] ?? "Guest";

// Null coalescing assignment (PHP 7.4+)
$config["debug"] ??= false; // Set only if null/unset

// โ”€โ”€ SWITCH โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
$day = "Monday";
switch ($day) {
    case "Saturday":
    case "Sunday":
        echo "Weekend! ๐ŸŽ‰";
        break;
    case "Monday":
        echo "Start of week.";
        break;
    default:
        echo "Weekday.";
}

// โ”€โ”€ MATCH EXPRESSION (PHP 8.0+) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// Strict comparison, no type coercion, returns a value!
$statusCode = 404;
$message = match($statusCode) {
    200, 201 => "Success",
    301, 302 => "Redirect",
    400      => "Bad Request",
    401      => "Unauthorized",
    403      => "Forbidden",
    404      => "Not Found",
    500      => "Server Error",
    default  => "Unknown Status",
};
echo $message; // "Not Found"

// โ”€โ”€ FOR LOOP โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
for ($i = 0; $i < 5; $i++) {
    echo $i . " "; // 0 1 2 3 4
}

// โ”€โ”€ WHILE LOOP โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
$n = 1;
while ($n <= 5) {
    echo $n++;
}

// โ”€โ”€ DO...WHILE (runs at least once) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
$attempts = 0;
do {
    echo "Attempt " . ++$attempts;
} while ($attempts < 3);

// โ”€โ”€ FOREACH โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
$fruits = ["Apple", "Banana", "Mango"];
foreach ($fruits as $index => $fruit) {
    echo "$index: $fruit\n";
}

$user = ["name" => "Dara", "age" => 25];
foreach ($user as $key => $value) {
    echo "$key = $value\n";
}

// โ”€โ”€ BREAK / CONTINUE โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
for ($i = 0; $i < 10; $i++) {
    if ($i === 5) break;    // Exit loop at 5
    if ($i % 2 === 0) continue; // Skip even numbers
    echo $i . " ";          // 1 3
}

๐Ÿ’ก Tip: Prefer match over switch in PHP 8+! match uses strict type comparison (no type juggling), requires exhaustive cases (throws UnhandledMatchError otherwise), and returns a value directly โ€” much cleaner and safer!


7. Functions Reference

<?php
// โ”€โ”€ FUNCTION DECLARATION โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
function greet(string $name): string {
    return "Hello, $name!";
}
echo greet("Rean"); // "Hello, Rean!"

// โ”€โ”€ DEFAULT PARAMETER VALUES โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
function createUser(string $name, string $role = "Member", int $age = 0): array {
    return ["name" => $name, "role" => $role, "age" => $age];
}
$user = createUser("Bopha"); // role="Member", age=0

// โ”€โ”€ TYPE DECLARATIONS (PHP 7+) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
function add(int $a, int $b): int {
    return $a + $b;
}

// Nullable type (can be null OR the type)
function findUser(?int $id): ?array {
    if ($id === null) return null;
    return ["id" => $id, "name" => "Dara"];
}

// Union types (PHP 8+)
function process(int|string $value): int|string {
    return is_int($value) ? $value * 2 : strtoupper($value);
}

// โ”€โ”€ VARIADIC FUNCTIONS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
function sum(int ...$numbers): int {
    return array_sum($numbers);
}
echo sum(1, 2, 3, 4, 5); // 15

// Spread operator in function call
$nums = [1, 2, 3];
echo sum(...$nums); // 6

// โ”€โ”€ ANONYMOUS FUNCTIONS (Closures) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
$multiply = function(int $a, int $b): int {
    return $a * $b;
};
echo $multiply(5, 4); // 20

// Closure with `use` (capture outer variable)
$prefix = "Hello";
$greetFn = function(string $name) use ($prefix): string {
    return "$prefix, $name!";
};
echo $greetFn("Dara"); // "Hello, Dara!"

// Capture by reference (can modify outer var)
$count = 0;
$increment = function() use (&$count): void {
    $count++;
};
$increment();
$increment();
echo $count; // 2

// โ”€โ”€ ARROW FUNCTIONS (PHP 7.4+) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// Shorter closure syntax โ€” automatically captures outer scope!
$tax = 0.1;
$withTax = fn(float $price): float => $price * (1 + $tax);
echo $withTax(100.0); // 110.0

// Arrow functions with array_map
$prices = [10.0, 20.0, 30.0];
$withTaxPrices = array_map(fn($p) => $p * 1.1, $prices);
// [11.0, 22.0, 33.0]

// โ”€โ”€ RECURSIVE FUNCTION โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
function factorial(int $n): int {
    if ($n <= 1) return 1;
    return $n * factorial($n - 1);
}
echo factorial(5); // 120

// โ”€โ”€ FIRST-CLASS CALLABLES (PHP 8.1+) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// Pass built-in functions as callbacks without wrapping in closures
$nums = [3, 1, 4, 1, 5];
$filtered = array_filter($nums, is_int(...)); // Cleaner than fn($n)=>is_int($n)

๐Ÿ’ก Tip: Always add type declarations to your function parameters and return types (PHP 7+). This catches bugs at development time, serves as self-documentation, and enables better IDE auto-complete!


8. Object-Oriented PHP (OOP)

<?php
// โ”€โ”€ CLASS DEFINITION โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class User {
    // Properties with visibility & type
    private string $name;
    private string $email;
    protected int $age;
    public string $role;

    // Class constant
    const MAX_NAME_LENGTH = 100;

    // Constructor
    public function __construct(
        string $name,
        string $email,
        int    $age    = 0,
        string $role   = "Member"
    ) {
        $this->name  = $name;
        $this->email = $email;
        $this->age   = $age;
        $this->role  = $role;
    }

    // Getters
    public function getName(): string  { return $this->name; }
    public function getEmail(): string { return $this->email; }
    public function getAge(): int      { return $this->age; }

    // Setter with validation
    public function setName(string $name): void {
        if (strlen($name) > self::MAX_NAME_LENGTH) {
            throw new \InvalidArgumentException("Name too long!");
        }
        $this->name = trim($name);
    }

    // Method
    public function getProfile(): array {
        return [
            "name"  => $this->name,
            "email" => $this->email,
            "age"   => $this->age,
            "role"  => $this->role,
        ];
    }

    // Static method (call without instance)
    public static function create(string $name, string $email): static {
        return new static($name, $email);
    }

    // Magic method โ€” toString
    public function __toString(): string {
        return "{$this->name} <{$this->email}>";
    }
}

// โ”€โ”€ INHERITANCE โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class AdminUser extends User {
    private array $permissions;

    public function __construct(string $name, string $email, array $permissions = []) {
        parent::__construct($name, $email, role: "Admin");
        $this->permissions = $permissions;
    }

    public function hasPermission(string $permission): bool {
        return in_array($permission, $this->permissions, true);
    }

    // Override parent method
    public function getProfile(): array {
        return array_merge(parent::getProfile(), [
            "permissions" => $this->permissions,
        ]);
    }
}

// โ”€โ”€ INTERFACES โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
interface Serializable {
    public function toJson(): string;
    public function toArray(): array;
}

interface Cacheable {
    public function getCacheKey(): string;
    public function getTtl(): int;
}

// Implement multiple interfaces
class Product implements Serializable, Cacheable {
    public function __construct(
        private readonly int    $id,
        private readonly string $name,
        private readonly float  $price,
    ) {}

    public function toJson(): string   { return json_encode($this->toArray()); }
    public function toArray(): array   { return get_object_vars($this); }
    public function getCacheKey(): string { return "product:{$this->id}"; }
    public function getTtl(): int      { return 3600; }
}

// โ”€โ”€ ABSTRACT CLASS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
abstract class Shape {
    abstract public function area(): float;
    abstract public function perimeter(): float;

    // Concrete shared method
    public function describe(): string {
        return sprintf(
            "%s: area=%.2f, perimeter=%.2f",
            static::class, $this->area(), $this->perimeter()
        );
    }
}

class Circle extends Shape {
    public function __construct(private float $radius) {}
    public function area(): float      { return M_PI * $this->radius ** 2; }
    public function perimeter(): float { return 2 * M_PI * $this->radius; }
}

// โ”€โ”€ TRAITS (reusable code mixin) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
trait Timestamps {
    private \DateTime $createdAt;
    private \DateTime $updatedAt;

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

    public function touch(): void {
        $this->updatedAt = new \DateTime();
    }

    public function getCreatedAt(): \DateTime { return $this->createdAt; }
}

trait SoftDeletes {
    private ?\DateTime $deletedAt = null;

    public function softDelete(): void { $this->deletedAt = new \DateTime(); }
    public function isDeleted(): bool  { return $this->deletedAt !== null; }
    public function restore(): void    { $this->deletedAt = null; }
}

class Post {
    use Timestamps, SoftDeletes; // Use multiple traits!

    public function __construct(public readonly string $title) {
        $this->initTimestamps();
    }
}

// โ”€โ”€ READONLY PROPERTIES (PHP 8.1+) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class Point {
    public function __construct(
        public readonly float $x,
        public readonly float $y,
        public readonly float $z = 0.0,
    ) {}
}

$p = new Point(1.0, 2.0);
// $p->x = 5; // Fatal Error! readonly!

// โ”€โ”€ ENUMS (PHP 8.1+) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
enum Status: string {
    case Active   = "active";
    case Inactive = "inactive";
    case Pending  = "pending";

    public function label(): string {
        return match($this) {
            Status::Active   => "๐ŸŸข Active",
            Status::Inactive => "๐Ÿ”ด Inactive",
            Status::Pending  => "๐ŸŸก Pending",
        };
    }
}

$status = Status::Active;
echo $status->value;   // "active"
echo $status->label(); // "๐ŸŸข Active"
echo $status->name;    // "Active"

๐Ÿ’ก Tip: Use readonly properties (PHP 8.1+) and constructor property promotion to eliminate boilerplate getter/setter code for simple value objects. Combine with Enum for type-safe constants instead of define() or class constants!


PDO (PHP Data Objects) is the modern, secure way to connect to MySQL โ€” it supports prepared statements to prevent SQL injection.

<?php
// โ”€โ”€ DATABASE CONNECTION โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
function getConnection(): PDO {
    $dsn = "mysql:host=localhost;dbname=mydb;charset=utf8mb4";
    $options = [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES   => false, // Real prepared statements!
    ];

    try {
        return new PDO($dsn, "root", "secret", $options);
    } catch (\PDOException $e) {
        // NEVER expose connection details to user!
        error_log($e->getMessage());
        throw new \RuntimeException("Database connection failed.");
    }
}

$pdo = getConnection();

// โ”€โ”€ CREATE TABLE โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
$pdo->exec("
    CREATE TABLE IF NOT EXISTS users (
        id    INT AUTO_INCREMENT PRIMARY KEY,
        name  VARCHAR(100) NOT NULL,
        email VARCHAR(150) NOT NULL UNIQUE,
        role  ENUM('member','admin') DEFAULT 'member',
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    )
");

// โ”€โ”€ INSERT (CREATE) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// โœ… Always use prepared statements โ€” NEVER string interpolation!
$stmt = $pdo->prepare("INSERT INTO users (name, email, role) VALUES (?, ?, ?)");
$stmt->execute(["Sok Dara", "dara@example.com", "member"]);
$newId = $pdo->lastInsertId();
echo "New user ID: $newId";

// Named placeholders (more readable)
$stmt = $pdo->prepare("INSERT INTO users (name, email) VALUES (:name, :email)");
$stmt->execute([":name" => "Keo Bopha", ":email" => "bopha@example.com"]);

// โ”€โ”€ SELECT (READ) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// Fetch all rows
$stmt = $pdo->prepare("SELECT * FROM users WHERE role = ? ORDER BY name ASC");
$stmt->execute(["member"]);
$users = $stmt->fetchAll(); // Returns array of assoc arrays

// Fetch single row
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$newId]);
$user = $stmt->fetch();
if ($user) {
    echo "Found: " . $user["name"];
}

// Fetch single column value
$stmt = $pdo->prepare("SELECT COUNT(*) FROM users WHERE role = ?");
$stmt->execute(["admin"]);
$adminCount = $stmt->fetchColumn(); // Returns scalar value

// โ”€โ”€ UPDATE โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
$stmt = $pdo->prepare("UPDATE users SET role = ? WHERE id = ?");
$stmt->execute(["admin", $newId]);
echo "Rows updated: " . $stmt->rowCount();

// โ”€โ”€ DELETE โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
$stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
$stmt->execute([$newId]);
echo "Rows deleted: " . $stmt->rowCount();

// โ”€โ”€ TRANSACTIONS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
try {
    $pdo->beginTransaction();

    $stmt = $pdo->prepare("INSERT INTO orders (user_id, total) VALUES (?, ?)");
    $stmt->execute([1, 150.00]);
    $orderId = $pdo->lastInsertId();

    $stmt = $pdo->prepare("UPDATE inventory SET qty = qty - ? WHERE product_id = ?");
    $stmt->execute([1, 5]);

    $pdo->commit();
    echo "Transaction committed!";
} catch (\Exception $e) {
    $pdo->rollBack();
    error_log("Transaction failed: " . $e->getMessage());
    echo "Transaction rolled back.";
}

๐Ÿ’ก Tip: NEVER build SQL queries with string concatenation like "SELECT * FROM users WHERE id = $id" โ€” this is SQL injection vulnerable! Always use prepared statements with ? or :named placeholders. This is the #1 PHP security rule!


10. REST API Development

<?php
// โ”€โ”€ api/index.php โ€” Simple REST API Router โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");

// Handle preflight OPTIONS request (CORS)
if ($_SERVER["REQUEST_METHOD"] === "OPTIONS") {
    http_response_code(200);
    exit;
}

// โ”€โ”€ HELPER FUNCTIONS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
function sendJson(mixed $data, int $status = 200): void {
    http_response_code($status);
    echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
    exit;
}

function sendError(string $message, int $status = 400): void {
    sendJson(["success" => false, "error" => $message], $status);
}

function getRequestBody(): array {
    $raw = file_get_contents("php://input");
    $data = json_decode($raw, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
        sendError("Invalid JSON body", 400);
    }
    return $data ?? [];
}

// โ”€โ”€ ROUTING โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
$method = $_SERVER["REQUEST_METHOD"];
$path   = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
$path   = trim($path, "/");
$parts  = explode("/", $path); // e.g. ["api", "users", "42"]

// GET /api/users
if ($method === "GET" && $parts[1] === "users" && !isset($parts[2])) {
    $pdo = getConnection();
    $stmt = $pdo->query("SELECT id, name, email, role FROM users ORDER BY id DESC");
    $users = $stmt->fetchAll();
    sendJson(["success" => true, "data" => $users]);
}

// GET /api/users/:id
if ($method === "GET" && $parts[1] === "users" && isset($parts[2])) {
    $id = (int) $parts[2];
    $pdo = getConnection();
    $stmt = $pdo->prepare("SELECT id, name, email, role FROM users WHERE id = ?");
    $stmt->execute([$id]);
    $user = $stmt->fetch();

    if (!$user) sendError("User not found", 404);
    sendJson(["success" => true, "data" => $user]);
}

// POST /api/users
if ($method === "POST" && $parts[1] === "users") {
    $body = getRequestBody();

    // Validate required fields
    $name  = trim($body["name"]  ?? "");
    $email = trim($body["email"] ?? "");

    if (empty($name))  sendError("Name is required");
    if (empty($email)) sendError("Email is required");
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) sendError("Invalid email format");

    $pdo = getConnection();
    $stmt = $pdo->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
    $stmt->execute([$name, $email]);
    $id = $pdo->lastInsertId();

    sendJson(["success" => true, "data" => ["id" => (int)$id, "name" => $name, "email" => $email]], 201);
}

// PUT /api/users/:id
if ($method === "PUT" && $parts[1] === "users" && isset($parts[2])) {
    $id   = (int) $parts[2];
    $body = getRequestBody();
    $name = trim($body["name"] ?? "");

    if (empty($name)) sendError("Name is required");

    $pdo = getConnection();
    $stmt = $pdo->prepare("UPDATE users SET name = ? WHERE id = ?");
    $stmt->execute([$name, $id]);

    if ($stmt->rowCount() === 0) sendError("User not found", 404);
    sendJson(["success" => true, "message" => "User updated"]);
}

// DELETE /api/users/:id
if ($method === "DELETE" && $parts[1] === "users" && isset($parts[2])) {
    $id = (int) $parts[2];
    $pdo = getConnection();
    $stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
    $stmt->execute([$id]);

    if ($stmt->rowCount() === 0) sendError("User not found", 404);
    sendJson(["success" => true, "message" => "User deleted"]);
}

// No route matched
sendError("Endpoint not found", 404);

๐Ÿ’ก Tip: Always set Content-Type: application/json header before any output. Return consistent response shapes โ€” e.g. {"success": true, "data": {...}} for success and {"success": false, "error": "..."} for errors โ€” so API clients can handle responses predictably!


11. Security Best Practices

<?php
// โ”€โ”€ 1. PASSWORD HASHING (NEVER store plain text!) โ”€โ”€
$plainPassword = "mySecret123";

// Hash password before storing in database
$hashedPassword = password_hash($plainPassword, PASSWORD_BCRYPT);
// e.g. "$2y$10$abcdefghijk..." โ€” bcrypt hash

// Verify password on login
$isValid = password_verify($plainPassword, $hashedPassword); // true
$isWrong = password_verify("wrongpassword", $hashedPassword); // false

// Always re-hash if algorithm needs update
if (password_needs_rehash($hashedPassword, PASSWORD_BCRYPT)) {
    $hashedPassword = password_hash($plainPassword, PASSWORD_BCRYPT);
    // Update in database
}

// โ”€โ”€ 2. PREPARED STATEMENTS (prevent SQL injection) โ”€โ”€
// โœ… Safe โ€” user input never touches SQL structure
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$_POST["email"]]);

// โŒ Vulnerable โ€” NEVER do this!
// $email = $_POST["email"];
// $pdo->query("SELECT * FROM users WHERE email = '$email'");

// โ”€โ”€ 3. INPUT VALIDATION & SANITIZATION โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// Validate
$email  = filter_var($_POST["email"] ?? "", FILTER_VALIDATE_EMAIL);
$url    = filter_var($_POST["url"]   ?? "", FILTER_VALIDATE_URL);
$age    = filter_var($_POST["age"]   ?? "", FILTER_VALIDATE_INT, [
    "options" => ["min_range" => 0, "max_range" => 150]
]);
$ip     = filter_var($_SERVER["REMOTE_ADDR"], FILTER_VALIDATE_IP);

if ($email === false) {
    sendError("Invalid email address");
}

// Sanitize (remove dangerous characters)
$name   = filter_var($_POST["name"] ?? "", FILTER_SANITIZE_SPECIAL_CHARS);
$search = filter_var($_GET["q"]     ?? "", FILTER_SANITIZE_SPECIAL_CHARS);

// โ”€โ”€ 4. OUTPUT ESCAPING (prevent XSS) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
$userInput = "<script>alert('XSS')</script>";

// โœ… Always escape when outputting user data in HTML
echo htmlspecialchars($userInput, ENT_QUOTES | ENT_HTML5, "UTF-8");
// &lt;script&gt;alert(&#039;XSS&#039;)&lt;/script&gt;

// โ”€โ”€ 5. CSRF TOKEN (protect forms) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// Generate token
session_start();
if (empty($_SESSION["csrf_token"])) {
    $_SESSION["csrf_token"] = bin2hex(random_bytes(32));
}
$token = $_SESSION["csrf_token"];

// Validate token on POST
function validateCsrfToken(string $token): bool {
    return isset($_SESSION["csrf_token"])
        && hash_equals($_SESSION["csrf_token"], $token);
}

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    if (!validateCsrfToken($_POST["csrf_token"] ?? "")) {
        http_response_code(403);
        die("CSRF validation failed!");
    }
}

// โ”€โ”€ 6. SECURE SESSION CONFIGURATION โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
ini_set("session.cookie_httponly", "1");   // Prevent JS access to cookie
ini_set("session.cookie_secure",   "1");   // HTTPS only
ini_set("session.cookie_samesite", "Strict"); // Prevent CSRF via cookies
ini_set("session.use_strict_mode", "1");   // Reject uninitialized session IDs

// Regenerate session ID after login (prevent session fixation)
session_regenerate_id(true);

// โ”€โ”€ 7. SECURE FILE UPLOAD โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
function validateUpload(array $file): string {
    $allowedTypes = ["image/jpeg", "image/png", "image/webp"];
    $maxSize      = 5 * 1024 * 1024; // 5MB

    if ($file["error"] !== UPLOAD_ERR_OK) {
        throw new \RuntimeException("Upload error code: " . $file["error"]);
    }
    if ($file["size"] > $maxSize) {
        throw new \RuntimeException("File too large (max 5MB).");
    }

    // Verify MIME type from actual file content โ€” NOT the extension!
    $finfo    = new \finfo(FILEINFO_MIME_TYPE);
    $mimeType = $finfo->file($file["tmp_name"]);

    if (!in_array($mimeType, $allowedTypes, true)) {
        throw new \RuntimeException("Invalid file type: $mimeType");
    }

    // Generate safe filename
    $ext      = pathinfo($file["name"], PATHINFO_EXTENSION);
    $safeName = bin2hex(random_bytes(16)) . "." . strtolower($ext);
    $destPath = "/var/www/uploads/$safeName";

    // Store OUTSIDE web root if possible!
    move_uploaded_file($file["tmp_name"], $destPath);

    return $safeName;
}

๐Ÿ’ก Tip: Use password_hash() with PASSWORD_DEFAULT (not PASSWORD_BCRYPT directly) โ€” PASSWORD_DEFAULT automatically uses the best available algorithm and will upgrade automatically in future PHP versions!


12. File System & Date/Time Reference

<?php
// โ”€โ”€ FILE OPERATIONS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// Read entire file
$content = file_get_contents("/path/to/file.txt");

// Write (overwrite) file
file_put_contents("/path/to/file.txt", "New content here");

// Append to file
file_put_contents("/path/to/file.txt", "\nMore content", FILE_APPEND);

// Check if file/directory exists
echo file_exists("/path/to/file.txt"); // true/false
echo is_file("/path/to/file.txt");     // true if file (not dir)
echo is_dir("/path/to/");             // true if directory

// File info
echo filesize("/path/to/file.txt");  // bytes
echo filemtime("/path/to/file.txt"); // last modified (Unix timestamp)
echo pathinfo("/path/file.txt", PATHINFO_EXTENSION);  // "txt"
echo pathinfo("/path/file.txt", PATHINFO_BASENAME);   // "file.txt"
echo pathinfo("/path/file.txt", PATHINFO_FILENAME);   // "file"
echo pathinfo("/path/file.txt", PATHINFO_DIRNAME);    // "/path"
echo realpath("../relative/path");   // Absolute path

// Directory listing
$files = scandir("/path/to/dir");          // Array of filenames (incl. . and ..)
$files = glob("/path/to/dir/*.php");       // Pattern match
$files = glob("/path/to/dir/*.{php,html}", GLOB_BRACE); // Multiple extensions

// Create / delete directory
mkdir("/path/to/new-dir", 0755, true);    // recursive=true
rmdir("/path/to/empty-dir");

// Copy, rename, delete
copy("/src/file.txt", "/dest/file.txt");
rename("/old/path.txt", "/new/path.txt"); // Also moves files!
unlink("/path/to/file.txt");             // Delete file

// โ”€โ”€ DATE & TIME โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// Current timestamp
$now = time();              // Unix timestamp (seconds since 1970)
echo date("Y-m-d H:i:s");  // "2026-09-19 21:12:00"
echo date("d/m/Y");         // "19/09/2026"
echo date("l, F j, Y");     // "Saturday, September 19, 2026"
echo date("D M j G:i");     // "Sat Sep 19 21:12"

// From specific timestamp
echo date("Y-m-d", mktime(0, 0, 0, 12, 25, 2026)); // "2026-12-25"
echo date("Y-m-d", strtotime("next Monday")); // Next Monday's date
echo date("Y-m-d", strtotime("+30 days"));    // 30 days from now

// DateTime class (OOP style โ€” preferred!)
$dt = new DateTime("now", new DateTimeZone("Asia/Phnom_Penh"));
echo $dt->format("Y-m-d H:i:s");

$future = new DateTime("+1 month");
$diff   = $dt->diff($future);
echo $diff->days . " days"; // Days between two dates

// DateTimeImmutable (PHP 5.5+ โ€” can't be modified!)
$immutable = new DateTimeImmutable("2026-01-01");
$next      = $immutable->modify("+1 year"); // Returns NEW object!

๐Ÿ’ก Tip: Use DateTimeImmutable instead of DateTime when you want to ensure a date object can't be accidentally modified. Immutable objects make code easier to reason about and prevent subtle bugs in complex date calculations!


13. Error Handling & Exceptions

<?php
// โ”€โ”€ EXCEPTION HANDLING โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
try {
    $data = json_decode($jsonString, true, 512, JSON_THROW_ON_ERROR);
    echo $data["name"];
} catch (\JsonException $e) {
    echo "JSON error: " . $e->getMessage();
} catch (\TypeError $e) {
    echo "Type error: " . $e->getMessage();
} catch (\Exception $e) {
    echo "General error: " . $e->getMessage();
} finally {
    // Always runs
    echo "Done.";
}

// โ”€โ”€ CUSTOM EXCEPTIONS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class ValidationException extends \RuntimeException {
    private array $errors;

    public function __construct(array $errors, string $message = "Validation failed") {
        parent::__construct($message, 422);
        $this->errors = $errors;
    }

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

class NotFoundException extends \RuntimeException {
    public function __construct(string $resource, int|string $id) {
        parent::__construct("$resource with ID '$id' not found", 404);
    }
}

// Usage
function findUserById(int $id): array {
    $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
    $stmt->execute([$id]);
    $user = $stmt->fetch();

    if (!$user) {
        throw new NotFoundException("User", $id);
    }

    return $user;
}

// โ”€โ”€ GLOBAL ERROR HANDLER โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
set_exception_handler(function(\Throwable $e) {
    http_response_code($e->getCode() ?: 500);

    // Log full error details (not exposed to user!)
    error_log(sprintf(
        "[%s] %s in %s:%d\nStack trace:\n%s",
        date("Y-m-d H:i:s"),
        $e->getMessage(),
        $e->getFile(),
        $e->getLine(),
        $e->getTraceAsString()
    ));

    // Return safe generic message to API consumers
    header("Content-Type: application/json");
    echo json_encode([
        "success" => false,
        "error"   => $e->getMessage(),
    ]);
});

// โ”€โ”€ PHP ERROR SETTINGS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// Development: show all errors
ini_set("display_errors", "1");
ini_set("display_startup_errors", "1");
error_reporting(E_ALL);

// Production: log errors, hide from users!
ini_set("display_errors", "0");
ini_set("log_errors", "1");
ini_set("error_log", "/var/log/php-errors.log");
error_reporting(E_ALL);

๐Ÿ’ก Tip: Set a global set_exception_handler() in your app entry point to catch all unhandled exceptions gracefully. Always log full error details server-side but never expose stack traces or database errors to the user in production!


14. PHP Best Practices Summary

โœ… DoโŒ Avoid
Use === strict comparisonUsing == loose comparison
Use prepared statements with PDOString-interpolated SQL queries
Use password_hash() & password_verify()MD5/SHA1 for passwords
Use htmlspecialchars() for HTML outputOutputting raw user data
Add type declarations to functionsUntyped function parameters
Use match over switch (PHP 8+)switch without break (fall-through bugs)
Use random_bytes() / random_int() for tokensrand() / mt_rand() for security tokens
Use null ?? "default" null coalescingisset($x) ? $x : "default" verbose form
Validate with filter_var()Trusting raw $_GET / $_POST directly
Use Composer for dependenciesManual file include for libraries
Use environment variables (.env) for secretsHardcoding passwords in code
Use DateTimeImmutable for date manipulationMutating DateTime objects
Log errors server-side with error_log()Displaying errors to production users
Use arrow functions fn()=> for short closuresVerbose function() use (&$var) closures
Use str_contains() (PHP 8+)strpos() !== false pattern

Summary Tag Cloud Directory

Happy coding and building awesome PHP web applications! ๐Ÿ˜๐Ÿš€

โ† Back to php
Share: