1. 程式人生 > 程式設計 >PHP的名稱空間

PHP的名稱空間

前言

做開發也有2年的時間了,發現一個問題,做的越久,對基礎知識掌握的越少,越喜歡理解一些很“高深”的東西,看似高深,其實low到不行。慢慢你會發現,越是學習高深的東西,對基礎知識要求越高,比如本節說的名稱空間,你對這個不瞭解,那麼你對composer的檔案自動載入就是一頭霧水

踩過一些坑發現在學習這件路上,不能懶,只要你一懶下來,你之前學習的東西,會忘記的一乾二淨,什麼都沒有,記下來,記下來是最好的方式,哪怕這個知識點簡單的不能再簡單了,記下來

什麼是名稱空間

在回答這個問題之前,我想先回答為什麼要有名稱空間,如果我們反著問,沒有名稱空間是什麼樣子呢。想象一下你現在寫了一個名為User的類,我也寫了一個User的類,我們把這兩個類放在一個檔案裡面。這個時候就會出錯,提示不能有兩個類名相同的類出現

如果我們給這兩個類加上名稱空間,namespae jacknamespae tom,這個時候就不出現剛才的問題了。

在PHP官網中,提到可以通過名稱空間解決在編寫類庫或函式的時候遇到這兩類問題:

  • 使用者編寫的程式碼與PHP內建的類/函式/常量或第三方類/函式/常量之間的名字衝突。
  • 為很長的識別符號名稱建立一個別名,提高原始碼的可讀性

上面的第一點我明白,第二點有點糊塗,後面發現這個要在使用名稱空間的時候才會說到,一句話來說,就是名稱空間可以建立別名,比如使用use App\Models\User,在引入User後同時也建立了一個User的別名,往後我們使用User就相當於使用\APP\Models\User

其實名稱空間就是藉助檔案系統中的目錄來解決命名這個難題的,應用到程式設計領域就是名稱空間的概念

要記住一點就是,不能說名稱空間就等同於目錄,只不過藉助目錄這樣一個原理,來實現同名的函式、類等等

定義名稱空間

在PHP中定義名稱空間非常的簡單,使用namespage關鍵詞來宣告就可以了,如下:

namespace Jack;

const CARD_ID=5;
class User{}
function getInfo(){}

複製程式碼

雖然說PHP的程式碼都可以包含在名稱空間中,但也只有類、介面、函式、常量會受到影響。像變數就不起作用,所以在使用變數前一定得初始化。

名稱空間也可以像目錄檔案一樣,指定層次化的名稱空間的名稱,比如Laravel框架中定義的名稱空間namespace App\Http\Controllers

,這在你使用composer引入第三方類庫更常見了

使用名稱空間

使用名稱空間是非常簡單的操作,使用use關鍵字就可以匯入外部的完全限定名稱,並且還可以名別名

namespace foo;

// 下面兩個是等價的
use My\Full\Classname;
use My\Full\Classname as Classname;
複製程式碼

通過上面的例子,我們知道use關鍵字可以在引入外部名稱空間的同時還給它取別名,後面就可以使用別名,來代替之前的完全限定名稱了

因為匯入的名稱空間必須是完全限定名稱,前導的反斜槓是不必需要的,例如use Http\Models

不僅可以對名稱空間使用別名,還可以給類名稱、介面、函式、常量設定別名,例如:

use My\Full\Classname as Another;
use function My\Full\functionName as func;
use const My\Full\CONSTANT;
複製程式碼

也可以不使用use關鍵字匯入類,直接在呼叫類的程式碼處使用完全限定名稱,例如:\App\Model\Use ::first()

如何解析名稱空間

現在已經知道如何使用名稱空間,但對PHP使用哪一個名稱空間中的元素是不知道的,比如有\foo()、\config\foo()兩個函式,到底使用哪一個函式我們是不知道的,對此PHP有三種使用名稱空間名稱的方式

非限定名稱

名稱中不含有名稱空間分隔符,例如 $a=new foo()。如果當前名稱空間是currentnamespace,foo將被解析為currentnamespace\foo;如果當前名稱空間是全域性的,則foo會被解析為foo。

在PHP中對非限定的類、函式、常量,採用的是不同的優先策略來解析這些名稱

類名稱總會解析到當前名稱空間中

namespace A\B\C;
class Exception extends \Exception {}

$a = new Exception('hi'); // $a 是類 A\B\C\Exception 的一個物件
$b = new \Exception('hi'); // $b 是類 Exception 的一個物件
複製程式碼

函式和常量如果在當前名稱空間中不存在,那麼會在全域性空間中使用他們

