PHP-Namespace

PHP-Namespaces wurden in PHP 5.3 eingeführt, um das Problem doppelter Namen zu lösen. In PHP können zwei Funktionen oder Klassen nicht denselben Namen haben; andernfalls tritt ein fataler Fehler auf.

PHP-Namespaces können die folgenden zwei Arten von Problemen lösen:

  1. Namenskonflikte zwischen vom Benutzer geschriebenem Code und internen Klassen/Funktionen/Konstanten von PHP oder Klassen/Funktionen/Konstanten von Drittanbietern.
  2. Erstellen Sie einen Alias- (oder Kurz-)Namen für sehr lange Bezeichnernamen (normalerweise definiert, um die erste Art von Problem abzumildern), wodurch die Lesbarkeit des Quellcodes verbessert wird.

Namespaces definieren

Alle Konstanten, Klassen und Funktionsnamen werden im globalen Namensraum abgelegt, genau wie bevor PHP Namespaces unterstützte.

Namespaces werden mit dem Schlüsselwort `namespace` deklariert. Wenn eine Datei einen Namespace enthält, muss der Namespace vor allen anderen Code deklariert werden.

Die Syntax lautet wie folgt:

<?php  
// 定义代码在 'MyProject' 命名空间中  
namespace MyProject;  
 
// ... 代码 ...  

Sie können auch verschiedenen Namespace-Code in derselben Datei definieren, zum Beispiel:

<?php  
namespace MyProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }

namespace AnotherProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
?>  

Es wird nicht empfohlen, diese Syntax zu verwenden, um mehrere Namespaces in einer einzelnen Datei zu definieren. Es wird empfohlen, die folgende Syntax mit geschweiften Klammern zu verwenden.

<?php
namespace MyProject {
    const CONNECT_OK = 1;
    class Connection { /* ... */ }
    function connect() { /* ... */  }
}

namespace AnotherProject {
    const CONNECT_OK = 1;
    class Connection { /* ... */ }
    function connect() { /* ... */  }
}
?>

Um globalen Nicht-Namespace-Code mit Namespace-Code zu kombinieren, können Sie nur die Syntax mit geschweiften Klammern verwenden. Der globale Code muss in eine namenlose `namespace`-Anweisung plus geschweifte Klammern eingeschlossen werden, zum Beispiel:

<?php
namespace MyProject {

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
}

namespace { // 全局代码
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}
?>

Der einzige zulässige Code vor der Deklaration eines Namespace ist die `declare`-Anweisung, die zur Definition der Kodierung der Quelldatei verwendet wird. Jeglicher Nicht-PHP-Code, einschließlich Leerzeichen, darf nicht vor der Namespace-Deklaration erscheinen.

<?php
declare(encoding='UTF-8'); //定义多个命名空间和不包含在命名空间中的代码
namespace MyProject {

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
}

namespace { // 全局代码
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}
?>

Der folgende Code verursacht einen Syntaxfehler:

<html>
<?php
namespace MyProject; // 命名空间前出现了“<html>” 会致命错误 - 命名空间必须是程序脚本的第一条语句
?>

Unter-Namespaces

Ähnlich wie die Beziehung zwischen Verzeichnissen und Dateien ermöglichen PHP-Namespaces auch die Angabe hierarchischer Namespace-Namen. Daher können Namespace-Namen hierarchisch definiert werden:

<?php
namespace MyProject\Sub\Level;  //声明分层次的单个命名空间

const CONNECT_OK = 1;
class Connection { /* ... */ }
function Connect() { /* ... */  }

?>

Das obige Beispiel erstellt die Konstante `MyProject\Sub\Level\CONNECT_OK`, die Klasse `MyProject\Sub\Level\Connection` und die Funktion `MyProject\Sub\Level\Connect`.


Namespaces verwenden

