php命名空间

一、概述

1、场景:

我们在电脑上的文件夹中创建文件文件时,文件名称不能是重复的,否则将不能创建;这是为了保证我们的资源访问标识是唯一的,当我们通过资源访问路径访问我们的文件时能定位到是哪个文件;那么我们如果需要创建相同的文件名文件时,我们就需要先创建文件夹,然后将我们的文件放置其中,这里的文件夹作用就是命名空间的理解。

2、主要为解决以下问题

1、用户编写的代码与PHP源码或者第三方的源码(类、函数、常量)之间的名字冲突。
2、为了解决很长的标识符名称(一般由于第一个问题,定义的名称),提高代码的可读性
所以PHP的命名空间,提供了一种将相关的类、方法、常量等组合在一起的方式

注意:

  • 命名空间名称大小写不敏感
  • 名为PHP的命名空间,以及这些名字开头的命名空间(例如:PHP\Classes)被保留作为语言的内核使用,而不应该在用户空间的代码中使用。

3、命名空间的定义

php命名空间只会对:类(含抽象类,triat)、接口、函数、常量产生影响。
声明命名空间的关键字:namespace

注意:

  • 须在除declare之前其他语句前声明,否则无效;
  • 完全限定名称(以反斜杠开头的名称),不能用于命名空间的声明。因为该结构会解析成相对命名空间的表达式;
  • 声明命名空间之前唯一合法的代码是用于定义文件编码方式的declare语句;
  • 同一个命名空间可以定义在不同的文件中(这个和其他语言的特征不太相同),即允许同一个命名空间的内容被放在不同的文件中。
<?php
  declare(encoding='UTF-8'); // 命名空间之前的代码如果存在,只能时declare
  namespace MyNameSpace;

4、子命名空间的定义

与目录和文件的关系很像,PHP命名空间允许指定层次化的命名空间的名称,因此,命名空间可以使用分层次的方式定义。

<?php
  namespace MyProject\Sub\Level;
  
  const STATU = 'ok';
  class Person {}
	function connect () {}

// 这里定义了常量:MyProject\Sub\Level\STATU: 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
// __NAMESPACE__
<?php
  namespace Myproject;
	echo __NAMESPACE__; // 'Myproject' 

<?php
  echo __NAMESPACE__; // ''

<?php
  namespace Myproject;
	
	function dynamic($classname) {
  	$A = __NAMESPACE__.'\\'.$classname;
		return new $A()
  }

// namespace
<?php
  use blah\blah as mine;

	blah\mine(); // 调用函数 MyProject\blah\mine()
	namespace\blah\mine(); // 调用函数 MyProject\blah\mine()
9、导入与别名

php允许通过别名引用和导入外部的完全限定名称,这是命名空间重要的特征。
可以导入和设置别名的有:常量、函数、类、接口、命名空间,别名通过use的方式

<?php
  namespace foo;
  use My\Full\Classname as Another;

  // 下面的例子与 use My\Full\NSname as NSname 相同
  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; // 实例化 foo\Another 对象
  $obj = new Another; // 实例化 My\Full\Classname 对象
  NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func
  $a = new ArrayObject(array(1)); // 实例化 ArrayObject 对象
  // 如果不使用 "use \ArrayObject" ,则实例化一个 foo\ArrayObject 对象
  func(); // 调用函数 My\Full\functionName
  echo CONSTANT; // 输出 My\Full\CONSTANT 的值
?> 
	

对命名空间中的名称来说,前导的反斜杠是不必要的也不推荐的,因为导入的名称必须是完全限定的,不会根据当前的命名空间作相对解析(use 后的命名空间不用加上)

  • 通过use导入\使用别名,可以在一行中包含多个use语句
  • 导入是在编译时执行的,动态的类、函数、常量则不是
  • 导入操作只会影响非限定名称和限定名称,对于非限定名称(已经确定了)没有影响
  • use语句必须放在文件最外层中使用(全局作用域中)或者命名空间声明内,不能放在块级作用域中(因为发生在编译时,而非运行时)
  • 导入规则是独立于每个文件的,意味着包含的文件不会继承父文件的导入规则
  • use可以声明编组,通过单个use语句可以将来自同一个namespace的类、函数、常量一起编组导入(有点javasript中的结构的意思)
