Skip to content

子命名空间

概述

子命名空间(Sub-namespaces)是命名空间的层级扩展。通过反斜杠 \ 分隔符,可以创建多级命名空间结构,如 App\Http\Controllers。子命名空间不要求父命名空间存在——它们之间没有继承关系,纯粹是逻辑层级。

在 PSR-4 规范下,子命名空间与目录结构直接映射,是构建模块化 PHP 应用的核心机制。

基础概念

层级命名空间

命名空间的层级使用 \ 分隔,类似于文件系统中的目录路径:

App
├── Http
│   ├── Controllers
│   ├── Middleware
│   └── Requests
├── Models
├── Services
└── Repositories

父子关系只是逻辑概念

App\HttpApp\Models 之间没有任何代码层面的关联。子命名空间不会继承父命名空间中的类、函数或常量。

语法与代码

多级命名空间声明

php
<?php

declare(strict_types=1);

namespace App\Http\Controllers\Admin;

class DashboardController
{
    public function index(): string
    {
        return 'Admin Dashboard';
    }

    public function settings(): string
    {
        return 'Admin Settings';
    }
}

子命名空间的完全限定名

php
<?php

declare(strict_types=1);

namespace App\Services\Auth;

class AuthenticationService
{
    public function authenticate(string $username, string $password): bool
    {
        return $username === 'admin' && $password === 'secret';
    }
}

// 完全限定名:App\Services\Auth\AuthenticationService

子命名空间与目录映射

src/
└── App/
    └── Services/
        └── Auth/
            └── AuthenticationService.php

文件 AuthenticationService.php 中声明:

php
<?php

declare(strict_types=1);

namespace App\Services\Auth;

PSR-4 映射规则:App\ 前缀 -> src/ 目录。

不同子命名空间使用同名类

php
<?php

declare(strict_types=1);

// 文件: src/App/Repositories/UserRepository.php
namespace App\Repositories;

class UserRepository
{
    public function find(int $id): ?array
    {
        return ['id' => $id, 'name' => 'Alice'];
    }
}
php
<?php

declare(strict_types=1);

// 文件: src/App/Repositories/ProductRepository.php
namespace App\Repositories;

class ProductRepository
{
    public function find(int $id): ?array
    {
        return ['id' => $id, 'name' => 'Laptop'];
    }
}
php
<?php

declare(strict_types=1);

// 文件: src/App/Cache/UserRepository.php
namespace App\Cache;

class UserRepository
{
    public function find(int $id): ?array
    {
        // 从缓存中获取
        return ['id' => $id, 'cached' => true];
    }
}

// App\Repositories\UserRepository 和 App\Cache\UserRepository 不冲突

子命名空间不继承父命名空间

php
<?php

declare(strict_types=1);

namespace App\Utils;

function formatDate(\DateTimeImmutable $date): string
{
    return $date->format('Y-m-d');
}
php
<?php

declare(strict_types=1);

namespace App\Utils\String;

// 无法直接调用 App\Utils\formatDate()
// 必须使用完全限定名或导入

class Formatter
{
    public function formatDateTime(\DateTimeImmutable $date): string
    {
        // 需要完全限定名才能调用父命名空间函数
        return \App\Utils\formatDate($date);
    }
}

详细说明

PSR-4 中的子命名空间体现

PSR-4 规范定义了从命名空间到文件路径的映射规则:

  1. 完全限定类名 的命名空间前缀必须对应一个基准目录
  2. 子命名空间 对应基准目录下的子目录
  3. 类名 对应 .php 文件名
命名空间前缀: App\
基准目录:     src/

App\Http\Request           → src/Http/Request.php
App\Http\Controllers\User  → src/Http/Controllers/User.php
App\Models\User             → src/Models/User.php
App\Services\Payment        → src/Services/Payment.php

子命名空间的Composer 配置

json
{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "App\\Tests\\": "tests/"
        }
    }
}

多个基准目录的映射

json
{
    "autoload": {
        "psr-4": {
            "App\\": "src/App/",
            "Library\\": "lib/",
            "Plugin\\": "plugins/src/"
        }
    }
}
App\Http\Controller      → src/App/Http/Controller.php
Library\Database\Connection → lib/Database/Connection.php
Plugin\Auth\Middleware    → plugins/src/Auth/Middleware.php

子命名空间与类自动加载

php
<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Models\User;
use App\Services\UserService;

class UserController
{
    private UserService $userService;

    public function __construct(UserService $userService)
    {
        $this->userService = $userService;
    }

    public function show(int $id): array
    {
        return $this->userService->findById($id);
    }
}

// Composer 的 autoload 会根据 PSR-4 规则自动加载:
// App\Http\Controllers\UserController → src/Http/Controllers/UserController.php
// App\Models\User                      → src/Models/User.php
// App\Services\UserService              → src/Services/UserService.php

