🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# :-: 一、命名空间的作用 * 解决全局成员的命名冲突问题 * 全局成员: 类 / 函数 / 常量 ```php // 命名空间的作用与适用对象 require 'funciton.php'; function func($a, $b){ return $a.' + '.$b.' = '.($a+$b); } // 直接调用会出现函数未定义错误 echo func(10, 20); // 如果想访问外部加载的函数func,就要带上命名空间 echo \my\func(10, 20); // 而如果还想访问在当前脚本中定义的函数func,也要用命名空间访问 echo \func(10,20); ``` ```php // 1、使用较长的命名来进行区分 function my_func($a, $b) { return $a.' * '.$b.' = '.($a*$b); } // 2、使用命名空间, 不改变原来的函数名称 namespace my; function func($a, $b) { return $a.' * '.$b.' = '.($a*$b); } ``` * 命名空间解决了什么问题呢? * php 全局成员的命名冲突的问题 * 全局成员, 是指在当前脚本中,并不受使用域的限制,总是可以访问的 * php 中哪些是全局成员呢? 类, 函数, 常量 * 因为这三个成员, 不受作用域的限制, 所以无法像变量那样,用作用域对他们的可见性进行区隔 * 所以对于全局成员, 我们之前是通过一个很长的名称来进行区分, 例如: my_func, 难写难记 * 使用命名空间, 可以防止命名恐惧症, 在开发中可以使用相同名称的全局成员,只要放在不同的空间中即可 ***** # :-: 二、命名空间的定义 * `namespace`: 创建命名空间, 必须是脚本的第一行代码 * 在一个脚本中定义多个命名空间与成员(除全局空间) ```php namespace one; // 在one空间中定义三个全局成员 class Pig {} function hello(){ return 'Hello 欧阳克'; } const SITE = '白驼山庄'; // 访问成员 echo Pig::class . '<br>'; // 完整类名 echo hello() . '<br>'; echo SITE . '<hr>'; ``` > 两个命名空间 ```php namespace one; // 在one空间中定义三个全局成员 class Pig {} function hello(){ return 'Hello 欧阳克'; } const SITE = '白驼山庄'; // 访问成员 echo Pig::class . '<br>'; // 完整类名 echo hello() . '<br>'; echo SITE . '<hr>'; namespace two; class Pig {} function hello(){ return 'Hello 黄蓉'; } const SITE = '桃花岛'; echo Pig::class . '<br>'; // 完整类名 echo hello() . '<br>'; echo SITE . '<br>'; ``` > 访问另一个域名空间 ```php // 如果要在当前空间下面, 访问其它空间的成员, 例如one空间 // 与文件系统类似,从根空间开始,根空间: "\" echo '<br>'; echo \one\Pig::class . '<br>'; // 完整类名 echo \one\hello() . '<br>'; echo \one\SITE . '<hr>'; ``` * 尽管可以在一个脚本中, 可以声明多个命名空间,但并不推荐这样去做 * 使用本例的方法, 在同一个脚本中声明多个空间,但无法自定义根空间成员,只能调用 ***** # :-: 三、同时定义多个空间 * `namespace {}`: 用大括号在一个脚本中声明多个命名空间 ```php namespace one{ class Pig {} function hello(){ return 'Hello 欧阳克'; } const SITE = '白驼山庄'; // 访问成员 echo Pig::class . '<br>'; // 完整类名 echo hello() . '<br>'; echo SITE . '<hr>'; } namespace two{ class Pig {} function hello(){ return 'Hello 黄蓉'; } const SITE = '桃花岛'; echo Pig::class . '<br>'; // 完整类名 echo hello() . '<br>'; echo SITE . '<br>'; // 在空间two中访问one空间 echo '<br>'; echo \one\Pig::class . '<br>'; // 完整类名 echo \one\hello() . '<br>'; echo \one\SITE . '<hr>'; } // 定义全局空间, 空间名称为空,表示全局空间 namespace{ class Pig {} function hello(){ return 'Hello 郭靖'; } const SITE = '襄阳'; } namespace three{ // 调用全局成员 echo \Pig::class . '<br>'; // 完整类名 echo \hello() . '<br>'; echo \SITE; } ``` ***** # :-: 四、子命名空间 * `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>'; ``` ***** # :-: 五、空间的类文件自动加载技术 * `str_replace()`: 字符串替换函数,将空间分隔符替换成路径分隔符 * `DIRECTORY_SEPARATOR`: 路径分隔符常量 * `spl_autoload_register()`: 自动加载函数 ```php // 传统方式 require 'inc/Class1.php'; require 'inc/Class2.php'; $obj1 = new inc\Class1(); $obj2 = new inc\Class2(); echo get_class($obj1) . '<br>'; echo get_class($obj2) . '<br>'; echo '<hr>'; ``` > Class1.php ```php <?php namespace inc; class Class1 { } ``` > Class2.php ```php <?php namespace inc; class Class2 { } ``` > 自动加载技术 ```php spl_autoload_register(function ($class){ // 这里将"\"替换成路径分隔符, 推荐使用常量:DIRECTORY_SEPARATOR,而不是"/",可苑跨平台支持 $path = str_replace('\\', DIRECTORY_SEPARATOR, $class); // 相对路径 // $path = $path . '.php'; // 绝对路径 $path = __DIR__ . '/' . $path . '.php'; // 不是文件或文件不存在,则抛出异常 if (!(is_file($path) && file_exists($path))) { throw new \Exception('不是文件或文件不存在'); } require $path; }); $obj1 = new inc\Class1(); $obj2 = new inc\Class2(); echo get_class($obj1) . '<br>'; echo get_class($obj2) . '<br>'; ``` ***** # :-: 六、空间别名 * `use namespace/className`: 通过`use`关键字导入空间别名 * 为较长的类名提供一种简化方案 * 导入空间别名默认从全局空间开始 * 导入空间别名, 不能代替外部文件的加载 * 注意很多框架采用了自动加载技术,会导致一些同学误以后use可以加载类 * `as`: 解决导入的类别名与当前空间类重名问题 ```php namespace current; include 'inc/Class1.php'; // 如果要使用Class1类,需要先实例化 $obj = new \inc\Class1(); echo get_class($obj) . '<br>'; echo '<hr>'; # 你会发现,这样的类名有点长了, 实际开发过程中, 比这个长的多的是 # 如何来简化呢? 使用别名导入 use \inc\Class1 AS C1; // 现在就简单多了,其实使用use导入类别名时, 默认就是根空间(全局)开始 // 所以 "\"可以省略, 实际上也不推荐加上 // use \inc\Class1 AS C1; // 如果当前脚本中没有与导入的类名冲突的类, 就没必要用AS 起一个别名了 class Class1{}; // 此时的类的默认别名, 就是原来的名字: Class1 // use \inc\Class1; // $obj = new C1(); $obj = new Class1(); echo get_class($obj) . '<br>'; ``` ***** # :-: 七、命名空间实战 ```php namespace db; // 连接数据库: 目前在空间中写代码, PDO类中全局中, 建议加上"\" $pdo = new \PDO('mysql:host=localhost;dbname=ouyangke','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>"; } ```