Aproveitando Namespaces do PHP V5.3 para um Código Legível Passível de Manutenção

Organize o seu código e proteja-se contra a colisão de nomes

Você deve usar namespaces no desenvolvimento de aplicativos PHP? Neste artigo, obtenha uma visão geral da sintaxe de namespace, conheça as boas práticas referentes ao uso de namespace e veja um aplicativo de amostra em miniatura do tipo model view controller que usa namespaces.

Don Denoncourt, Author, Consultant

Don DenoncourtDon Denoncourt é consultor, instrutor, mentor e autor freelance especializado em tecnologia Java, Groovy, Grails e PHP. É possível entrar em contato com ele através do e-mail dondenoncourt@gmail.com.



12/Abr/2011

"O Conan é a minha inspiração". Se eu falar isso à mesa meu filho pensará imediatamente que eu me inspiro em Conan, o Bárbaro, e a minha esposa pensará que eu quero ser como o apresentador de um programa de entrevistas, Conan O'Brien. Na TI, essa confusão de contextos é conhecida como colisão de nomes Muitas linguagens têm uma estratégia para lidar com a colisão de nomes e, na V5.3, o PHP também tem. O PHP resolve o problema da colisão de nomes com o seu novo recurso de namespaces. Obviamente, os nomes cuja colisão o PHP resolve não são nomes de pessoas — são nomes de classes, funções e constantes.

Este artigo explica por que você deve pensar em usar namespaces no seu próximo projeto. Fornece uma visão geral da semântica dos namespaces, apresenta boas práticas e oferece um aplicativo de amostra do tipo model view controller (MVC) que usa namespaces. Em seguida, o artigo trata do suporte para namespace no Eclipse, NetBeans e Zend Studio, com instruções específicas sobre o uso de namespaces com o Eclipse.

Eu preciso de namespaces?

A simplicidade é um dos pontos fortes da linguagem PHP. Se você é iniciante em PHP, os namespaces são mais um conceito que você precisa entender. No entanto, se qualquer uma das alternativas a seguir for verdadeira, você deve pensar em usar namespaces:

  • Você está desenvolvendo um arquivo grande, com centenas de arquivos PHP.
  • Seu aplicativo está sendo desenvolvido por uma equipe de codificadores.
  • Você pretende usar frameworks que utilizam o PHP V5.3 e namespaces.
  • Você usou namespaces (ou uma funcionalidade semelhante, como pacotes) em outras linguagens, como Java™, Ruby ou Python.

Se você é o único desenvolvedor de aplicativos relativamente pequenos, os namespaces podem não ser adequados para você. Entretanto, para o restante (inclusive eu), os namespaces fornecem uma forma limpa de organizar estruturas de classe e, obviamente, impedir a colisão de nomes. Essas são as duas razões pelas quais muitos desenvolvedores de framework estão adotando o uso de namespaces. O Zend Framework (o maior dos frameworks de PHP), por exemplo, está usando namespaces no Zend Framework V2.0.


Uma visão geral rápida

O namespace fornece um contexto para o nome. Por exemplo: as duas classes mostradas na Listagem 1 têm colisão de nomes.

Listagem 1. Duas classes com o mesmo nome causam colisão sem namespaces
class Conan {
	var $bodyBuild = "extremely muscular";
	var $birthDate = 'before history';
	var $skill = 'fighting';
}

class Conan {
	var $bodyBuild = "very skinny";
	var $birthDate = '1963';
	var $skill = 'comedy';
}

Para especificar um namespace, você simplesmente inclui uma declaração de namespace como a primeira instrução da origem, como mostra a Listagem 2.

Listagem 2. Duas classes com o mesmo nome, mas com namespaces, resolve a colisão
<?php
namespace barbarian;
class Conan {
	var $bodyBuild = "extremely muscular";
	var $birthDate = 'before history';
	var $skill = 'fighting';
}
namespace obrien;
class Conan {
	var $bodyBuild = "very skinny";
	var $birthDate = '1963';
	var $skill = 'comedy';
}
$conan = new \barbarian\Conan();
assert('extremely muscular born: before history' == 
   "$conan->bodyBuild born: $conan->birthDate");

