PHP定名空间(Namespace)初探

    添加时间:2013-5-4 点击量:

    探完闭包[查看],再探定名空间。


    对于定名空间,官方文档已经说得很具体[查看],我在这里做了一下实践和总结。


    定名空间一个最明白的目标就是解决重名题目,PHP中不容许两个函数或者类呈现雷同的名字,不然会产生一个致命的错误。这种景象下只要避免定名反复就可以解决,最常见的一种做法是商定一个前缀。


    例:项目中有两个模块:articlemessage board,它们各自有一个处理惩罚用户留言的类Comment之后我可能想要增长对所有效户留言的一些信息统计功能,比如说我想获得所有留言的数量。这时辰调用它们Comment供给的办法是很好的做法,然则同时引入各自的Comment类显然是不可的,代失足,在另一个处所重写任何一个Comment也会降落保护性。那这时只能重构类名,我商定了一个定名规矩,在类名前面加上模块名,像如许:Article_CommentMessageBoard_Comment


    可以看到,名字变得很长,那意味着今后应用Comment的时辰会写上更多的代码(至少字符多了)。并且,今后若是要对各个模块增长更多的一些整合功能,或者是互相调用,产生重名的时辰就须要重构名字。当然在项目开端的时辰就重视到这个题目,并规天定名规矩就能很好的避免这个题目。另一个解决办法可以推敲应用定名空间。



    注明:


    本文提到的常量:PHP5.3开端const关键字可以用在类的外部。constdefine都是用来声明常量的(它们的差别不胪陈),然则在定名空间里,define的感化是全局的,而const则感化于当前空间。我在文中提到的常量是指应用const声明的常量。




    根蒂根基


    定名空间将代码划分出不合的空间(区域),每个空间的常量、函数、类(为了偷懒,我下边都将它们称为元素)的名字互不影响, 这个有点类似我们经常提到的‘封装’的概念。


    创建一个定名空间须要应用namespace关键字,如许:



    <?php
    

    //创建一个名为Article的定名空间
    namespace Article;

    ?>



    要重视的是,当前脚本文件的第一个定名空间前面不克不及有任何代码,下面的写法都是错误的:



    //例一
    
    //在脚本前面写了一些逻辑代码


    <?php

    ¥path = /;

    class Comment { }

    namespace Article;

    ?>



    //例二
    //在脚本前面输出了一些字符


    <html></html>
    <?php

    namespace Article;

    ?>



    为什么要说第一个定名空间呢?因为同一脚本文件中可以创建多个定名空间。


    下面我创建了两个定名空间,趁便为这两个空间各自添加了一个Comment类元素:



    <?php
    

    //创建一个名为Article的定名空间
    namespace Article;

    //此Comment属于Article空间的元素
    class Comment { }



    //创建一个名为MessageBoard的定名空间
    namespace MessageBoard;

    //此Comment属于MessageBoard空间的元素
    class Comment { }
    ?>



    在不合空间之间不成以直接调用其它元素,须要应用定名空间的语法:



    <?php
    

    namespace Article;

    class Comment { }



    namespace MessageBoard;

    class Comment { }

    //调用当前空间(MessageBoard)的Comment类
    ¥comment = new Comment();

    //调用Article空间的Comment类
    ¥article_comment = new \Article\Comment();

    ?>



    可以看到,在MessageBoard空间中调用article空间里的Comment类时,应用了一种像文件路径的语法: \空间名\元素名


    除了类之外,对函数和常量的用法是一样的,下面我为两个空间创建了新的元素,并在MessageBoard空间中输出了它们的值。



    <?php
    

    namespace Article;

    const PATH = /article;

    function getCommentTotal() {
    return 100;
    }

    class Comment { }




    namespace MessageBoard;

    const PATH = /message_board;

    function getCommentTotal() {
    return 300;
    }

    class Comment { }

    //调用当前空间的常量、函数和类
    echo PATH; ///message_board
    echo getCommentTotal(); //300
    ¥comment = new Comment();

    //调用Article空间的常量、函数和类
    echo \Article\PATH; ///article
    echo \Article\getCommentTotal(); //100
    ¥article_comment = new \Article\Comment();

    ?>



    然后我的确获得了Article空间的元素数据。




    子空间


    定名空间的调用语法像文件路径一样是有事理的,它容许我们自定义子空间来描述各个空间之间的关系。


    抱愧我忘了说,articlemessage board这两个模块其实都是处于同一个blog项目内。若是用定名空间来表达它们的关系,是如许:



    <?php
    

    //我用如许的定名空间默示处于blog下的article模块
    namespace Blog\Article;

    class Comment { }



    //我用如许的定名空间默示处于blog下的message board模块
    namespace Blog\MessageBoard;

    class Comment { }

    //调用当前空间的类
    ¥comment = new Comment();

    //调用Blog\Article空间的类
    ¥article_comment = new \Blog\Article\Comment();

    ?>


    并且,子空间还可以定义很多层次,比如说 Blog\Article\Archives\Date




    公共空间


    我有一个common_inc.php脚本文件,里面有一些好用的函数和类:



    <?php
    

    function getIP() { }

    class FilterXSS { }

    ?>



    在一个定名空间里引入这个脚本,脚本里的元素不会归属到这个定名空间。若是这个脚本里没有定义其它定名空间,它的元素就始终处于公共空间中:



    <?php
    

    namespace Blog\Article;

    //引入脚本文件
    include ./common_inc.php;

    ¥filter_XSS = new FilterXSS(); //呈现致命错误:找不到Blog\Article\FilterXSS类

    ¥filter_XSS = new \FilterXSS(); //正确


    ?>



    调用公共空间的体式格式是直接在元素名称前加 \ 就可以了,不然PHP解析器会认为我想调用当前空间下的元素。除了自定义的元素,还包含PHP自带的元素,都属于公共空间。


    要提一下,其实公共空间的函数和常量不消加 \ 也可以正常调用(不熟悉打听PHP为什么要如许做),然则为了正确区分元素,还是建议调用函数的时辰加上 \ 




    名称术语


    在说别号和导入之前,须要知道关于空间三种名称的术语,以及PHP是如何解析它们的。官方文档说得很是好,我就直接拿来套了。



    1. 非限制名称,或不包含前缀的类名称,例如 ¥comment = new Comment();。若是当前定名空间是Blog\ArticleComment将被解析为Blog\Article\Comment。若是应用Comment的代码不包含在任何定名空间中的代码(全局空间中),则Comment会被解析为Comment

    2. 限制名称,或包含前缀的名称,例如 ¥comment = new Article\Comment();。若是当前的定名空间是Blog,则Comment会被解析为Blog\Article\Comment。若是应用Comment的代码不包含在任何定名空间中的代码(全局空间中),则Comment会被解析为Comment

    3. 完全限制名称,或包含了全局前缀操纵符的名称,例如 ¥comment = new \Article\Comment();。在这种景象下,Comment老是被解析为代码中的文字名(literal name)Article\Comment



    其实可以把这三种名称类比为文件名(例如 comment.php)、相对路径名(例如 ./article/comment.php)、绝对路径名(例如 /blog/article/comment.php),如许可能会更轻易懂得。


    我用了几个示例来默示它们:



    <?php
    

    //创建空间Blog
    namespace Blog;

    class Comment { }

    //非限制名称,默示当前Blog空间
    //这个调用将被解析成 Blog\Comment();

    ¥blog_comment = new Comment();

    //限制名称,默示相对于Blog空间
    //这个调用将被解析成 Blog\Article\Comment();

    ¥article_comment = new Article\Comment(); //类前面没有反斜杆
    //完全限制名称,默示绝对于Blog空间
    //这个调用将被解析成 Blog\Comment();

    ¥article_comment = new \Blog\Comment(); //类前面有反斜杆
    //完全限制名称,默示绝对于Blog空间
    //这个调用将被解析成 Blog\Article\Comment();

    ¥article_comment = new \Blog\Article\Comment(); //类前面有反斜杆


    //创建Blog的子空间Article

    namespace Blog\Article;

    class Comment { }


    ?>



    其实之前我就一向在应用非限制名称和完全限制名称,如今它们终于可以叫出它们的名称了。


     


    别号和导入


    别号和导入可以看作是调用定名空间元素的一种快捷体式格式。PHP并不支撑导入函数或常量。


    它们都是经由过程应用use操纵符来实现:



    <?php
    

    namespace Blog\Article;

    class Comment { }



    //创建一个BBS空间(我有筹算开个论坛)
    namespace BBS;

    //导入一个定名空间
    use Blog\Article;
    //导入定名空间后可应用限制名称调用元素
    ¥article_comment = new Article\Comment();

    //为定名空间应用别号
    use Blog\Article as Arte;
    //应用别号庖代空间名
    ¥article_comment = new Arte\Comment();

    //导入一个类
    use Blog\Article\Comment;
    //导入类后可应用非限制名称调用元素
    ¥article_comment = new Comment();

    //为类应用别号
    use Blog\Article\Comment as Comt;
    //应用别号庖代空间名
    ¥article_comment = new Comt();

    ?>



    我重视到,若是导入元素的时辰,当前空间有雷同的名字元素将会如何?显然成果会产生致命错误。


    例:



    <?php
    

    namespace Blog\Article;

    class Comment { }



    namespace BBS;

    class Comment { }

    Class Comt { }


    //导入一个类
    use Blog\Article\Comment;
    ¥article_comment = new Comment(); //与当前空间的Comment产生冲突,法度产生致命错误

    //为类应用别号

    use Blog\Article\Comment as Comt;
    ¥article_comment = new Comt(); //与当前空间的Comt产生冲突,法度产生致命错误

    ?>




    动态调用


    PHP供给了namespace关键字和__NAMESPACE__魔法常量动态的接见元素,__NAMESPACE__可以经由过程组合字符串的情势来动态接见:



    <?php
    

    namespace Blog\Article;

    const PATH = /Blog/article;

    class Comment { }


    //namespace关键字默示当前空间
    echo namespace\PATH; ///Blog/article
    ¥comment = new namespace\Comment();

    //魔法常量__NAMESPACE__的值是当前空间名称
    echo __NAMESPACE__; //Blog\Article
    //可以组合成字符串并调用

    ¥comment_class_name = __NAMESPACE__ . \Comment;
    ¥comment = new ¥comment_class_name();

    ?>



    字符串情势调用题目


    上方的动态调用的例子中,我们看到了字符串情势的动态调用体式格式,若是要应用这种体式格式要重视两个题目。


    1. 应用双引号的时辰特别字符可能被转义



    <?php
    

    namespace Blog\Article;

    class name { }

    //我是想调用Blog\Article\name
    ¥class_name = __NAMESPACE__ . \name; //然则\n将被转义为换行符

    ¥name = new ¥class_name(); //产生致命错误

    ?>



    2. 不会认为是限制名称


    PHP在编译脚本的时辰就断定了元素地点的空间,以及导入的景象。而在解析脚本时字符串情势调用只能认为长短限制名称和完全限制名称,而永远不成能是限制名称。



    <?php
    

    namespace Blog;

    //导入Common类
    use Blog\Article\Common;
    //我想应用非限制名称调用Blog\Article\Common
    ¥common_class_name = Common;
    //实际会被算作非限制名称,也就默示当前空间的Common类,但我当前类没有创建Common类
    ¥common = new ¥common_class_name(); //产生致命错误:Common类不存在

    //我想应用限制名称调用Blog\Article\Common

    ¥common_class_name = Article\Common;
    //实际会被算作完全限制名称,也就默示Article空间下的Common类,但我下面只定义了Blog\Article空间而不是Article空间
    ¥common = new ¥common_class_name(); //产生致命错误:Article\Common类不存在


    namespace Blog\Article;

    class Common { }

    ?>




    总结


    我对PHP的定名空间方才接触,也不克不及随便给一些没有实践的建议。我小我认为定名空间的感化和功能都很强大,若是要写插件或者通用库的时辰再也不消愁闷重名题目。不过若是项目进行到必然程度,要经由过程增长定名空间去解决重名题目,我感觉工作量不会比重构名字少。也不得不承认它的语对项目增长必然的错杂度,是以从项目一开端的时辰就应当很好的规划它,并制订一个定名规范。



    我俩之间有着强烈的吸引力。短短几个小时后,我俩已经明白:我们的心是一个整体的两半,我俩的心灵是孪生兄妹,是知己。她让我感到更有活力,更完美,更幸福。即使她不在我身边,我依然还是感到幸福,因为她总是以这样或者那样的方式出现在我心头。——恩里克·巴里奥斯《爱的文明》
    分享到: