PHP Mastery Guide
Complete guide from syntax to advanced web development
The Complete PHP Mastery Guide
Master server-side web development with PHP - from basic syntax to advanced frameworks, security patterns, and modern development practices
Understanding PHP: The Web's Backbone
PHP (Hypertext Preprocessor) is a widely-used open source server-side scripting language specifically designed for web development. Since its creation in 1994, PHP has powered millions of websites and continues to evolve with modern features and performance improvements.
Why PHP Matters
- • Powers WordPress, Facebook, Wikipedia
- • Excellent documentation and community
- • Vast ecosystem of frameworks and tools
- • Easy deployment and hosting
- • Continual modernization and improvement
Modern PHP Features
- • Just-In-Time compilation (JIT)
- • Strong typing with union types
- • Attributes (annotations)
- • Match expressions
- • Named arguments
1. PHP Fundamentals & Setup
What is PHP?
PHP is a server-side scripting language that executes on the web server before the page is sent to the client's browser. Unlike client-side languages like JavaScript, PHP code is never visible to end users.
Key Insight: PHP is embedded within HTML and processed on the server. The client only receives the final HTML output, making PHP ideal for dynamic content, database operations, and secure applications.
Setting Up PHP Development Environment
Get started with PHP by setting up a proper development environment. You'll need a web server (Apache/Nginx), PHP, and a database (MySQL).
Option 1: All-in-One Packages
- XAMPP - Cross-platform (Windows, Mac, Linux)
- WAMP - Windows Apache MySQL PHP
- MAMP - Mac Apache MySQL PHP
- Laragon - Lightweight Windows option
Option 2: Manual Setup
- Ubuntu:
sudo apt install php mysql-server apache2
- Windows: Download from windows.php.net
- Mac: Use Homebrew:
brew install php
- Docker: Use official PHP images
Verifying PHP Installation
<?php
// Create info.php file and access via browser
phpinfo();
?>
Your First PHP Script
PHP code is embedded within <?php ?> tags. Let's create a simple 'Hello World' script to understand the basic structure.
Basic PHP File Structure
<!DOCTYPE html>
<html>
<head>
<title>My First PHP Page</title>
</head>
<body>
<h1>
<?php
// PHP code embedded in HTML
echo "Hello, World!";
?>
</h1>
<p>
Current server time:
<?php echo date('Y-m-d H:i:s'); ?>
</p>
<p>
PHP Version: <?php echo PHP_VERSION; ?>
</p>
</body>
</html>
Note: PHP files must have a .php
extension. The server processes PHP code and sends only HTML to the browser.
2. PHP Basic Syntax & Variables
Variables and Data Types
PHP is a loosely typed language, meaning you don't need to declare variable types. However, PHP 7+ introduced type declarations for better code quality.
Variable Declaration and Data Types
<?php
// Variable declaration
$name = "John Doe";
$age = 30;
$price = 19.99;
$is_active = true;
$colors = ["red", "green", "blue"];
// Output variables
echo "Name: " . $name . "<br>";
echo "Age: $age<br>"; // Variable parsing in double quotes
echo 'Age: $age<br>'; // No parsing in single quotes
// Type checking
var_dump($name); // string(8) "John Doe"
var_dump($age); // int(30)
var_dump($price); // float(19.99)
var_dump($is_active); // bool(true)
// Type casting
$string_number = "123";
$actual_number = (int)$string_number;
$float_number = (float)$string_number;
$string_float = (string)$price;
// Null coalescing operator (PHP 7+)
$username = $_GET['username'] ?? 'guest';
$color = $preferred_color ?? $default_color ?? 'blue';
// Spaceship operator (PHP 7+)
echo 1 <=> 1; // 0 (equal)
echo 1 <=> 2; // -1 (less than)
echo 2 <=> 1; // 1 (greater than)
?>
Operators and Expressions
PHP supports all standard operators for arithmetic, comparison, logical operations, and more.
PHP Operators
<?php
// Arithmetic operators
$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
echo $a ** $b; // 1000 (exponentiation - PHP 5.6+)
// Comparison operators
var_dump($a == 10); // true
var_dump($a === "10"); // false (strict comparison)
var_dump($a != 5); // true
var_dump($a !== "10"); // true
var_dump($a > 5); // true
var_dump($a <= 10); // true
// Logical operators
$is_admin = true;
$is_logged_in = false;
var_dump($is_admin && $is_logged_in); // false
var_dump($is_admin || $is_logged_in); // true
var_dump(!$is_logged_in); // true
// String operators
$firstName = "John";
$lastName = "Doe";
$fullName = $firstName . " " . $lastName; // Concatenation
$fullName .= " Jr."; // Concatenation assignment
// Increment/Decrement
$counter = 5;
echo $counter++; // 5 (post-increment)
echo ++$counter; // 7 (pre-increment)
echo $counter--; // 7 (post-decrement)
echo --$counter; // 5 (pre-decrement)
?>
Control Structures
PHP provides familiar control structures for conditional execution and looping.
Conditional Statements and Loops
<?php
// If-else statements
$age = 25;
$has_license = true;
if ($age >= 18 && $has_license) {
echo "You can drive!";
} elseif ($age >= 18) {
echo "You need a license to drive.";
} else {
echo "You're too young to drive.";
}
// Switch statement
$day = "Monday";
switch ($day) {
case "Monday":
echo "Start of work week";
break;
case "Friday":
echo "Thank God it's Friday!";
break;
case "Saturday":
case "Sunday":
echo "Weekend!";
break;
default:
echo "Regular work day";
}
// Match expression (PHP 8+)
$status_code = 404;
$message = match($status_code) {
200 => "OK",
301, 302 => "Redirect",
404 => "Not Found",
500 => "Server Error",
default => "Unknown status"
};
echo $message;
// Loops
// For loop
for ($i = 0; $i < 5; $i++) {
echo "Number: $i<br>";
}
// While loop
$count = 0;
while ($count < 3) {
echo "Count: $count<br>";
$count++;
}
// Do-while loop
$number = 1;
do {
echo "Number: $number<br>";
$number++;
} while ($number <= 3);
// Foreach loop (for arrays)
$fruits = ["apple", "banana", "orange"];
foreach ($fruits as $fruit) {
echo "Fruit: $fruit<br>";
}
foreach ($fruits as $index => $fruit) {
echo "Index $index: $fruit<br>";
}
?>
Modern PHP Tip: Use match()
expressions (PHP 8+) instead of switch statements for simpler syntax and strict comparisons.
Arrays and Array Functions
PHP arrays are incredibly flexible - they can act as lists, dictionaries, stacks, and queues.
Working with Arrays
<?php
// Indexed arrays
$colors = ["red", "green", "blue"];
$numbers = array(1, 2, 3, 4, 5);
// Associative arrays
$person = [
"name" => "John Doe",
"age" => 30,
"city" => "New York"
];
// Multidimensional arrays
$users = [
["id" => 1, "name" => "Alice", "email" => "alice@example.com"],
["id" => 2, "name" => "Bob", "email" => "bob@example.com"],
["id" => 3, "name" => "Charlie", "email" => "charlie@example.com"]
];
// Array functions
echo count($colors); // 3
echo implode(", ", $colors); // red, green, blue
// Adding elements
$colors[] = "yellow"; // Add to end
array_push($colors, "purple"); // Add to end
array_unshift($colors, "black"); // Add to beginning
// Removing elements
$last_color = array_pop($colors); // Remove from end
$first_color = array_shift($colors); // Remove from beginning
// Array sorting
sort($colors); // Sort by values (ascending)
rsort($colors); // Sort by values (descending)
asort($person); // Sort associative array by values
ksort($person); // Sort associative array by keys
// Array filtering
$numbers = [1, 2, 3, 4, 5, 6];
$even_numbers = array_filter($numbers, function($n) {
return $n % 2 === 0;
});
// Array mapping
$squared_numbers = array_map(function($n) {
return $n * $n;
}, $numbers);
// Array searching
$key = array_search("green", $colors); // Find key by value
$has_red = in_array("red", $colors); // Check if value exists
// Array destructuring (PHP 7.1+)
[$first, $second, $third] = $colors;
["name" => $user_name, "age" => $user_age] = $person;
?>
3. Functions & Object-Oriented Programming
Functions in PHP
Functions allow you to organize code into reusable blocks. PHP supports both built-in and user-defined functions.
Creating and Using Functions
<?php
// Basic function
function greet($name) {
return "Hello, $name!";
}
echo greet("John"); // Hello, John!
// Function with default parameters
function create_email($username, $domain = "example.com") {
return "$username@$domain";
}
echo create_email("john"); // john@example.com
echo create_email("jane", "test.com"); // jane@test.com
// Type declarations (PHP 7+)
function add_numbers(int $a, int $b): int {
return $a + $b;
}
echo add_numbers(5, 3); // 8
// Nullable types (PHP 7.1+)
function find_user(?string $username): ?array {
if ($username === null) {
return null;
}
// Search for user...
return ["name" => $username, "id" => 1];
}
// Variadic functions (PHP 5.6+)
function sum(...$numbers) {
return array_sum($numbers);
}
echo sum(1, 2, 3, 4, 5); // 15
// Anonymous functions (closures)
$multiplier = 2;
$double = function($number) use ($multiplier) {
return $number * $multiplier;
};
echo $double(5); // 10
// Arrow functions (PHP 7.4+)
$numbers = [1, 2, 3, 4, 5];
$squared = array_map(fn($n) => $n * $n, $numbers);
// Built-in functions examples
$text = "Hello World";
echo strlen($text); // 11
echo strtoupper($text); // HELLO WORLD
echo str_replace("World", "PHP", $text); // Hello PHP
$number = -5.75;
echo abs($number); // 5.75
echo round($number); // -6
echo rand(1, 100); // Random number between 1-100
$date = date('Y-m-d H:i:s'); // Current date/time
?>
Object-Oriented Programming
PHP supports full object-oriented programming with classes, objects, inheritance, interfaces, and traits.
Classes and Objects
<?php
// Basic class definition
class User {
// Properties
public string $name;
public string $email;
private string $password;
protected int $user_id;
// Constructor (PHP 8 promoted properties)
public function __construct(
string $name,
string $email,
string $password
) {
$this->name = $name;
$this->email = $email;
$this->password = password_hash($password, PASSWORD_DEFAULT);
$this->user_id = rand(1000, 9999);
}
// Methods
public function getProfile(): string {
return "Name: {$this->name}, Email: {$this->email}";
}
public function verifyPassword(string $password): bool {
return password_verify($password, $this->password);
}
// Getter method
public function getUserId(): int {
return $this->user_id;
}
// Static method
public static function createFromArray(array $data): User {
return new User($data['name'], $data['email'], $data['password']);
}
}
// Using the class
$user1 = new User("John Doe", "john@example.com", "secret123");
echo $user1->getProfile();
echo $user1->getUserId();
$user_data = [
'name' => 'Jane Smith',
'email' => 'jane@example.com',
'password' => 'password123'
];
$user2 = User::createFromArray($user_data);
// Inheritance
class Admin extends User {
public string $role = 'admin';
public function __construct(string $name, string $email, string $password) {
parent::__construct($name, $email, $password);
}
public function getProfile(): string {
$profile = parent::getProfile();
return "$profile (Role: {$this->role})";
}
public function manageUsers(): string {
return "Managing users...";
}
}
$admin = new Admin("Admin User", "admin@example.com", "admin123");
echo $admin->getProfile();
echo $admin->manageUsers();
?>
Advanced OOP Features
Modern PHP includes interfaces, abstract classes, traits, and namespaces for robust object-oriented design.
Interfaces, Traits, and Namespaces
<?php
// Namespace declaration
namespace App\Models;
// Interface
interface Authenticatable {
public function login(): bool;
public function logout(): bool;
public function isLoggedIn(): bool;
}
// Abstract class
abstract class Model {
protected ?int $id = null;
public function getId(): ?int {
return $this->id;
}
public function setId(int $id): void {
$this->id = $id;
}
abstract public function save(): bool;
abstract public function delete(): bool;
}
// Trait for common functionality
trait Timestamps {
protected ?string $created_at = null;
protected ?string $updated_at = null;
public function setCreatedAt(string $date): void {
$this->created_at = $date;
}
public function setUpdatedAt(string $date): void {
$this->updated_at = $date;
}
public function getCreatedAt(): ?string {
return $this->created_at;
}
}
// Class implementing interface and using traits
class User extends Model implements Authenticatable {
use Timestamps;
private bool $logged_in = false;
public function login(): bool {
$this->logged_in = true;
return true;
}
public function logout(): bool {
$this->logged_in = false;
return true;
}
public function isLoggedIn(): bool {
return $this->logged_in;
}
public function save(): bool {
// Save to database logic
$this->setUpdatedAt(date('Y-m-d H:i:s'));
return true;
}
public function delete(): bool {
// Delete from database logic
return true;
}
}
// Using the classes
$user = new User();
$user->login();
echo $user->isLoggedIn() ? "Logged in" : "Logged out";
$user->save();
// Enums (PHP 8.1+)
enum UserStatus: string {
case PENDING = 'pending';
case ACTIVE = 'active';
case SUSPENDED = 'suspended';
case DELETED = 'deleted';
public function getColor(): string {
return match($this) {
self::PENDING => 'yellow',
self::ACTIVE => 'green',
self::SUSPENDED => 'orange',
self::DELETED => 'red'
};
}
}
$status = UserStatus::ACTIVE;
echo $status->value; // 'active'
echo $status->getColor(); // 'green'
// Readonly classes (PHP 8.2+)
readonly class Product {
public function __construct(
public string $name,
public float $price,
public int $quantity
) {}
}
$product = new Product("Laptop", 999.99, 5);
// $product->price = 899.99; // Error: readonly property
?>
Modern PHP: PHP 8+ introduces many modern OOP features like constructor property promotion, match expressions, enums, and readonly properties that make code more concise and type-safe.
4. PHP Web Development
Handling Forms and User Input
PHP excels at processing web forms. Learn how to securely handle user input from GET and POST requests.
Form Processing
<?php
// Simple contact form handler
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Sanitize input
$name = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_STRING);
$email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
$message = filter_input(INPUT_POST, 'message', FILTER_SANITIZE_STRING);
// Validate input
$errors = [];
if (empty($name)) {
$errors[] = "Name is required";
}
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = "Valid email is required";
}
if (empty($message) || strlen($message) < 10) {
$errors[] = "Message must be at least 10 characters";
}
// Process if no errors
if (empty($errors)) {
// Send email, save to database, etc.
$to = "admin@example.com";
$subject = "New Contact Form Submission";
$body = "Name: $name\nEmail: $email\nMessage: $message";
$headers = "From: $email";
if (mail($to, $subject, $body, $headers)) {
$success = "Thank you for your message!";
} else {
$errors[] = "Failed to send message. Please try again.";
}
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Contact Form</title>
<style>
.error { color: red; }
.success { color: green; }
</style>
</head>
<body>
<h1>Contact Us</h1>
<?php if (!empty($success)): ?>
<div class="success"><?= htmlspecialchars($success) ?></div>
<?php endif; ?>
<?php if (!empty($errors)): ?>
<?php foreach ($errors as $error): ?>
<div class="error"><?= htmlspecialchars($error) ?></div>
<?php endforeach; ?>
<?php endif; ?>
<form method="POST">
<div>
<label for="name">Name:</label>
<input type="text" id="name" name="name"
value="<?= htmlspecialchars($_POST['name'] ?? '') ?>" required>
</div>
<div>
<label for="email">Email:</label>
<input type="email" id="email" name="email"
value="<?= htmlspecialchars($_POST['email'] ?? '') ?>" required>
</div>
<div>
<label for="message">Message:</label>
<textarea id="message" name="message" required><?=
htmlspecialchars($_POST['message'] ?? '')
?></textarea>
</div>
<button type="submit">Send Message</button>
</form>
</body>
</html>
Sessions and Cookies
Sessions and cookies are essential for maintaining state in stateless HTTP protocol. Sessions store data on the server, while cookies store data on the client.
Session and Cookie Management
<?php
// Session management
session_start();
// Store data in session
$_SESSION['user_id'] = 123;
$_SESSION['username'] = 'john_doe';
$_SESSION['last_login'] = date('Y-m-d H:i:s');
// Check if user is logged in
function is_logged_in(): bool {
return isset($_SESSION['user_id']);
}
// Login function
function login($user_id, $username) {
$_SESSION['user_id'] = $user_id;
$_SESSION['username'] = $username;
$_SESSION['login_time'] = time();
}
// Logout function
function logout() {
session_unset();
session_destroy();
setcookie(session_name(), '', time() - 3600, '/');
}
// Cookie management
$cookie_name = "user_preferences";
$cookie_value = json_encode(['theme' => 'dark', 'language' => 'en']);
$expiry = time() + (30 * 24 * 60 * 60); // 30 days
// Set cookie
setcookie($cookie_name, $cookie_value, $expiry, "/");
// Read cookie
if (isset($_COOKIE[$cookie_name])) {
$preferences = json_decode($_COOKIE[$cookie_name], true);
$theme = $preferences['theme'] ?? 'light';
} else {
$theme = 'light';
}
// Secure session configuration
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1); // Use with HTTPS
ini_set('session.use_strict_mode', 1);
// Session hijacking prevention
if (!isset($_SESSION['user_agent'])) {
$_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
} elseif ($_SESSION['user_agent'] !== $_SERVER['HTTP_USER_AGENT']) {
// Possible session hijacking
logout();
header('Location: login.php');
exit;
}
?>
File Uploads
PHP makes it relatively easy to handle file uploads, but security is crucial when accepting files from users.
Secure File Upload Handling
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file_upload'])) {
$upload_dir = 'uploads/';
$max_file_size = 5 * 1024 * 1024; // 5MB
$allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
$file = $_FILES['file_upload'];
$file_name = basename($file['name']);
$file_tmp = $file['tmp_name'];
$file_size = $file['size'];
$file_error = $file['error'];
// Check for upload errors
if ($file_error !== UPLOAD_ERR_OK) {
die("Upload error: $file_error");
}
// Validate file size
if ($file_size > $max_file_size) {
die("File too large. Maximum size: 5MB");
}
// Validate file type using finfo
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$file_type = finfo_file($finfo, $file_tmp);
finfo_close($finfo);
if (!in_array($file_type, $allowed_types)) {
die("Invalid file type. Allowed: JPEG, PNG, GIF, PDF");
}
// Generate secure filename
$file_extension = pathinfo($file_name, PATHINFO_EXTENSION);
$safe_filename = uniqid() . '_' . bin2hex(random_bytes(8)) . '.' . $file_extension;
$destination = $upload_dir . $safe_filename;
// Move uploaded file
if (move_uploaded_file($file_tmp, $destination)) {
echo "File uploaded successfully: " . htmlspecialchars($file_name);
// Additional security for images
if (strpos($file_type, 'image/') === 0) {
// Re-process image to remove potential malicious content
$image_info = getimagesize($destination);
if (!$image_info) {
unlink($destination);
die("Invalid image file");
}
}
} else {
die("Failed to move uploaded file");
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>File Upload</title>
</head>
<body>
<h1>Upload a File</h1>
<form method="POST" enctype="multipart/form-data">
<input type="file" name="file_upload" accept=".jpg,.jpeg,.png,.gif,.pdf" required>
<button type="submit">Upload File</button>
</form>
</body>
</html>
Security Warning: Always validate file types using finfo
instead of trusting the file extension. Never execute uploaded files and store them outside the web root when possible.
5. Databases & MySQL with PHP
MySQLi and PDO
PHP offers two main extensions for database interaction: MySQLi (MySQL Improved) and PDO (PHP Data Objects). PDO is generally preferred for its database-agnostic approach.
Database Connection with PDO
<?php
// Database configuration
$host = 'localhost';
$dbname = 'myapp';
$username = 'root';
$password = '';
$charset = 'utf8mb4';
// PDO connection with error handling
try {
$dsn = "mysql:host=$host;dbname=$dbname;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
$pdo = new PDO($dsn, $username, $password, $options);
} catch (PDOException $e) {
throw new PDOException($e->getMessage(), (int)$e->getCode());
}
// MySQLi connection
$mysqli = new mysqli($host, $username, $password, $dbname);
if ($mysqli->connect_error) {
die("Connection failed: " . $mysqli->connect_error);
}
// Basic CRUD operations with PDO
class UserRepository {
private PDO $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
// Create (Insert)
public function createUser(string $name, string $email, string $password): bool {
$sql = "INSERT INTO users (name, email, password) VALUES (?, ?, ?)";
$stmt = $this->pdo->prepare($sql);
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
return $stmt->execute([$name, $email, $hashed_password]);
}
// Read (Select)
public function getUserById(int $id): ?array {
$sql = "SELECT id, name, email, created_at FROM users WHERE id = ?";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([$id]);
return $stmt->fetch() ?: null;
}
public function getUserByEmail(string $email): ?array {
$sql = "SELECT id, name, email, password, created_at FROM users WHERE email = ?";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([$email]);
return $stmt->fetch() ?: null;
}
// Update
public function updateUser(int $id, string $name, string $email): bool {
$sql = "UPDATE users SET name = ?, email = ? WHERE id = ?";
$stmt = $this->pdo->prepare($sql);
return $stmt->execute([$name, $email, $id]);
}
// Delete
public function deleteUser(int $id): bool {
$sql = "DELETE FROM users WHERE id = ?";
$stmt = $this->pdo->prepare($sql);
return $stmt->execute([$id]);
}
// Get all users with pagination
public function getUsers(int $page = 1, int $per_page = 10): array {
$offset = ($page - 1) * $per_page;
$sql = "SELECT id, name, email, created_at FROM users LIMIT ? OFFSET ?";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([$per_page, $offset]);
return $stmt->fetchAll();
}
}
// Using the repository
$userRepo = new UserRepository($pdo);
// Create user
$userRepo->createUser("John Doe", "john@example.com", "password123");
// Get user
$user = $userRepo->getUserByEmail("john@example.com");
if ($user && password_verify("password123", $user['password'])) {
echo "Login successful!";
}
// Transactions
try {
$pdo->beginTransaction();
// Multiple operations
$userRepo->createUser("Alice", "alice@example.com", "pass123");
$userRepo->createUser("Bob", "bob@example.com", "pass456");
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
echo "Transaction failed: " . $e->getMessage();
}
?>
Database Security and Best Practices
Always use prepared statements to prevent SQL injection. Never concatenate user input directly into SQL queries.
Security Best Practices
<?php
// ❌ DANGEROUS: SQL injection vulnerability
$user_input = $_GET['username'];
$sql = "SELECT * FROM users WHERE username = '$user_input'";
$result = $pdo->query($sql); // Vulnerable to SQL injection!
// ✅ SECURE: Using prepared statements
$user_input = $_GET['username'];
$sql = "SELECT * FROM users WHERE username = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$user_input]);
$result = $stmt->fetchAll();
// Advanced prepared statements with named parameters
$search_term = "%{$_GET['search']}%";
$min_age = $_GET['min_age'] ?? 0;
$sql = "SELECT * FROM users
WHERE (name LIKE :search OR email LIKE :search)
AND age >= :min_age
ORDER BY created_at DESC
LIMIT :limit OFFSET :offset";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':search', $search_term, PDO::PARAM_STR);
$stmt->bindValue(':min_age', $min_age, PDO::PARAM_INT);
$stmt->bindValue(':limit', 10, PDO::PARAM_INT);
$stmt->bindValue(':offset', 0, PDO::PARAM_INT);
$stmt->execute();
$users = $stmt->fetchAll();
// Database configuration security
class DatabaseConfig {
public static function getConfig(): array {
return [
'host' => getenv('DB_HOST') ?: 'localhost',
'dbname' => getenv('DB_NAME') ?: 'myapp',
'username' => getenv('DB_USER') ?: 'root',
'password' => getenv('DB_PASS') ?: '',
'charset' => 'utf8mb4',
'options' => [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"
]
];
}
}
// Connection factory
class DatabaseConnection {
private static ?PDO $instance = null;
public static function getInstance(): PDO {
if (self::$instance === null) {
$config = DatabaseConfig::getConfig();
$dsn = "mysql:host={$config['host']};dbname={$config['dbname']};charset={$config['charset']}";
try {
self::$instance = new PDO(
$dsn,
$config['username'],
$config['password'],
$config['options']
);
} catch (PDOException $e) {
error_log("Database connection failed: " . $e->getMessage());
throw new Exception("Database connection error");
}
}
return self::$instance;
}
}
// Usage
$pdo = DatabaseConnection::getInstance();
?>
Security First: Always use prepared statements with parameter binding. This is the most effective defense against SQL injection attacks. Never trust user input!
6. Security & Input Validation
Input Validation and Sanitization
Proper input validation is crucial for application security. PHP provides several functions to help validate and sanitize user input.
Comprehensive Input Validation
<?php
class Validator {
// Validate email
public static function validateEmail(string $email): bool {
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
// Sanitize email
public static function sanitizeEmail(string $email): string {
return filter_var($email, FILTER_SANITIZE_EMAIL);
}
// Validate URL
public static function validateUrl(string $url): bool {
return filter_var($url, FILTER_VALIDATE_URL) !== false;
}
// Sanitize URL
public static function sanitizeUrl(string $url): string {
return filter_var($url, FILTER_SANITIZE_URL);
}
// Validate and sanitize string
public static function validateString(string $input, int $min = 1, int $max = 255): ?string {
$sanitized = trim($input);
$sanitized = htmlspecialchars($sanitized, ENT_QUOTES, 'UTF-8');
if (strlen($sanitized) < $min || strlen($sanitized) > $max) {
return null;
}
return $sanitized;
}
// Validate integer
public static function validateInt($input, int $min = PHP_INT_MIN, int $max = PHP_INT_MAX): ?int {
$options = [
'options' => [
'min_range' => $min,
'max_range' => $max
]
];
return filter_var($input, FILTER_VALIDATE_INT, $options) ?: null;
}
// Validate float
public static function validateFloat($input, float $min = -PHP_FLOAT_MAX, float $max = PHP_FLOAT_MAX): ?float {
$options = [
'options' => [
'min_range' => $min,
'max_range' => $max
]
];
return filter_var($input, FILTER_VALIDATE_FLOAT, $options) ?: null;
}
// Validate password strength
public static function validatePassword(string $password): array {
$errors = [];
if (strlen($password) < 8) {
$errors[] = "Password must be at least 8 characters";
}
if (!preg_match('/[A-Z]/', $password)) {
$errors[] = "Password must contain at least one uppercase letter";
}
if (!preg_match('/[a-z]/', $password)) {
$errors[] = "Password must contain at least one lowercase letter";
}
if (!preg_match('/[0-9]/', $password)) {
$errors[] = "Password must contain at least one number";
}
if (!preg_match('/[^A-Za-z0-9]/', $password)) {
$errors[] = "Password must contain at least one special character";
}
return $errors;
}
// CSRF token generation and validation
public static function generateCsrfToken(): string {
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
public static function validateCsrfToken(string $token): bool {
return isset($_SESSION['csrf_token']) &&
hash_equals($_SESSION['csrf_token'], $token);
}
}
// Usage example
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$errors = [];
// Validate CSRF token
if (!Validator::validateCsrfToken($_POST['csrf_token'] ?? '')) {
$errors[] = "Invalid CSRF token";
}
// Validate email
$email = Validator::sanitizeEmail($_POST['email'] ?? '');
if (!Validator::validateEmail($email)) {
$errors[] = "Invalid email address";
}
// Validate name
$name = Validator::validateString($_POST['name'] ?? '', 2, 100);
if ($name === null) {
$errors[] = "Name must be between 2 and 100 characters";
}
// Validate password
$password_errors = Validator::validatePassword($_POST['password'] ?? '');
$errors = array_merge($errors, $password_errors);
if (empty($errors)) {
// Process valid data
$hashed_password = password_hash($_POST['password'], PASSWORD_DEFAULT);
// Save to database, etc.
echo "Registration successful!";
} else {
foreach ($errors as $error) {
echo "<div class='error'>$error</div>";
}
}
}
// In your form
$csrf_token = Validator::generateCsrfToken();
?>
<form method="POST">
<input type="hidden" name="csrf_token" value="<?= $csrf_token ?>">
<!-- form fields -->
</form>
Password Security
Always hash passwords using PHP's built-in password_hash() function. Never store plain text passwords!
Secure Password Handling
<?php
class PasswordManager {
// Hash password with current best practices
public static function hashPassword(string $password): string {
return password_hash($password, PASSWORD_DEFAULT);
}
// Verify password against hash
public static function verifyPassword(string $password, string $hash): bool {
return password_verify($password, $hash);
}
// Check if password needs rehashing (if algorithm changes)
public static function needsRehash(string $hash): bool {
return password_needs_rehash($hash, PASSWORD_DEFAULT);
}
// Generate secure random password
public static function generateSecurePassword(int $length = 16): string {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_-=+';
$password = '';
for ($i = 0; $i < $length; $i++) {
$password .= $chars[random_int(0, strlen($chars) - 1)];
}
return $password;
}
}
// User registration
$password = $_POST['password'];
$hashed_password = PasswordManager::hashPassword($password);
// Store $hashed_password in database
// User login
$user = $userRepository->getUserByEmail($_POST['email']);
if ($user && PasswordManager::verifyPassword($_POST['password'], $user['password'])) {
// Check if password needs rehashing
if (PasswordManager::needsRehash($user['password'])) {
$new_hash = PasswordManager::hashPassword($_POST['password']);
// Update user record with new hash
$userRepository->updatePassword($user['id'], $new_hash);
}
// Login successful
$_SESSION['user_id'] = $user['id'];
} else {
// Login failed
echo "Invalid email or password";
}
// Rate limiting for login attempts
class LoginThrottle {
private const MAX_ATTEMPTS = 5;
private const LOCKOUT_TIME = 900; // 15 minutes
public static function isLocked(string $identifier): bool {
$key = "login_attempts_$identifier";
if (!isset($_SESSION[$key])) {
return false;
}
$attempts = $_SESSION[$key];
// Check if lockout period has expired
if (isset($_SESSION["lockout_$identifier"])) {
$lockout_time = $_SESSION["lockout_$identifier"];
if (time() - $lockout_time > self::LOCKOUT_TIME) {
self::clearAttempts($identifier);
return false;
}
}
return $attempts >= self::MAX_ATTEMPTS;
}
public static function recordAttempt(string $identifier): void {
$key = "login_attempts_$identifier";
if (!isset($_SESSION[$key])) {
$_SESSION[$key] = 1;
} else {
$_SESSION[$key]++;
}
// Lock account if max attempts reached
if ($_SESSION[$key] >= self::MAX_ATTEMPTS) {
$_SESSION["lockout_$identifier"] = time();
}
}
public static function clearAttempts(string $identifier): void {
unset($_SESSION["login_attempts_$identifier"]);
unset($_SESSION["lockout_$identifier"]);
}
}
// Usage in login
$identifier = $_SERVER['REMOTE_ADDR'] . '_' . ($_POST['email'] ?? '');
if (LoginThrottle::isLocked($identifier)) {
die("Too many login attempts. Please try again in 15 minutes.");
}
if (/* login failed */) {
LoginThrottle::recordAttempt($identifier);
} else {
LoginThrottle::clearAttempts($identifier);
}
?>
7. Advanced PHP Techniques
Error Handling and Logging
Proper error handling and logging are essential for production applications. PHP provides several mechanisms for handling errors and exceptions.
Comprehensive Error Handling
<?php
// Custom error handler
class ErrorHandler {
public static function register(): void {
// Set error reporting level
error_reporting(E_ALL);
// Set custom error handler
set_error_handler([self::class, 'handleError']);
// Set exception handler
set_exception_handler([self::class, 'handleException']);
// Set shutdown function for fatal errors
register_shutdown_function([self::class, 'handleShutdown']);
}
public static function handleError(int $errno, string $errstr, string $errfile, int $errline): bool {
// Don't execute PHP internal error handler
if (!(error_reporting() & $errno)) {
return false;
}
$error_type = match($errno) {
E_ERROR => 'Error',
E_WARNING => 'Warning',
E_PARSE => 'Parse Error',
E_NOTICE => 'Notice',
E_CORE_ERROR => 'Core Error',
E_CORE_WARNING => 'Core Warning',
E_COMPILE_ERROR => 'Compile Error',
E_COMPILE_WARNING => 'Compile Warning',
E_USER_ERROR => 'User Error',
E_USER_WARNING => 'User Warning',
E_USER_NOTICE => 'User Notice',
E_STRICT => 'Strict',
E_RECOVERABLE_ERROR => 'Recoverable Error',
E_DEPRECATED => 'Deprecated',
E_USER_DEPRECATED => 'User Deprecated',
default => 'Unknown Error'
};
$message = "$error_type: $errstr in $errfile on line $errline";
// Log error
self::logError($message);
// Display error based on environment
if (self::isDevelopment()) {
echo "<div style='color: red; padding: 10px; margin: 10px; border: 1px solid red;'>$message</div>";
}
return true;
}
public static function handleException(Throwable $exception): void {
$message = "Uncaught Exception: " . $exception->getMessage() .
" in " . $exception->getFile() .
" on line " . $exception->getLine();
self::logError($message);
http_response_code(500);
if (self::isDevelopment()) {
echo "<h1>Exception</h1>";
echo "<p><strong>Message:</strong> " . $exception->getMessage() . "</p>";
echo "<p><strong>File:</strong> " . $exception->getFile() . "</p>";
echo "<p><strong>Line:</strong> " . $exception->getLine() . "</p>";
echo "<pre>" . $exception->getTraceAsString() . "</pre>";
} else {
echo "An error occurred. Please try again later.";
}
}
public static function handleShutdown(): void {
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
self::handleError($error['type'], $error['message'], $error['file'], $error['line']);
}
}
private static function logError(string $message): void {
$log_file = __DIR__ . '/logs/errors.log';
$timestamp = date('Y-m-d H:i:s');
$log_message = "[$timestamp] $message" . PHP_EOL;
// Create logs directory if it doesn't exist
if (!is_dir(dirname($log_file))) {
mkdir(dirname($log_file), 0755, true);
}
error_log($log_message, 3, $log_file);
}
private static function isDevelopment(): bool {
return $_ENV['APP_ENV'] === 'development';
}
}
// Register error handlers
ErrorHandler::register();
// Custom exception classes
class ValidationException extends Exception {
private array $errors;
public function __construct(array $errors, string $message = "Validation failed") {
$this->errors = $errors;
parent::__construct($message);
}
public function getErrors(): array {
return $this->errors;
}
}
class DatabaseException extends Exception {
public function __construct(string $message, ?Throwable $previous = null) {
parent::__construct($message, 0, $previous);
}
}
// Usage with try-catch
try {
$user = $userRepository->getUserById($user_id);
if (!$user) {
throw new ValidationException(['user_id' => 'User not found']);
}
// Process user data
} catch (ValidationException $e) {
$errors = $e->getErrors();
// Display validation errors to user
} catch (DatabaseException $e) {
error_log("Database error: " . $e->getMessage());
http_response_code(500);
echo "Database error occurred";
} catch (Exception $e) {
error_log("Unexpected error: " . $e->getMessage());
http_response_code(500);
echo "An unexpected error occurred";
}
?>
Performance Optimization
Optimize your PHP applications for better performance and scalability.
Performance Tips and Caching
<?php
// OPcache configuration (in php.ini)
// opcache.enable=1
// opcache.memory_consumption=256
// opcache.max_accelerated_files=20000
// opcache.validate_timestamps=0 // In production
// Database optimization
class OptimizedUserRepository {
private PDO $pdo;
private array $cache = [];
public function getUserWithCache(int $id): ?array {
// Simple in-memory cache
if (isset($this->cache[$id])) {
return $this->cache[$id];
}
$user = $this->getUserById($id);
$this->cache[$id] = $user;
return $user;
}
// Use indexes in database queries
public function getUsersByStatus(string $status, int $limit = 100): array {
// Ensure there's an index on status column
$sql = "SELECT * FROM users WHERE status = ? LIMIT ?";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([$status, $limit]);
return $stmt->fetchAll();
}
}
// File caching
class FileCache {
private string $cache_dir;
public function __construct(string $cache_dir) {
$this->cache_dir = $cache_dir;
if (!is_dir($cache_dir)) {
mkdir($cache_dir, 0755, true);
}
}
public function set(string $key, $data, int $ttl = 3600): bool {
$filename = $this->getFilename($key);
$cache_data = [
'data' => $data,
'expires' => time() + $ttl
];
return file_put_contents($filename, serialize($cache_data)) !== false;
}
public function get(string $key) {
$filename = $this->getFilename($key);
if (!file_exists($filename)) {
return null;
}
$cache_data = unserialize(file_get_contents($filename));
if ($cache_data['expires'] < time()) {
unlink($filename);
return null;
}
return $cache_data['data'];
}
public function delete(string $key): bool {
$filename = $this->getFilename($key);
if (file_exists($filename)) {
return unlink($filename);
}
return true;
}
private function getFilename(string $key): string {
return $this->cache_dir . '/' . md5($key) . '.cache';
}
}
// Usage
$cache = new FileCache(__DIR__ . '/cache');
$users = $cache->get('all_users');
if ($users === null) {
$users = $userRepository->getAllUsers();
$cache->set('all_users', $users, 300); // Cache for 5 minutes
}
// Memory usage optimization
function processLargeDataset(array $data): void {
$batch_size = 1000;
$total = count($data);
for ($i = 0; $i < $total; $i += $batch_size) {
$batch = array_slice($data, $i, $batch_size);
foreach ($batch as $item) {
// Process item
processItem($item);
}
// Clear memory periodically
if ($i % 10000 === 0) {
gc_collect_cycles();
}
}
}
// Lazy loading for better performance
class LazyUserLoader {
private ?array $user_data = null;
private int $user_id;
private UserRepository $repository;
public function __construct(int $user_id, UserRepository $repository) {
$this->user_id = $user_id;
$this->repository = $repository;
}
public function getName(): string {
$this->loadIfNeeded();
return $this->user_data['name'];
}
public function getEmail(): string {
$this->loadIfNeeded();
return $this->user_data['email'];
}
private function loadIfNeeded(): void {
if ($this->user_data === null) {
$this->user_data = $this->repository->getUserById($this->user_id);
}
}
}
// Usage
$user_loader = new LazyUserLoader(123, $userRepository);
// Data is loaded only when actually needed
echo $user_loader->getName();
?>
Modern PHP Features (PHP 8+)
PHP 8 introduced many modern features that make code more expressive and type-safe.
PHP 8+ Features
<?php
// Attributes (Annotations)
#[Attribute]
class Route {
public function __construct(
public string $path,
public string $method = 'GET'
) {}
}
class UserController {
#[Route('/users', 'GET')]
public function index(): array {
return ['users' => []];
}
#[Route('/users/{id}', 'GET')]
public function show(int $id): array {
return ['user' => ['id' => $id]];
}
}
// Match expression (enhanced switch)
$status_code = 404;
$message = match($status_code) {
200, 201 => 'OK',
301, 302 => 'Redirect',
404 => 'Not Found',
500 => 'Server Error',
default => 'Unknown status'
};
// Named arguments
function create_user(
string $name,
string $email,
?string $phone = null,
int $age = 0,
string $country = 'US'
): array {
return compact('name', 'email', 'phone', 'age', 'country');
}
// Using named arguments (order doesn't matter)
$user = create_user(
name: 'John Doe',
email: 'john@example.com',
country: 'UK',
age: 30
);
// Constructor property promotion
class Product {
public function __construct(
public string $name,
public float $price,
public int $quantity = 0,
private ?string $description = null
) {}
}
// Equivalent to:
class ProductOld {
public string $name;
public float $price;
public int $quantity;
private ?string $description;
public function __construct(
string $name,
float $price,
int $quantity = 0,
?string $description = null
) {
$this->name = $name;
$this->price = $price;
$this->quantity = $quantity;
$this->description = $description;
}
}
// Union types
function process_input(int|string|float $input): string {
return match(gettype($input)) {
'integer' => "Integer: $input",
'string' => "String: $input",
'float' => "Float: $input",
default => 'Unknown type'
};
}
// Mixed type (PHP 8.0+)
function debug(mixed $value): void {
var_dump($value);
}
// Nullsafe operator (PHP 8.0+)
class OrderService {
public function getOrderUserEmail(?Order $order): ?string {
// Instead of: return $order ? $order->getUser() ? $order->getUser()->getEmail() : null : null
return $order?->getUser()?->getEmail();
}
}
// Stringable interface (PHP 8.0+)
class User implements Stringable {
public function __construct(private string $name) {}
public function __toString(): string {
return $this->name;
}
}
$user = new User('John');
echo "User: $user"; // Automatically calls __toString()
// First-class callable syntax (PHP 8.1+)
$numbers = [1, 2, 3, 4, 5];
$double = array_map(fn($n) => $n * 2, $numbers);
// Using first-class callable
$double = array_map(strlen(...), $numbers);
// Readonly properties (PHP 8.1+)
class Configuration {
public function __construct(
public readonly string $database_url,
public readonly string $api_key,
public readonly array $settings = []
) {}
}
$config = new Configuration('mysql://localhost', 'secret123');
// $config->database_url = 'new_url'; // Error: readonly property
// Enums (PHP 8.1+)
enum UserRole: string {
case ADMIN = 'admin';
case EDITOR = 'editor';
case USER = 'user';
case GUEST = 'guest';
public function getPermissions(): array {
return match($this) {
self::ADMIN => ['read', 'write', 'delete', 'manage_users'],
self::EDITOR => ['read', 'write', 'delete'],
self::USER => ['read', 'write'],
self::GUEST => ['read']
};
}
public function isAdmin(): bool {
return $this === self::ADMIN;
}
}
$role = UserRole::ADMIN;
echo $role->value; // 'admin'
print_r($role->getPermissions()); // ['read', 'write', 'delete', 'manage_users']
?>
Stay Current: PHP continues to evolve rapidly. Always use the latest stable version and take advantage of new features that make your code more robust, readable, and maintainable.
Conclusion
🎉 Congratulations on completing the PHP Mastery Guide! You now have comprehensive knowledge of PHP from basic syntax to advanced web development techniques.
Next Steps
- • Build a complete project using all concepts
- • Explore PHP frameworks (Laravel, Symfony)
- • Learn about API development
- • Study design patterns in PHP
- • Contribute to open source PHP projects
Best Practices
- • Always validate and sanitize user input
- • Use prepared statements for database queries
- • Follow PSR standards for code style
- • Implement proper error handling
- • Use Composer for dependency management
Remember: PHP is a powerful, evolving language that continues to power the majority of the web. Keep learning, stay updated with new features, and always prioritize security and performance!