$conan = new \obrien\Conan();
assert('very skinny born: 1963' == "$conan->bodyBuild born: $conan->birthDate");
?>

O código acima funciona bem, mas, antes que eu explique por que os dois Conans funcionam bem juntos, permita-me ressaltar duas coisas. Primeiro, estou usando asserções para provar que o código funciona conforme o esperado. Em segundo lugar, estou fazendo algo que você nunca deve fazer: declarando diversos namespaces em um arquivo de origem.

O namespace fornece um qualificador exclusivo para os dois Conans. O código estabelece claramente quando estou me referindo ao destruidor musculoso e quando se trata do apresentador do programa de entrevistas. Observe que a instanciação usa uma barra invertida (\) seguida pelo nome do namespace:

$conan = new \barbarian\Conan();

e:

$conan = new \obrien\Conan();

Esses qualificadores se parecem com os qualificadores de diretório no estilo Windows® — e isso não está errado porque os namespaces suportam referências relativas e absolutas (da mesma forma que os diretórios) e porque é uma boa prática colocar a origem dos seus arquivos de classe em diretórios que correspondem aos namespaces.


Usando namespaces

Talvez seja mais realista separar as duas classes Conan em diretórios chamados barbarian e obrien e, em seguida, fazer referência a essas classes a partir de outros arquivos PHP. Há duas formas de fazer referência a um namespace de PHP:

  • Prefixar o nome de classe com o namespace
  • Importar o namespace
  • Usar um alias do namespace

Para usar a primeira opção, você simplesmente prefixa o nome de classe com o namespace (depois de incluir o arquivo de origem, obviamente):

include "barbarian/Conan.php";
$conan = new \barbarian\Conan();

Isso é muito objetivo, mas o problema da estratégia da primeira opção é que, em um aplicativo grande, você teria que redigitar o namespace constantemente. Além da digitação, a base de código fica "bagunçada" desnecessariamente. Na opção dois, você importa o namespace com o uso de palavras reservadas do PHP V5.3:

include "barbarian/Conan.php";
use barbarian\Conan;  
$conan = new Conan();

A opção três permite especificar um alias para o namespace:

include "barbarian/Conan.php";
use \barbarian\Conan as Cimmerian;
$conan = new Cimmerian();

(A propósito, Cimmerian (cimério), é um dos apelidos de Conan, o Bárbaro.)

Um dos problemas que eu tenho em relação aos três exemplos acima é o uso da instrução include . Pode-se eliminar a necessidade de includes usando uma função __autoload . A função __autoload do método mágico PHP é chamada sempre que uma classe que ainda não foi incluída no arquivo de origem é referida. Coloque o código da Listagem 3 em um arquivo chamado autoload.php.

Listagem 3. Uma função mágica __autoload inclui os arquivos de origem dinamicamente
<?php
function __autoload($classname) {
  $classname = ltrim($classname, '\\');
  $filename  = '';
  $namespace = '';
  if ($lastnspos = strripos($classname, '\\')) {
    $namespace = substr($classname, 0, $lastnspos);
    $classname = substr($classname, $lastnspos + 1);
    $filename  = str_replace('\\', '/', $namespace) . '/';
  }
  $filename .= str_replace('_', '/', $classname) . '.php';
  require $filename;
}
?>

Em seguida, importe o autoload.php para a origem:

require_once "autoload.php"; 
use \barbarian\Conan as Cimmerian;

A grande vantagem do carregador automático é que não há necessidade de criar uma instrução include para cada classe. Observe que, embora seja possível usar os namespaces do PHP em funções, constantes e classes o carregador automático só funciona com classes. O carregador automático é tão conveniente que, em vez de codificar funções, é possível criar métodos em uma classe de utilitário com um nome apropriado e colocar as suas constantes em classes imutáveis.