Klassennamen in PHP-Namespaces können auf drei Arten referenziert werden:

  1. Unqualifizierte Namen oder Klassennamen ohne Präfix,Zum Beispiel `$a = new foo();` oder `foo::staticmethod();`. Wenn der aktuelle Namespace `currentnamespace` ist, wird `foo` zu `currentnamespace\foo` aufgelöst. Wenn der Code, der `foo` verwendet, global ist und in keinem Namespace enthalten ist, wird `foo` zu `foo` aufgelöst. Warnung: Wenn eine Funktion oder Konstante in einem Namespace nicht definiert ist, wird der unqualifizierte Funktionsname oder Konstantenname zum globalen Funktionsnamen oder Konstantennamen aufgelöst.

  2. Qualifizierte Namen oder Namen mit einem Präfix,Zum Beispiel `$a = new subnamespace\foo();` oder `subnamespace\foo::staticmethod();`. Wenn der aktuelle Namespace `currentnamespace` ist, wird `foo` zu `currentnamespace\subnamespace\foo` aufgelöst. Wenn der Code, der `foo` verwendet, global ist und in keinem Namespace enthalten ist, wird `foo` zu `subnamespace\foo` aufgelöst.

  3. Vollqualifizierte Namen oder Namen, die den globalen Präfixoperator enthalten,Zum Beispiel `$a = new \currentnamespace\foo();` oder `\currentnamespace\foo::staticmethod();`. In diesem Fall wird `foo` immer zum literalen Namen `currentnamespace\foo` im Code aufgelöst.

Das folgende ist ein Beispiel, das diese drei Methoden verwendet:

Code der Datei file1.php

<?php
namespace Foo\Bar\subnamespace; 

const FOO = 1;
function foo() {}
class foo
{
    static function staticmethod() {}
}
?>

Code der Datei file2.php

<?php
namespace Foo\Bar;
include 'file1.php';

const FOO = 2;
function foo() {}
class foo
{
    static function staticmethod() {}
}

/* 非限定名称 */
foo(); // 解析为函数 Foo\Bar\foo
foo::staticmethod(); // 解析为类 Foo\Bar\foo ,方法为 staticmethod
echo FOO; // 解析为常量 Foo\Bar\FOO

/* 限定名称 */
subnamespace\foo(); // 解析为函数 Foo\Bar\subnamespace\foo
subnamespace\foo::staticmethod(); // 解析为类 Foo\Bar\subnamespace\foo,
                                  // 以及类的方法 staticmethod
echo subnamespace\FOO; // 解析为常量 Foo\Bar\subnamespace\FOO
                                  
/* 完全限定名称 */
\Foo\Bar\foo(); // 解析为函数 Foo\Bar\foo
\Foo\Bar\foo::staticmethod(); // 解析为类 Foo\Bar\foo, 以及类的方法 staticmethod
echo \Foo\Bar\FOO; // 解析为常量 Foo\Bar\FOO
?>

Beachten Sie, dass auf jede globale Klasse, Funktion oder Konstante mit einem vollqualifizierten Namen zugegriffen werden kann, wie z. B. `\strlen()`, `\Exception` oder `\INI_ALL`.

Zugriff auf globale Klassen, Funktionen und Konstanten in einem Namespace:

<?php
namespace Foo;

function strlen() {}
const INI_ALL = 3;
class Exception {}

$a = \strlen('hi'); // 调用全局函数strlen
$b = \INI_ALL; // 访问全局常量 INI_ALL
$c = new \Exception('error'); // 实例化全局类 Exception
?>

Namespaces und dynamische Sprachfunktionen

Die Implementierung von PHP-Namespaces wird durch die dynamischen Funktionen der Sprache selbst beeinflusst. Um den folgenden Code in Namespaces umzuwandeln, müssen Sie daher dynamisch auf Elemente zugreifen.

Code der Datei example1.php:

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

$a = 'classname';
$obj = new $a; // prints classname::__construct
$b = 'funcname';
$b(); // prints funcname
echo constant('constname'), "\n"; // prints global
?>

Sie müssen vollqualifizierte Namen verwenden (Klassennamen einschließlich des Namespace-Präfixes). Beachten Sie, dass der führende Backslash nicht erforderlich ist, da bei dynamischen Klassennamen, Funktionsnamen oder Konstantennamen kein Unterschied zwischen qualifizierten Namen und vollqualifizierten Namen besteht.

Dynamischer Zugriff auf Namespace-Elemente

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

include 'example1.php';

$a = 'classname';
$obj = new $a; // 输出 classname::__construct
$b = 'funcname';
$b(); // 输出函数名
echo constant('constname'), "\n"; // 输出 global

