命名空间
1. 非限定名称, 限定名称和完全限定名称
非限定名称
- 从当前空间中访问当前空间中的成员, 可以省略当前空间名称. 即, 非限定名称: 成员之前的命名空间可省略
- 不能有”\”, 可以理解成文件当前路径.
完全限定名称
- 当需要访问其他命名空间下的成员时, 需要使用完全限定名称, 类似全局路径.
- 必须以”\”开始.
非完全限定名称
- 在成员前面出现了空间名称, 但是并不是从根空间开始, 就是非完全限定名称
- 在命名空间之间有层级关系时, 如上级空间访问下级空间的成员时使用非完全限定名称.
- 至少要有一个”\“, 且不能在首位, 可以理解成相对路径
全局成员的四大家族: 类, 接口, 函数和常量.
namespace ns1 {
require('../../out.php');
class Demo1
{
public static $prop1 = 'hello';
public static function func1()
{
return __METHOD__;
}
}
// 在当前命名空间中使用本命名空间的成员, 可以省略当前空间名称, 即使用非限定名称即可.
/* Demo1::$prop1 等效于 \ns1\Demo1::$prop1 */
echobr(Demo1::$prop1);
/* Demo1::func1() 等效于 \ns1\Demo1::func1() */
echobr(Demo1::func1());
/* result:
hello
ns1\Demo1::func1
*/
}
namespace ns2\ns2_1 {
const PI = 3.14;
class Demo1
{
public static $prop = 'hi';
public static function func1()
{
return __METHOD__;
}
}
}
namespace ns2 {
// 访问其他命名空间中的成员, 需要使用完全限定名称
echobr(\ns1\Demo1::$prop1);
echobr(\ns1\Demo1::func1());
/* result:
hello
ns1\Demo1::func1
*/
// 访问具有层级关系的下级命名空间中的成员时, 可以使用非完全限定名称
echobr(ns2_1\Demo1::$prop);
echobr(ns2_1\Demo1::func1());
echobr(ns2_1\PI);
/* result:
hi
ns2\ns2_1\Demo1::func1
3.14
*/
}
2. 创建和使用命名空间
2.1 创建命名空间
- 在同一个脚本中可以创建多个命名空间
- 获取当前命名空间, 可以使用
__NAMESPACE__
. - 使用
namespace
关键字创建命名空间 创建命名空间的语法
创建语法 1:
namespace 命名空间名称;
// do something...
这种方式只能创建命名空间, 不能创建匿名空间. 匿名空间就是默认空间, 根空间, 全局空间.
/* 创建命名空间 */
namespace ns1;
// do something...
/* 这样创建匿名空间会报错 */
namespace ;
// can not do something...
创建语法 2:
namespace 命名空间名称 {
// do something...
}
这种方式可以创建命名空间和匿名空间
/* 创建命名空间 */
namespace ns1 {
// do something...
}
/* 创建全局空间 */
namespace {
// 这里是全局空间
}
2.2 使用命名空间
- 对于函数和常量来说, 如果当前命名空间中不存在该函数或常量, PHP 会退而使用全局空间中的函数或常量.
- 类名称总是解析到当前命名空间中的名称. 因此在访问系统内部或不包含在命名空间中的类名称时, 必须使用完全限定名称.
/* 全局空间 */
namespace {
$username = '张三';
function sayHello($username)
{
echobr('hello ' . $username);
}
class Demo
{
public static $prop = 'hello';
}
}
/* 非全局命名空间 */
namespace np1 {
// 当前命名空间中并没有$username变量, PHP会到全局空间中去找该变量
echobr($username);
/* result: 张三 */
// 跟变量类似
sayHello('James');
/* result: hello James */
// 当前控件没有Demo类, 但PHP也不会到全局空间中查找, 直接报错
echobr(Demo::$props);
/* result: Fatal error: Uncaught Error: Class 'np1\Demo' not found in D:\phpstudy_pro\WWW\php11\PHP\0505\homework\homework.php:112 Stack trace: #0 {main} thrown in D:\phpstudy_pro\WWW\php11\PHP\0505\homework\homework.php on line 112 */
}
/* 命名空间中有跟根空间同名的成员 */
namespace np2 {
CONST NAME = 'lisi';
function sayHello($username) {
echobr('hi ' . $username);
}
class Demo {
public static $prop = 'hi';
}
// 当前命名空间中已有相关成员的情况
/* 使用当前命名空间中的常量 */
echobr(NAME);
/* 使用根空间中的同名常量 */
echobr(\NAME);
/* result:
lisi
全局
*/
/* 使用当前命名空间的函数 */
sayHello('Marry');
/* 使用根空间中的同名函数 */
\sayHello('Lily');
/* result:
hi Marry
hello Lily
*/
/* 使用当前命名空间中的类 */
echobr(Demo::$prop);
/* 使用根空间中的同名类 */
echobr(\Demo::$prop);
/* result:
hi
hello
*/
}
2.3 命名空间的别名
- 在使用其他命名空间的成员时, 为简化完全限定名称的书写, 可以为使用的其他命名空间起别名.
- 也可以为其他命名空间中的成员起别名。当别名跟成员名称相同时,可以省略别名。
- 使用
use
关键字来声明空间别名.use
默认从根空间开始. 使用as
关键字来指定空间/成员别名。 声明语法:
/* 为名称空间起别名 */
use 命名空间名称 as 别名名称;
/* 为名称空间的成员其别名 */
/* 1. 类 */
use 省略第一个\的完全限定名称类名/非完全限定名称类名 as 别名名称;
/* 或者 */
use 省略第一个\的完全限定名称类名/非完全限定名称类名;
/* 2. 函数 */
use function ... as ...;
/* 或者 */
use function ...;
/* 3. 常量 */
use constant ... as ...;
/* 或者 */
use constant ...;
示例:
namespace ns1\test1 {
class Demo {
public static $username = '张三';
}
class Demo1 {
public staitc $username = '李四';
}
}
namespace ns2 {
// 给命名空间起别名
use ns1\test1 as nt1;
// 给命名空间中的类起别名
use ns1\test1\Demo1 as Demo1;
// 当别名跟类名相同时, 可以省略别名
use ns1\test1\Demo;
// 使用别名简化调用
echobr('姓名: ' . nt1\Demo1::$username);
/* result:
李四
*/
// 给类起别名
echobr('姓名: ' . Demo1::$username);
echobr('姓名: ' . Demo::$username);
/* result:
姓名: 李四
姓名: 张三
*/
}
3. 类的自动加载
实现类自动加载的条件
命名空间必须跟类文件的绝对路径一一对应。
当前类的名称必须跟当前文件的名称完全一致。
类自动加载的目的是要省略使用
require
引入各种文件。
一般把实现类自动加载的函数用系统函数 spl_autoload_register()函数进行注册。当代码中使用到未被加载的类时,PHP 会自动调用由 spl_autoload_register()函数注册的函数去尝试加载这个类。
类自动加载示例:
- autoload.php: 根据实现类自动加载的条件实现的自动加载函数,并使用 spl_autoload_register()函数进行注册。
// autoload.php
// 封装自动加载器
try {
// 系统函数: spl_autoload_register(): 注册执行加载类的函数
spl_autoload_register(function ($className) {
// 1. 将类名中的反斜线改为当前系统中的目录分隔符
$path = str_replace('\\', DIRECTORY_SEPARATOR, $className);
// echobr($path);
// 2. 生成真正要加载的类文件名称
$file = __DIR__ . DIRECTORY_SEPARATOR . $path . '.php';
// echobr($file);
// 3. 加载这个文件
require $file;
});
} catch (Exception $e) {
die($e->getMessage());
}
- Demo1.php: 待加载的类文件
// Demo1.php
namespace tool;
class Demo1 {
public static function func1() {
return __METHOD__;
}
public static function echobr($str = '') {
echo $str . '<br>';
}
}
- work.php: 需要加载类实现业务逻辑的类
namespace work;
// 1. 加载自动加载器文件
require('autoload.php');
// 2. 使用use为待加载的类起别名
use tool\Demo1;
// 3. 使用待加载的类
Demo1::echobr(Demo1::func1());
/* 成功执行需加载类的方法,即自动加载生效。
result: tool\Demo1::func1 */
4. 学习心得
- 命名空间和三种限定名称,还是很容易理解的,可以认为是程序员把各种成员按照功能进行“归档”,当要用到这些成员时,顺着归档路径去加载即可。限定名称,是指在归档路径中的某个文件,要找到另外一个成员时,相对于该文件,或相对于归档根目录增加的路径。
- 为命名空间或成员起别名,是为了简化使用其他命名空间的成员时的调用,或防止使用不同命名空间中的相同名字的成员时引起的冲突。
- 类自动加载,在实际编写业务逻辑时较少用到,一般应用于框架的底层实现,是为了简化一些繁琐的文件或成员引用过程(如老师上课举的例子是为了简化加载类文件的引用过程)。