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
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
XAMPP Local Server (Recommended for beginners)
# 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:8000for quick local testing without installing XAMPP! Just run the command inside your project folder and openhttp://localhost:8000in your browser.
๐ท๏ธ Related Posts:
- ๐ How to Download, Install & Configure XAMPP for PHP & Laravel (with Video)
- ๐ Best IDE for PHP Development
- ๐ Essential VS Code Extensions for PHP & Laravel
2. Variables & Data Types
PHP is a dynamically typed language โ variable types are determined at runtime.
| Type | Example | Description |
|---|---|---|
string | "Hello" 'World' | Text value |
integer | 42 -10 | Whole number |
float | 3.14 -0.5 | Decimal number |
boolean | true false | True or False |
array | [1, 2, 3] | Ordered collection |
object | new MyClass() | Instance of a class |
null | null | No value / empty |
resource | fopen(...) | 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" == falseevaluates totruewith loose comparison, which causes hard-to-find bugs."0" === falsecorrectly returnsfalse.
๐ท๏ธ Related Posts:
3. Strings โ Complete Method Reference
| Function | Returns | Description |
|---|---|---|
strlen($str) | int | Length of string |
strtolower($str) | string | Convert to lowercase |
strtoupper($str) | string | Convert to UPPERCASE |
trim($str) | string | Remove leading/trailing whitespace |
ltrim($str) | string | Remove left whitespace |
rtrim($str) | string | Remove right whitespace |
str_contains($str, $needle) | bool | Check if substring exists (PHP 8+) |
str_starts_with($str, $prefix) | bool | Check prefix (PHP 8+) |
str_ends_with($str, $suffix) | bool | Check suffix (PHP 8+) |
strpos($str, $needle) | int|false | Position of first match |
strrpos($str, $needle) | int|false | Position of last match |
str_replace($find, $replace, $str) | string | Replace all occurrences |
str_ireplace($find, $replace, $str) | string | Case-insensitive replace |
substr($str, $start, $len) | string | Extract substring |
str_pad($str, $len, $pad) | string | Pad string to length |
str_repeat($str, $n) | string | Repeat string n times |
str_split($str, $len) | array | Split into chunks |
explode($sep, $str) | array | Split by delimiter |
implode($sep, $arr) | string | Join array into string |
sprintf($format, ...$args) | string | Format string |
number_format($n, $dec) | string | Format number with commas |
nl2br($str) | string | Convert newlines to <br> |
htmlspecialchars($str) | string | Escape HTML entities (security!) |
strip_tags($str) | string | Remove all HTML/PHP tags |
wordwrap($str, $width) | string | Wrap long strings |
ucfirst($str) | string | Capitalize first letter |
ucwords($str) | string | Capitalize each word |
md5($str) | string | MD5 hash (not for passwords!) |
sha1($str) | string | SHA1 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: <script>alert('XSS')</script>
๐ก 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 ofrand()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
| Type | Syntax | Example |
|---|---|---|
| Indexed | [val1, val2] | ["Apple", "Banana"] |
| Associative | ["key" => "val"] | ["name" => "Rean"] |
| Multidimensional | Nested 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 callarray_values()afterwards to re-index from 0 if you need sequential keys!
๐ท๏ธ Related Posts:
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
matchoverswitchin PHP 8+!matchuses strict type comparison (no type juggling), requires exhaustive cases (throwsUnhandledMatchErrorotherwise), and returns a value directly โ much cleaner and safer!
๐ท๏ธ Related Posts:
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!
๐ท๏ธ Related Posts:
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
Enumfor type-safe constants instead ofdefine()or class constants!
9. MySQL with PDO (Recommended)
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:namedplaceholders. This is the #1 PHP security rule!
๐ท๏ธ Related Posts:
- ๐ PHP CRUD with MySQL Database (with Video)
- ๐ PHP API with Database & Test with Postman (with Video)
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/jsonheader 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!
๐ท๏ธ Related Posts:
- ๐ PHP API with Database & Test with Postman (with Video)
- ๐ PHP CRUD with MySQL Database (with Video)
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");
// <script>alert('XSS')</script>
// โโ 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()withPASSWORD_DEFAULT(notPASSWORD_BCRYPTdirectly) โPASSWORD_DEFAULTautomatically uses the best available algorithm and will upgrade automatically in future PHP versions!
๐ท๏ธ Related Posts:
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
DateTimeImmutableinstead ofDateTimewhen 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 comparison | Using == loose comparison |
| Use prepared statements with PDO | String-interpolated SQL queries |
Use password_hash() & password_verify() | MD5/SHA1 for passwords |
Use htmlspecialchars() for HTML output | Outputting raw user data |
| Add type declarations to functions | Untyped function parameters |
Use match over switch (PHP 8+) | switch without break (fall-through bugs) |
Use random_bytes() / random_int() for tokens | rand() / mt_rand() for security tokens |
Use null ?? "default" null coalescing | isset($x) ? $x : "default" verbose form |
Validate with filter_var() | Trusting raw $_GET / $_POST directly |
| Use Composer for dependencies | Manual file include for libraries |
Use environment variables (.env) for secrets | Hardcoding passwords in code |
Use DateTimeImmutable for date manipulation | Mutating DateTime objects |
Log errors server-side with error_log() | Displaying errors to production users |
Use arrow functions fn()=> for short closures | Verbose function() use (&$var) closures |
Use str_contains() (PHP 8+) | strpos() !== false pattern |
Summary Tag Cloud Directory
Happy coding and building awesome PHP web applications! ๐๐