转载于:黑马程序员武汉中心
命名空间
学习目标:理解命名空间的概念,掌握命名空间存在的价值以及解决的实际问题,利用命名空间实现复杂项目开发
- 命名空间基础
- 子空间
- 命名空间访问
- 全局空间
- 命名空间引入
概念
命名空间:namespace,指人为的将内存进行分隔
,让不同内存区域的同名结构共存,从而解决在大型项目中可能出现的重名结构问题
- PHP中,同一脚本运行周期内不允许出现同名结构
- 常量
- 函数
- 类(接口)
- 在大型项目中,除了使用复杂的命名方式,很难避免团队间使用同名结构
- 命名空间可以通过给各类命名增加
路径
来实现不同名字的“本质不同”,从而允许同名存在
示例
在Windows系统下:同一个磁盘上不允许出现同名文件,但是可以通过路径的方式(文件夹)实现同名文件存
index.php
index(1).php # 系统不允许同名,所以会重命名
但是我们可以通过不同的路径和逻辑分区来实现同名文件共同存在
C:/index.php
D:/index.php
D:/server/web/index.php # 可以同时存在:路径本身可以理解为文件的名字(三个都不同)
- 命名空间就是类似上述的操作,在同一块内存里进行
逻辑分区
实现同名结构的同时存在
小结
1、命名空间是为了解决结构同名问题而出现的一种解决方案
2、命名空间本质是在内存中进行逻辑划分,让相同的名字分到不同的区域,从而实现不同名
1、命名空间基础
目标:了解命名空间的基本语法,掌握命名空间的限制逻辑和基本应用
概念
命名空间基础:命名空间的基本语法和作用
- 命名空间使用namespace关键字定义空间
namespace 命名空间名字;
- 命名空间必须定义在所有代码之前(前面不能有任何代码性的内容,注释除外)
- 命名空间后可以写任意内容,但是只有命名空间元素真正受空间限制
- 命名空间元素是结构性内容(不允许重复的内容),有三类:
- 常量
- 函数
- 类(接口)
步骤
1、确定使用命名空间:有一些结构性的内容可能与其他开发者冲突(重名)
2、在一开始定义命名空间
示例
1、基本语法:namespace 空间名字;
# 定义空间
namespace my_space; # 定义一个叫做my_space的空间
2、命名空间的命名规则
- 由字母、下划线和数字构成
- 可以以字母和下划线开头
- 较少出现多单词空间名,一般使用下划线法
3、命名空间的作用:能够创建同名结构,包含函数、常量和类
namespace space1;
function display(){
echo __NAMESPACE__,'<br/>';
}
const PI = 3;
class Human{}
namespace space2;
function display(){
echo __NAMESPACE__,'<br/>';
}
const PI = 3.14;
class Human{}
4、命名空间里的内容
- 命名空间里可以定义同名的函数、常量和类(结构):因为此类结构不允许同名,这些是命名空间规范的目标(称为空间元素)
- 命名空间里可以有其他代码
namespace space;
class Human{}
function display(){}
const PI = 3.14;
$a = 100;
echo $a;
5、命名空间注意事项:命名空间的声明(第一次)必须在所有代码之前
# 命名空间之前不能有任何代码
namespace space1; # 正确
echo 'test';
namespace space1; # 错误:第一次命名空间之前不能有任何其他代码
注意:命名空间在一个脚本中只会定义一个(最开始),但是在讲课的时候可能会定义多个
小结
1、命名空间是使用namespace + 空间名字定义
2、不同命名空间里可以定义同名的函数、常量和类(同名结构)
3、命名空间里可以书写任意代码
4、命名空间的定义必须在脚本的最前面
5、一个脚本中通常只会定义一个空间
6、命名空间其实就好比是磁盘上划分的不同文件夹,用来保存同名文件
2、命名空间子空间
目标:理解子空间的概念,掌握子空间的创建规范
概念
子空间:subspace,即在已有空间之上,再在内部进行空间划分,让每个小空间独立起来。
- 子空间其实就是路径的分层
- 子空间可以基于已有空间创建,也可以直接创建,使用反斜杠分隔
\
# 基于已有父空间
namespace father;
namespace father\son;
# 直接创建子空间
namespace mother\daughter;
示例
1、命名空间子空间是直接通过namespace+路径符号\
实现
namespace space; # 创建一个一级空间
function display(){}
# 创建子空间
namespace space\space1; # 在space空间下创建一个叫做space1的子空间
function display(){}
2、子空间的创建不一定非要在前面创建了上级空间,即可以直接在某个脚本中创建子空间
# 脚本最上面
namespace space\space2;
function display(){}
小结
1、子空间也是通过namespace实现,用namespace+\
区分上下级空间名
2、基于一个脚本中通常只有一个空间名,所以子空间的创建可以直接创建
- 不用一定先创建一级空间
- 但是一般是存在上级空间的,而不是凭空创建子空间
3、子空间理论上可以创建无限多层,但是实际层次根据项目需求确定(一般不超过四层)
3、命名空间访问
目标:掌握命名空间的元素调用方式,能够用合适的方式调用空间元素
概念
命名空间访问:是指访问不同空间里的结构元素
- 空间里有除了函数、常量和类的其他代码,会自动执行,只有空间元素本身(函数、常量和类)是需要通过空间进行访问的
- 在PHP命名空间中,提供了三种空间元素的访问方式
非限定名称
:直接访问元素名,系统自动匹配当前空间(向上找到的第一个空间)限定名称
:带空间名字,而且是基于当前空间的子空间(相对路径,在当前空间下,找到向下的空间的)完全限定名称
:从全局空间(根目录)开始访问(绝对路径)
示例
1、非限定名称访问:即直接访问空间元素的名字,此类访问访问的是当前代码所属空间内的元素
namespace space1;
function display(){
echo 'space1';
}
namespace space2;
function display(){
echo 'space2';
}
# 非限定名称访问
display(); # 输出space2,因为当前display函数调用所属空间为space2
注意:非限定名称访问就好比是访问当前自己文件夹下的所有文件
2、限定名称访问,即在访问元素的前面使用相应的空间名字,非限定名称的访问是基于子空间来实现的
# 定义子空间
namespace space\space1;
function display(){
echo 'space\space1<br/>';
}
# 定义子空间
namespace space\space2;
function display(){
echo 'space\space2<br/>';
}
# 所属父空间
namespace space;
function display(){
echo 'space<br/>';
}
# 非限定名称访问
display(); # space:当前向上所属空间
space1\display(); # space\space1:实际为当前空间space + space1\display()
注意:限定名称访问好比访问当前文件夹下的子文件夹内容(针对当前空间的子空间访问)
3、完全限定名称访问,即从根目录(全局空间)开始访问,使用\
作为全局空间开始符号
# 接上述代码
# 完全限定名称访问
\space\display(); # space空间下的display
\space\space1\display(); # space下space1空间的display
注意:完全限定名称访问好比从磁盘根目录访问对应路径下的内容(绝对路径)
小结
1、命名空间的访问分为三种模式
- 非限定名称访问,直接访问元素本身,代表当前所属空间(当前目录)
- 限定名称访问,使用空间名+元素,代表访问当前空间子空间(当前目录子目录)
- 完全限定名称访问,使用全局空间开始,代表从全局开始进行访问(根目录)
2、空间元素访问方式选择
- 当前空间内,一般使用
非限定名称
访问 - 不在当前空间,一般使用
完全限定名称
访问 - 在实际开发中:我们会对项目进行深度封装处理,尽量让开发者可以使用非限定名称访问(开发效率高)
4、全局空间
目标:了解全局空间的概念,掌握全局空间与其他命名空间的关系以及合作模式
概念
全局空间:即空间元素在没有定义空间的情况下所属的空间,也是所有定义的空间的顶级空间
- 所有空间都是从全局空间分离出来的
- 所有不指定空间的元素本质都属于全局空间
- 全局空间与其他空间存在空间约束关系,在进行文件包含时需要注意到空间关系
示例
1、没有指定空间的元素所属的空间属于全局空间
# 不定义空间
function display(){
echo __NAMESPACE__,'<br/>';
}
2、所有的空间本质都是在全局空间下的划分
# 定义空间
namespace space;
function display(){
echo __NAMESPACE__,'<br/>';
}
# space空间属于从全局空间里划分出一部分用于space空间管理
3、全局空间元素的访问:使用完全限定名称访问
# 不定义空间
function display(){
echo __NAMESPACE__,'<br/>';
}
display(); # 非限定名称访问:本身当前就是全局空间内,所以可以访问
\display(); # 完全限定名称访问:全局符号"\"+全局空间元素
4、一旦命名空间出现,那么空间元素(类、常量和函数)的访问就被限定在空间内,如果使用非限定名称访问,那么系统会以下解析逻辑(限定名称或者完全限定名称是直接按路径准确找)
- 首先一定是在自己空间内查找
- 如果找不到元素,不同空间元素的处理不同
- 系统常量、系统函数如果找不到,会自动去全局空间找(也就是能找到)
- 系统类是不会自动去全局空间找的(报错,提示当前所属空间内元素找不到)
# 定义空间
namespace space;
function display(){
echo __FUNCTION__,'<br/>';
}
# 当前所有访问如果使用非限定名称都代表访问当前空间内的元素
display(); # space下的display函数
# 想访问函数
define('PI',3.14); # 正确:space下没有define函数,但是全局空间有(系统函数属于全局空间)
# 访问系统常量
echo PHP_VERSION; # 正确:space下没有,但全局空间有
# 想访问类
# $m = new Mysqli('localhost','root','root');
# 错误:系统提示space\Mysqli不存在
# 正确方案
$m = new \Mysqli('localhost','root','root');
5、同样的,如果一个文件有空间,包含了一个没有空间(全局空间)的文件,那么要访问文件中的内容,需要使用全局空间
# 无空间文件:nospace.php
function display(){ # 属于全局空间
echo __FUNCTION__;
}
# 有空间文件
namespace space();
function display(){
echo 'space';
}
# 包含无空间文件
include_once 'nospace.php';
# 访问元素
display(); # 访问的是space空间下的display函数
\display(); # 正确:访问全局空间的display函数
# 注意:如果space空间没有display的话,直接访问display函数也是正确的,因为系统会自动寻找全局空间
小结
1、全局空间就是没有使用namespace定义空间的空间(所有空间本质都是在全局空间下划分)
2、全局空间的元素访问使用完全限定名称访问
3、系统内置的函数、常量和类都属于全局空间(涉及到全局空间被包含到子空间里)
- 系统函数、常量,在空间内访问的时候系统会自动在自己空间找,如果找不到会去全局空间
- 系统类必须使用全局空间访问:\类名
5、命名空间应用
目标:根据实际开发需求,设定合适的命名空间进行管理
概念
命名空间应用:模拟真实的开发环境,来运用命名空间的规则
- 命名空间分层一般是文件夹进行分层(建议使用与文件夹同名空间名)
- 文件夹的分层是根据业务的需求和项目开发所选的复杂程度来设计
步骤
1、确定项目开发复杂程度
2、实现文件分层管理布局
3、针对文件分层进行命名空间的使用(一个文件只属于一个空间)
示例
1、创建文件夹:模拟项目不同文件PHP文件放到不同文件夹下
–|root --------根目录
–|--|controller --------业务模块
–|--|model --------数据模块
–|--|core --------核心工具
2、业务说明
- root根目录,存放用户可以直接访问的文件,文件都是请求controller里的文件
- controller目录,存放业务逻辑文件,所有业务都是类文件,业务要操作数据库,请求model里的文件,属于controller空间
- model目录,存放数据库操作的类文件,一张表一个类文件,属于model空间
- core目录,核心工具的存放,属于core空间
3、创建3个文件:分表代表root目录下(不需要空间),controller目录下,controller空间,model目录下的model空间
# root目录下:index.php
# 啥都不用做,直接包含controller文件
include_once 'controller/User.php';
# $u = new User(); # 错误:当前空间没有User类
$u = new controller\User(); # 限定名称访问:因为controller空间属于全局空间
$u->display();
# root/core目录下:DB.php
namespace core;
class DB{
private $link;
public function __construct(){
# 数据库初始化
}
# 简单效果:查询全部数据
public function getAll($sql){
return '数据查询结果<br>';
}
}
# root/model目录下:User.php
namespace model;
# 加载DB类
include_once '../DB.php'; # 建议使用绝对路径:../和./都会因为嵌套包含而错误
class User{
public function getAllUsers(){
# 假设数据库连接、数据库、表都已经存在
$sql = "select * from user";
# 调用更高级的操作类实现SQL执行并返回结果:DB属于Core空间,使用完全限定名称访问
$db = new \Core\DB();
return $db->getAll($sql);
}
}
# root/controller目录下:User.php
namespace controller;
class User{
public function display(){
# 调用模型目录下的user类实现数据库操作:使用完全限定名称访问
include_once '../Model/User.php'; # 建议使用绝对路径:../和./都会因为嵌套包含而错误
$u = new \Model\User();
$users = $u->getAllUsers();
var_dump($users);
}
}
4、代码说明
- index.php在root目录下,没有定义空间,内部元素属于全局空间:index.php包含了子目录controller下的User.php,而User类属于controller空间,所以在index.php中访问User类的使用,可以使用限定名称(全局空间的子空间controller\空间元素),或者完全限定名称(\子空间\空间元素)
- controller/User.php在root/controller文件夹下,定义了空间controller,所以文件里面所有的访问,默认都是在controller下找。controller/User类中用到了model/User类,所以需要使用完全限定名称访问(同级别不同空间)\model\User
- model/User.php在root/model文件夹下,定义了空间model,所以文件里所有的访问,默认都是在model下找。model/User类中用到了core/DB类,所以需要使用完全限定名称访问\core\DB
- core/DB.php在root/core文件夹下,定义了空间core
小结
1、空间的实际应用是以文件为单位定义空间的
2、空间的划分是按业务对应的脚本进行划分的,如业务controller,数据model之类
3、文件的包含和空间的包含没有联系,二者是独立的:文件是在加载文件时,而空间是在进入内存后
4、空间应用,通常是采用非限定名称(自己空间里)和完全限定名称访问(其他空间),实际上我们建议将完全限定名称转换成非限定名称
6、命名空间引入
目标:理解空间引入的逻辑和优势,掌握空间引入
概念
命名空间引入:是将另外一个空间的元素引入到当前空间来,当做当前空间的元素访问
- 空间元素引入可以减少复杂的完全限定名称访问,取而代之的是非限定名称访问
- 可以快速的实现将当前需要使用的空间元素引入到当前文件
- 空间引入语法:use关键字
- 元素关键字:默认是类,可以有function(函数)和const(常量)
use [元素关键字] [\]空间名\元素名; # 空间名是一级空间
- 别名:引入的元素与当前空间已有元素重名或者想换成其他合适的名字
- 使用别名后,原来的名字在当前就不可用,只能使用别名
use [元素关键字] 空间名\元素名 as 别名;
- 空间引入:可以一次性引入一个空间的所有元素,或者某个空间指定类型的多个元素
- 空间引入是将被引入的空间的最后一级空间,当做当前空间的子空间
- 类引入不需要class关键字(默认就是class)
# 引入多个元素
use [元素关键字] 空间名\元素名1 [as 别名],空间名\元素名2 ...; # 空间名可以不同,但是元素类型必须相同
# 引入空间(全部元素)
use 空间名; # 将当前引入的空间(最后一级)当前当前所在空间的子空间
示例
1、空间引入方式:use关键字
namespace space;
class Man{}
namespace space1;
# 引入空间元素
use space\Man;
new Man();
注意:use进行空间包含的时候,默认是从全局空间开始构建空间路径的(不是自己空间的相对路径),所以上述代码等价于以下代码
namespace space;
class Man{}
namespace space1;
# 引入空间元素
use \space\Man;
2、空间引入的元素默认是类,如果要引入其他元素,就必须使用相应关键字:function和const
namespace space;
function display(){}
class Man{}
const PI = 3.14;
namespace space1;
# 引入空间元素
use function space\display; # 引入函数
use space\Man; # 引入类
use const space\PI; # 引入常量
display();
new Man();
echo PI;
3、如果被引入的元素在当前空间已经存在,则会出现重名,解决方案是使用别名 as alias
namespace space;
function display(){}
class Man{}
const PI = 3.14;
namespace space1;
class Man{}
# 引入空间元素
# use space\Man; # 错误:当前空间已经存在Man
use space\Man as M;
use function space\display as dis;
use const space\PI as D;
4、一旦引入的时候使用了别名,那么在使用的时候就直接通过别名使用
namespace space;
function display(){}
class Man{}
namespace space1;
class Man{}
# 引入空间元素
use space\Man as M;
new M(); # 使用别名
5、如果一个空间有多个元素要引入,那么可以进行一次引入多个,使用逗号,
分隔即可
namespace space;
function display(){}
class Man{}
class Woman{}
namespace space1;
class Man{}
# 一次引入多个
use space\Man as M,space\Woman; # 虽然可以批量引入(不同空间也行),但是每个元素都需要带上空间
# 引入了一个Man类别名为M,Woman类没有定义别名
6、如果说确定一个空间里的所有元素都需要引入进来,也可以直接引入空间
namespace space;
class Man{}
namespace space1;
# 引入空间
use space;
7、如果是直接进行空间引入,非限定名称的访问访问的只会是当前自己空间而非引入的空间,要访问引入空间,得使用限定名称访问:即引入的空间最后一级空间名字+元素(引入空间当做当前空间的子空间)
namespace space\space1\space2;
class Man{}
namespace space3;
class Man{}
# 引入空间
use space\space1\space2;
new Man(); # 访问的是space3\Man
new space2\Man(); # 使用引入空间的最后一级空间访问
小结
1、空间引入是解决访问时的麻烦:由完全限定名称(限定名称)变成非限定名称访问
2、空间元素都可以引入,但是引入方式有区别
- 类直接引入
- 函数需要在use之后跟关键字function
- 常量需要在use之后跟关键字const
3、空间引入过程中如果出现重名,需要使用别名来处理,引入后在空间里可以直接访问别名
4、可以一次性引入多个空间内的多个元素(类型相同:每个元素必须在前面加上空间路径)
5、如果必要的情况下,也可以直接使用空间引入,但是注意被引入空间的元素不允许直接使用非限定名称访问,必须使用被引入空间的最后一级空间+元素访问(不常使用,引入方便但是使用不方便)
7、总结
1、命名空间namespace不是PHP特有的,是很多编程语言都有的用来解决结构同名的方案
2、命名空间一般只针对某个具体的结构(类和函数,较少针对常量)
3、命名空间的存在会让元素的访问变得不那么直接,我们需要选择合适的方式来调用
- 非限定名称访问
- 限定名称访问
- 完全限定名称访问
4、成熟的大中型项目都会使用命名空间,命名空间的命名规范通常与项目的文件夹名字同名
- 方便后期内部加工处理:根据空间来选择文件所在路径(自动加载)
- 方便维护:根据空间来找到文件进行维护