Правила разбора имён
Разбор имён происходит по следующим правилам:
-
Все квалифицированные имена транслируются во время компиляции в
соответствии с текущими импортированными пространствами имён.
К примеру, если импортировано постранство имён A::B::C, вызов
C::D::e()
будет транслирован какA::B::C::D::e()
. -
Неквалифицированные имена классов транслируются во время компиляции
в соответствии с текущими импортированными пространствами имён
(полные имена заменяют короткие импортированные имена). К примеру,
если пространство имён A::B::C импортировано,
new C()
будет транслировано какnew A::B::C()
. - Внутри пространства имён вызов неквалифицированных функций, определенных в этом же пространстве имён интерпертируется как вызов в данном пространстве имён во время компиляции.
-
Внутри пространства имён (например A::B) вызов
неквалифицированных функций, не определенных в этом пространстве имён
будет разрешаться во время выполнения.
Вызов функции foo() будет разрешаться следующим образом:
- Поиск в текущем пространстве имён: A::B::foo().
- Поиск внутренней PHP функции foo().
-
Внутри пространства имён (например A::B), вызов
неквалифицированных классов разрешается во время выполнения. Например
вызов
new C()
будет разрешаться следующим образом:- Поиск класса в текущем пространстве имён: A::B::C.
- Попытка вызова внутреннего PHP-класса C.
- Попытка автозагрузки A::B::C.
new ::C()
. -
Вызов квалифицированных функций разрешается во время выполнения.
Например вызов A::B::foo() будет разрешаться следующим образом :
- Поиск функции foo() в пространстве имён A::B.
- Поиск класса A::B и вызов его статического метода foo(). Будет сделана автозагрузка класса, если необходимо.
-
Квалифицированные имена классов разрешаются во время компиляции, как
классы соответствующего пространства имён. К примеру
new A::B::C()
будет ссылаться на класс C пространства имён A::B.
Пример #1 Примеры разбора имён
<?php
namespace A;
// вызовы функций
foo(); // сначала попытка вызвать "foo" определенную в пространстве имён "A"
// затем вызов внутренней функции "foo"
::foo(); // вызоы функции "foo" определенной глобально
// class references
new B(); // сначала попытка создать объект класса "B" определенного в пространстве имён "A"
// затем создание объекта внутреннего класса "B"
new ::B(); // создать объект класса "B" определенного глобально
// статические методы/функции пространства имён из других (не текущих) пространств имён
B::foo(); // сначала попытка вызвать функцию "foo" из пространства имён "A::B"
// затем вызов метода "foo" внутреннего класса "B"
::B::foo(); // сначала попытка вызова функции "foo" из пространства имён "B"
// затем вызов метода "foo" класса "B" из глобального пространства имён
// статические методы/функции пространства имён из текущего пространства имён
A::foo(); // сначала попытка вызова функции "foo" пространства имён "A::A"
// затем попытка вызова метода "foo" класса "A" из пространства имён "A"
// затем попытка вызова функции "foo" пространства имён "A"
// затем вызов метода "foo" внутреннего класса "A"
::A::foo(); // сначала попытка вызова функции "foo" пространства имён "A"
// затем вызов метода "foo" класса "A" из глобального пространства имён
?>
- Обзор пространств имен
- Определение пространств имен
- Определение подпространств имен
- Описание нескольких пространств имен в одном файле
- Использование пространства имен: основы
- Пространства имен и динамические особенности языка
- Ключевое слово namespace и константа __NAMESPACE__
- Использование пространств имен: импорт/создание псевдонима имени
- Глобальное пространство
- Использование пространств имен: переход к глобальной функции/константе
- Правила разрешения имен
- Часто задаваемые вопросы (FAQ): вещи, которые вам необходимо знать о пространствах имен
Коментарии
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!
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!
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);
}
}
?>
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.
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".
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.
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');
?>