Entendendo namespaces no PHP

Namespaces possibilitam o agrupamento de classes, interfaces, funções e constantes, visando evitar o conflito entre seus nomes, atuando como um encapsulador para estes itens, seu funcionamento é equivalente ao de diretórios em sistemas operacionais, onde dois arquivos de mesmo nome não podem existir em um único diretório, mas nada impede a existência de dois arquivos de mesmo nome localizados em diretórios distintos, este mesmo princípio é aplicado no PHP através de namespaces, ao utilizar este recurso temos mais liberdade na hora de criar classes, funções e etc, não sendo mais necessário utilizar prefixo para diferenciar seus nomes.

Este recurso está disponível a partir da versão 5.3 do PHP.

Definindo namespaces

Namespaces são declarados utilizando a palavra-chave namespace. Um arquivo que contenha namespace deve realizar a declaração do mesmo logo no inicio, antes de qualquer código, com exceção de comentários, espaços em branco e declare.

<?php
namespace Project;
// ...

Seguindo o pensamento dos diretórios utilizado na introdução deste artigo, o recurso de namespaces também permite adicionar nomes com estrutura hierárquica, ou seja, sub-namespaces.

<?php
namespace Project\Model;
// ...

Declarando diversos namespaces em um arquivo

Embora não seja uma prática recomendada, podemos declarar mais de um namespace no mesmo arquivo, confira:

1. Combinação de sintaxe

<?php
namespace Project;
class User {}

namespace AnotherProject;
class User {}
?>

2. Sintaxe entre colchetes

<?php
namespace Project {
class User {}
}

namespace AnotherProject {
class User {}
}
?>

3. Namespace indefinido

<?php
namespace Project {
class User {}
}

// Global scope
namespace {
class User {}
}
?>

Utilizando namespaces

Antes de utilizar namespaces no PHP, é importante entender como o PHP identifica o contexto do qual o elemento será solicitado. Para isso vamos realizar uma analogia de como os sistemas operacionais acessam arquivos.

Há três maneiras de acessar um arquivo em sistemas operacionais:

  • Nome de arquivo relativo, por exemplo: foo.txt, resultado: diretorioatual/foo.txt
  • Nome de caminho relativo, por exemplo: subdiretorio/foo.txt, resultado: diretorioatual/subdiretorio/foo.txt
  • Nome de caminho absoluto, por exemplo: /main/foo.txt, resultado: /main/foo.txt

O mesmo princípio pode ser aplicado a elementos utilizando namespace no PHP, por exemplo, uma classe pode ser solicitada de três maneiras.

1. Nome não qualificado, ex: $var = new User;

Caso o namespace atual seja Project\Model, será solicitado Project\Model\User. Se o código for global, ou seja, sem namespace definido, irá solicitar User.

// Example - Namespace scope
namespace Project\Model;
class User {}
$var = new User;
// Resultado: Project\Model\User

// Example - Global scope
class User {}
$var = new User;
// Resultado: User

2. Nome qualificado, ex: $var = new Model\User();

Caso o namespace atual seja Project, será solicitado Project\Model\User. Se o código for global, ou seja, sem namespace definido, irá solicitar Model\User.

// Example - Namespace scope
namespace Project;
$var = new Model\User;
// Resultado: Project\Model\User

// Example - Global scope
$var = new Model\User;
// Resultado: Model\User

3. Nome absoluto, ex: $var = new \Project\Model\User();

Este tipo sempre irá solicitar pelo nome absoluto \Project\Model\User.

// Example - Namespace scope
namespace Project;
$var = new \Project\Model\User;
// Resultado: Project\Model\User

// Example - Global scope
$var = new \Project\Model\User;
// Resultado: Project\Model\User

Aliasing / Importing

Uma caracteristica importante ao trabalhar com namespaces é a possibilidade de importar e atribuir apelidos. A palavra-chave use é utilizada para importar classes, interfaces ou namespaces através de seus nomes, não é possível importar funções ou constantes. Para adicionar apelidos, utilizamos a palavra-chave as, escolhendo um nome mais acessível, ou seja, um apelido, existem duas maneiras de realizar importações:

1. Importação simples

use Project\Model\User;
// É o mesmo que: use Project\Model\User as User;

$var = new User;
// Resultado: Project\Model\User

2. Múltiplas importações

O PHP oferece um atalho para realizar múltiplas importações na mesma linha, onde cada importação é separada através de uma virgula.

use Project\Model\User, Project\Model\Post as Article;

$var = new Article;
// Resultado: Project\Model\Post

A importação não afeta nomes dinâmicos de funções, classes ou constantes.

use Project\Model\Post;

$var = new Post;
// Resultado: Project\Model\Post

$name = 'Post';
$var = new $name;
// Resultado: Post