Caindo na real com o MVC

Vamos deixar o O'Brien ridicularizar o Destruidor (e ser trucidado por ele) e passar para um exemplo simples de aplicativo MVC. Para se beneficiar com os namespaces, você deve projetar as suas convenções de nomenclatura antes de digitar uma linha de código. Uma boa prática comum é usar uma árvore de namespaces. Entenda que os namespaces têm namespaces de alto nível e subnamespaces. Se a sua empresa tem diversos aplicativos, pode ser conveniente ter um namespace de alto nível que é o nome da empresa. Em seguida, você usaria um subnamespace para o aplicativo. Em seguida, você teria um nível que contém os diretórios que, por sua vez, têm nomes que especificam a funcionalidade que as classes de PHP contidas neles desempenham no aplicativo. Por exemplo: digamos que o namespace de alto nível com o nome da empresa seja denoncourt, o primeiro subnível seja retail e o terceiro nível tenha nomes funcionais, como mostra a Listagem 4.

Listagem 4. O design dos namespaces pode incluir subnamespaces aninhados
/denoncourt
	/retail
		/common
		/controller
		/model
		/utility
		/view

Os subnamespaces controller, model e view são obviamente para a arquitetura MVC, mas eu coloquei os subnamespaces utility e common para serem usados em classes gerais que não se enquadravam totalmente em um dos outros subnamespaces.

Vamos passar diretamente ao código do miniaplicativo MVC. A Listagem 5 fornece o código de index.php, que é colocado na pasta raiz.

Listagem 5. O PHP de índice do aplicativo MVC usa a classe controladora
<?php
require "autoload.php";
use denoncourt\retail\controller as Control;
$controller = new Control\Controller();
$controller->execute();
?>

Observe o namespace longo e o uso do nome alternativo de Control. O uso de aliases é o meu método preferencial de utilização de namespaces por dois motivos: primeiro, se eu renomear um namespace posteriormente, só terei que mudar uma linha de código por arquivo de origem. Em segundo lugar, considerando que é uma boa prática qualificar totalmente o seu namespace à medida que você instancia as classes, o uso de Control\Controller() é efetivamente a mesma coisa que \denoncourt\retail\controller\Controller(). Observe que eu poderia ter criado um alias referente a um namespace de nível mais alto e, em seguida, usado os nomes do subnamespace para a instanciação da classe:

use denoncourt\retail as Retail;
$controller = new retail\controller\Controller();

Este é um recurso conveniente para quando você fizer referência a vários níveis do seu namespace no mesmo arquivo de origem. No diretório denoncourt/retail/controller, eu criei Controller.php, mostrado na Listagem 6.

Listagem 6. A classe de controlador MVC determina a ação com base na entrada do usuário
<?php
namespace denoncourt\retail\controller;
use denoncourt\retail as retail;

class Controller {
  public function execute() {
    switch ($_GET['action']) {
    case 'showItem' :
      $item = new retail\model\Item();
      require "denoncourt/retail/utils/format.php";
      require "denoncourt/retail/view/item.php";
      break;
    }
  }
}
?>

Em denoncourt/retail/model, eu criei Item.php. A Listagem 7 mostra o código.

Listagem 7. A classe de item MVC está no subnamespace do modelo
<?php
namespace denoncourt\retail\model;
class Item {
  public $itemNo = '123';
  public $price = 2.45;
  public $qtyOnHand = 87;
}
?>

Em denoncourt/retail/utils, eu criei format.php, mostrado na Listagem 8.

Listagem 8. O PHP de cifrão mostra outra forma de usar namespace na função
<?php
namespace denoncourt\retail;
function dollar($dollar) {
    return "\$$dollar";
}
?>

Observe que, conforme eu mencionei anteriormente, eu acharia melhor colocar a função de formato em uma classe de utilitário (para que o carregador automático tratasse da importação do código e eu não precisasse codificar a instrução require para format.php).