/* 如果使用双引号,使用方法为 "\\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
?>

Das Schlüsselwort namespace und die Konstante __NAMESPACE__

PHP unterstützt zwei abstrakte Methoden für den Zugriff auf Elemente im aktuellen Namespace: die magische Konstante `__NAMESPACE__` und das Schlüsselwort `namespace`.

Der Wert der Konstante `__NAMESPACE__` ist eine Zeichenkette, die den Namen des aktuellen Namespace enthält. In globalem Code, der zu keinem Namespace gehört, enthält sie eine leere Zeichenkette.

`__NAMESPACE__`-Beispiel, Code in einem Namespace

<?php
namespace MyProject;

echo '"', __NAMESPACE__, '"'; // 输出 "MyProject"
?>

`__NAMESPACE__`-Beispiel, globaler Code

<?php

echo '"', __NAMESPACE__, '"'; // 输出 ""
?>

Die Konstante `__NAMESPACE__` ist nützlich, wenn Namen dynamisch erstellt werden, zum Beispiel:

Verwendung von `__NAMESPACE__` zum dynamischen Erstellen von Namen

<?php
namespace MyProject;

function get($classname)
{
    $a = __NAMESPACE__ . '\\' . $classname;
    return new $a;
}
?>

Das Schlüsselwort `namespace` kann verwendet werden, um explizit auf Elemente im aktuellen Namespace oder einem Unter-Namespace zuzugreifen. Es ist äquivalent zum `self`-Operator in Klassen.

Der `namespace`-Operator, Code in einem Namespace

<?php
namespace MyProject;

use blah\blah as mine; // 引入了 blah\blah 命名空间,并定义了个别名mine

mine\mine(); // 调用函数 blah\blah\mine()
namespace\blah\mine(); // 调用函数 MyProject\blah\mine()

namespace\func(); // 调用函数 MyProject\func()
namespace\sub\func(); // 调用函数 MyProject\sub\func()
namespace\cname::method(); // 调用 MyProject\cname 类的静态方法
$a = new namespace\sub\cname(); // 实例化 MyProject\sub\cname 类的对象
$b = namespace\CONSTANT; // 将常量 MyProject\CONSTANT 的值赋给 $b
?>

Der `namespace`-Operator, globaler Code

<?php

namespace\func(); // calls function func()
namespace\sub\func(); // calls function sub\func()
namespace\cname::method(); // calls static method "method" of class cname
$a = new namespace\sub\cname(); // instantiates object of class sub\cname
$b = namespace\CONSTANT; // assigns value of constant CONSTANT to $b
?>

Verwendung von Namespaces: Aliase/Importe

Die Namespace-Unterstützung von PHP bietet zwei Wege, Aliase oder Importe zu nutzen: einen Alias für Klassennamen oder einen Alias für Namespace-Namen.

In PHP werden Aliase mit dem `use`-Operator implementiert. Das folgende Beispiel verwendet alle drei möglichen Importmethoden:

1. Importieren/Verwenden von Aliasen mit dem `use`-Operator

<?php
namespace foo;
use My\Full\Classname as Another;

// 下面的例子与 use My\Full\NSname as NSname 相同
use My\Full\NSname;

// 导入一个全局类
use \ArrayObject;

$obj = new namespace\Another; // 实例化 foo\Another 对象
$obj = new Another; // 实例化 My\Full\Classname 对象
NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func
$a = new ArrayObject(array(1)); // 实例化 ArrayObject 对象
// 如果不使用 "use \ArrayObject" ,则实例化一个 foo\ArrayObject 对象
?>

2. Mehrere `use`-Anweisungen in einer Zeile einbinden

<?php
use My\Full\Classname as Another, My\Full\NSname;

$obj = new Another; // 实例化 My\Full\Classname 对象
NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func
?>

Der Importvorgang wird zur Kompilierungszeit ausgeführt, dynamische Klassennamen, Funktionsnamen oder Konstantennamen jedoch nicht.

3. Importe und dynamische Namen

<?php
use My\Full\Classname as Another, My\Full\NSname;

$obj = new Another; // 实例化一个 My\Full\Classname 对象
$a = 'Another';
$obj = new $a;      // 实际化一个 Another 对象
?>

Darüber hinaus betrifft der Importvorgang nur unqualifizierte Namen und qualifizierte Namen. Vollqualifizierte Namen sind deterministisch und werden von Importen nicht beeinflusst.

4. Importe und vollqualifizierte Namen

<?php
use My\Full\Classname as Another, My\Full\NSname;

$obj = new Another; // 实例化 My\Full\Classname 类
$obj = new \Another; // 实例化 Another 类
$obj = new Another\thing; // 实例化 My\Full\Classname\thing 类
$obj = new \Another\thing; // 实例化 Another\thing 类
?>

Namespaces verwenden: Rückfall auf globale Funktionen/Konstanten

Wenn PHP in einem Namespace auf einen unqualifizierten Klassen-, Funktions- oder Konstantennamen trifft, verwendet es unterschiedliche Prioritätsstrategien, um den Namen aufzulösen. Klassennamen werden immer zu Namen im aktuellen Namespace aufgelöst. Daher müssen Sie vollqualifizierte Namen verwenden, wenn Sie auf interne Systemklassennamen oder Klassennamen zugreifen, die nicht in einem Namespace enthalten sind, zum Beispiel:

1. Zugriff auf globale Klassen in einem Namespace

<?php
namespace A\B\C;
class Exception extends \Exception {}

$a = new Exception('hi'); // $a 是类 A\B\C\Exception 的一个对象
$b = new \Exception('hi'); // $b 是类 Exception 的一个对象

$c = new ArrayObject; // 致命错误, 找不到 A\B\C\ArrayObject 类
?>

Bei Funktionen und Konstanten gilt: Wenn die Funktion oder Konstante im aktuellen Namespace nicht existiert, fällt PHP auf die Funktion oder Konstante im globalen Bereich zurück.

2. Rückfall auf globale Funktionen/Konstanten in Namespaces

<?php
namespace A\B\C;

const E_ERROR = 45;
function strlen($str)
{
    return \strlen($str) - 1;
}

echo E_ERROR, "\n"; // 输出 "45"
echo INI_ALL, "\n"; // 输出 "7" - 使用全局常量 INI_ALL

echo strlen('hi'), "\n"; // 输出 "1"
if (is_array('hi')) { // 输出 "is not array"
    echo "is array\n";
} else {
    echo "is not array\n";
}
?>

Globaler Namensraum

Wenn kein Namespace definiert ist, befinden sich alle Klassen- und Funktionsdefinitionen im globalen Namensraum, genau wie bevor PHP das Konzept der Namespaces einführte. Das Hinzufügen des Präfixes `\` vor einem Namen zeigt an, dass der Name ein Name im globalen Namensraum ist, selbst wenn der Name sich in anderen Namespaces befindet.

Erklärung zur Verwendung des globalen Namensraums

<?php
namespace A\B\C;

/* 这个函数是 A\B\C\fopen */
function fopen() { 
     /* ... */
     $f = \fopen(...); // 调用全局的fopen函数
     return $f;
} 
?>

