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.
(PHP 5 >= 5.3.0, PHP 7, PHP 8)
A continuación se exponen unas definiciones importantes para estas reglas de resolución:
Es un identificador sin un separador de espacios de nombres, como Foo
Es un identificador con un separador de espacios de nombres, como Foo\Bar
Es un identificador con un separador de espacios de nombres que comienza con un separador
de espacios de nombres, como \Foo\Bar
. namespace\Foo
también es un nombre completamente cualificado.
Los nombres se resuelven siguiendo estas reglas de resolución:
new \A\B
se resuelve con la clase A\B
.
A\B\C
se importa como
C
, una llamada a
C\D\e()
se traduce como A\B\C\D\e()
.
C\D\e()
se lleva a cabo dentro del espacio de nombres A\B
,
se traduce como A\B\C\D\e()
.
A\B\C
se importa como C, new C()
se
traduce como new A\B\C()
.
foo()
:
A\B\foo()
.
foo()
.
A\B
), las llamadas a nombres de clases
no cualificados o cualificados (no los completamente cualificados) se resuelven durante la
ejecución. Así se resuelve una llamada a
new C()
o a new D\E()
.
Para new C()
:
A\B\C
.
A\B\C
.
new D\E()
:
A\B\D\E
.
A\B\D\E
.
new \C()
.
Ejemplo #1 Las resoluciones de nombres ilustradas
<?php
namespace A;
use B\D, C\E as F;
// llamadas a funciones
foo(); // primero se intenta llamar a "foo" definida en el espacio de nombres "A"
// después se llama a la función global "foo"
\foo(); // se llama a la función "foo" definidia en el ámbito global
mi\foo(); // se llama a la función "foo" definida en el espacio de nombres "A\mi"
F(); // primero se intenta llamar a "F" definida en el espacio de nombres "A"
// después se llama a la función global "F"
// referecias a clases
new B(); // crea un objeto de la clase "B" definida en el espacio de nombres "A"
// si no se encuentra, se intenta autocargar la clase "A\B"
new D(); // usando las reglas de importación, se crea un objeto de la clase "D" definida en el espacio de nombres "B"
// si no se encuentra, se intenta autocargar la clase "B\D"
new F(); // usando las reglas de importación, se crea un objeto de la clase "E" definida en el espacio de nombres "C"
// si no se encuentra, se intenta autocargar la clase "C\E"
new \B(); // crea un objeto de la clase "B" definida en el ámbito global
// si no se encuentra, se intenta autocargar la clase "B"
new \D(); // crea un objeto de la clase "D" definida en el ámbito global
// si no se encuentra, se intenta autocargar la clase "D"
new \F(); // crea un objeto de la clase "F" definida en el ámbito global
// si no se encuentra, se intenta autocargar la clase "F"
// métodos estáticos y funciones de un espacio de nombres desde otro espacio de nombres
B\foo(); // se llama a la función "foo" desde el espacio de nombres "A\B"
B::foo(); // se llama al método "foo" de la clase "B" definidia en el espacio de nombres "A"
// si no se encuentra la clase "A\B", se intenta autocargar la clase "A\B"
D::foo(); // usando las reglas de importación, se llama al método "foo" de la clase "D" definida en el espacio de nombres "B"
// si no se encuentra la clase "B\D", se intenta autocargar la clase "B\D"
\B\foo(); // se llama a la función "foo" desde el espacio de nombres "B"
\B::foo(); // se llama al método "foo" de la clase "B" desde el ámbito global
// si no es encuentra la clase "B", se intenta autocargar la clase "B"
// métodos estáticos yfunciones de un espacio de nombres del espacio de nombres actual
A\B::foo(); // se llama al método "foo" de la clase "B" desde el espacio de nombres "A\A"
// si no se encuentra la clase "A\A\B", se intenta autocargar la clase "A\A\B"
\A\B::foo(); // se llama al método "foo" de la clase "B" desde el espacio de nombres "A"
// si no se encuentra la clase "A\B", se intenta autocargar la clase "A\B"
?>
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.
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);
}
}
?>
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.
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!
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.
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');
?>
Can someone explain to me - why do we need p.4 if we have p.2 (which covers both unqualified and qualified names)?