2024 PHP Conference Japan

命名空間和動態語言特性

(PHP 5 >= 5.3.0, PHP 7, PHP 8)

PHP 的命名空間實作受到其作為程式語言的動態特性的影響。因此,要將程式碼像以下範例轉換為命名空間程式碼

範例 #1 動態存取元素

example1.php

<?php
class classname
{
function
__construct()
{
echo
__METHOD__,"\n";
}
}
function
funcname()
{
echo
__FUNCTION__,"\n";
}
const
constname = "global";

$a = 'classname';
$obj = new $a; // 顯示 classname::__construct
$b = 'funcname';
$b(); // 顯示 funcname
echo constant('constname'), "\n"; // 顯示 global
?>
必須使用完整的限定名稱(帶有命名空間前綴的類別名稱)。請注意,因為在動態類別名稱、函數名稱或常數名稱內,限定名稱和完整限定名稱之間沒有區別,所以開頭的反斜線不是必要的。

範例 #2 動態存取命名空間元素

<?php
namespace namespacename;
class
classname
{
function
__construct()
{
echo
__METHOD__,"\n";
}
}
function
funcname()
{
echo
__FUNCTION__,"\n";
}
const
constname = "namespaced";

/* 請注意,如果使用雙引號,則必須使用 "\\namespacename\\classname" */
$a = '\namespacename\classname';
$obj = new $a; // 顯示 namespacename\classname::__construct
$a = 'namespacename\classname';
$obj = new $a; // 也顯示 namespacename\classname::__construct
$b = 'namespacename\funcname';
$b(); // 顯示 namespacename\funcname
$b = '\namespacename\funcname';
$b(); // 也顯示 namespacename\funcname
echo constant('\namespacename\constname'), "\n"; // 顯示 namespaced
echo constant('namespacename\constname'), "\n"; // 也顯示 namespaced
?>

請務必閱讀關於在字串中跳脫命名空間名稱的注意事項

新增註解

使用者貢獻的註解 8 則註解

Alexander Kirk
13 年前
當繼承來自另一個命名空間的類別,且該類別需要實例化目前命名空間內的類別時,您需要傳遞命名空間。

<?php // File1.php
namespace foo;
class
A {
public function
factory() {
return new
C;
}
}
class
C {
public function
tell() {
echo
"foo";
}
}
?>

<?php // File2.php
namespace bar;
class
B extends \foo\A {}
class
C {
public function
tell() {
echo
"bar";
}
}
?>

<?php
include "File1.php";
include
"File2.php";
$b = new bar\B;
$c = $b->factory();
$c->tell(); // 顯示 "foo",但您想要的是 "bar"
?>

您需要這樣做

當繼承來自另一個命名空間的類別,且該類別需要實例化目前命名空間內的類別時,您需要傳遞命名空間。

<?php // File1.php
namespace foo;
class
A {
protected
$namespace = __NAMESPACE__;
public function
factory() {
$c = $this->namespace . '\C';
return new
$c;
}
}
class
C {
public function
tell() {
echo
"foo";
}
}
?>

<?php // File2.php
namespace bar;
class
B extends \foo\A {
protected
$namespace = __NAMESPACE__;
}
class
C {
public function
tell() {
echo
"bar";
}
}
?>

<?php
include "File1.php";
include
"File2.php";
$b = new bar\B;
$c = $b->factory();
$c->tell(); // "bar"
?>

(看來在預覽中,命名空間的反斜線從原始碼中被移除,或許在主要視圖中可以正常顯示。如果不行:fooA 寫作 \foo\A 而 barB 寫作 bar\B)
Daan
5 年前
重要的是,您需要在動態類別名稱中使用 *完整限定名稱*。以下範例強調了動態類別名稱和一般類別名稱之間的差異。

<?php
namespace namespacename\foo;

class
classname
{
function
__construct()
{
echo
'bar';
}
}

$a = '\namespacename\foo\classname'; // 可行,是完整限定名稱
$b = 'namespacename\foo\classname'; // 可行,會被視為前面加了 "\" 處理
$c = 'foo\classname'; // 無法運作,它應該是完整限定名稱

// 使用動態類別名稱
new $a; // bar
new $b; // bar
new $c; // [500]: / - Uncaught Error: Class 'foo\classname' not found in

// 使用一般類別名稱
new \namespacename\foo\classname; // bar
new namespacename\foo\classname; // [500]: / - Uncaught Error: Class 'namespacename\foo\namespacename\foo\classname' not found
new foo\classname; // [500]: / - Uncaught Error: Class 'namespacename\foo\foo\classname' not found
museyib dot e at gmail dot com
5 年前
在動態存取命名空間元素時要小心。如果您使用雙引號,反斜線會被解析為跳脫字元。

<?php
$a
="\namespacename\classname"; //使用方式錯誤,會造成致命錯誤。
$a="\\namespacename\\classname"; //使用方式正確。
$a='\namespacename\classname'; //使用方式正確。
?>
guilhermeblanco at php dot net
15 年前
請注意 FQCN(完整限定類別名稱)的重點。
許多人會在這方面遇到問題

<?php

// File1.php
namespace foo;

class
Bar { ... }

function
factory($class) {
return new
$class;
}

// File2.php
$bar = \foo\factory('Bar'); // 會嘗試實例化 \Bar,而不是 \foo\Bar

?>

