zoukankan      html  css  js  c++  java
  • PHP命名空间

    前言

    命名空间不算新东西了,在PHP5.3.0之后就存在。曾经学次c#的时候接触过命名空间这个概念,后来发现php也出现,但是当时认为符号来使用命名空间很丑陋,一直不敢兴趣,现在我觉得符越来越好看,人的眼光总是在进步。

    命名空间是新时代PHP不可或缺的一部分,它是现代主流php框架的基石。

    什么是命名空间

    说白了,命名空间是解决文件、函数、类、常量等的重名问题,它虚拟了类似操作系统中文件系统的目录结构,保证PHP组件和框架的全局唯一。

    比如我在在a目录下有个db.php,在b目录下也有个db.php。那么按照古老的方式肯定是通过类名保持和文件一致来区别.

    例如a目录下的命名为:

    class A_DB{}

    b目录下的命名为:

    class B_DB{}

    编写函数时可能通过每次加上一个function_exist的判断去定义是否重名。
    常量的定义同理,都是可以有解决冲突的方案,但是现在composer盛行,PHP生态环境极好,各种组件层出不穷,我们不能保证所有组件的命名都不一样,并且这种通过文件名和类名来做区分十分不优雅,如果目录层级很深,那么类名可能会十分冗长。显然,我们应该选择更先进的方法。

    声明命名空间

    每个php文件的第一个命名空间必须声明在php的顶部,在<?php标签之后的第一行做声明。命名空间是通过namespace声明,然后跟上一个空格,再然后跟上命名空间的名称,最后用;符号结尾。

    例如,声明一个名称为A的命名空间:

    <?php
    namespace A;

    在这个命名空间后面的所有php类,函数,接口,常量都在这个A命名空间里面。

    同一个文件中定义多个命名空间

    我们先创建一个app目录:

    mkdir app

    命名空间是通过namespace来声明的,通过use关键字来引入的。
    创建a.php

    namespace A;
    
    const NUM = 1;
    function output()
    {
        echo "A output
    ";
    }
    
    namespace A2;
    
    const NUM = 2;
    function output(){
        echo "A2 output
    ";
    }
    
    //通过关键字use来引入命名空间
    use A;
    use A2;
    
    echo "NUM from A:" . ANUM . "
    ";
    echo "NUM from A2:" . A2NUM . "
    ";
    Aoutput();
    A2output();

    运行结果如下:

    NUM from A:1
    NUM from A2:2
    A output
    A2 output

    上面我们定义了两个命名空间,分别是A和A2,这两个命名空间下面有常量NUM,函数output,是的,故意命名成一模一样的,用来测试命名空间是否有效。这个时候我们通过use引入后,ANUM可以根据在A命名空间下找到它的NUM,等于1,同理A2NUM等于2,而output函数也是通过Aoutput(),A2output()找到各自的函数,并且输出各自的内容。

    我们初尝命名空间,知道用namespace定义命名空间,用use引入命名空间,通过命名空间常量,命名空间函数名调用。

    注意:命名空间的定义和文件名乃至目录结构没有任何关系,比如这里的namespace A完全可以换成namespace Apple,没有任何关系,只要调用正确即可。

    在同一个文件中声明不建议采用上述写法,建议用大括号包起来,如下:

    <?php
    namespace A {
    const NUM = 1;
    function output()
    {
        echo "A output
    ";
    }
    }
    
    namespace A2 {
    const NUM = 2;
    function output(){
        echo "A2 output
    ";
    }
    }
    
    namespace {
    use A;
    use A2;
    
    echo "NUM from A:" . ANUM . "
    ";
    echo "NUM from A2:" . A2NUM . "
    ";
    Aoutput();
    A2output();
    }

    以上代码输出的结果和上面是一样的,这种写法更佳。

    虽然php允许在一个php文件定义多个命名空间,但是违背了“一个文件定义一个类”的良好实践。一个文件定义一个类,只声明一个命名空间,这样更清晰简洁。

    在两个文件中使用命名空间

    创建a.php:

    <php
    namespace A;
    
    const NUM = 1;
    
    class DB
    {
        public function output()
        {
            echo "DB from A
    ";
        }
    }

    创建b.php:

    <?php
    namespace B;
    
    use A;
    
    const NUM = 2;
    
    class DB
    {
        public function output()
        {
            echo "DB from B
    ";
        }
    }
    
    include "a.php";//必须引入a.php,才能使用a.php中的命名空间
    echo "NUM from namespace A:" . ANUM . "
    ";
    echo "NUM from namespace B:" . NUM . "
    ";
    
    $aObject = new ADB();
    $aObject->output();
    
    $bObject = new DB();
    $bObject->output();

    然后执行b.php文件,输出如下:

    NUM from namespace A:1
    NUM from namespace B:2
    DB from A
    DB from B

    这次没有使用函数,通过类来实践,证明类在命名空间下也是有效的。
    常量不多说,它输出还是正常的,这里定义了同名的DB类,但是它们并没有冲突,输出了各自的内容。

    有人问为什么调用$bObject = new DB();为什么不需要写成$bObject = new BDB();,前面为什么不号也可以调用,因为我们执行的是b.php文件,当前代码是属于B这个命名空间的,默认已经加上了。

    我们可以尝试加上号,报错如下:

    PHP Fatal error: Uncaught Error: Class 'BBDB' not found in b.php

    所以,我们在当前命名空间是不需要加B的。

    全局空间

    如果没有定义任何命名空间,所有的类与函数的定义都是在全局空间。在名称前加上前缀 表示该名称是全局空间中的名称。

    创建comon.php

    <?php
    function test()
    {
        echo "test from common.php
    ";
    }

    创建a.php

    <php
    namespace A;
    
    function test()
    {
        echo "test from a.php
    ";
    }
    include "common.php";
    test();   //调用a.php
    	est(); //调用common.php

    执行a.php,结果下:

    test from a.php
    test from common.php

    其实php官方把开头带有符号的调用叫做完全限定名称,实际上就是当前整个作用于有效的,比如上面引入了common.php,里面有一个test(),那么就应该通过 est()调用。当然这是在重名下的调用,如果common中的test叫做test2,那么可以直接test2();调用,应为在A这个命名空间下没有和它冲突的函数名。

    注意:访问任意全局类、函数或常量,都可以使用完全限定名称,例如 strlen() 或 Exception 或 INI_ALL。

    例如:

    <?php
    namespace A;
    
    $str = "123";
    function strlen($string){
        return strlen($string) + 1;
    }
    echo strlen($str). "
    ";
    echo strlen($str) . "
    ";

    输出结果:

        3

        4

    显然当前a.php文件中strlen和系统函数strlen重名了,我们通过strlen($str)调用系统的函数, strlen($str)是调用命名空间A的函数,只不过它内部又是通过系统函数strlen实现的。

    命名空间的导入和别名

    在我们的实际项目中遇到的命名空间的层次会比较深,比如:

    <?php
    use AppComponentNetHttpResponsePostTool;
    //这里是伪代码
    $postobject = new HttpResponsePostTool('http://google.com');
    $postobject->go();

    这个HttpResponsePostTool很长,可以用as关键字定义别名:

    <?php
    use AppComponentNetHttpResponsePostTool as MyPost;
    //这里是伪代码
    $postobject = new MyPost('http://google.com');
    $postobject->go();

    这样是否优雅很多。

    多重导入

    虽然良好实践是在一个php文件中做一个命名空间的声明,但是这不妨碍我们导入多个命名空间,而且这是经常用的。

    php允许只使用一次use,通过逗号分割命名空间进行导入,最后一个命名空间加上分号,比如:

    <?php
    use    A,
           B,
           C;

    但是不建议这么写,不利于阅读,最好每行都使用use引入:

    <?php
    use A;
    use B;
    use C;

    了解以上,基本就能使用命名空间进行开发了。

    补充:

    命名空间的三种访问方式(和相对路径与绝对路径相似)
    A. 非限定名称访问方式
    B. 限定名称访问方式
    C. 完全限定名称访问方式

    <?php
    namespace appget1
    function getUser () {
      return $username1;
    }
    namespace get2
    function getUser () {
      return $username2;
    }
    
    getUser(); // 非限定名称访问方式
    appget1getUser(); //完全限定名称访问方式,从根路径开始,类似绝对路径
    appget1getUser(); //限定名称访问方式,不是从根路径开始,类似相对路径

    命名空间的引入机制

    1)空间的引入:关键字use,注意:当移入空间后,必须要用限定名称访问方式访问引入空间里面的函数(或类、常量),不能使用非限定名称方式访问,这样会访问到当前命名空间下的函数(或类、常量)。
    2)空间类元素的引入:关键字use。注意:只能引入类,然后可以使用非限定名称访问。

  • 相关阅读:
    数据库锁表处理汇总
    2021,顺其自然
    NetCore中跨域策略的一个坑
    Furion框架亮点之-动态WebAPI
    sql中where in的数量限制
    动态规划学习笔记
    用Go编写Web应用程序
    Asp.net Core AutoFac根据程序集实现依赖注入
    Linux+Docker+Gitee+Jenkins自动化部署.NET Core服务
    CentOS8.0安装Nacos
  • 原文地址:https://www.cnblogs.com/boundless-sky/p/6972269.html
Copyright © 2011-2022 走看看