2024年PHP开发者大会日本站

命名空间和动态语言特性

(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]: / - 未捕获错误:类“foo\classname”未找到

// 使用普通类名
new \namespacename\foo\classname; // bar
new namespacename\foo\classname; // [500]: / - 未捕获错误:类“namespacename\foo\namespacename\foo\classname”未找到
new foo\classname; // [500]: / - 未捕获错误:类“namespacename\foo\foo\classname”未找到
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.net/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); // 将产生:致命错误:类“B”未在 ...namespaced.php 的 ... 行中找到
?>
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";
//致命错误:未捕获的错误:找不到类 ' ame_of_country\Japan'
//在此语句中,“\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";
//致命错误:未捕获的错误:找不到类 '\Country_Name ame'

//在此语句中,“\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