要解決這個問題,並且包含兩步驟的命名空間解析,您可以檢查 $class 的第一個字元是否為 \,如果不是,則手動構建 FQCN(完整合格類別名稱)。

<?php

// File1.php
namespace foo;

function
factory($class) {
if (
$class[0] != '\\') {
echo
'->';
$class = '\\' . __NAMESPACE__ . '\\' . $class;
}

return new
$class();
}

// File2.php
$bar = \foo\factory('Bar'); // 將會正確地實例化 \foo\Bar

$bar2 = \foo\factory('\anotherfoo\Bar'); // 將會正確地實例化 \anotherfoo\Bar

?>
akhoondi+php at gmail dot com
11 年前
如果這樣說可能會更清楚

必須注意的是,當使用動態類別名稱、函式名稱或常數名稱時,「目前命名空間」,如 https://php.dev.org.tw/manual/en/language.namespaces.basics.php 中所述,是全域命名空間。

使用動態類別名稱的一種情況是在「工廠」模式中。因此,在變數名稱之前新增目標類別所需的命名空間。

namespaced.php
<?php
// namespaced.php
namespace Mypackage;
class
Foo {
public function
factory($name, $global = FALSE)
{
if (
$global)
$class = $name;
else
$class = 'Mypackage\\' . $name;
return new
$class;
}
}

class
A {
function
__construct()
{
echo
__METHOD__ . "<br />\n";
}
}
class
B {
function
__construct()
{
echo
__METHOD__ . "<br />\n";
}
}
?>

global.php
<?php
// global.php
class A {
function
__construct()
{
echo
__METHOD__;
}
}
?>

index.php
<?php
// index.php
namespace Mypackage;
include(
'namespaced.php');
include(
'global.php');

$foo = new Foo();

$a = $foo->factory('A'); // Mypackage\A::__construct
$b = $foo->factory('B'); // Mypackage\B::__construct

$a2 = $foo->factory('A',TRUE); // A::__construct
$b2 = $foo->factory('B',TRUE); // 將會產生:致命錯誤:在 ...namespaced.php 第 ... 行找不到類別 'B'
?>
m dot mannes at gmail dot com
7 年前
如果您嘗試呼叫靜態方法,這就是該怎麼做的方式

<?php
class myClass
{
public static function
myMethod()
{
return
"You did it!\n";
}
}

$foo = "myClass";
$bar = "myMethod";

echo
$foo::$bar(); // 印出 "You did it!"
?>
anisgazig at gmail dot com
3 年前
<?php

// 在動態命名空間類別中使用單引號或雙引號搭配單個或兩個反斜線。

namespace Country_Name{
class
Mexico{
function
__construct(){
echo
__METHOD__,"<br>";
}
}

$a = 'Country_Name\Mexico';//Country_Name\Mexico::__construct
$a = "Country_Name\Mexico";
//Country_Name\Mexico::__construct
$a = '\Country_Name\Mexico';//Country_Name\Mexico::__construct
$a = "\Country_Name\Mexico";
//Country_Name\Mexico::__construct
$a = "\\Country_Name\\Mexico";
//Country_Name\Mexico::__construct
$o = new $a;

}

/* 如果您的命名空間名稱或類別名稱以小寫 n 開頭,那麼您應該注意使用單引號或雙引號搭配反斜線 */

namespace name_of_country{
class
Japan{
function
__construct()
{
echo
__METHOD__,"<br>";
}

}

$a = 'name_of_country\Japan';//name_of_country\Japan::__construct
$a = "name_of_country\Japan";
//name_of_country\Japan::__construct
$a = '\name_of_country\Japan';//name_of_country\Japan::__construct
//$a = "\name_of_country\Japan";
//Fatal error: Uncaught Error: Class ' ame_of_country\Japan' not found
//在此語句中,"\name_of_country\Japan" 表示第一個字母 n 與 "\ 相當於換行("\n)。要修復它,我們可以使用雙反斜線或單引號搭配單個反斜線。
$a = "\\name_of_country\\Japan";
//name_of_country\Japan::__construct
$o = new $a;
}

namespace Country_Name{
class
name{
function
__construct(){
echo
__METHOD__,"<br>";
}
}

$a = 'Country_Name\name';//Country_Name\Norway::__construct
$a = "Country_Name\name";
//Country_Name\Norway::__construct
$a = '\Country_Name\name';//Country_Name\Norway::__construct
//$a = "\Country_Name\name";
//Fatal error: Uncaught Error: Class '\Country_Name ame' not found

//在此語句中,"\Country_Name\name" 類別名稱的第一個字母 n 與 "\ 相當於換行("\n)。要修復它,我們可以使用雙反斜線或單引號搭配單個反斜線
$a = "\\Country_Name\\name";
//Country_Name\name::__construct
$o = new $a;

}

//"\n == 換行不區分大小寫,所以 "\N 不受影響

?>
scott at intothewild dot ca
15 年前
如同 guilhermeblanco at php dot net 所述,

<?php

// fact.php

namespace foo;

class
fact {

public function
create($class) {
return new
$class();
}
}

?>

<?php

// bar.php

namespace foo;

class
bar {
...
}

?>

<?php

// index.php

namespace foo;

include(
'fact.php');

$foofact = new fact();
$bar = $foofact->create('bar'); // 嘗試建立 \bar
// 即使 foofact 和
// bar 位於 \foo

?>
To Top