//一行包含多个use 语句
<?php
  use MyProject\Test as Mt, OtherProject\Test as Ot;
?>

// 不会影响动态的类、函数、常量
<?php
  use My\Full\Classname as Another, My\Full\NSname;

  $obj = new Another; // 实例化一个 My\Full\Classname 对象
  $a = 'Another';
  $obj = new $a;      // 实际化一个 Another 对象
?>
  
// 只会影响非限定名称、限定名称,对完全限定名称没有影响
<?php
  use My\Full\Classname as Another, My\Full\NSname;

  $obj = new Another; // class My\Full\Classname 的实例对象
  $obj = new \Another; // class Another 的实例对象
  $obj = new Another\thing; // class My\Full\Classname\thing 的实例对象
  $obj = new \Another\thing; // class Another\thing 的实例对象
?>
  
// use 编组导入 
<?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 声明
  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;

  /* 这个函数是 A\B\C\fopen */
  function fopen() { 
       /* ... */
       $f = \fopen(...); // 调用全局的fopen函数
       return $f;
  } 
?> 
11、命名空间名称解析规则

命名空间名称定义
非限定名称(Unqualified name)
名称中不包含命名空间分隔符的标识符,例如 Foo
限定名称(Qualified name)
名称中含有命名空间分隔符的标识符,例如 Foo\Bar
完全限定名称(Fully qualified name)
名称中包含命名空间分隔符,并以命名空间分隔符开始的标识符,例如 \Foo\Bar。 namespace\Foo 也是一个完全限定名称。
相对名称(Relative name)
这是个以 namespace 开头的标识符, 例如 namespace\Foo\Bar。

名称解析遵循下列规则:

  1. 完全限定名称总是会解析成没有前缀符号的命名空间名称。 \A\B 解析为 A\B。
  2. 解析相对名称时,会用当前命名空间的名称替换掉 namespace。 如果名称出现在全局命名空间,会截掉 namespace\ 前缀。 例如,在命名空间 X\Y 里的 namespace\A 会被解析成 X\Y\A。 在全局命名空间里,同样的名字却被解析成 A。
  3. 对于限定名称,名字的第一段会根据当前 class/namespace 导入表进行翻译。 比如命名空间 A\B\C 被导入为 C, 名称 C\D\E 会被翻译成 A\B\C\D\E。
  4. 对于限定名称,如果没有应用导入规则,就将当前命名空间添加为名称的前缀。 例如,位于命名空间 A\B 内的名称 C\D\E 会解析成 A\B\C\D\E。
  5. 根据符号类型和对应的当前导入表,解析非限定名称。 这也就是说,根据 class/namespace 导入表翻译类名称; 根据函数导入表翻译函数名称; 根据常量导入表翻译常量名称。 比如,在 use A\B\C; 后,类似 new C() 这样的名称会解析为 A\B\C()。 类似的,use function A\B\fn; 后, fn() 的用法,解析名称为 A\B\fn。
  6. 如果没有应用导入规则,对于类似 class 符号的非限定名称,会添加当前命名空间作为前缀。 比如命名空间 A\B 内的 new C() 会把名称解析为 A\B\C。
  7. 如果没有应用导入规则,非限定名称指向函数或常量,且代码位于全局命名空间之外,则会在运行时解析名称。 假设代码位于命名空间 A\B 中, 下面演示了调用函数 foo() 是如何解析的:
  8. 在当前命名空间中查找函数: A\B\foo()。
  9. 它会尝试找到并调用 全局 的函数 foo()。
12、其他
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值