子命名空间
概述
子命名空间(Sub-namespaces)是命名空间的层级扩展。通过反斜杠 \ 分隔符,可以创建多级命名空间结构,如 App\Http\Controllers。子命名空间不要求父命名空间存在——它们之间没有继承关系,纯粹是逻辑层级。
在 PSR-4 规范下,子命名空间与目录结构直接映射,是构建模块化 PHP 应用的核心机制。
基础概念
层级命名空间
命名空间的层级使用 \ 分隔,类似于文件系统中的目录路径:
App
├── Http
│ ├── Controllers
│ ├── Middleware
│ └── Requests
├── Models
├── Services
└── Repositories父子关系只是逻辑概念
App\Http 和 App\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 规范定义了从命名空间到文件路径的映射规则:
- 完全限定类名 的命名空间前缀必须对应一个基准目录
- 子命名空间 对应基准目录下的子目录
- 类名 对应
.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
{
}