实战示例

场景一:分层架构中的子命名空间

src/
└── App/
    ├── Domain/
    │   ├── User/
    │   │   ├── User.php           → namespace App\Domain\User;
    │   │   ├── UserId.php         → namespace App\Domain\User;
    │   │   └── UserRepositoryInterface.php → namespace App\Domain\User;
    │   └── Order/
    │       ├── Order.php          → namespace App\Domain\Order;
    │       └── OrderRepositoryInterface.php → namespace App\Domain\Order;
    ├── Application/
    │   ├── UseCase/
    │   │   ├── CreateUser.php    → namespace App\Application\UseCase;
    │   │   └── PlaceOrder.php    → namespace App\Application\UseCase;
    │   └── DTO/
    │       ├── UserInput.php      → namespace App\Application\DTO;
    │       └── OrderInput.php      → namespace App\Application\DTO;
    ├── Infrastructure/
    │   ├── Persistence/
    │   │   └── UserMapper.php     → namespace App\Infrastructure\Persistence;
    │   └── External/
    │       └── PaymentGateway.php  → namespace App\Infrastructure\External;
    └── Presentation/
        ├── Http/
        │   ├── UserController.php → namespace App\Presentation\Http;
        │   └── OrderController.php → namespace App\Presentation\Http;
        └── Console/
            └── CreateUserCommand.php → namespace App\Presentation\Console;
php
<?php

declare(strict_types=1);

namespace App\Application\UseCase;

use App\Domain\User\User;
use App\Domain\User\UserId;
use App\Application\DTO\UserInput;

class CreateUser
{
    public function execute(UserInput $input): User
    {
        $user = new User(
            new UserId(),
            $input->name,
            $input->email
        );

        return $user;
    }
}

场景二:模块化应用的子命名空间

src/
└── App/
    ├── Modules/
    │   ├── Blog/
    │   │   ├── Controllers/
    │   │   │   └── PostController.php    → namespace App\Modules\Blog\Controllers;
    │   │   ├── Models/
    │   │   │   └── Post.php              → namespace App\Modules\Blog\Models;
    │   │   └── Services/
    │   │       └── PublishingService.php  → namespace App\Modules\Blog\Services;
    │   └── Shop/
    │       ├── Controllers/
    │       │   └── ProductController.php → namespace App\Modules\Shop\Controllers;
    │       ├── Models/
    │       │   └── Product.php           → namespace App\Modules\Shop\Models;
    │       └── Services/
    │           └── InventoryService.php → namespace App\Modules\Shop\Services;
    └── Shared/
        ├── Events/
        │   └── DomainEvent.php          → namespace App\Shared\Events;
        └── Exceptions/
            └── NotFoundException.php     → namespace App\Shared\Exceptions;
php
<?php

declare(strict_types=1);

namespace App\Modules\Blog\Controllers;

use App\Modules\Blog\Services\PublishingService;
use App\Modules\Blog\Models\Post;

class PostController
{
    private PublishingService $publishingService;

    public function __construct(PublishingService $publishingService)
    {
        $this->publishingService = $publishingService;
    }

    public function publish(int $postId): Post
    {
        return $this->publishingService->publish($postId);
    }
}

注意事项

注意事项

  • 子命名空间与父命名空间之间没有继承关系,只是逻辑层级
  • 子命名空间中无法直接使用父命名空间的类/函数/常量,需要导入或使用完全限定名
  • 子命名空间的命名应保持语义清晰,避免过于冗长的层级
  • 在 PSR-4 下,命名空间层级必须与目录层级完全一致

小贴士

  • 对于大型项目,推荐使用 DDD(领域驱动设计)风格的子命名空间
  • 每个模块可以有自己的独立子命名空间树,降低耦合
  • 使用 Composer 的 autoload-dev 将测试类映射到独立的子命名空间

最佳实践

1. 保持命名空间层级与目录一致

这是 PSR-4 自动加载的基本要求,也是代码可预测性的保障。

2. 使用语义清晰的层级命名

php
<?php

declare(strict_types=1);

// 推荐 - 语义清晰
namespace App\Domain\Order\Entity;
namespace App\Infrastructure\Database;

// 不推荐 - 无意义的层级
namespace App\Things\Stuff\Items;

3. 子命名空间不宜超过4层

php
<?php

declare(strict_types=1);

// 合理
namespace App\Http\Controllers\Admin;

// 过深
namespace App\Modules\Admin\Http\Controllers\Api\V1;

4. 使用共享命名空间存放公共代码

php
<?php

declare(strict_types=1);

// 不同模块共用的异常类
namespace App\Shared\Exception;

class ValidationException extends \RuntimeException
{
}

参考链接