Regras de resolução de nomes

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

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

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

Este é um identificador sem separador de namespace, como Foo.

Nome qualificado

Este é um identificador com separador de namespace, como Foo\Bar.

Nome totalmente qualificado

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

Nome relativo

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

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

  1. Nomes totalmente qualificados sempre são resolvidos para o nome sem separador de namespace à esquerda. Por exemplo, \A\B será resolvido para A\B.

  2. Nomes relativos sempre são resolvidos para o nome com namespace substituído pelo namespace atual. Se o nome ocorrer no namespace global, o prefixo namespace\ será removido. Por exemplo, namespace\A dentro do namespace X\Y será resolvido para X\Y\A. O mesmo nome dentro do namespace global será resolvido para A.

  3. Para nomes qualificados, o primeiro segmento do nome será traduzido conforme a tabela de importação de classe/namespace atual. Por exemplo, se o namespace A\B\C for importado como C, o nome C\D\E será traduzido para A\B\C\D\E.

  4. Para nomes qualificados, se nenhuma regra de importação se aplicar, o namespace atual será prefixo ao nome. Por exemplo, o nome C\D\E dentro do namespace A\B, será resolvido para A\B\C\D\E.

  5. Para nomes não qualificados, o nome será traduzido conforme a tabela de importação atual para o respectivo tipo de símbolo. Isso significa que nomes semelhantes a classes serão traduzidos conforme a tabela de importação de classe/namespace, nomes de funções conforme a tabela de importação de funções e constantes conforme a tabela de importação de constantes. Por exemplo, após a declaração use A\B\C; um uso como new C() será resolvido para o nome A\B\C(). Da mesma forma, após a declaração use function A\B\foo; um uso como foo() será resolvido para o nome A\B\foo.

  6. Para nomes não qualificados, se nenhuma regra de importação se aplicar e o nome se referir a um símbolo de classe, o namespace atual será prefixo. Por exemplo new C() dentro do namespace A\B será resolvido 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 código estiver fora do namespace global, o nome será resolvido em tempo de execução. Supondo que o código esteja no namespace A\B, é assim que uma chamada para a função foo() será resolvida:

    1. Procura pela função no namespace atual: A\B\foo().

    2. Tenta encontrar e 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" definida no namespace "A"
             // então chama a função global "foo"

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

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

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

// referências de classe

new B();    // cria um objeto da classe "B" definida no namespace "A"
            // se não for encontrada, tenta carregar automaticamente a classe  "A\B"

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

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

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

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

new \F();   // cria um objeto da classe "F" definida no escopo global
            // se não for encontrada, 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 a classe "A\B" não for encontrada, 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, 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 a classe "B" não for encontrada, 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, 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, tenta carregar automaticamente a classe "A\B"
+adicionar nota

Notas de Usuários 5 notes

up
41
kdimi
15 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
35
rangel
17 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
16 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
llmll
11 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
-1
Kavoir.com
12 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".
To Top