Reihenfolge der Namespace-Auflösung

Seit der Einführung von Namespaces ist das Fehleranfälligste bei der Verwendung von Klassen: Wie lautet der Suchpfad der Klasse?

<?php
namespace A;
use B\D, C\E as F;

// 函数调用

foo();      // 首先尝试调用定义在命名空间"A"中的函数foo()
            // 再尝试调用全局函数 "foo"

\foo();     // 调用全局空间函数 "foo" 

my\foo();   // 调用定义在命名空间"A\my"中函数 "foo" 

F();        // 首先尝试调用定义在命名空间"A"中的函数 "F" 
            // 再尝试调用全局函数 "F"

// 类引用

new B();    // 创建命名空间 "A" 中定义的类 "B" 的一个对象
            // 如果未找到,则尝试自动装载类 "A\B"

new D();    // 使用导入规则,创建命名空间 "B" 中定义的类 "D" 的一个对象
            // 如果未找到,则尝试自动装载类 "B\D"

new F();    // 使用导入规则,创建命名空间 "C" 中定义的类 "E" 的一个对象
            // 如果未找到,则尝试自动装载类 "C\E"

new \B();   // 创建定义在全局空间中的类 "B" 的一个对象
            // 如果未发现,则尝试自动装载类 "B"

new \D();   // 创建定义在全局空间中的类 "D" 的一个对象
            // 如果未发现,则尝试自动装载类 "D"