namespace A\B\C;

const E_ERROR = 45;
function strlen($str)
{
echo 'hello';
}

echo E_ERROR,"\n"; // 輸出 "45"
echo INI_ALL,"\n"; // 輸出 "7" - 使用全域性常量 INI_ALL

echo strlen('hi'),"\n"; // 輸出 "hello"
複製程式碼

限定名稱

限定名稱,名稱中含有名稱空間分隔符,例如 $a = new subnamespace\foo()。如果當前的名稱空間是 currentnamespace,則 foo 會被解析為 currentnamespace\subnamespace\foo;如果當前的名稱空間是全域性的,foo 會被解析為subnamespace\foo。

namespace Foo\Bar\subnamespace;

function foo() {}

namespace Foo\Bar;

subnamespace\foo(); // 解析為函式 Foo\Bar\subnamespace\foo
複製程式碼

完全限定名稱

名稱以名稱空間分隔符開始的識別符號,例如$a = new \currentnamespace\foo(),在這種情況下,foo總是被解析為程式碼中的文字名currentnamespace\foo。

namespace Foo\Bar\subnamespace;

function foo() {}

\Foo\Bar\foo(); // 解析為函式 Foo\Bar\foo
複製程式碼

說了這麼多,意義是什麼呢,對於平時的程式碼編寫有什麼幫助,個人認為總結下來,在這些方面平時可以注意一下。在使用函式的時候,使用非限定名稱,如果當前空間沒有找到函式,PHP會自動在全域性空間查詢;對於類、介面要是使用完全限定名稱,明確它們所在的空間,不會因為當前環境的變化而受到影響

其他

名稱空間常量

__NAMESPACE__的值是包含當前名稱空間名稱的字串,如果是在全域性空間,則是一個空字串

// file1.php
namespace MyProject;
echo '"',__NAMESPACE__,'"'; // 輸出 "MyProject"

// file2.php
echo '"','"'; // 輸出 ""
複製程式碼

關鍵字namespace可以顯示訪問當前名稱空間或子名稱空間中的元素

namespace Help;

function test(){echo 'test';};
echo namespace\test();  //calls function Help\test()
複製程式碼

這些東西到底有什麼意義呢,有的,就是我們可以的動態建立名稱,例如:

namespace MyProject;

class Student
{
	public function __construct()
	{
		echo 'new Student';
	}
}
function get($classname)
{
    $a = __NAMESPACE__ . '\\' . $classname;
    return new $a;
}

get('Student');
複製程式碼

仔細發現,你會覺得這個程式碼有問題,都在同一個名稱空間,而且你使用的又是限定名稱,__NAMESPACE__ . '\\' . $classname會被解析成MyProject\MyProject\Student,執行程式碼發現又沒有這樣的問題,這到底是怎麼回事

這是因為在動態的類名稱、函式名稱或常量名稱中,必須要使用完全限定名稱,又因為限定名稱和完全限定名稱沒有區別,所以前導反斜槓是不必要加的

這樣就很好的解釋前面的問題,__NAMESPA CE__ . '\\' . $classname雖然是限定名稱,但是它是動態類名稱,不必要加前導反斜槓

如果上面的例子中的__NAMESPACE__ . '\\' . $classname改為$classname,結果會怎麼樣呢

Fatal error: Uncaught Error: Class 'Student' not found

PHP會把$classname解析為完全限定名稱,在我們這個例子中就是全域性空間下的Student類

為什麼PHP會有這樣的設計呢,我想這跟PHP的自動載入有很大的關係,完全限定名稱就不需要在當前名稱空間查詢元素了,效率比非限定名稱要好一些

囉嗦幾句

在這篇文章寫完之後,我發現對名稱空間這塊,有了新的認識,比如名稱空間是怎麼來的,是因為類重名、函式重名,參照檔案目錄的原理,設計了這麼一個東西。它的出生是來解決問題的,不是製造問題的,在使用名稱空間的時候,就要對它的原理掌握清楚,使用非限定名稱會有哪些問題,那些地方是必須要使用完全限定名稱的。

通過這麼記下來,總結下來,要比以前自己走馬觀花式的學習方式要好一下,我不能說這會好太多,只是在某一兩個知識點面前,會有深刻的認識,就是這個地方深入的瞭解,你對其他塊的知識慢慢的也會瞭解

就比如下面一篇,我要學習的PSR-4自動載入規則,就要求你對PHP的名稱空間要有很好的理解,不然的話,很多地方你只能記住,而不能有深刻的認識,時間一長你就忘了