PHP Conference Nagoya 2025

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

(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

?>
Добавить

Примечания пользователей 7 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
5
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
0
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
-2
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
-5
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
-5
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');
?>
To Top