一、概述
1、场景:
我们在电脑上的文件夹中创建文件文件时,文件名称不能是重复的,否则将不能创建;这是为了保证我们的资源访问标识是唯一的,当我们通过资源访问路径访问我们的文件时能定位到是哪个文件;那么我们如果需要创建相同的文件名文件时,我们就需要先创建文件夹,然后将我们的文件放置其中,这里的文件夹作用就是命名空间的理解。 ?
2、主要为解决以下问题
1、用户编写的代码与PHP源码或者第三方的源码(类、函数、常量)之间的名字冲突。 2、为了解决很长的标识符名称(一般由于第一个问题,定义的名称),提高代码的可读性 所以PHP的命名空间,提供了一种将相关的类、方法、常量等组合在一起的方式 ?
注意:
- 命名空间名称大小写不敏感
- 名为PHP的命名空间,以及这些名字开头的命名空间(例如:PHP\Classes)被保留作为语言的内核使用,而不应该在用户空间的代码中使用。
?
3、命名空间的定义
php命名空间只会对:类(含抽象类,triat)、接口、函数、常量产生影响。 声明命名空间的关键字:namespace ?
注意:
- 须在除declare之前其他语句前声明,否则无效;
- 完全限定名称(以反斜杠开头的名称),不能用于命名空间的声明。因为该结构会解析成相对命名空间的表达式;
- 声明命名空间之前唯一合法的代码是用于定义文件编码方式的declare语句;
- 同一个命名空间可以定义在不同的文件中(这个和其他语言的特征不太相同),即允许同一个命名空间的内容被放在不同的文件中。
<?php
declare(encoding='UTF-8');
namespace MyNameSpace;
?
4、子命名空间的定义
与目录和文件的关系很像,PHP命名空间允许指定层次化的命名空间的名称,因此,命名空间可以使用分层次的方式定义。
<?php
namespace MyProject\Sub\Level;
const STATU = 'ok';
class Person {}
function connect () {}
类: MyProject\Sub\Level\Person
方法:MyProject\Sub\Level\connect
?
5、同一个文件中定义多个命名空间
PHP可以在同一个文件中定义不同的命名空间,但是一般情况下不建议这样做 定义方式:
<?php
namespace Myproject;
const STATU = 'ok';
class Person{}
function connect() {}
namespace Otherproject;
const STATU = 'ok';
class Person{}
function connect(){}
<?php
namespace Myproject {
const STATU = 'ok';
class Person{}
function connect() {}
}
namespace Otherproject {
const STATU = 'ok';
class Person{}
function connect() {}
}
如果真的需要在同一个文件中创建不同的命名空间,建议使用第二种方式。 ?
?
6、将非命名空间的代码和命名空间的代码放在同同一个文件中
如果存在将非命名空间的代码和命名空间的代码放在一个文件中的情况时,我们需要将非命名空间的代码用namespace和大括号包裹起来
<?php
namespace Myproject {
const STATU = 'ok'
class Person {}
function connect() {}
}
namespace {
$a = MyProject\connect();
echo MyProject\Connection::start();
}
7、命名空间的使用:
和访问文件夹中的文件类似,我们知道文件所处的路径存在相对路径和绝对路径,如下面的三种方式:
- 相对文件名形式:foo.txt, 如果当前目录是project,将会被解析成project/foo.txt;如果当前目录是home/project,将会被解析成home/project/foo.txt;
- 相对路径形式:project/foo.txt, 如果当前目录为current,将会被解析成:current/project/foo.txt
- 绝对路径形式:/home/project/foo.txt,将会被解析成/home/project/foo.txt
?
类比后:
- 非限定名称,或不包含前缀的类名:例如:
a
=
n
e
w
F
o
o
(
)
,
如
果
当
前
命
名
空
间
为
M
y
p
r
o
j
e
c
t
,
将
会
被
解
析
成
a = new Foo(),如果当前命名空间为Myproject,将会被解析成
a=newFoo(),如果当前命名空间为Myproject,将会被解析成a = new Myproject/Foo()
- 限定名称,或包含前缀的名称:例如:KaTeX parse error: Undefined control sequence: \Foo at position 16: a = new Subject\?F?o?o?(),如果当前命名空间为Mai…a = Main\Subject\Foo()
- 完全限定名称,或包含了全局操作符的名称:例如$a = new \Main\Subject\Foo()
注意:
- 如果该命名空间中不存在调用的函数或常量,将会被解析到全局空间中的同名函数或者常量上。
- 任意全局的类、函数、常量,都可以使用全局限定名称,例如:\strlen()、\Exception等。
8、namespace关键字和__NAMESPCE__常量的使用
PHP支持使用两种抽象方式来访问命名空间中的元素:
- 魔术常量**NAMESPCE**:它的值是包含当前命名空间名称的字符串,在全局的,不包含任何命名空间的代码下,它将是一个空的字符串
- namespace关键字:它用来显示当前命名空间或子命名空间中的元素,类似类中的self
<?php
namespace Myproject;
echo __NAMESPACE__;
<?php
echo __NAMESPACE__;
<?php
namespace Myproject;
function dynamic($classname) {
$A = __NAMESPACE__.'\\'.$classname;
return new $A()
}
<?php
use blah\blah as mine;
blah\mine();
namespace\blah\mine();
9、导入与别名
php允许通过别名引用和导入外部的完全限定名称,这是命名空间重要的特征。 可以导入和设置别名的有:常量、函数、类、接口、命名空间,别名通过use的方式
<?php
namespace foo;
use My\Full\Classname as Another;
use My\Full\NSname;
use ArrayObject;
use function My\Full\functionName;
use function My\Full\functionName as func;
use const My\Full\CONSTANT;
$obj = new namespace\Another;
$obj = new Another;
NSname\subns\func();
$a = new ArrayObject(array(1));
func();
echo CONSTANT;
?>
对命名空间中的名称来说,前导的反斜杠是不必要的也不推荐的,因为导入的名称必须是完全限定的,不会根据当前的命名空间作相对解析(use 后的命名空间不用加上) ?
- 通过use导入\使用别名,可以在一行中包含多个use语句
- 导入是在编译时执行的,动态的类、函数、常量则不是
- 导入操作只会影响非限定名称和限定名称,对于非限定名称(已经确定了)没有影响
- use语句必须放在文件最外层中使用(全局作用域中)或者命名空间声明内,不能放在块级作用域中(因为发生在编译时,而非运行时)
- 导入规则是独立于每个文件的,意味着包含的文件不会继承父文件的导入规则
- use可以声明编组,通过单个use语句可以将来自同一个namespace的类、函数、常量一起编组导入(有点javasript中的结构的意思)
<?php
use MyProject\Test as Mt, OtherProject\Test as Ot;
?>
<?php
use My\Full\Classname as Another, My\Full\NSname;
$obj = new Another;
$a = 'Another';
$obj = new $a;
?>
<?php
use My\Full\Classname as Another, My\Full\NSname;
$obj = new Another;
$obj = new \Another;
$obj = new Another\thing;
$obj = new \Another\thing;
?>
<?php
use some\namespace\ClassA;
use some\namespace\ClassB;
use some\namespace\ClassC as C;
use function some\namespace\fn_a;
use function some\namespace\fn_b;
use function some\namespace\fn_c;
use const some\namespace\ConstA;
use const some\namespace\ConstB;
use const some\namespace\ConstC;
use some\namespace\{ClassA, ClassB, ClassC as C};
use function some\namespace\{fn_a, fn_b, fn_c};
use const some\namespace\{ConstA, ConstB, ConstC};
?>
10、全局空间
如果没有定义全局空间,即名称定义在全局空间中,只需要在名称前加上/就好,在其他命名空间中使用全局空间类、函数、常量也是如此
<?php
namespace A\B\C;
function fopen() {
$f = \fopen(...);
return $f;
}
?>
11、命名空间名称解析规则
命名空间名称定义 非限定名称(Unqualified name) 名称中不包含命名空间分隔符的标识符,例如 Foo 限定名称(Qualified name) 名称中含有命名空间分隔符的标识符,例如 Foo\Bar 完全限定名称(Fully qualified name) 名称中包含命名空间分隔符,并以命名空间分隔符开始的标识符,例如 \Foo\Bar。 namespace\Foo 也是一个完全限定名称。 相对名称(Relative name) 这是个以 namespace 开头的标识符, 例如 namespace\Foo\Bar。 ?
名称解析遵循下列规则:
- 完全限定名称总是会解析成没有前缀符号的命名空间名称。 \A\B 解析为 A\B。
- 解析相对名称时,会用当前命名空间的名称替换掉 namespace。 如果名称出现在全局命名空间,会截掉 namespace\ 前缀。 例如,在命名空间 X\Y 里的 namespace\A 会被解析成 X\Y\A。 在全局命名空间里,同样的名字却被解析成 A。
- 对于限定名称,名字的第一段会根据当前 class/namespace 导入表进行翻译。 比如命名空间 A\B\C 被导入为 C, 名称 C\D\E 会被翻译成 A\B\C\D\E。
- 对于限定名称,如果没有应用导入规则,就将当前命名空间添加为名称的前缀。 例如,位于命名空间 A\B 内的名称 C\D\E 会解析成 A\B\C\D\E。
- 根据符号类型和对应的当前导入表,解析非限定名称。 这也就是说,根据 class/namespace 导入表翻译类名称; 根据函数导入表翻译函数名称; 根据常量导入表翻译常量名称。 比如,在 use A\B\C; 后,类似 new C() 这样的名称会解析为 A\B\C()。 类似的,use function A\B\fn; 后, fn() 的用法,解析名称为 A\B\fn。
- 如果没有应用导入规则,对于类似 class 符号的非限定名称,会添加当前命名空间作为前缀。 比如命名空间 A\B 内的 new C() 会把名称解析为 A\B\C。
- 如果没有应用导入规则,非限定名称指向函数或常量,且代码位于全局命名空间之外,则会在运行时解析名称。 假设代码位于命名空间 A\B 中, 下面演示了调用函数 foo() 是如何解析的:
- 在当前命名空间中查找函数: A\B\foo()。
- 它会尝试找到并调用 全局 的函数 foo()。
12、其他
|