1. php
  2. /advanced
  3. /namespaces

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());
    }
}
?>

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.