Além disso, a importação só afeta nomes não qualificados e qualificados, nomes absolutos não são afetados por importações.

use Project\Model\Post;

$var = new Post;
// Resultado: Project\Model\Post

$var = new \Post;
// Resultado: Post

A palavra-chave use deve ser declarada em escopo global ou dentro de declarações de namespace.

Constante __NAMESPACES__

A constante mágica __NAMESPACE__ é uma constante dinâmica que possui seu valor baseado no namespace corrente, ou seja, seu valor irá variar de acordo com o escopo/namespace no qual for utilizada.

<?php
namespace Project\Model;

echo __NAMESPACE__;
// Resultado: Project\Model
?>

A palavra-chave namespace também pode ser utilizada para solicitar um elemento do namespace atual ou de um sub-namespace.

<?php
namespace Project

$var = new namespace\Model\User;
// Resultado: Project\Model\User
?>

Caso o arquivo não tenha namespace, ou seja, um arquivo global, o valor da constante __NAMESPACE__ será uma string vazia.

Global space

Sem a definição de um namespace, todas as definições de classes, interfaces, funções e constantes são colocadas no escopo global, como era no PHP antes dos namespaces serem suportados.

// file User.php
class User {}

// Example - Namespace scope
namespace Project\Model;

require_once("User.php");
$var = new User;
// Resultado: Project\Model\User

// Example - Global scope
require_once("User.php");
$var = new User;
// Resultado: User

Prefixando o nome do elemento com \ especificará que o elemento é requerido do escopo global até mesmo no contexto de namespace.

// file User.php
class User {}

// Example - Namespace scope
namespace Project\Model;

require_once("User.php");
$var = new \User;
// Resultado: User

// Example - Global scope
require_once("User.php");
$var = new \User;
// Resultado: User

Utilizando autoload para carregar arquivos

Utilizar autoload nunca foi tão fácil, principalmente se você estruturar seus arquivos em diretórios nomeados de acordo com seus namespaces, por exemplo, no namespace “Project\Model” existe uma classe nomeada User, se estruturarmos os arquivos de maneira que User.php fique no diretório “Project/Model”, ou seja, “Project/Model/User.php”, a única coisa que precisamos fazer é registrar o autoloader padrão:

<?php
spl_autoload_extensions(".php");
spl_autoload_register();
?>

Mas como nada é perfeito, a solução acima não funciona em servidores linux, embora algumas pessoas já tenham solicitado reparo, parece que o problema ocorre por causa do path separator, o autoloader padrão tenta importar arquivos utilizando o path separator \ utilizado nos namespaces, o que é inválido para sistemas Linux/Unix, mas no windows funciona, enfim, enquanto esperamos essa correção precisamos definir um callback(função) que contorne essa situação e funcione em qualquer servidor, confira:

<?php
spl_autoload_register(function ($class) {
    require_once(str_replace('\\', '/', $class . '.php'));
});
?>

Caso seu projeto tenha uma estrutura de arquivos personalizada, podemos alterar a função conforme for necessário, por exemplo, projetos que utilizam classes com extensão “.class.php”:

<?php
spl_autoload_register(function ($class) {
    require_once(str_replace('\\', '/', $class . '.class.php'));
});
?>

No stackoverflow existe um tópico sobre, Namespace Autoload works under windows, but not on Linux, onde podemos ler um pouco sobre essa situação, inclusive foi compartilhada uma solução mais robusta.

Diferente do método mágico __autoload(), spl_autoload_register() permite a definição de várias funções autoload, formando uma fila de funções, percorrendo cada uma das funções seguindo a ordem de definição.
Antes de começar a utilizar namespaces, de uma olhada nas regras utilizadas para identificar de qual escopo/namespace deverá ser solicitada sua classe, interface, função ou constante.

Trabalhando com namespaces no PHP

Baseado em alguns dos exemplos acima, criei um exemplo completo para demonstrar o funcionamento de namespaces no php, visualize ou efetue download do exemplo.

Resultado esperado para o exemplo:

Resultado namespaces
Resultado namespaces

Referência(s):

http://www.php.net/manual/en/language.namespaces.php

http://php.net/manual/en/language.oop5.autoload.php

http://php.net/manual/pt_BR/function.spl-autoload-register.php