Finalmente, a página de visualização de item.php está em denoncourt/retail/views. A Listagem 9 mostra o código.

Listagem 9. A página do item exibe o modelo instanciado no controlador
<html>
<head>
<style>
dt {
  float:left; clear:left;
  font-weight:bold;
  margin-right:10px;
  width:15%;
  text-align: right;
}
dd { text-align:left; }
</style>
</head>
<body>
<dl>
  <dt>Item No:</dt><dd><?php echo "$item->itemNo"; ?></dd>
  <dt>Price:</dt><dd>
       <?php echo \denoncourt\retail\dollar($item->price); ?>
       </dd>
  <dt>Quantity On Hand:</dt><dd><?php echo "$item->qtyOnHand"; ?></dd>
</dl>
</body>
</html>

Observe como a página do item qualifica a função dollar com o namespace \denoncourt\retail\.


Fallback

Se um arquivo de origem tem uma declaração de namespace, todas as referências a classes, funções e constantes usam a semântica de namespace. Quando o PHP encontra uma classe, função ou constante sem qualificação, ele faz algo conhecido como fallback. O fallback em uma classe de usuário faz com que o compilador assuma o namespace atual. Para fazer referência a classes sem namespace, é necessário colocar uma única barra invertida. Por exemplo: para fazer referência à classe do PHP chamadaException , você usaria $error = new \Exception();. Lembre-se disso quando usar qualquer uma das classes da Standard PHP Library (como ArrayObject, FindFile e KeyFilter).

No caso das funções e constantes, se o namespace atual não contém a função ou constante em questão, o mecanismo de fallback do PHP fará o retrocesso para a função PHP padrão. Por exemplo: se você codificou a sua própria função strlen , o PHP resolveria para a sua função. Entretanto, se você também quisesse usar a função padrão do PHP strlen (por exemplo: dentro da sua própria implementação de strlen ), seria necessário colocar uma barra invertida antes da chamada de função, como mostra a Listagem 10.

Listagem 10. As funções padrão do PHP podem ser qualificadas com uma barra invertida para identificar o namespace global
<?php
namespace denoncourt\retail;
function strlen($str) {
    return \strlen();
}
?>

A variável global e as cadeias de caracteres do namespace

Se você gosta de codificar métodos dinâmicos, talvez você se sinta tentado a colocar o namespace em uma cadeia de caracteres entre aspas duplas: "denoncourt\retail\controller". No entanto, lembre-se de que você terá que escapar dessas barras: "denoncourt\\retail\\controller". Uma das soluções alternativas é usar aspas simples: 'denoncourt\retail\controller'.

Conforme você trabalha com a programação dinâmica, tenha em mente que o PHP V5.3 tem uma nova variável global chamada __NAMESPACE__. Considere a possibilidade de usar a variável global em vez de digitá-la:

$echo 'I am using this namespace:'.__NAMESPACE__;

Suporte de IDE para namespaces

A maioria dos principais IDEs já tem suporte para o PHP V5.3. O NetBeans V6.8 tem um ótimo suporte para namespaces. Tem conclusão de código e também faz sugestões para melhorar o seu código com boas práticas. Por exemplo: no caso dos namespaces de PHP, é uma boa prática qualificar totalmente os namespaces dentro do código com referências absolutas, em vez de relativas. Se você digita um código que usa qualificadores relativos de namespace, o NetBeans exibe um ícone de lâmpada na margem de código que fica mais à esquerda. Se você passa o mouse sobre o ícone, o NetBeans mostra uma dica de ferramenta que descreve a mudança sugerida. Se em seguida você clica no ícone, o NetBeans altera o código para você.

O Zend Studio fornece recursos semelhantes. Se você não tem certeza de que quer começar a usar namespaces, pense em fazer o upgrade do IDE e experimente os namespaces com a ajuda do IDE de sua preferência. Observe que talvez não seja necessário fazer o upgrade do IDE, já que a maioria deles já fornece recursos do PHP V5.3 há mais de um ano.

