Regeln für Namensauflösung

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

Hier einige wichtige Definitionen für die Zwecke der Namensauflösungsregeln:

Namespace-Namen-Definitionen
Unqualifizierter Name

Dies ist ein Bezeichner ohne einen Namespaceseparator, z. B. Foo

Qualifizierter Name

Dies ist ein Bezeichner mit einem Namespaceseparator, z. B. Foo\Bar

Vollständig qualifizierter Name

Dies ist ein Bezeichner mit einem Namespaceseparator, der mit einem Namespaceseparator beginnt, z. B. \Foo\Bar. Der Namespace namespace\Foo ist ebenfalls ein vollständig qualifizierter Name.

Relativer Name

Dies ist ein Bezeichner, der mit namespace beginnt, wie etwa namespace\Foo\Bar.

Namen werden gemäß den folgenden Regeln aufgelöst:

  1. Vollständig qualifizierte Namens werden immer zu dem Namen ohne führenden Namensraumtrenner aufgelöst. \A\B wird z. B. zu A\B aufgelöst.
  2. Relative Namen werden immer zu dem Namen aufgelöst, in dem namespace durch den aktuellen Namensraum ersetzt wurde. Taucht der Namen im globalen Geltungsbereich auf, wird das namespace\-Präfix entfernt. Beispielsweise wird namespace\A innerhalb des Namensraums X\Y zu X\Y\A aufgelöst. Derselbe Name im globalen Geltungsbereich wird zu A aufgelöst.
  3. Für qualifizierte Namen wird das erste Segment des Namens gemäß der aktuellen Klassen-/Namensraum-Importtabelle übersetzt. Wird beispielsweise der Namensraum A\B\C als C importiert, wird der Name C\D\E zu A\B\C\D\E übersetzt.
  4. Wenn für qualifizierte Namen keine Importregel zutrifft, wird der aktuelle Namensraum dem Namen vorangestellt. Beispielsweise wird der Name C\D\E innerhalb des Namensraums A\B zu A\B\C\D\E aufgelöst.
  5. Für unqualifizierte Namen wird der Name gemäß der aktuellen Importtabelle für die betreffende Symboltabelle übersetzt. Das bedeutet, dass klassenartige Namen gemäß der Klassen-/Namensraum-Importtabelle, Funktionsnamen gemäß der Funktions-Importtabelle und Konstantennamen gemäß der Konstanten-Importtabelle übersetzt werden. Beispielsweise wird nach use A\B\C; eine Verwendung wie etwa new C() zum Namen A\B\C() aufgelöst. Analog wird nach use function A\B\foo; eine Verwendung wie etwa foo() zum Namen A\B\foo aufgelöst.
  6. Wenn für unqualifizierte Namen keine Importregel zutrifft, und sich der Name auf ein klassenartiges Symbol bezieht, wird der aktuelle Namensraum vorangestellt. Beispielsweise wird new C() innerhalb des Namensraums A\B zum Namen A\B\C aufgelöst.
  7. Wenn für unqualifizierte Namen keine Importregel zutrifft, und sich der Name auf eine Funktion oder Konstante bezieht, und der Code sich außerhalb des globalen Geltungsbereichs befindet, wird der Name zur Laufzeit aufgelöst. Angenommen der Code befindet sich im Namensraum A\B, wird im folgenden erläutert, wie der Aufruf einer Funktion foo() aufgelöst wird:
    1. Zuerst wird nach der Funktion im aktuellen Namespace gesucht: A\B\foo().
    2. Es wird versucht, die Funktion foo() im globalen Namensraum zu finden und aufzurufen.

Beispiel #1 Illustration der Namensauflösung

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

// Funktionsaufrufe

foo(); // versucht zuerst die Funktion "foo" im Namespace "A" aufzurufen
// danach wird die globale Funktion "foo" aufgerufen

\foo(); // ruft die Funktion "foo" im globalen Namensraum auf

my\foo(); // ruft die Funktion "foo" im Namespace "A\my" auf

F(); // versucht zuerst die Funktion "F" im Namespace "A" aufzurufen,
// danach wird die globale Funktion "F" aufgerufen

// Klassenreferenzen

new B(); // erzeugt ein Objekt der Klasse "B" im Namespace "A"
// wenn diese Klasse nicht bekannt ist, so wird versucht per
// Autoload die Klasse "A\B" zu laden

