Regras de resolução de nomes

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

Para efeitos dessas regras de resolução, aqui estão algumas definições importantes:

Definições de nomes de namespace
Nome não qualificado

Esse é um identificador sem o separador de namespace, tal como Foo

Nome qualificado

Esse é um identificador com separador de namespace, tal como Foo\Bar

Nome totalmente qualificado

Esse é um identificador com separador de namespace que começa com um separador de namespace, tal como \Foo\Bar. O namespace \Foo também é um nome totalmente qualificado.

Nome relativo

Esse é um identificador que começa com namespace, tal como namespace\Foo\Bar.

Os nomes são resolvidos seguindo estas regras de resolução:

  1. Nomes totalmente qualificados sempre resolvem para o nome sem separador de namespace à esquerda. Por exemplo, \A\B resolve para A\B.
  2. Nomes relativos sempre resolvem para nome com namespace substituído pelo namespace atual. Se o nome ocorrer no namespcace global, o prefixo do namespace\ é removido. Por exemplo namespace\A dentro do namespace X\Y resolve para X\Y\A. O mesmo nome dentro do namespace global resolve para A.
  3. Para nomes qualificados, o primeiro segmento do nome é traduzido de acordo com a atual tabela de importação de classe/namespace. Por exemplo, se o namespace A\B\C é importado como C, o nome C\D\E é traduzido para A\B\C\D\E.
  4. Para nomes qualificados, se nenhuma regra de importação se aplica, o namespace atual será anexado ao nome. Por examplo, o nome C\D\E dentro do namespace A\B, resolve para A\B\C\D\E.
  5. Para nomes não qualificados, o nome é traduzido de acordo com a atual tabela de importação para o respectivo tipo de simbolo. Isso significa que nomes semelhantes a classes são traduzidos de acordo com a tabela de importação de classe/namespace, nomes de funções de acordo com a tabela de importação de funções e constantes de acordo com a tabela de importação de constantes. Por exemplo, após o use A\B\C; um uso como new C() resolve para o nome A\B\C(). Da mesma forma, após o use function A\B\fn; um uso como fn() resolve para o nome A\B\fn.
  6. Para nomes não qualidaficado, se nenhuma regra de importação se aplicar e o nome se referir a um símbolo de classe, o namespace atual será prefixado. Por exemplo new C() dentro do namespace A\B resolve para o nome A\B\C.
  7. Para nomes não qualificados, se nenhuma regra de importação se aplicar e o nome se referir a uma função ou constante e o codigo está fora do namespace global, o nome é resolvido em tempo de execução. Supondo que o código seja no namespace A\B, aqui é como uma chamada para a função foo() é resolvida:
    1. Ele procura por uma função do namespace atual: A\B\foo().
    2. Ele tenta encontrar a chamar a função global foo().

Exemplo #1 Resoluções de nomes ilustradas

<?php
namespace A;
use
B\D, C\E as F;

// chamadas de funções

foo(); // primeiro tenta chamar "foo" definido no namespace "A"
// então chama a função global "foo"

\foo(); // chama a função "foo" definida no escopo global

my\foo(); // chama a função "foo" definida no namespace "A\my"

F(); // primeiro tenta chamar "F" definida no namespace "A"
// e depois chama a função global "F"

// referência de classe

new B(); // cria objeto da classe "B" definida no namespace "A"
// se não encontrou, ele tenta carregar autolomaticamente a classe "A\B"

new D(); // usando regras de importação, cria objeto de classe "D" definido no namespace "B"
// se não for encontrado, ele tenta carregar automaticamente a classe "B\D"

new F(); // usando regras de importação, cria objeto de classe "E" definido no namespace "C"
// se não for encontrado, ele tenta carregar automaticamente a classe "C\E"

new \B(); // cria objeto de classe "B" definida no escopo global
// se não for encontrado, ele tenta carregar automaticamente a classe "B"

new \D(); // cria objeto de classe "D" definida no escopo global
// se não for encontrado, ele tenta carregar automaticamente a classe "D"

new \F(); // cria objeto de classe "F" definido no escopo global
// se não for encontrado, ele tenta carregar automaticamente a classe "F"

// métodos estáticos/funções de namespace de outro namespace

B\foo(); // chama a função "foo" do namespace "A\B"

B::foo(); // chama o método "foo" da classe "B" definida no namespace "A"
// se classe "A\B" não for encontrada, ele tenta carregar automaticamente a classe "A\B"

D::foo(); // usando regras de importação, chama o método "foo" da classe "D" definida no namespace "B"
// se a classe "B\D" não for encontrada, ele tenta carregar automaticamente a classe "B\D"

\B\foo(); // chama a função "foo" do namespace "B"

\B::foo(); // chama o método "foo" da classe "B" do escopo global
// se classe "B" não for encontrada, ele tenta carregar automaticamente a classe "B"

// métodos estáticos/funções de namespace do namespace atual

A\B::foo(); // chama o método "foo" da classe "B" do namespace "A\A"
// se a classe "A\A\B" não for encontrada, ele tenta carregar automaticamente a class "A\A\B"

\A\B::foo(); // chama o método "foo" da classe "B" do namespace "A"
// se a classe "A\B" não for encontrada, ele tenta carregar automaticamente a classe "A\B"
?>
add a note

User Contributed Notes 9 notes

up
37
kdimi
12 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
13 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
4
safakozpinar at NOSPAM dot gmail dot com
12 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
Kavoir.com
9 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
-3
llmll
8 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
-6
rangel
13 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
-6
CJ Taylor
9 years ago
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.
up
-4
anrdaemon at freemail dot ru
7 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');
?>
up
-7
dn dot permyakov at gmail dot com
8 years ago
Can someone explain to me -  why do we need p.4 if we have p.2 (which covers both unqualified and qualified names)?
To Top