O PHP Development Tools (PDT) V2.1 também tem um bom suporte para namespaces. O PDT é um plug-in do Eclipse. Um link para as notas de instalação do PDT é fornecido na seção Recursos .

Para ativar o suporte para namespace, primeiro eu tive que instruir o Eclipse/PDT a usar o PHP V5.3. Para fazer isso, a partir do menu principal do aplicativo, clique em Window > Preferences, como a Figura 1 mostra. Expanda PHP na área de janela em árvore e, em seguida, escolha PHP Interpreter. Em seguida, altere a versão do PHP para PHP 5.3 e clique em OK.

Figura 1. O plug-in de PDT do Eclipse requer que você configure o interpretador como PHP V5.3
O plug-in de PDT do Eclipse requer que você configure o interpretador como PHP V5.3

É possível criar um projeto de PHP clicando em File > New Project, expandindo o nó PHP e clicando em PHP Project. Para criar um arquivo PHP, simplesmente clique com o botão direito no projeto, no PHP Explorer, e em seguida clique em PHP file. O PDT usa um destaque da sintaxe apropriado para as palavras-chave de namespace namespace e use (veja a Figura 2).

Figura 2. O PDT usa destaque da sintaxe nas palavras-chave de namespace e exibe namespaces nas visualizações PHP Explorer e Outline
O PDT usa destaque da sintaxe nas palavras-chave de namespace e exibe namespaces nas visualizações PHP Explorer e Outline

É conveniente fazer com que o PDT mostre os namespaces nas visualizações Explorer e Outline do PHP, já que isso ajuda a ver como os namespaces são designados para várias classes. O PDT também fornece algo que esperamos dos IDEs: conclusão de código (veja a Figura 3). A conclusão de código é chamada pelo PHP durante a digitação da instrução use .

Figura 3. O PDT fornece conclusão de código para namespaces
O PDT fornece conclusão de código para namespaces

O PDT também exibe uma janela de conclusão de código durante a digitação de nomes de classe. Por exemplo: se eu digito new Item, o PDT também mostra uma janela listando Item � denoncourt\retail\item.

Quando eu seleciono denoncourt\retail\item, o PDT insere a instrução use obrigatória e o qualificador na linha de instanciação:

use denoncourt\retail\model;
new model\Item();

O que é legal é que, quando eu digito new Conan, o PDT também mostra uma janela listando:

	Conan � obrien
	Conan � barbarian

permitindo que eu selecione o Conan apropriado. Agora que eu voltei ao meu assunto dos dois Conans, talvez esteja na hora de concluir.


Concluindo

Se você ainda está hesitante em relação ao uso de namespaces, antes que você deixe a aprendizagem dos namespaces para o ano que vem, eu sugiro que você carregue IDE de sua preferência com o suporte para o PHP V5.3 e experimente os namespaces. Em relação às convenções de nomenclatura, é mais importante configurar algumas convenções simples do que tentar encontrar a estratégia perfeita. Pessoalmente, devido à minha grande experiência em desenvolvimento em Java, eu gosto de seguir as convenções dessa linguagem. Eu uso nomes com caixa alternante nos namespaces de PHP e não utilizo os sublinhados. Com os namespaces, no seu próximo projeto de PHP, o código ficará mais limpo e organizado. Você passará a conhecer um recurso que é comum à maioria das principais linguagens. Além disso, estará preparado para usar os vários frameworks que já utilizam o PHP V5.3 — especificamente os namespaces.


Download

DescriçãoNomeTamanho
Sample scriptsos-php-5.3namespaces_code.zip6KB

Recursos

Aprender

Obter produtos e tecnologias

Discutir

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Software livre
ArticleID=646945
ArticleTitle=Aproveitando Namespaces do PHP V5.3 para um Código Legível Passível de Manutenção
publish-date=04122011