1. 命名空间的作用
解决全局成员的命名冲突问题
全局成员: 类 / 函数 / 常量
demo1.php
<?php // 命名空间的作用与适用对象 require 'inc/funciton.php'; function func1($a, $b) { return $a.' + '.$b.' = '.($a+$b); } // 直接调用会出现函数未定义错误 //echo fun1(10, 20); // 如果想访问外部加载的函数func1,就要带上命名空间 echo \my\func1(10, 20); // 做的乘法 echo '<hr>'; // 而如果还想访问在当前脚本中定义的函数func1,也要用命名空间访问 echo \func1(10,20); // 加法
inc/funciton.php
<?php // 使用命名空间, 不改变原来的函数名称 namespace my; function func1($a, $b) { return $a.' * '.$b.' = '.($a*$b); }
2.命名空间的定义
namespace: 创建命名空间, 必须是脚本的第一行代码
在一个脚本中定义多个命名空间与成员(除全局空间)
<?php // 定义空间one namespace one; // 在one空间中定义三个全局成员 class Pig {} function hello(){ return 'Hello 朱老师'; } const SITE = 'php.cn'; // 访问成员 echo Pig::class . '<br>'; // 完整类名 echo hello() . '<br>'; echo SITE . '<hr>'; /*************************************************/ // 定义命名空间: two namespace two{ class Pig {} function hello(){ return 'Hello 猪哥'; } const SITE = 'php中文网'; echo Pig::class . '<br>'; // 完整类名 echo hello() . '<br>'; echo SITE . '<br>'; // 如果要在当前空间下面, 访问其它空间的成员, 例如one空间 // 与文件系统类似,从根空间开始,根空间: "\" echo '<br>'; echo \one\Pig::class . '<br>'; // 完整类名 echo \one\hello() . '<br>'; echo \one\SITE . '<hr>'; } // 尽管可以在一个脚本中, 可以声明多个命名空间,但并不推荐这样去做 // 使用本例的方法, 在同一个脚本中声明多个空间,但无法自定义根空间成员,只能调用 //还能使用namespaace{}命名多个命名空间
3.子命名空间
namespace: 引用当前命名空间
__NAMESPACE__: 当前空间名称字符串(魔术常量)
one\two\three\...\ClassName: 类空间的分层管理
<?php // 子命名空间 // 命名空间是可以分层管理的 namespace think; class Dog {} echo Dog::class . '<hr>'; // 双下划线开头的魔术常量, 所谓魔术是指,尽管是常量,但可以随作用域发生变化 echo __NAMESPACE__ . '<br>'; namespace think\admin; echo __NAMESPACE__ . '<br>'; class Dog {} echo Dog::class . '<hr>'; // 如果我想访问空间:think\admin\model\Dog类 // 可以将当前空间看成当前目录,用关键字namespace来引用当前空间 echo namespace\model\Dog::class . '<hr>'; namespace think\admin\model; echo __NAMESPACE__ . '<br>'; class Dog {} echo Dog::class . '<hr>'; // "\"是命名空间分隔符, 将空间分层有什么卵用呢? // 作用非常大, 现代PHP编程中的类的自动加载技术就靠它撑着呢,框架没有它, 难以想像 // 多层级的命名空间,非常像多层级的目录结构,如果类名称中的空间部分与类文件的绝对路径一致,就可以实现 // 类文件的全自动加载,并且不会千万命名冲突,因为类名本身仍是带有命名空间的
4.带空间的类文件自动加载技术
str_replace(): 字符串替换函数,将空间分隔符替换成路径分隔符
DIRECTORY_SEPARATOR: 路径分隔符常量
spl_autoload_register(): 自动加载函数
<?php // 传统方式 //require 'inc/Class122.php'; //require 'inc/Class2.php'; // //$obj1 = new \code\inc\Class1(); //$obj2 = new \code\inc\Class2(); // //echo get_class($obj1) . '<br>'; //code\inc\Class1 //echo get_class($obj2) . '<br>'; //code\inc\Class2 // //echo '<hr>'; /**********************************************************************/ // 由于类空间名称与类文件所在路径一致 // 可以通过解析类空间名称实现自动加载 // 测试 $path = str_replace('\\', '/', 'code\inc\Class1'); echo $path . '<br>'; $path = __DIR__ . '/../' . $path . '.php'; echo $path . '<br>'; echo '<hr>'; spl_autoload_register(function ($class){ // 这里将"\"替换成路径分隔符, 推荐使用常量:DIRECTORY_SEPARATOR,而不是"/",可苑跨平台支持 $path = str_replace('\\', DIRECTORY_SEPARATOR, $class); $path = __DIR__ . '/../' . $path . '.php'; // 不是文件或文件不存在,则抛出异常 if (!(is_file($path) && file_exists($path))) { throw new \Exception('不是文件或文件不存在'); } require $path; }); $obj1 = new \code\inc\Class1(); $obj2 = new \code\inc\Class2(); echo get_class($obj1) . '<br>'; //code\inc\Class1 echo get_class($obj2) . '<br>'; //code\inc\Class2
5. 空间别名
use namespace/className: 通过use关键字导入空间别名
为较长的类名提供一种简化方案
导入空间别名默认从全局空间开始
导入空间别名, 不能代替外部文件的加载
注意很多框架采用了自动加载技术,会导致一些同学误以为use可以加载类
use: 解决类名过长的问题
as: 解决导入的类别名与当前空间类重名问题
<?php // 允许通过别名引用或导入外部的完全限定名称 namespace current; include 'inc/Class1.php'; // 如果要使用Class1类,需要先实例化 $obj = new \code\inc\Class1(); echo get_class($obj) . '<br>'; echo '<hr>'; // 你会发现,这样的类名有点长了, 实际开发过程中, 比这个长的多的是 // 如果来简化呢? 使用别名导入 //use \code\inc\Class1 AS C1; // 现在就简单多了,其实使用use导入类别名时, 默认就是根空间(全局)开始 // 所以 "\"可以省略, 实际上也不推荐加上 //use \code\inc\Class1 AS C1; // 如果当前脚本中没有与导入的类名冲突的类, 就没必要用AS 起一个别名了 //class Class1{}, // 此时的类的默认别名, 就是原来的名字: Class1 use code\inc\Class1; //$obj = new C1(); $obj = new Class1(); echo get_class($obj) . '<br>'; echo '<hr>';
案例:
<?php // 命名空间实战 namespace db; // 连接数据库: 目前在空间中写代码, PDO类中全局中, 建议加上"\" $pdo = new \PDO('mysql:host=localhost;dbname=php','root','root'); // 如果PDO类前不加"\"也不会报错, 因为系统在当前空间中没有找到,会自动转到全局中查找, 为提升查询效率,强烈建议加上 //查询点数据展示出来,以测试数据库操作正确 $stmt = $pdo->prepare('SELECT `id`,`name`,`position` FROM `staff` LIMIT :offset, :num'); $stmt->bindValue('offset',0, \PDO::PARAM_INT); $stmt->bindValue('num',5, \PDO::PARAM_INT); $stmt->execute(); // 将结果集中的列绑定到变量 $stmt->bindColumn('id', $id); $stmt->bindColumn('name', $name); $stmt->bindColumn('position', $position); // 遍历 while ($stmt->fetch(\PDO::FETCH_BOUND)) { echo "<li>{$id}. {$name} : {$position}</li>"; }
点击 "运行实例" 按钮查看在线实例