跳到主要内容

PHP 命名空间

本章将介绍 PHP 命名空间的概念和使用方法。

什么是命名空间?

命名空间是一种封装事物的方法,用于解决类名冲突问题。在大型项目中,不同库可能存在同名类,命名空间可以区分它们。

定义命名空间

基本语法

<?php
// 命名空间必须在文件顶部
namespace MyApp\Models;

class User {
public $name;
}
?>

子命名空间

<?php
namespace MyApp\Models\Auth;

class User {
// 完整类名:MyApp\Models\Auth\User
}
?>

多个命名空间

<?php
namespace MyApp\Models {
class User {
// ...
}
}

namespace MyApp\Controllers {
class UserController {
// ...
}
}

// 全局命名空间
namespace {
class GlobalClass {
// ...
}
}
?>

使用命名空间

完整类名

<?php
// 使用完整类名
$user = new \MyApp\Models\User();

// 调用静态方法
\MyApp\Models\User::find(1);

// 访问常量
echo \MyApp\Config\VERSION;
?>

use 导入

<?php
// 导入类
use MyApp\Models\User;

$user = new User();

// 导入并设置别名
use MyApp\Models\User as UserModel;
use MyApp\Controllers\User as UserController;

$model = new UserModel();
$controller = new UserController();

// 导入多个类
use MyApp\Models\{User, Post, Comment};

// 导入函数(PHP 5.6+)
use function MyApp\Utils\formatDate;

// 导入常量
use const MyApp\Config\VERSION;
?>

use 与命名空间组合

<?php
namespace MyApp\Controllers;

// 导入其他命名空间的类
use MyApp\Models\User;
use MyApp\Services\AuthService;

class UserController {
public function show($id) {
$user = User::find($id);
AuthService::check();
return $user;
}
}
?>

命名空间解析规则

相对命名空间

<?php
namespace MyApp\Controllers\Admin;

// 相对于当前命名空间
$user = new User(); // MyApp\Controllers\Admin\User

// 相对于根命名空间
$user = new \MyApp\Models\User();

// 相对于父命名空间
$user = new ..\Models\User(); // 错误!不支持

// 正确方式
use MyApp\Models\User;
$user = new User();
?>

解析优先级

<?php
namespace MyApp\Controllers;

use MyApp\Models\User;

class UserController {
public function test() {
// 1. 首先查找当前命名空间
// new User() -> MyApp\Controllers\User(如果存在)

// 2. 然后查找 use 导入
// new User() -> MyApp\Models\User(因为 use 导入)

// 3. 最后查找全局命名空间
// new \DateTime() -> 全局 DateTime 类
}
}
?>

自动加载

PSR-4 自动加载

PSR-4 是 PHP 标准推荐的自动加载规范,命名空间与目录结构对应。

项目结构:
src/
Models/
User.php
Post.php
Controllers/
UserController.php
Services/
AuthService.php
<?php
// src/Models/User.php
namespace MyApp\Models;

class User {
// ...
}
?>
<?php
// composer.json
{
"autoload": {
"psr-4": {
"MyApp\\": "src/"
}
}
}
?>
# 生成自动加载文件
composer dump-autoload
<?php
// 使用自动加载
require 'vendor/autoload.php';

use MyApp\Models\User;
use MyApp\Controllers\UserController;

$user = new User();
$controller = new UserController();
?>

自定义自动加载

<?php
spl_autoload_register(function ($class) {
// 将命名空间转换为文件路径
$prefix = 'MyApp\\';
$base_dir = __DIR__ . '/src/';

// 检查是否使用此命名空间前缀
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
return;
}

// 获取相对类名
$relative_class = substr($class, $len);

// 替换命名空间分隔符为目录分隔符
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';

// 加载文件
if (file_exists($file)) {
require $file;
}
});
?>

命名空间最佳实践

目录结构

project/
├── src/
│ ├── Controllers/
│ │ └── UserController.php
│ ├── Models/
│ │ └── User.php
│ ├── Services/
│ │ └── UserService.php
│ └── Helpers.php
├── tests/
├── vendor/
└── composer.json

命名规范

<?php
// 命名空间使用 PascalCase
namespace MyApp\Models;

// 类名使用 PascalCase
class UserProfile {}

// 常量使用 UPPER_CASE
const MAX_ITEMS = 100;

// 方法名使用 camelCase
public function getUserById($id) {}

// 变量名使用 camelCase 或 snake_case
$userName = "张三";
$user_name = "张三";
?>

一个文件一个类

<?php
// 推荐:一个文件只定义一个类
// src/Models/User.php
namespace MyApp\Models;

class User {
// ...
}
?>

命名空间与函数

<?php
namespace MyApp\Utils;

// 定义函数
function formatDate($timestamp) {
return date('Y-m-d', $timestamp);
}

// 定义常量
const DATE_FORMAT = 'Y-m-d';
?>
<?php
// 使用命名空间中的函数
use function MyApp\Utils\formatDate;
use const MyApp\Utils\DATE_FORMAT;

echo formatDate(time());
echo DATE_FORMAT;

// 或使用完整名称
echo \MyApp\Utils\formatDate(time());
?>

小结

本章我们学习了:

  1. 命名空间的定义和使用
  2. use 导入和别名
  3. 命名空间解析规则
  4. PSR-4 自动加载
  5. 命名空间最佳实践

下一章我们将学习异常处理。