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)
Dans le cadre des règles de résolution, il y a plusieurs définitions importantes :
Ceci est un identifiant ne contenant pas un séparateur d'espace de noms.
Par exemple : Foo
Ceci est un identifiant contenant un séparateur d'espace de noms.
Par exemple : Foo\Bar
Ceci est un identifiant qui commence par un séparateur d'espace de noms.
Par exemple : \Foo\Bar. le namespace Foo
est aussi un nom absolu.
C'est un identifiant commençant par namespace, tel que
namespace\Foo\Bar.
Les noms sont résolus en suivant les règles suivantes :
Les noms absolus sont toujours résolus en le nom sans le séparateur de namespace initial.
Par exemple, \A\B est résolu en A\B.
Les noms relatifs sont toujours résolus en remplaçant
namespace par le namespace courant.
Si le nom apparaît dans le namespace global, le préfixe
namespace\ est retiré. Par exemple, namespace\A
dans le namespace X\Y est résolu en X\Y\A.
Le même nom dans le namespace global est résolu en A.
Pour les noms qualifiés, le premier segment est traduit
en accord avec la table d'importation des class/namespace.
Par exemple, si le namespace A\B\C est importé comme C, le nom C\D\E est traduit par A\B\C\D\E.
Pour les noms qualifiés, si aucune règle d'import ne
s'applique, le namespace courant est préfixé au nom.
Par exemple le nom C\D\E dans le namespace A\B,
est traduit par A\B\C\D\E.
Pour les noms non qualifiés, le nom est traduit en lien avec la table courante d'importation pour le type de symbole respectif.
Cela signifie qu'un nom ressemblant à une classe est traduit en accord avec la table d'importation des
class/namespace, les noms de fonctions en utilisant la
table d'importation des fonctions, et les constantes en
utilisant la table d'importation des constantes.
Par exemple, après
use A\B\C; un usage tel que new C() correspond au nom
A\B\C(). De la même manière, après use function A\B\foo; un usage
tel que foo() correspond au nom A\B\foo.
Pour les noms non qualifiés, si aucune règle ne s'applique, et que le nom
fait référence à une classe, le namespace courant sert de préfixe.
Par exemple new C() dans le namespace
A\B correspond au nom A\B\C.
Pour les noms non qualifiés, si aucune règle ne s'applique, et que le nom
fait référence à une fonction ou une constante, et que le code est en
dehors du namespace global, le nom est résolu à l'exécution.
Supposons que le code est dans le namespace
A\B, voici comment un appel à la fonction foo() est résolu :
Il recherche une fonction dans le namespace courant :
A\B\foo().
Il essaie de trouver et appeler la fonction globale
foo().
Exemple #1 Exemples de résolutions d'espaces de noms
<?php
namespace A;
use B\D, C\E as F;
// appels de fonctions
foo(); // tente d'appeler la fonction "foo" dans le namespace "A"
// puis appelle la fonction globale "foo"
\foo(); // appelle la fonction "foo" définie dans le namespace global
my\foo(); // appelle la fonction "foo" définie dans le namespace "A\my"
F(); // tente d'appeler la fonction "F" définie dans l'espace "A"
// puis tente d'appeler la fonction globale "F"
// références de classes
new B(); // crée un objet de la classe "B" définie dans le namespace "A"
// si non trouvé, il essaie l'autochargement sur la classe "A\B"
new D(); // crée un objet de la classe "D" définie dans le namespace "B"
// si non trouvé, il essaie l'autochargement sur la classe "B\D"
new F(); // crée un objet de la classe "E" définie dans le namespace "C"
// si non trouvé, il essaie l'autochargement sur la classe "C\E"
new \B(); // crée un objet de la classe "B" définie dans le namespace global
// si non trouvé, il essaie l'autochargement sur la classe "B"
new \D(); // crée un objet de la classe "D" définie dans le namespace global
// si non trouvé, il essaie l'autochargement sur la classe "D"
new \F(); // crée un objet de la classe "F" définie dans le namespace global
// si non trouvé, il essaie l'autochargement sur la classe "F"
// méthodes statiques et fonctions d'espace de noms d'un autre espace
B\foo(); // appelle la fonction "foo" du namespace "A\B"
B::foo(); // appelle la méthode "foo" de la classe "B" définie dans le namespace "A"
// si la classe "A\B" n'est pas trouvée, il essaie l'autochargement sur la classe "A\B"
D::foo(); // appelle la méthode "foo" de la classe "D" définie dans l'espace de noms "B"
// si la classe "B\D" n'est pas trouvée, il essaie l'autochargement sur la classe "B\D"
\B\foo(); // appelle la fonction "foo" de l'espace de noms "B"
\B::foo(); // appelle la méthode "foo" de la classe "B" située dans l'espace de noms global
// si la classe "B" n'est pas trouvée, il essaie l'autochargement sur la classe "B"
// méthodes statiques et fonctions d'espace de noms de l'espace courant
A\B::foo(); // appelle la méthode "foo" de la classe "B" de l'espace de noms "A\A"
// si la classe "A\A\B" n'est pas trouvée, il essaie l'autochargement sur la classe "A\A\B"
\A\B::foo(); // appelle la méthode "foo" de la classe "B" de l'espace de noms "A"
// si la classe "A\B" n'est pas trouvée, il essaie l'autochargement sur la classe "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);
}
}
?>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.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".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');
?>