new D(); // gemäß den Importregeln wird ein Objekt der Klasse "D"
// aus dem Namenspace "B" erzeugt
// wenn diese Klasse nicht bekannt ist, so wird versucht per
// Autoload die Klasse "B\D" zu laden

new F(); // gemäß den Importregeln wird ein Objekt der Klasse "E"
// aus dem Namespace "C" erzeugt
// wenn diese Klasse nicht bekannt ist, so wird versucht per
// Autoload die Klasse "C\E" zu laden

new \B(); // erzeugt ein Objekt der Klasse "B" aus dem globalen Namensraum
// wenn diese Klasse nicht bekannt ist, so wird versucht per
// Autoload die Klasse "B" zu laden

new \D(); // erzeugt ein Objekt der Klasse "D" aus dem globalen Namensraum
// wenn diese Klasse nicht bekannt ist, so wird versucht per
// Autoload die Klasse "D" zu laden

new \F(); // erzeugt ein Objekt der Klasse "F" aus dem globalen Namensraum
// wenn diese Klasse nicht bekannt ist, so wird versucht per
// Autoload die Klasse "F" zu laden

// statische Methoden und Funktionen mit Namespace aus anderen Namespaces

B\foo(); // ruft die Funktion "foo" aus dem Namensraum "A\B" auf

B::foo(); // ruft die Methode "foo" der Klasse "B" im Namensraum "A" auf
// wenn die Klasse "A\B" nicht bekannt ist, so wird versucht
// die Klasse "A\B" mittels Autoload zu laden

D::foo(); // ruft gemäß den Importregeln die Methode "foo" der Klasse "D"
// im Namensraum "B" auf
// wenn die Klasse "B\D" nicht bekannt ist, so wird versucht
// die Klasse "B\D" mittels Autoload zu laden

\B\foo(); // ruft die Funktion "foo" im Namespace "B" auf

\B::foo(); // ruft die Methode "foo" der Klasse "B" im
// globalen Namensraum auf
// wenn die Klasse "B" nicht bekannt ist, so wird versucht
// die Klasse "B" mittels Autoload zu laden

// statische Methoden und Funktionen mit Namespace aus den gleichen Namespaces

A\B::foo(); // ruft die Methode "foo" der Klasse "B" aus dem Namespace "A\A" auf
// wenn die Klasse "A\A\B" nicht bekannt ist, so wird
// versucht die Klasse "A\A\B" mittels Autoload zu laden

\A\B::foo(); // ruft die Methode "foo" der Klasse "B" aus dem Namespace "A" auf
// wenn die Klasse "A\B" nicht bekannt ist, so wird
// versucht die Klasse "A\B" mittels Autoload zu laden
?>
add a note add a note

User Contributed Notes 9 notes

up
37
kdimi
14 years ago
If you like to declare an __autoload function within a namespace or class, use the spl_autoload_register() function to register it and it will work fine.
up
33
rangel
15 years ago
The term "autoload" mentioned here shall not be confused with __autoload function to autoload objects. Regarding the __autoload and namespaces' resolution I'd like to share the following experience:

->Say you have the following directory structure:

- root
      | - loader.php
      | - ns
             | - foo.php

->foo.php

<?php
namespace ns;
class
foo
{
    public
$say;
   
    public function
__construct()
    {
       
$this->say = "bar";
    }
   
}
?>

-> loader.php

<?php
//GLOBAL SPACE <--
function __autoload($c)
{
    require_once
$c . ".php";
}

class
foo extends ns\foo // ns\foo is loaded here
{
    public function
__construct()
    {
       
parent::__construct();
        echo
"<br />foo" . $this->say;
    }
}
$a = new ns\foo(); // ns\foo also loads ns/foo.php just fine here.
echo $a->say;   // prints bar as expected.
$b = new foo// prints foobar just fine.
?>

If you keep your directory/file matching namespace/class consistence the object __autoload works fine.
But... if you try to give loader.php a namespace you'll obviously get fatal errors.
My sample is just 1 level dir, but I've tested with a very complex and deeper structure. Hope anybody finds this useful.

Cheers!
up
4
safakozpinar at NOSPAM dot gmail dot com
14 years ago
As working with namespaces and using (custom or basic) autoload structure; magic function __autoload must be defined in global scope, not in a namespace, also not in another function or method.

<?php
namespace Glue {
   
/**
     * Define your custom structure and algorithms
     * for autoloading in this class.
     */
   
class Import
   
{
        public static function
load ($classname)
        {
            echo
'Autoloading class '.$classname."\n";
            require_once
$classname.'.php';
        }
    }
}

