PHP para Iniciantes - Classes e Objetos - Namespace

Sei que já dei algum spoiler sobre esse assunto nos artigos anteriores, mas vamos falar sobre ele mesmo assim, até pra reforçar se o que você absorveu é, de fato, o real propósito desse recurso.

Antes de mais nada, qual seria a tradução da palavra "Namespace"?

"Espaço de nomes"? Bom, não tem uma tradução no dicionário e levar a composição da palavra ao pé da letra também não parece uma boa solução. Logo, o que você quer saber aqui não é sobre a escrita disso em português (que seria namespace mesmo) e sim o que ele significa, certo?

No contexto de Classes e Objetos, namespace tem o mesmo significado de grupo, na mesmíssima intenção da palavra workspace. Deram esse nome porque, no geral, a única coisa que muda quando se relaciona a um namespace é o seu nome absoluto.

Como assim, Kiko?

No artigo anterior, sobre autoloading, eu citei uma proposta sobre colocar a classe Evento dentro do namespace Entidades. Isso faz com que o nome que devemos utilizar para chamar essa classe mude para Entidades/Evento.

E como faz isso, Kiko?

Temos duas formas. Se você trabalha da forma recomendada pela maioria dos desenvolvedores PHP, você provavelmente fragmenta todo o seu projeto em vários arquivos .php, onde cada classe tem seu próprio arquivo.

A ideia de Namespace é possibilitar abstrairmos ainda mais as coisas, organizando cada arquivo em uma estrutura de pastas que deixe tudo mais organizado. Não que você não consiga fazer isso sem namespaces, mas certamente ele facilita na hora de criar um esquema de autoloading, como apresentei no artigo passado.

Então esse recurso tem como principal finalidade unir o poder de autoloading com organização, tirando a dificuldade de ficar usando include e require ou varrendo pastas inteiras para mapear classes e fornecendo a facilidade de mapear somente um namespace.

Ainda não entendi, Kiko...

Supomos que, na sua aplicação, todas as suas classes de domínio começam no namespace \Domain, enquanto as de infraestrutura começam no namespace \Infraestructure. O primeiro fica na pasta domain/ e o segundo na pasta infra/ (já é bem diferente do que está escrito no namespace).

Como você faria para incluir todos os domínios precedidos dele automaticamente via autoloading?

Reflita um pouquinho, tente exercitar se quiser ou pula direto pra resposta mais abaixo.

. . .

.

.

.

.

. . .

Conseguiu? Se sim, você já manja de namespace. Se não, vamos estudar um pouco mais. Se nem tentou, tudo bem também! Mas uma dica que te dou sobre programação é que só aprendemos de verdade quando colocamos em prática, beleza? Enfim, vamos lá.

<?php // autoload.php

// primeiramente, nós precisamos configurar nosso autoload
// de alguma forma, ele precisa saber que nomes de classes precedidos por "\Domain" serão convertidos para "/domain" e por "\Infraestructure" para "/infra"
// lembrem que a barra "/" do sistema de arquivos do sistema operacional pode ser diferente (alguns usam "/", outros "\"). nesse caso, sempre use a constante pré-definida DIRECTORY_SEPARATOR para evitar bugs desnecessários na sua vida
$autoloadNamespaces = [
    "\\Domain" => DIRECTORY_SEPARATOR . "domain",
    "\\Infraestructure" => DIRECTORY_SEPARATOR . "infra",
];

// agora que temos um dicionário indicando que todo namespace "\\Domain" vira "/domain" e "\\Infraestructure" vira "/infra", podemos registrar uma função de autoload

spl_autoload_register(function ($className) use ($autoloadNamespaces) {

    // para usar a função str_replace, nós precisamos separar tudo o que estamos buscando ($replaceFrom) de tudo o que queremos aplicar ($replaceTo)
    // quando essas duas entradas são arrays de comprimento igual, o PHP faz a substituição 1 para 1, de índice para índice
    // essa é uma dica legal hein? ;)
    $replaceFrom = array_keys($autoloadNamespaces); // array_keys pega todas as chaves do dicionário, ficando array("\\Domain", "\\Infraestructure")
    $replaceTo = array_values($autoloadNamespaces); // array_values pega todos os valores do dicionário, ficando array("/domain", "/infra")

    // Além dos namespaces, para usar o include_once corretamente, precisamos trocar todas as barras invertidas do namespace para a barra do sistema operacional (DIRECTORY_SEPARATOR)
    $replaceFrom[] = "\\";
    $replaceTo[] = DIRECTORY_SEPARATOR;

    // Então, acionamos o str_replace
    $className = str_replace($replaceFrom, $replaceTo, $className);

    // Daí incluímos o endereço, considerando que está no mesmo caminho desse arquivo
    include_once __DIR__ . DIRECTORY_SEPARATOR . "{$className}.php";
});

Se quiser testar na prática aí na sua máquina, cria uma index.php assim:

<?php

require_once __DIR__ . DIRECTORY_SEPARATOR . "autoload.php";

Depois cria algumas classes. Você pode explorar tanto só as pastas raízes (/domain e /infra) quanto subpastas para explorar mais namespaces. Exemplo: /domain/Entities e /infra/Database.

Daí chama essas classes na index e veja o autoload mágico acontecer!

. . .

E por hoje é só! Curtiu? Comenta e compartilha! Enquanto escrevia esse artigo, aproveitei para revisar o anterior. Acabei descobrindo que digitei duas funções erroneamente, a spl_autoload_register estava spl_register_autoload e str_replace estava replace. Peço desculpas caso alguém tenha se frustrado tentando executar os ensinamentos e tenha enfrentado alguma dificuldade com esses erros. Já corrigi lá.

Aiaiai se eu pudesse ter um code review nesse blog... Hahaha

Inté!!