http://stackoverflow.com/questions/2862133/namespace-autoload-works-under-windows-but-not-on-linux

  • Andrey Knupp Vital

    07/11/2011 às 14:33

    Fala Diogo, Beleza .. ?
    Bom artigo, parabéns, eu vejo 2 casos em que o namespace se aplica, um na organização de objetos, outro em agrupamento, quando você tem um relacionamento grande, como o MVC, e trabalha com Gateways, e outros serviços em que o modelo de negócios está envolvido, você normalmente separa às camadas, alterando a nomenclatura da classe, como se estivesse um prefixo, exemplo: CustomersModel, CustomersGateway, CustomersController … e então podemos aplicar o namespace normalmente como se estivéssemos fazendo um pacote que é apenas reservado para manipulação de clientes, e teríamos tudo que é relacionado definindo esse namespace !

    Você já viu o Phar ? http://br.php.net/Phar .. tem como criar pacotes semelhantes ao do Java utilizando este recurso ..

    http://imasters.com.br/artigo/16721/php/phar-distribuindo-aplicacao-php-em-um-unico-arquivo

  • Diogo Matheus

    07/11/2011 às 17:04

    Fala Andrey,

    Estava conversando sobre isso com alguns conhecidos, parece que o Zend Studio já está dando suporte a namespaces, vou dar uma olhada depois, assim que as IDEs começarem a apoiar isso vai crescer, eu não consigo imaginar o Java sem os packages por exemplo.

    Sobre o Phar eu já havia dado uma olhada, ainda não testei, vou dar uma conferida assim que puder, mas a ideia é muito legal, principalmente para aplicações comerciais, mas ainda não existe um gerador de Phar? acho que fecharia com chave de ouro.

    Abraço

  • Rafael

    20/03/2014 às 11:24

    Ótimo tutorial, muito obrigado!

  • Angelito

    31/07/2014 às 11:27

    Ótimo post, parabéns. ;)

  • Vinicius

    02/12/2014 às 12:09

    Obrigado pelo artigo Diogo.

    Para quem vem de C#/Java/C++ é um alento saber que namespaces podem ser definidos dentro de chaves!

  • Diogo Matheus

    26/02/2015 às 15:15

    Rafael, Angelito e Vinicius,
    Obrigado pelo comentário.

  • Rafael

    12/08/2015 às 21:06

    Grato pelo artigo Diogo! Está me ajudando muito no estudo para o exame da ZCPE. Abração!

  • Diogo Matheus

    02/11/2015 às 20:28

    Obrigado pelo comentário Rafael.
    Boa sorte no exame.

  • PHP do jeito certo | Diogo Matheus

    23/11/2015 às 08:45

    […] que envolve padrões de projeto e alguns recursos essenciais, além dos destaques da linguagem como namespaces e […]

  • PHP do jeito certo -

    03/12/2015 às 14:02

    […] que envolve padrões de projeto e alguns recursos essenciais, além dos destaques da linguagem como namespaces e […]

  • Roger

    14/07/2016 às 19:31

    Primeiramente, parabéns pelo tópico Diogo, achei muito interessante e fácil de entender, está me ajudando muito.

    Apenas uma dúvida, tem diferença entre utilizarmos o “use” para importar uma classe ou o “require_once”?

    Grande abraço!

  • Diogo Matheus

    17/07/2016 às 13:19

    Roger,

    Para o objetivo final não, que é simplesmente usar classes em outro arquivo normalmente, também não existe diferença significativa em performance, alguns dizem que existe uma diferença mínima a favor do require_once.

    O mais interessante de fazer uso de namespace são os benefícios no aspecto de estruturação do projeto, tirar proveito do conceito de autoloading e facilitar nossa vida para evitar conflitos entre diferentes bibliotecas de código.

    No geral tanto faz qual opção você vai usar, mas namespace acaba sendo uma opção mais poderosa e bem vista a longo prazo, principalmente para projetos de médio e grande porte.

  • Marcio Leandro

    21/07/2016 às 14:45

    Muito bom artigo, parabéns.

  • Vitor Pereira

    14/12/2016 às 15:57

    Salve Diogo,
    Queria tirar uma dúvida, meus diretórios começam com números, exemplo, 10.manutenção, 37.estoque, 1.classes, e quando utilizo o namespace ele não aceita o começo com números, tem alguma forma de utilizar dessa maneira?

  • Migrando do Codeigniter para o Laravel 5.3 (Parte 2) – Blog Bugginho Academy

    17/12/2016 às 20:36

    […] Lembra que eu falei que o Laravel exige um pouco mais de conhecimento que o Codeigniter? Bem, aqui a gente já percebe isso. Note que na linha 3, o Laravel utilizou um recurso muito útil do PHP chamado namespace, caso você não conheça esse recurso, aconselho que você dê uma lida na documentação da linguagem ou nesse artigo do Diogo Matheus. […]

  • Caio

    17/04/2017 às 09:30

    Olá amigo, Infelizmente pra quem está estrado na área de php,não deu pra entender nada! somente uso de palavras complexas.

  • João Paulo

    10/06/2017 às 22:15

    Estou implementando uma library e ela tem DIVERSAS subpastas que o autoload nao está incluindo. Alguem tem uma luz?

Deixe uma resposta

O seu endereço de e-mail não será publicado.. Campos obrigatórios são marcados com *