/**
* Define function __autoload in global namespace.
*/
namespace {
   
    function
__autoload ($classname)
    {
        \
Glue\Import::load($classname);
    }

}
?>
up
1
Kavoir.com
10 years ago
For point 4, "In example, if the namespace A\B\C is imported as C" should be "In example, if the class A\B\C is imported as C".
up
-3
llmll
9 years ago
The mentioned filesystem analogy fails at an important point:

Namespace resolution *only* works at declaration time. The compiler fixates all namespace/class references as absolute paths, like creating absolute symlinks.

You can't expect relative symlinks, which should be evaluated during access -> during PHP runtime.

In other words, namespaces are evaluated like __CLASS__ or self:: at parse-time. What's *not* happening, is the pendant for late static binding like static:: which resolves to the current class at runtime.

So you can't do the following:

namespace Alpha;
class Helper {
    public static $Value = "ALPHA";
}
class Base {
    public static function Write() {
        echo Helper::$Value;
    }
}

namespace Beta;
class Helper extends \Alpha\Helper {
    public static $Value = 'BETA';
}   
class Base extends \Alpha\Base {}   

\Beta\Base::Write(); // should write "BETA" as this is the executing namespace context at runtime.

If you copy the write() function into \Beta\Base it works as expected.
up
-6
rangel
15 years ago
The term "autoload" mentioned here shall not be confused with __autoload function to autoload objects. Regarding the __autoload and namespaces' resolution I'd like to share the following experience:

->Say you have the following directory structure:

- root
      | - loader.php
      | - ns
             | - foo.php

->foo.php

<?php
namespace ns;
class
foo
{
    public
$say;
   
    public function
__construct()
    {
       
$this->say = "bar";
    }
   
}
?>

-> loader.php

<?php
//GLOBAL SPACE <--
function __autoload($c)
{
    require_once
$c . ".php";
}

class
foo extends ns\foo // ns\foo is loaded here
{
    public function
__construct()
    {
       
parent::__construct();
        echo
"<br />foo" . $this->say;
    }
}
$a = new ns\foo(); // ns\foo also loads ns/foo.php just fine here.
echo $a->say;   // prints bar as expected.
$b = new foo// prints foobar just fine.
?>

If you keep your directory/file matching namespace/class consistence the object __autoload works fine.
But... if you try to give loader.php a namespace you'll obviously get fatal errors.
My sample is just 1 level dir, but I've tested with a very complex and deeper structure. Hope anybody finds this useful.

Cheers!
up
-6
CJ Taylor
10 years ago
It took me playing with it a bit  as I had a hard time finding documentation on when a class name matches a namespace, if that's even legal and what behavior to expect.  It IS explained in #6 but I thought I'd share this with other souls like me that see it better by example.  Assume all 3 files below are in the same directory.

file1.php
<?php
namespace foo;

class
foo {
  static function
hello() {
    echo
"hello world!";
  }
}
?>

file2.php
<?php
namespace foo;
include(
'file1.php');

foo::hello(); //you're in the same namespace, or scope.
\foo\foo::hello(); //called on a global scope.
?>

file3.php
<?php
include('file1.php');

foo\foo::hello(); //you're outside of the namespace
\foo\foo::hello(); //called on a global scope.
?>

Depending upon what you're building (example: a module, plugin, or package on a larger application), sometimes declaring a class that matches a namespace makes sense or may even be required.  Just be aware that if you try to reference any class that shares the same namespace, omit the namespace unless you do it globally like the examples above.

I hope this is useful, particularly for those that are trying to wrap your head around this 5.3 feature.
up
-4
anrdaemon at freemail dot ru
8 years ago
Namespaces may be case-insensitive, but autoloaders most often do.
Do yourself a service, keep your cases consistent with file names, and don't overcomplicate autoloaders beyond necessity.
Something like this should suffice for most times:

<?php

namespace org\example;

function
spl_autoload($className)
{
 
$file = new \SplFileInfo(__DIR__ . substr(strtr("$className.php", '\\', '/'), 11));
 
$path = $file->getRealPath();
  if(empty(
$path))
  {
    return
false;
  }
  else
  {
    return include_once
$path;
  }
}

\
spl_autoload_register('\org\example\spl_autoload');
?>
up
-7
dn dot permyakov at gmail dot com
10 years ago
Can someone explain to me -  why do we need p.4 if we have p.2 (which covers both unqualified and qualified names)?
To Top