首頁  >  文章  >  後端開發  >  PHP-命名空間(namespace)使用詳細介紹

PHP-命名空間(namespace)使用詳細介紹

王林
王林轉載
2019-08-28 16:01:422159瀏覽

對於命名空間,官方文件已經說得很詳細[查看],我在這裡做了實踐和總結。

命名空間一個最明確的目的就是解決重名問題,PHP不允許兩個函數或類別出現相同的名字,否則會產生一個致命的錯誤。這種情況只要避免命名重複就可以解決,最常見的做法就是約定一個前綴。

範例:專案中有兩個模組:article和message board,它們各自有一個處理使用者留言的類別Comment。之後我可能想要增加對所有用戶留言的一些資訊統計功能,例如說我想得到所有留言的數量。這時候呼叫它們Comment提供的方法是很好的做法,但是同時引入各自的Comment類別顯然是不行的,程式碼會出錯,在另一個地方重寫任何一個Comment也會降低維護性。那這時只能重構類別名,我約定了一個命名規則,在類別名稱前面加上模組名,像這樣:Article_Comment、MessageBoard_Comment

可以看到,名字變得很長,那意味著以後使用Comment的時候會寫更多的程式碼(至少字元多了)。並且,以後如果要對各個模組增加更多的一些整合功能,或者是互相調用,發生重名的時候就需要重構名字。當然在專案開始的時候就注意到這個問題,並規定命名規則就能很好的避免這個問題。另一個解決方法可以考慮使用命名空間。

註明:

本文提到的常數:PHP5.3開始const關鍵字可以用在類別的外部。 const和define都是用來聲明常數的(它們的區別不詳述),但是在命名空間裡,define的作用是全局的,而const則作用於當前空間。我在文中提到的常數是指使用const宣告的常數。

基礎:

命名空間將程式碼分割出不同的空間(區域),每個空間的常數、函數、類別(為了偷懶,我下邊都稱它們為元素)的名字互不影響, 這個有點類似我們常常提到的'封裝'的概念。

建立一個命名空間需要使用namespace關鍵字

程式碼如下:

<?php 
//创建一个名为&#39;Article&#39;的命名空间
namespace Article;
?>

要注意的是,目前腳本檔案的第一個命名空間前面不能有任何程式碼,下面的寫法都是錯誤的:

程式碼如下:

//在脚本前面写了一些逻辑代码
<?php
$path = "/";
class Comment { }
namespace Article;
?>

或如下:

//在脚本前面输出了一些字符
<html></html>
<?php
namespace Article;
?>

為什麼要說第一個命名空間呢?因為在同一腳本檔案中可以建立多個命名空間。

下面我建立了兩個命名空間,順便為這兩個空間各自添加了一個Comment類別元素:

程式碼如下:

<?php 
//创建一个名为&#39;Article&#39;的命名空间
namespace Article;
//此Comment属于Article空间的元素
class Comment { }

//创建一个名为&#39;MessageBoard&#39;的命名空间
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 = &#39;/article&#39;;
function getCommentTotal() {
    return 100;
}
class Comment { }

namespace MessageBoard;
const PATH = &#39;/message_board&#39;;
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和message 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();
?>

公共空間

我有一個common_inc.php腳本文件,裡面有一些好用的函數和類別:

<?php 
function getIP() { }
class FilterXSS { }
?>

在一個命名空間裡引入這個腳本,腳本裡的元素不會歸屬到這個命名空間。如果這個腳本裡沒有定義其它命名空間,它的元素就始終處於公共空間中:

<?php 
namespace Blog\Article;
//引入脚本文件
include &#39;./common_inc.php&#39;;
$filter_XSS = new FilterXSS(); //出现致命错误:找不到Blog\Article\FilterXSS类
$filter_XSS = new \FilterXSS(); //正确
?>

調用公共空間的方式是直接在元素名稱前加\ 就可以了,否則PHP解析器會認為我想調用當前空間下的元素。除了自訂的元素,還包括PHP自帶的元素,都屬於公共空間

要提一下,其實公共空間的函數和常數不用加\ 也可以正常調用(不明白PHP為什麼要這樣做),但是為了正確區分元素,還是建議呼叫函數的時候加上\

3、名稱術語

在說別名和導入之前,需要知道關於空間三種名稱的術語,以及PHP是怎麼解析它們的。官方文件說得非常好,我就直接拿來套了。

1.非限定名称,或不包含前缀的类名称,例如 $comment = new Comment();。如果当前命名空间是Blog\Article,Comment将被解析为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 = &#39;/Blog/article&#39;;
class Comment { }

//namespace关键字表示当前空间
echo namespace\PATH; ///Blog/article
$comment = new namespace\Comment();
//魔法常量__NAMESPACE__的值是当前空间名称
echo __NAMESPACE__; //Blog\Article
//可以组合成字符串并调用
$comment_class_name = __NAMESPACE__ . &#39;\Comment&#39;;
$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 = &#39;Common&#39;;
//实际会被当作非限定名称,也就表示当前空间的Common类,但我当前类没有创建Common类
$common = new $common_class_name(); //发生致命错误:Common类不存在
//我想使用限定名称调用Blog\Article\Common
$common_class_name = &#39;Article\Common&#39;;
//实际会被当作完全限定名称,也就表示Article空间下的Common类,但我下面只定义了Blog\Article空间而不是Article空间
$common = new $common_class_name(); //发生致命错误:Article\Common类不存在

namespace Blog\Article;
class Common { }
?>

总结
我对PHP的命名空间刚刚接触,也不能随便给一些没有实践的建议。我个人认为命名空间的作用和功能都很强大,如果要写插件或者通用库的时候再也不用担心重名问题。不过如果项目进行到一定程度,要通过增加命名空间去解决重名问题,我觉得工作量不会比重构名字少。也不得不承认它的语法会对项目增加一定的复杂度,因此从项目一开始的时候就应该很好的规划它,并制定一个命名规范。

更多相关问题请访问PHP中文网:PHP视频教程

以上是PHP-命名空間(namespace)使用詳細介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:cnblogs.com。如有侵權,請聯絡admin@php.cn刪除