PHP Namespaces
Introduction to PHP Namespaces
Namespaces in PHP provide a way to group related classes, interfaces, functions, and constants. They help avoid naming conflicts and allow for better code organization, especially in large applications and when using multiple libraries.
Namespaces are essential for modern PHP development and are a cornerstone of PSR-4 autoloading standards.
Basic Namespace Declaration
Simple Namespace Declaration
<?php
// File: src/Utils/StringHelper.php
namespace Utils;
class StringHelper
{
public static function camelCase($string)
{
return lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $string))));
}
public static function slugify($string)
{
return strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $string)));
}
}
// Usage
$helper = new Utils\StringHelper();
$slug = Utils\StringHelper::slugify('Hello World');
?>
Nested Namespaces
<?php
// File: src/App/Services/User/UserService.php
namespace App\Services\User;
class UserService
{
public function createUser($name, $email)
{
return new \App\Models\User($name, $email);
}
}
// File: src/App/Models/User.php
namespace App\Models;
class User
{
private $name;
private $email;
public function __construct($name, $email)
{
$this->name = $name;
$this->email = $email;
}
public function getName()
{
return $this->name;
}
public function getEmail()
{
return $this->email;
}
}
?>
Using Namespaces
The use
Statement
<?php
// Import single class
use App\Models\User;
use App\Services\User\UserService;
// Import with alias
use App\Utils\StringHelper as StrHelper;
use App\Models\User as UserModel;
// Import multiple classes from same namespace
use App\Services\{
UserService,
EmailService,
AuthService
};
// Import functions and constants
use function App\Utils\formatDate;
use const App\Config\DEFAULT_TIMEZONE;
class UserController
{
private $userService;
public function __construct()
{
$this->userService = new UserService();
}
public function createUser($name, $email)
{
$user = new User($name, $email); // Uses imported class
$slug = StrHelper::slugify($name); // Uses alias
return $this->userService->save($user);
}
}
?>
Global Namespace
<?php
namespace App\Services;
class DatabaseService
{
private $pdo;
public function __construct()
{
// Access global class with leading backslash
$this->pdo = new \PDO('sqlite::memory:');
}
public function getCurrentTime()
{
// Access global function
return \date('Y-m-d H:i:s');
}
public function getRandomNumber()
{
// Access global function
return \rand(1, 100);
}
}
?>
Autoloading with PSR-4
Directory Structure
project/
├── composer.json
├── src/
│ ├── App/
│ │ ├── Controllers/
│ │ │ ├── UserController.php
│ │ │ └── PostController.php
│ │ ├── Models/
│ │ │ ├── User.php
│ │ │ └── Post.php
│ │ └── Services/
│ │ ├── UserService.php
│ │ └── EmailService.php
│ └── Utils/
│ ├── StringHelper.php
│ └── DateHelper.php
└── vendor/
Composer Configuration
{
"autoload": {
"psr-4": {
"App\\": "src/App/",
"Utils\\": "src/Utils/"
}
}
}
Class Implementation
<?php
// src/App/Controllers/UserController.php
namespace App\Controllers;
use App\Models\User;
use App\Services\UserService;
use App\Services\EmailService;
use Utils\StringHelper;
class UserController
{
private UserService $userService;
private EmailService $emailService;
public function __construct(UserService $userService, EmailService $emailService)
{
$this->userService = $userService;
$this->emailService = $emailService;
}
public function register($data)
{
$username = StringHelper::slugify($data['name']);
$user = new User(
$data['name'],
$data['email'],
$username
);
$savedUser = $this->userService->create($user);
$this->emailService->sendWelcomeEmail($savedUser);
return $savedUser;
}
}
// src/App/Models/User.php
namespace App\Models;
class User
{
private string $name;
private string $email;
private string $username;
private \DateTime $createdAt;
public function __construct(string $name, string $email, string $username)
{
$this->name = $name;
$this->email = $email;
$this->username = $username;
$this->createdAt = new \DateTime();
}
public function getName(): string
{
return $this->name;
}
public function getEmail(): string
{
return $this->email;
}
public function getUsername(): string
{
return $this->username;
}
public function getCreatedAt(): \DateTime
{
return $this->createdAt;
}
}
?>
Advanced Namespace Features
Namespace Resolution
<?php
namespace App\Services;
use App\Models\User as UserModel;
class UserService
{
public function example()
{
// Fully qualified name (starts with \)
$pdo = new \PDO('sqlite::memory:');
// Qualified name (contains namespace separator)
$user = new \App\Models\User('John', '[email protected]');
// Unqualified name (no namespace separator)
$helper = new StringHelper(); // Resolves to App\Services\StringHelper
// Using imported alias
$userModel = new UserModel('Jane', '[email protected]');
// Relative namespace (starts with namespace\)
$validator = new namespace\Validators\UserValidator();
// Resolves to App\Services\Validators\UserValidator
}
}
?>
Dynamic Namespace Usage
<?php
namespace App\Factory;
class ModelFactory
{
private const NAMESPACE_PREFIX = 'App\\Models\\';
public function create(string $modelName, array $data)
{
$className = self::NAMESPACE_PREFIX . ucfirst($modelName);
if (!class_exists($className)) {
throw new \InvalidArgumentException("Model {$modelName} not found");
}
$reflection = new \ReflectionClass($className);
return $reflection->newInstanceArgs($data);
}
public function getAvailableModels(): array
{
$models = [];
$directory = __DIR__ . '/../Models';
foreach (glob($directory . '/*.php') as $file) {
$className = basename($file, '.php');
$fullClassName = self::NAMESPACE_PREFIX . $className;
if (class_exists($fullClassName)) {
$models[] = $className;
}
}
return $models;
}
}
// Usage
$factory = new ModelFactory();
$user = $factory->create('User', ['John', '[email protected]']);
$models = $factory->getAvailableModels(); // ['User', 'Post', 'Comment']
?>
Namespace Best Practices
Vendor Namespacing
<?php
// Good: Vendor prefix prevents conflicts
namespace Acme\Blog\Models;
namespace Acme\Blog\Services;
namespace Acme\Blog\Controllers;
// Good: Descriptive, hierarchical structure
namespace MyCompany\Ecommerce\Payment\Gateways;
namespace MyCompany\Ecommerce\Shipping\Providers;
namespace MyCompany\Ecommerce\Inventory\Managers;
// Avoid: Too generic, likely to conflict
namespace Models;
namespace Services;
namespace Utils;
?>
Interface and Trait Namespacing
<?php
// src/App/Contracts/UserRepositoryInterface.php
namespace App\Contracts;
interface UserRepositoryInterface
{
public function find(int $id): ?User;
public function save(User $user): User;
public function delete(int $id): bool;
}
// src/App/Traits/Timestampable.php
namespace App\Traits;
trait Timestampable
{
private \DateTime $createdAt;
private \DateTime $updatedAt;
public function setTimestamps(): void
{
$now = new \DateTime();
if (!isset($this->createdAt)) {
$this->createdAt = $now;
}
$this->updatedAt = $now;
}
public function getCreatedAt(): \DateTime
{
return $this->createdAt;
}
public function getUpdatedAt(): \DateTime
{
return $this->updatedAt;
}
}
// src/App/Repositories/UserRepository.php
namespace App\Repositories;
use App\Contracts\UserRepositoryInterface;
use App\Models\User;
use App\Traits\Timestampable;
class UserRepository implements UserRepositoryInterface
{
use Timestampable;
private \PDO $pdo;
public function __construct(\PDO $pdo)
{
$this->pdo = $pdo;
}
public function find(int $id): ?User
{
$stmt = $this->pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
$data = $stmt->fetch(\PDO::FETCH_ASSOC);
return $data ? new User($data['name'], $data['email']) : null;
}
public function save(User $user): User
{
$this->setTimestamps();
$stmt = $this->pdo->prepare("
INSERT INTO users (name, email, created_at)
VALUES (?, ?, ?)
");
$stmt->execute([
$user->getName(),
$user->getEmail(),
$this->getCreatedAt()->format('Y-m-d H:i:s')
]);
return $user;
}
public function delete(int $id): bool
{
$stmt = $this->pdo->prepare("DELETE FROM users WHERE id = ?");
return $stmt->execute([$id]);
}
}
?>
Namespace Constants and Functions
Constants in Namespaces
<?php
// src/App/Config/Constants.php
namespace App\Config;
const APP_VERSION = '1.0.0';
const DEFAULT_LOCALE = 'en_US';
const MAX_UPLOAD_SIZE = 10485760; // 10MB
class DatabaseConfig
{
const DEFAULT_DRIVER = 'mysql';
const DEFAULT_PORT = 3306;
const CONNECTION_TIMEOUT = 30;
}
// Usage
use App\Config\DatabaseConfig;
use const App\Config\{APP_VERSION, DEFAULT_LOCALE};
echo APP_VERSION; // 1.0.0
echo DEFAULT_LOCALE; // en_US
echo DatabaseConfig::DEFAULT_DRIVER; // mysql
?>
Functions in Namespaces
<?php
// src/Utils/functions.php
namespace Utils;
function formatBytes(int $bytes, int $precision = 2): string
{
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
$bytes /= 1024;
}
return round($bytes, $precision) . ' ' . $units[$i];
}
function sanitizeFilename(string $filename): string
{
return preg_replace('/[^a-zA-Z0-9._-]/', '_', $filename);
}
function isValidEmail(string $email): bool
{
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
// Usage
use function Utils\{formatBytes, sanitizeFilename, isValidEmail};
echo formatBytes(1048576); // 1.00 MB
echo sanitizeFilename('file name.txt'); // file_name.txt
var_dump(isValidEmail('[email protected]')); // true
?>
Testing with Namespaces
<?php
// tests/Unit/UserServiceTest.php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use App\Services\UserService;
use App\Models\User;
use App\Repositories\UserRepository;
class UserServiceTest extends TestCase
{
private UserService $userService;
private UserRepository $userRepository;
protected function setUp(): void
{
$this->userRepository = $this->createMock(UserRepository::class);
$this->userService = new UserService($this->userRepository);
}
public function testCreateUser(): void
{
$userData = ['name' => 'John Doe', 'email' => '[email protected]'];
$this->userRepository
->expects($this->once())
->method('save')
->willReturn(new User($userData['name'], $userData['email']));
$user = $this->userService->createUser($userData);
$this->assertInstanceOf(User::class, $user);
$this->assertEquals('John Doe', $user->getName());
}
}
?>
Related Topics
- PHP Object-Oriented Programming - OOP concepts and namespaces
- PHP Autoloading - Autoloading mechanisms
- Composer and Packagist - Package management
- PHP Testing - Testing namespaced code
- PHP Advanced Features - Advanced PHP concepts
Summary
PHP namespaces provide essential organization features:
- Naming Conflict Prevention: Avoid class/function name collisions
- Code Organization: Logical grouping of related functionality
- PSR-4 Compatibility: Standard autoloading integration
- Better Maintainability: Clearer code structure and dependencies
Key benefits:
- Eliminates global namespace pollution
- Enables better code organization
- Facilitates package distribution
- Improves IDE support and autocomplete
- Supports modern PHP development practices
Namespaces are fundamental to modern PHP development and essential for building maintainable, scalable applications.