命名空间是PHP中一种封装事物的方法,类似于文件系统的目录结构。它可以解决两类问题:
默认情况下,所有常量、类和函数名都放在全局空间下。命名空间通过关键字 namespace 来声明,必须在文件所有代码之前声明(除了declare语句)。
基本语法:
<?php
// 定义代码在 'MyProject' 命名空间中
namespace MyProject;
const PROJECT_NAME = 'My Application';
class Database {
public function connect() {
return "Connected to database";
}
}
function logMessage($message) {
echo "Log: " . $message;
}
// 使用当前命名空间中的元素
$db = new Database();
echo $db->connect();
logMessage("Application started");
?>
在同一个文件中定义多个命名空间:
<?php
namespace MyProject1;
const VERSION = '1.0';
class User {
public function getName() {
return "User from MyProject1";
}
}
namespace MyProject2;
const VERSION = '2.0';
class User {
public function getName() {
return "User from MyProject2";
}
}
// 使用大括号语法定义命名空间
namespace MyProject3 {
class Logger {
public function log($msg) {
echo "MyProject3 Logger: " . $msg;
}
}
}
// 全局命名空间
namespace {
// 可以在这里编写不在任何命名空间中的代码
$user1 = new MyProject1\User();
$user2 = new MyProject2\User();
echo $user1->getName() . "\n";
echo $user2->getName() . "\n";
echo "MyProject1 Version: " . MyProject1\VERSION . "\n";
}
?>
正确的声明位置:
<?php
declare(encoding='UTF-8'); // 在命名空间之前唯一合法的代码
namespace MyApp;
class Application {
// 类代码
}
?>
错误的声明位置:
<html>
<?php
namespace MyProject; // 致命错误 - 命名空间前出现了HTML代码
?>
PHP命名空间支持层次化结构,类似于文件系统的目录结构。
<?php
namespace MyCompany\Project\Utils;
class StringHelper {
public static function truncate($string, $length) {
if (strlen($string) > $length) {
return substr($string, 0, $length) . '...';
}
return $string;
}
}
namespace MyCompany\Project\Database;
class Connection {
public static function getConnection() {
return "Database connection established";
}
}
// 使用子命名空间
$helper = new \MyCompany\Project\Utils\StringHelper();
$db = new \MyCompany\Project\Database\Connection();
echo $helper->truncate("This is a long text", 10) . "\n";
echo $db->getConnection() . "\n";
?>
PHP命名空间中的类名可以通过三种方式引用:
| 类型 | 语法 | 解析方式 |
|---|---|---|
| 非限定名称 | $a = new Foo(); |
解析为当前命名空间下的 Foo |
| 限定名称 | $a = new Sub\Foo(); |
解析为当前命名空间下的 Sub\Foo |
| 完全限定名称 | $a = new \Full\Path\Foo(); |
总是解析为指定的完全路径 |
完整示例:
file1.php 文件代码:
<?php
namespace Foo\Bar\subnamespace;
const FOO = 1;
function foo() {
echo "Function foo from subnamespace\n";
}
class Foo
{
static function staticMethod() {
echo "Static method from Foo\Bar\subnamespace\Foo\n";
}
}
?>
file2.php 文件代码:
<?php
namespace Foo\Bar;
include 'file1.php';
const FOO = 2;
function foo() {
echo "Function foo from Foo\Bar\n";
}
class Foo
{
static function staticMethod() {
echo "Static method from Foo\Bar\Foo\n";
}
}
/* 非限定名称 */
foo(); // 解析为 Foo\Bar\foo
Foo::staticMethod(); // 解析为类 Foo\Bar\Foo 的静态方法
echo FOO; // 解析为常量 Foo\Bar\FOO
echo "\n--- 限定名称 ---\n";
/* 限定名称 */
subnamespace\foo(); // 解析为函数 Foo\Bar\subnamespace\foo
subnamespace\Foo::staticMethod(); // 解析为类 Foo\Bar\subnamespace\Foo
echo subnamespace\FOO; // 解析为常量 Foo\Bar\subnamespace\FOO
echo "\n--- 完全限定名称 ---\n";
/* 完全限定名称 */
\Foo\Bar\foo(); // 解析为函数 Foo\Bar\foo
\Foo\Bar\Foo::staticMethod(); // 解析为类 Foo\Bar\Foo
echo \Foo\Bar\FOO; // 解析为常量 Foo\Bar\FOO
?>
访问全局函数和类:
<?php
namespace MyApp;
// 在命名空间内定义同名函数和类
function strlen($str) {
return "Custom strlen: " . \strlen($str);
}
class Exception extends \Exception {
public function __construct($message) {
parent::__construct("MyApp Exception: " . $message);
}
}
// 访问全局函数和类
$length = \strlen('hello'); // 调用全局函数strlen
$globalException = new \Exception('Global exception'); // 实例化全局类Exception
$customException = new Exception('Custom exception'); // 实例化当前命名空间的Exception
echo strlen('test') . "\n";
echo $length . "\n";
?>
在动态访问元素时,必须使用完全限定名称。
<?php
namespace MyCompany\Utils;
class Logger {
function __construct() {
echo __METHOD__ . "\n";
}
}
function helper() {
echo __FUNCTION__ . "\n";
}
const APP_NAME = "MyApp";
// 动态访问
$classname = 'MyCompany\Utils\Logger';
$obj = new $classname; // 输出 MyCompany\Utils\Logger::__construct
$function = 'MyCompany\Utils\helper';
$function(); // 输出 MyCompany\Utils\helper
echo constant('MyCompany\Utils\APP_NAME') . "\n"; // 输出 MyApp
// 使用双引号时需要注意转义
$classname = "\\MyCompany\\Utils\\Logger";
$obj2 = new $classname;
?>
PHP提供了两种访问当前命名空间的方式:__NAMESPACE__魔术常量和namespace关键字。
<?php
namespace MyProject\Sub;
echo '当前命名空间: "' . __NAMESPACE__ . '"' . "\n"; // 输出 "MyProject\Sub"
class Example {
public function showNamespace() {
echo '类所在的命名空间: ' . __NAMESPACE__ . "\n";
}
}
// 使用namespace关键字
$obj = new namespace\Example(); // 相当于 new MyProject\Sub\Example
$obj->showNamespace();
// 动态创建类名
$className = __NAMESPACE__ . '\\Example';
$obj2 = new $className();
$obj2->showNamespace();
?>
全局命名空间中的__NAMESPACE__:
<?php
// 全局代码,不在任何命名空间中
echo '当前命名空间: "' . __NAMESPACE__ . '"' . "\n"; // 输出空字符串
class GlobalClass {
public function test() {
echo '全局类方法';
}
}
?>
PHP支持通过use关键字为类或命名空间创建别名。
基本导入示例:
<?php
namespace MyApp;
use MyCompany\Project\Utils\StringHelper as Helper;
use MyCompany\Project\Database\Connection; // 不使用别名
use \ArrayObject; // 导入全局类
$helper = new Helper();
echo $helper->truncate("Long text here", 8) . "\n";
$conn = new Connection();
echo $conn->getConnection() . "\n";
$arrayObj = new ArrayObject([1, 2, 3]); // 使用全局ArrayObject类
print_r($arrayObj);
?>
一行多个use语句:
<?php
namespace MyApp;
use MyCompany\Project\Utils\StringHelper as Helper,
MyCompany\Project\Database\Connection as DB;
$helper = new Helper();
$db = new DB();
?>
导入和动态名称:
<?php
namespace MyApp;
use MyCompany\Project\Utils\StringHelper as Helper;
$obj = new Helper(); // 正常工作
$class = 'Helper';
$obj2 = new $class(); // 错误!动态名称不受use影响
// 正确的方式
$fullClass = 'MyCompany\Project\Utils\StringHelper';
$obj3 = new $fullClass(); // 正常工作
?>
在一个命名空间中,当 PHP 遇到一个非限定的类、函数或常量名称时,它使用不同的优先策略来解析该名称。类名称总是解析到当前命名空间中的名称。因此在访问系统内部或不包含在命名空间中的类名称时,必须使用完全限定名称,例如:
1、在命名空间中访问全局类
<?php
namespace A\B\C;
class Exception extends \Exception {}
$a = new Exception('hi'); // $a 是类 A\B\C\Exception 的一个对象
$b = new \Exception('hi'); // $b 是类 Exception 的一个对象
$c = new ArrayObject; // 致命错误, 找不到 A\B\C\ArrayObject 类
?>
对于函数和常量来说,如果当前命名空间中不存在该函数或常量,PHP 会退而使用全局空间中的函数或常量。
2、 命名空间中后备的全局函数/常量
<?php
namespace A\B\C;
const E_ERROR = 45;
function strlen($str)
{
return \strlen($str) - 1;
}
echo E_ERROR, "\n"; // 输出 "45"
echo INI_ALL, "\n"; // 输出 "7" - 使用全局常量 INI_ALL
echo strlen('hi'), "\n"; // 输出 "1"
if (is_array('hi')) { // 输出 "is not array"
echo "is array\n";
} else {
echo "is not array\n";
}
?>
类名称解析:
<?php
namespace A\B\C;
class Exception extends \Exception {
// 自定义异常类
}
// 在当前命名空间中查找类
$a = new Exception('hi'); // 使用 A\B\C\Exception
// 使用完全限定名称访问全局类
$b = new \Exception('hi'); // 使用全局Exception类
// 错误示例 - 在当前命名空间查找ArrayObject
// $c = new ArrayObject(); // 致命错误,找不到A\B\C\ArrayObject
// 正确方式
$c = new \ArrayObject(); // 使用全局ArrayObject类
?>
函数和常量解析:
<?php
namespace MyApp\Utils;
const DEBUG = true;
function strlen($str) {
return "Custom: " . \strlen($str);
}
echo strlen('hello') . "\n"; // 使用当前命名空间的strlen函数
echo \strlen('hello') . "\n"; // 使用全局strlen函数
echo DEBUG . "\n"; // 使用当前命名空间的DEBUG常量
echo \PHP_VERSION . "\n"; // 使用全局PHP_VERSION常量
if (\defined('MyApp\Utils\DEBUG')) {
echo "DEBUG constant is defined\n";
}
?>
所有没有定义在命名空间中的代码都在全局空间中。
<?php
// 全局空间
class GlobalClass {
public function test() {
return "Global class method";
}
}
function globalFunction() {
return "Global function";
}
namespace MyApp;
class LocalClass {
public function test() {
// 访问全局类
$global = new \GlobalClass();
return $global->test();
}
}
$local = new LocalClass();
echo $local->test() . "\n";
// 调用全局函数
echo \globalFunction() . "\n";
?>
自动加载与命名空间:
<?php
// 自动加载函数
spl_autoload_register(function ($class) {
// 将命名空间中的反斜线转换为目录分隔符
$file = __DIR__ . '/' . str_replace('\\', '/', $class) . '.php';
if (file_exists($file)) {
require $file;
}
});
// 现在可以自动加载任何符合PSR-4标准的类
use MyCompany\Project\Controllers\UserController;
use MyCompany\Project\Models\User;
$controller = new UserController();
$user = new User();
?>
组织大型项目结构:
<?php
namespace MyApp\Controllers;
use MyApp\Models\User;
use MyApp\Services\EmailService;
use MyApp\Utils\Validator;
class UserController {
private $userModel;
private $emailService;
public function __construct() {
$this->userModel = new User();
$this->emailService = new EmailService();
}
public function register($userData) {
if (Validator::validateEmail($userData['email'])) {
$userId = $this->userModel->create($userData);
$this->emailService->sendWelcomeEmail($userData['email']);
return $userId;
}
return false;
}
}
?>