Правила разрешения имён

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

Для целей этих правил разрешения приведём важные определения:

Определения имени пространства имён
Неполное имя

Идентификатор без разделителя пространств имён, например Foo

Полное имя

Идентификатор с разделителем пространств имён, например Foo\Bar

Абсолютное имя

Идентификатор с разделителем пространств имён, который начинается с разделителя пространств имён, например \Foo\Bar. Пространство имён \Foo — также абсолютное имя.

Относительное имя

Идентификатор, который начинается с ключевого слова namespace, например namespace\Foo\Bar.

Имена разрешаются по следующим правилам:

  1. Абсолютные имена разрешаются в имя без ведущего разделителя пространства имён. Например, \A\B разрешается в A\B.

  2. Относительные имена разрешаются в имя с заменой ключевого слова namespace текущим пространством имён. Префикс namespace\ удаляется, если имя встречается в глобальном пространстве имён. Например, имя namespace\A внутри пространства имён X\Y разрешается в X\Y\A. То же имя в глобальном пространстве имён разрешается в A.

  3. В полных именах первый сегмент имени преобразовывается с учётом текущей таблицы импорта класса или пространства имён. Например, если пространство имён A\B\C импортировано как C, то имя C\D\E преобразуется в A\B\C\D\E.

  4. В полных именах, если не применялось правило импорта, текущее пространство имён добавляется к имени. Например, имя C\D\E внутри пространства имён A\B разрешится в A\B\C\D\E.

  5. Неполные имена преобразовываются с учётом текущей таблицы импорта и типа элемента. То есть имена как у классов преобразовываются с учётом таблицы импорта классов или пространств имён, имена функций — с учётом таблицы импорта функций, а константы — таблицы импорта констант. Например, при записи use A\B\C;, вызов new C() разрешается в A\B\C(). Аналогично, при записи use function A\B\foo; вызов foo() разрешается в A\B\foo.

  6. В начало неполных имён, если не применялось правило импорта и имя относится к элементу с именем как у класса, добавляется текущее пространство имён. Например, имя класса в выражении new C() внутри пространства имён A\B разрешится в имя A\B\C.

  7. В неполных именах, если не применялось правило импорта и имя относится к функции или константе, а код лежит за пределами глобального пространства имён, имя разрешается при выполнении. Вот как разрешится вызов функции foo() в коде в пространстве имён A\B:

    1. Выполняется поиск функции из текущего пространства имён: A\B\foo().

    2. PHP пытается найти и вызвать функцию foo() из глобального пространства имён.

Пример #1 Примеры разрешения имён

<?php

namespace A;

use B\D, C\E as F;

// Вызовы функций

foo();      // Сперва пытается вызвать функцию foo, которую определили в пространстве имён A,
            // затем вызывает глобальную функцию foo

\foo();     // Вызывает функцию foo, которую определили в глобальном пространстве

my\foo();   // Вызывает функцию foo, которую определили в пространстве имён A\my

F();        // Сперва пытается вызвать функцию F, которую определили в пространстве имён A,
            // затем вызывает глобальную функцию F

// Ссылки на классы

new B();    // Создаёт объект класса B, который определили в пространстве имён A.
            // Если класс не найден, то пытается автоматически загрузить класс A\B

new D();    // Следуя правилам импорта, создаёт объект класса D, который определили в пространстве имён B,
            // если класс не найден, то пытается автоматически загрузить класс B\D

new F();    // Следуя правилам импорта, создаёт объект класса E, который определили в пространстве имён C,
            // если класс не найден, то пытается автоматически загрузить класс класса C\E

new \B();   // Создаёт объект класса B, который определили в глобальном пространстве,
            // если класс не найден, то пытается автоматически загрузить класс B

new \D();   // Создаёт объект класса D, который определили в глобальном пространстве,
            // если класс не найден, то пытается автоматически загрузить класс D

new \F();   // Создаёт объект класса F, который определили в глобальном пространстве,
            // если класс не найден, то пытается автоматически загрузить класс F

// Статические методы и функции пространства имён из другого пространства имён

B\foo();    // Вызывает функцию foo из пространства имён A\B

B::foo();   // Вызывает метод foo из класса B, который определили в пространстве имён A,
            // если класс A\B не найден, то пытается автоматически загрузить класс A\B

D::foo();   // Следуя правилам импорта, вызывает метод foo класса D, который определили в пространстве имён B,
            // если класс B\D не найден, то пытается автоматически загрузить класс B\D

\B\foo();   // Вызывает функцию foo из пространства имён B

\B::foo();  // Вызывает метод foo класса B из глобального пространства,
            // если класс B не найден, то пытается автоматически загрузить класс B

// Статические методы и функции пространства имён из текущего пространства имён

A\B::foo();   // Вызывает метод foo класса B из пространства имён A\A,
              // если класс A\A\B не найден, то пытается автоматически загрузить класс A\A\B

\A\B::foo();  // Вызывает метод foo класса B из пространства имён A,
              // если класс A\B не найден, то пытается автоматически загрузить класс A\B
+Добавить

Примечания пользователей 5 notes

up
41
kdimi
15 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
35
rangel
17 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
5
safakozpinar at NOSPAM dot gmail dot com
15 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
llmll
11 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
-1
Kavoir.com
12 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".
To Top