php命名空间


一、命名空间

1、命名空间的作用

命名空间的作用与适用对象
    require 'inc/funciton.php';
    function func1($a,$b){
        return $a.' + '.$b.' = '.($a+$b);
    }
    // 直接调用会出现函数未定义错误
    echo fun1(10, 20);
    echo '<hr>';
    // 如果想访问外部加载的函数func1,就要带上命名空间
    echo \my\func1(10, 20); // 做的乘法
    echo '<hr>';
    // 而如果还想访问在当前脚本中定义的函数func1,也要用命名空间访问
    echo \func1(10,20); // 加法
    // 命名空间解决了什么问题呢?
    // php 全局成员的命名冲突的问题
    // 全局成员, 是指在当前脚本中,并不受使用域的限制,总是可以访问的
    // php 中哪些是全局成员呢? 类, 函数, 常量
    // 因为这三个成员, 不受作用域的限制, 所以无法像变量那样,用作用域对他们的可见性进行区隔
    // 所以对于全局成员, 我们之前是通过一个很长的名称来进行区分, 例如: my_func1, 难写难记
    // 使用命名空间, 可以防止命名恐惧症, 在开发中可以使用相同名称的全局成员,只要放在不同的空间中即可

    // 例如, 合肥有条长江路, 上海也有一条长江路, 在合肥提到长江路, 不会有人想到这是上海的长江路,这是因
    // 为我们用城市名称, 做了区隔, 因为这二条路有不同的命名空间, 分别是合肥和上海

    // 类似电脑中的文件, 例如有二个index.php文件,只要他们不在同一个目录下,就不会有命名冲突
    // admin/index.php 和  home/index.php , 尽管文件同名,但用户不会认为这是同一个文件
    // 引用的时候,必须带上他们所在的路径即可
funciton.php示例
    // 使用较长的命名来进行区分
    function my_func1($a, $b){
        return $a.' * '.$b.' = '.($a*$b);
    }
    // 使用命名空间, 不改变原来的函数名称
    namespace my;
    function func1($a, $b){
        return $a.' * '.$b.' = '.($a*$b);
    }

2、命名空间的定义

    // 定义空间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>';

    // 尽管可以在一个脚本中, 可以声明多个命名空间,但并不推荐这样去做
    // 使用本例的方法, 在同一个脚本中声明多个空间,但无法自定义根空间成员,只能调用

    // 如何才能在一个脚本中, 既可以自定义命名空间, 也可以自定义根空间成员呢?
    // 使用下个案例的大括号可以解决

3、同时定义多个空间

    namespace one{
        class Pig {}
        function hello(){ return 'Hello 朱老师'; }
        const SITE = 'php.cn';

        // 访问成员
        echo Pig::class . '<br>';   // 完整类名
        echo hello() . '<br>';
        echo SITE . '<hr>';
    }

    namespace two{
        class Pig {}
        function hello(){ return 'Hello 猪哥'; }
        const SITE = 'php中文网';

        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;
    }

4、子命名空间

    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编程中的类的自动加载技术就靠它撑着呢,框架没有它, 难以想像
    // 多层级的命名空间,非常像多层级的目录结构,如果类名称中的空间部分与类文件的绝对路径一致,就可以实现
    // 类文件的全自动加载,并且不会千万命名冲突,因为类名本身仍是带有命名空间的

二、命名空间实用

class1示例
    namespace code\inc;
    class Class1{

    }
class2示例
    namespace code\inc;
    class Class2{

    }

1、带空间的类文件自动加载技术

传统方式
    require 'inc/class1.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>';

2、自动加载

    $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 = $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>'; //code\inc\Class1
    echo get_class($obj2) . '<br>'; //code\inc\Class2

3、空间别名

允许通过别名引用或导入外部的完全限定名称

index.php示例
    namespace current;
    include 'inc/class1.php';
    // 如果要使用Class1类,需要先实例化
    $obj = new \inc\class1();
    $obj = use \inc\Class1 AS C1; // 别名,解决重名
    echo get_class($obj) . '<br>';
    echo '<hr>';