new \F();   // 创建定义在全局空间中的类 "F" 的一个对象
            // 如果未发现,则尝试自动装载类 "F"

// 调用另一个命名空间中的静态方法或命名空间函数

B\foo();    // 调用命名空间 "A\B" 中函数 "foo"

B::foo();   // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法
            // 如果未找到类 "A\B" ,则尝试自动装载类 "A\B"

D::foo();   // 使用导入规则,调用命名空间 "B" 中定义的类 "D" 的 "foo" 方法
            // 如果类 "B\D" 未找到,则尝试自动装载类 "B\D"

\B\foo();   // 调用命名空间 "B" 中的函数 "foo" 

\B::foo();  // 调用全局空间中的类 "B" 的 "foo" 方法
            // 如果类 "B" 未找到,则尝试自动装载类 "B"

// 当前命名空间中的静态方法或函数

A\B::foo();   // 调用命名空间 "A\A" 中定义的类 "B" 的 "foo" 方法
              // 如果类 "A\A\B" 未找到,则尝试自动装载类 "A\A\B"

\A\B::foo();  // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法
              // 如果类 "A\B" 未找到,则尝试自动装载类 "A\B"
?>

Die Namensauflösung folgt den folgenden Regeln:

  1. Aufrufe von Funktionen, Klassen und Konstanten mit vollständig qualifizierten Namen werden zur Kompilierzeit aufgelöst. Zum Beispielnew \A\Bwird zur Klasse aufgelöstA\B。
  2. Alle unqualifizierten Namen und qualifizierten Namen (nicht vollständig qualifizierte Namen) werden zur Kompilierzeit gemäß den aktuellen Importregeln umgewandelt. Wenn zum Beispiel der NamespaceA\B\CalsCimportiert wird, dann wird ein Aufruf vonC\D\e()umgewandelt inA\B\C\D\e()。
  3. Innerhalb eines Namespace wird allen qualifizierten Namen, die nicht gemäß den Importregeln umgewandelt wurden, der aktuelle Namespace-Name vorangestellt. Zum Beispiel im NamespaceA\B, wennC\D\e()aufgerufen wird, dann wirdC\D\e()umgewandelt inA\B\C\D\e() 。
  4. Unqualifizierte Klassennamen werden zur Kompilierzeit gemäß den aktuellen Importregeln umgewandelt (der vollständige Name ersetzt den kurzen importierten Namen). Wenn zum Beispiel der NamespaceA\B\Cals C importiert wird, dann wirdnew C()umgewandelt innew A\B\C() 。
  5. Innerhalb eines Namespace (z. B. A\B) werden Funktionsaufrufe an unqualifizierte Namen zur Laufzeit aufgelöst. Zum Beispiel wird für die Funktionfoo()der Aufruf wie folgt aufgelöst:
    1. Im aktuellen Namespace nach einer Funktion namensA\B\foo()suchen
    2. Versuchen Sie, dieglobaleFunktion im globalen Namensraumfoo() zu finden und aufzurufen。
  6. Innerhalb eines Namensraums (z. B.A\B) werden Aufrufe nicht qualifizierter oder qualifizierter Klassennamen (nicht vollständig qualifizierter Namen) zur Laufzeit aufgelöst. Das Folgende ist der Auflösungsprozess für den Aufruf vonnew C()undnew D\E()der Auflösungsprozess: new C()Auflösung von:
    1. Suchen Sie nach derA\B\CKlasse im aktuellen Namensraum.
    2. Versuchen Sie, die Klasse automatisch zu ladenA\B\C。
    new D\E()Auflösung von:
    1. Stellen Sie dem Klassennamen den Namen des aktuellen Namensraums voran, was Folgendes ergibt:A\B\D\E, suchen Sie dann nach dieser Klasse.
    2. Versuchen Sie, die Klasse automatisch zu ladenA\B\D\E。
    Um auf eine globale Klasse im globalen Namensraum zu verweisen, müssen Sie den vollständig qualifizierten Namennew \C() verwenden。
Weitere Erweiterungen