Advanced PHP Features
Introduction to Advanced PHP
Modern PHP has evolved significantly from its early days. PHP 7, 8, and beyond introduce powerful features that enable more elegant, performant, and maintainable code. This guide covers advanced PHP concepts essential for professional development.
Understanding these features allows you to write more sophisticated applications and leverage the full power of modern PHP.
Namespaces
Namespaces solve naming conflicts and organize code in large applications.
Basic Namespace Usage
<?php
// File: src/Database/Connection.php
namespace MyApp\Database;
class Connection {
private $dsn;
public function __construct($dsn) {
$this->dsn = $dsn;
}
public function connect() {
return new \PDO($this->dsn);
}
}
// File: src/Http/Request.php
namespace MyApp\Http;
class Request {
public function getMethod() {
return $_SERVER['REQUEST_METHOD'];
}
public function getPath() {
return $_SERVER['REQUEST_URI'];
}
}
?>
Using Namespaces
<?php
// Import specific classes
use MyApp\Database\Connection;
use MyApp\Http\Request;
// Import with alias
use MyApp\Database\Connection as DbConnection;
// Import multiple classes from same namespace
use MyApp\Http\{Request, Response, Router};
// Usage
$connection = new Connection('mysql:host=localhost;dbname=test');
$request = new Request();
// Fully qualified name
$router = new \MyApp\Http\Router();
?>
Namespace Organization
<?php
namespace MyApp\Services\Email;
interface MailerInterface {
public function send($to, $subject, $body);
}
class SmtpMailer implements MailerInterface {
private $config;
public function __construct(array $config) {
$this->config = $config;
}
public function send($to, $subject, $body) {
// SMTP implementation
}
}
class SendgridMailer implements MailerInterface {
private $apiKey;
public function __construct($apiKey) {
$this->apiKey = $apiKey;
}
public function send($to, $subject, $body) {
// Sendgrid API implementation
}
}
?>
Traits
Traits provide horizontal code reuse and solve multiple inheritance limitations.
Basic Trait Usage
<?php
trait Timestampable {
private $createdAt;
private $updatedAt;
public function setCreatedAt(\DateTime $date = null) {
$this->createdAt = $date ?: new \DateTime();
}
public function setUpdatedAt(\DateTime $date = null) {
$this->updatedAt = $date ?: new \DateTime();
}
public function getCreatedAt() {
return $this->createdAt;
}
public function getUpdatedAt() {
return $this->updatedAt;
}
public function touch() {
$this->setUpdatedAt();
}
}
trait Validatable {
private $errors = [];
abstract protected function getRules();
public function validate() {
$this->errors = [];
$rules = $this->getRules();
foreach ($rules as $field => $rule) {
$value = $this->$field ?? null;
if ($rule['required'] && empty($value)) {
$this->errors[$field] = "$field is required";
}
if (isset($rule['max']) && strlen($value) > $rule['max']) {
$this->errors[$field] = "$field is too long";
}
}
return empty($this->errors);
}
public function getErrors() {
return $this->errors;
}
}
class User {
use Timestampable, Validatable;
private $name;
private $email;
public function __construct($name, $email) {
$this->name = $name;
$this->email = $email;
$this->setCreatedAt();
}
protected function getRules() {
return [
'name' => ['required' => true, 'max' => 50],
'email' => ['required' => true, 'max' => 100]
];
}
}
// Usage
$user = new User("John Doe", "[email protected]");
if ($user->validate()) {
echo "User is valid";
} else {
print_r($user->getErrors());
}
?>
Trait Conflict Resolution
<?php
trait Logger {
public function log($message) {
echo "Logger: $message\n";
}
}
trait Debugger {
public function log($message) {
echo "Debugger: $message\n";
}
public function debug($data) {
var_dump($data);
}
}
class Application {
use Logger, Debugger {
Logger::log insteadof Debugger;
Debugger::log as debugLog;
}
public function run() {
$this->log("Application started"); // Uses Logger::log
$this->debugLog("Debug information"); // Uses Debugger::log
}
}
?>
Anonymous Functions and Closures
Basic Anonymous Functions
<?php
// Simple anonymous function
$greet = function($name) {
return "Hello, $name!";
};
echo $greet("World"); // Output: Hello, World!
// Anonymous function with use clause
$multiplier = 3;
$multiply = function($number) use ($multiplier) {
return $number * $multiplier;
};
echo $multiply(5); // Output: 15
// Modifying external variables
$counter = 0;
$increment = function() use (&$counter) {
$counter++;
return $counter;
};
echo $increment(); // 1
echo $increment(); // 2
?>
Closures in Practice
<?php
class EventManager {
private $listeners = [];
public function on($event, callable $callback) {
$this->listeners[$event][] = $callback;
}
public function fire($event, $data = null) {
if (isset($this->listeners[$event])) {
foreach ($this->listeners[$event] as $callback) {
$callback($data);
}
}
}
}
// Usage
$events = new EventManager();
// Register event listeners
$events->on('user.created', function($user) {
echo "Sending welcome email to {$user['email']}\n";
});
$events->on('user.created', function($user) {
echo "Creating user profile for {$user['name']}\n";
});
// Fire event
$events->fire('user.created', [
'name' => 'John Doe',
'email' => '[email protected]'
]);
?>
Array Functions with Closures
<?php
$users = [
['name' => 'John', 'age' => 30, 'active' => true],
['name' => 'Jane', 'age' => 25, 'active' => false],
['name' => 'Bob', 'age' => 35, 'active' => true]
];
// Filter active users
$activeUsers = array_filter($users, function($user) {
return $user['active'];
});
// Map to names only
$names = array_map(function($user) {
return $user['name'];
}, $users);
// Find users over 30
$adults = array_filter($users, function($user) {
return $user['age'] > 30;
});
// Sort by age
usort($users, function($a, $b) {
return $a['age'] <=> $b['age'];
});
// Custom validation with closures
function validateForm($data, $rules) {
$errors = [];
foreach ($rules as $field => $validator) {
if (!$validator($data[$field] ?? null)) {
$errors[] = "Invalid $field";
}
}
return $errors;
}
$formRules = [
'email' => function($value) {
return filter_var($value, FILTER_VALIDATE_EMAIL);
},
'age' => function($value) {
return is_numeric($value) && $value >= 18;
}
];
$errors = validateForm($_POST, $formRules);
?>
Generators
Generators provide memory-efficient iteration over large datasets.
Basic Generator Usage
<?php
function numberGenerator($start, $end) {
for ($i = $start; $i <= $end; $i++) {
yield $i;
}
}
// Memory efficient - doesn't create array
foreach (numberGenerator(1, 1000000) as $number) {
if ($number > 5) break;
echo $number . "\n";
}
// File reading generator
function readFileLines($filename) {
$file = fopen($filename, 'r');
try {
while (($line = fgets($file)) !== false) {
yield trim($line);
}
} finally {
fclose($file);
}
}
// Process large files efficiently
foreach (readFileLines('large-file.txt') as $lineNumber => $line) {
echo "Line $lineNumber: $line\n";
}
?>
Advanced Generator Features
<?php
function fibonacci() {
$a = 0;
$b = 1;
yield $a;
yield $b;
while (true) {
$next = $a + $b;
yield $next;
$a = $b;
$b = $next;
}
}
// Get first 10 Fibonacci numbers
$fib = fibonacci();
for ($i = 0; $i < 10; $i++) {
echo $fib->current() . " ";
$fib->next();
}
// Generator with send()
function calculator() {
$result = 0;
while (true) {
$operation = yield $result;
if ($operation) {
eval("\$result $operation;");
}
}
}
$calc = calculator();
$calc->current(); // Initialize
echo $calc->send("+ 5"); // 5
echo $calc->send("* 3"); // 15
echo $calc->send("- 2"); // 13
// Delegating generators
function generateNumbers() {
yield from range(1, 5);
yield from range(10, 15);
}
foreach (generateNumbers() as $number) {
echo "$number ";
}
// Output: 1 2 3 4 5 10 11 12 13 14 15
?>
Modern PHP Features (PHP 8+)
Match Expression
<?php
// Match expression (PHP 8+)
function getStatusMessage($status) {
return match($status) {
'pending' => 'Order is pending',
'processing' => 'Order is being processed',
'shipped' => 'Order has been shipped',
'delivered' => 'Order delivered',
'cancelled' => 'Order was cancelled',
default => 'Unknown status'
};
}
// Multiple conditions
function getGrade($score) {
return match(true) {
$score >= 90 => 'A',
$score >= 80 => 'B',
$score >= 70 => 'C',
$score >= 60 => 'D',
default => 'F'
};
}
?>
Named Arguments
<?php
function createUser($name, $email, $password, $active = true, $role = 'user') {
return [
'name' => $name,
'email' => $email,
'password' => $password,
'active' => $active,
'role' => $role
];
}
// Named arguments (PHP 8+)
$user = createUser(
name: 'John Doe',
email: '[email protected]',
password: 'secret',
role: 'admin'
);
// Skip optional parameters
$user2 = createUser(
name: 'Jane Doe',
email: '[email protected]',
password: 'secret'
);
?>
Union Types and Type Declarations
<?php
class DataProcessor {
// Union types (PHP 8+)
public function process(string|array $data): string|array {
if (is_string($data)) {
return strtoupper($data);
}
return array_map('strtoupper', $data);
}
// Mixed type
public function store(mixed $value): void {
// Can accept any type
}
// Nullable types
public function findUser(?int $id): ?User {
if ($id === null) {
return null;
}
// Find user logic
return new User();
}
}
// Constructor property promotion (PHP 8+)
class Product {
public function __construct(
public string $name,
public float $price,
public int $quantity = 0,
private string $sku = ''
) {}
public function getSku(): string {
return $this->sku;
}
}
$product = new Product(
name: 'Laptop',
price: 999.99,
quantity: 10
);
?>
Attributes (Annotations)
<?php
// Define attributes
#[Attribute]
class Route {
public function __construct(
public string $path,
public string $method = 'GET'
) {}
}
#[Attribute]
class Validate {
public function __construct(
public string $rule
) {}
}
// Use attributes
class UserController {
#[Route('/users', 'GET')]
public function index() {
return 'List of users';
}
#[Route('/users/{id}', 'GET')]
public function show(int $id) {
return "User $id";
}
#[Route('/users', 'POST')]
public function store(
#[Validate('required|email')] string $email,
#[Validate('required|min:8')] string $password
) {
return 'User created';
}
}
// Read attributes
function getRoutes($controller) {
$reflection = new ReflectionClass($controller);
$routes = [];
foreach ($reflection->getMethods() as $method) {
$attributes = $method->getAttributes(Route::class);
foreach ($attributes as $attribute) {
$route = $attribute->newInstance();
$routes[] = [
'path' => $route->path,
'method' => $route->method,
'handler' => [$controller, $method->getName()]
];
}
}
return $routes;
}
?>
Error Handling and Exceptions
Custom Exception Classes
<?php
abstract class ApplicationException extends Exception {
protected $context = [];
public function __construct($message = '', $code = 0, $context = [], Exception $previous = null) {
$this->context = $context;
parent::__construct($message, $code, $previous);
}
public function getContext() {
return $this->context;
}
}
class ValidationException extends ApplicationException {
private $errors;
public function __construct($errors, $message = 'Validation failed') {
$this->errors = $errors;
parent::__construct($message);
}
public function getErrors() {
return $this->errors;
}
}
class DatabaseException extends ApplicationException {
public function __construct($message, $query = '', Exception $previous = null) {
parent::__construct($message, 0, ['query' => $query], $previous);
}
public function getQuery() {
return $this->context['query'] ?? '';
}
}
// Exception handling with finally
function processUserData($data) {
$transaction = null;
try {
$transaction = beginTransaction();
if (!validateUserData($data)) {
throw new ValidationException(['email' => 'Invalid email']);
}
saveUser($data);
$transaction->commit();
} catch (ValidationException $e) {
if ($transaction) $transaction->rollback();
throw $e; // Re-throw validation errors
} catch (DatabaseException $e) {
if ($transaction) $transaction->rollback();
error_log("Database error: " . $e->getMessage());
throw new ApplicationException("Failed to save user data");
} finally {
// Cleanup code always runs
if ($transaction) {
$transaction->close();
}
}
}
?>
Dependency Injection and Service Containers
Simple Dependency Injection Container
<?php
class Container {
private $bindings = [];
private $instances = [];
public function bind($abstract, $concrete = null) {
if ($concrete === null) {
$concrete = $abstract;
}
$this->bindings[$abstract] = $concrete;
}
public function singleton($abstract, $concrete = null) {
$this->bind($abstract, $concrete);
$this->instances[$abstract] = null;
}
public function make($abstract) {
// Return singleton if it exists
if (isset($this->instances[$abstract])) {
if ($this->instances[$abstract] === null) {
$this->instances[$abstract] = $this->build($abstract);
}
return $this->instances[$abstract];
}
return $this->build($abstract);
}
private function build($abstract) {
$concrete = $this->bindings[$abstract] ?? $abstract;
if ($concrete instanceof Closure) {
return $concrete($this);
}
$reflector = new ReflectionClass($concrete);
if (!$reflector->isInstantiable()) {
throw new Exception("Target [$concrete] is not instantiable");
}
$constructor = $reflector->getConstructor();
if ($constructor === null) {
return new $concrete;
}
$dependencies = $this->resolveDependencies($constructor);
return $reflector->newInstanceArgs($dependencies);
}
private function resolveDependencies(ReflectionMethod $constructor) {
$dependencies = [];
foreach ($constructor->getParameters() as $parameter) {
$type = $parameter->getType();
if ($type && !$type->isBuiltin()) {
$dependencies[] = $this->make($type->getName());
} else {
if ($parameter->isDefaultValueAvailable()) {
$dependencies[] = $parameter->getDefaultValue();
} else {
throw new Exception("Cannot resolve parameter {$parameter->getName()}");
}
}
}
return $dependencies;
}
}
// Usage
interface LoggerInterface {
public function log($message);
}
class FileLogger implements LoggerInterface {
public function log($message) {
file_put_contents('app.log', $message . "\n", FILE_APPEND);
}
}
class UserService {
private $logger;
public function __construct(LoggerInterface $logger) {
$this->logger = $logger;
}
public function createUser($data) {
$this->logger->log("Creating user: " . $data['name']);
// User creation logic
}
}
// Container setup
$container = new Container();
$container->bind(LoggerInterface::class, FileLogger::class);
// Automatic dependency injection
$userService = $container->make(UserService::class);
$userService->createUser(['name' => 'John Doe']);
?>
Related Topics
Explore these related advanced PHP topics:
- PHP Design Patterns - Common patterns in PHP
- PHP Performance Optimization - Optimizing PHP applications
- PHP Testing - Unit and integration testing
- PHP Package Development - Creating reusable packages
- PHP 8+ Features - Latest PHP features
Summary
Advanced PHP features enable professional-grade development:
- Namespaces: Organize code and prevent naming conflicts
- Traits: Implement horizontal code reuse
- Closures: Create flexible, reusable functions
- Generators: Process large datasets efficiently
- Modern PHP Features: Leverage PHP 8+ improvements
- Exception Handling: Implement robust error management
- Dependency Injection: Create loosely coupled, testable code
These features form the foundation of modern PHP frameworks and enable scalable, maintainable applications. Master these concepts to write professional PHP code that follows industry best practices.