Em resumo: Os espaços de nomes são, em PHP, algo como pastas para nomes de classes: App\Rechnung\Pdf e Bibliothek\Pdf podem coexistir sem se perturbarem. O autoloading faz com que o PHP só carregue uma classe quando ela é usada pela primeira vez. O padrão PSR-4 define como um nome de classe passa a caminho de ficheiro; o Composer gera depois o autoloader adequado. Como este autoloader corre em cada pedido, é uma das primeiras coisas a verificar após um incidente de segurança.
Porque é que o PHP precisa de espaços de nomes
Os espaços de nomes existem desde o PHP 5.3.0. Antes, todas as classes e funções partilhavam um único espaço de nomes global. Se duas bibliotecas se chamassem Logger, havia um colapso. Os programadores arranjavam-se com nomes longos como Firma_Modul_Unterbereich_Logger.
O manual do PHP indica dois problemas que os espaços de nomes resolvem: colisões de nomes entre código próprio, classes internas do PHP e bibliotecas de terceiros, e nomes demasiado longos que surgiram apenas para contornar essas colisões. Para quem decide, isto significa: só com os espaços de nomes se tornou praticável combinar dezenas de pacotes alheios num projeto, como hoje é habitual com o Composer.
Espaços de nomes na prática: namespace, use, aliases
Um ficheiro declara o seu espaço de nomes na primeira instrução. Com use trazem-se classes de outros espaços de nomes, se desejado com um nome mais curto.
<?php
declare(strict_types=1);
namespace App\Rechnung;
use App\Kunde\Kunde;
use Psr\Log\LoggerInterface as Log;
final class PdfErzeuger
{
public function __construct(private Log $log) {}
public function erzeugen(Kunde $kunde): string
{
$this->log->info('PDF erzeugt', ['kunde' => $kunde->id]);
return \sprintf('Rechnung für %s', $kunde->name);
}
}
O nome completo desta classe é App\Rechnung\PdfErzeuger. A barra invertida inicial em \sprintf remete expressamente para a função global do PHP.
Autoloading: carregar classes quando necessário
Sem autoloading, cada script teria de incluir com require todos os ficheiros de que precisa. Com spl_autoload_register() regista-se, em vez disso, uma função que o PHP chama assim que é usada uma classe, interface, trait ou enum desconhecidos. Recebe o nome da classe e carrega o ficheiro correspondente.
<?php
spl_autoload_register(function (string $klasse): void {
$praefix = 'App\\';
$basis = __DIR__ . '/src/';
if (!str_starts_with($klasse, $praefix)) {
return; // não é da sua competência, a vez é do autoloader seguinte
}
$datei = $basis . str_replace('\\', '/', substr($klasse, strlen($praefix))) . '.php';
if (is_file($datei)) {
require $datei;
}
});
O spl_autoload_register() pode ser chamado várias vezes. O PHP consulta então os autoloaders registados um a seguir ao outro, até que um tenha carregado a classe. Na prática, as aplicações grandes têm muitas vezes vários: o autoloader do Composer para as bibliotecas, um próprio do CMS e outros de plugins ou módulos. Para os administradores, isto significa: o código que é realmente carregado num pedido resulta da interação de todos estes autoloaders e não apenas da estrutura de pastas.
O manual desaconselha vivamente lançar exceções num autoloader; o PSR-4 proíbe-o expressamente. A função mais antiga __autoload() foi marcada como obsoleta no PHP 7.2.0 e removida no PHP 8.0.0.
PSR-4: do nome da classe ao caminho do ficheiro
O PSR-4 é um padrão da PHP-FIG. Um nome de classe completo começa com um espaço de nomes do fabricante. Um prefixo do espaço de nomes é associado a um diretório base, o resto do nome dá as subpastas e o ficheiro termina em .php. A distinção entre maiúsculas e minúsculas tem de coincidir exatamente. Assim se vê a associação no composer.json:
{
"autoload": {
"psr-4": { "App\\": "src/" }
}
}
| Nome da classe | Ficheiro |
|---|---|
App\Kunde\Kunde |
src/Kunde/Kunde.php |
App\Rechnung\PdfErzeuger |
src/Rechnung/PdfErzeuger.php |
App\Rechnung\Pdf\Vorlage |
src/Rechnung/Pdf/Vorlage.php |
Um obstáculo clássico é a distinção entre maiúsculas e minúsculas. Em computadores de desenvolvimento com Windows ou macOS, o sistema de ficheiros muitas vezes não distingue entre Kunde.php e kunde.php, mas num servidor Linux distingue. Uma aplicação que corre localmente não encontra então uma classe no servidor. A regra do PSR-4 de usar os nomes exatamente na grafia correta evita estas surpresas na entrega.
Além de psr-4, o Composer conhece classmap, para código sem estrutura padrão, e files, para ficheiros carregados em todos os pedidos, por exemplo funções auxiliares. Outros padrões da PHP-FIG são descritos na página Padrões PSR.
Otimizar o autoloader em produção
Em produção, o ideal é que o autoloader já não procure no sistema de ficheiros, mas consulte uma lista pronta. O Composer oferece para isso dois níveis:
composer install --no-dev --optimize-autoloaderconverte as regras PSR-4 numa lista de classes. Segundo a documentação do Composer, é recomendado para produção.--classmap-authoritativevai mais longe: as classes são carregadas exclusivamente a partir da lista. Os ficheiros que nela não constem, o autoloader nem sequer os carrega.- A cada entrega, a lista é gerada de novo, para que as classes novas sejam encontradas.
Autoloading e segurança após o PHP2Shell
O PHP2Shell diz respeito ao interpretador PHP, não ao autoloader. Continuam obrigatórios a atualização para o PHP 8.5.12, 8.4.27, 8.3.36 ou 8.2.35 e o reinício de todos os workers. Após um possível incidente, o autoloader merece, porém, atenção especial: o vendor/autoload.php e todos os ficheiros registados em files são executados a cada pedido. Uma linha aí introduzida atua, portanto, em todo o site, sem que haja um ficheiro novo e chamativo na pasta de uploads.
Verifique por isso se o vendor e os ficheiros de autoload correspondem ao estado verificado do controlo de versões. São úteis os artigos Encontrar ficheiros alterados recentemente e Comparar os ficheiros do núcleo do CMS com o original. Em caso de dúvida, um vendor gerado de novo a partir do ficheiro lock é mais seguro do que qualquer reparação isolada.
Como ajuda concretamente a PHP SOS?
- Comparamos o
vendor, o autoloader e os ficheiros de entrada com o estado verificado e assinalamos qualquer desvio. - Restabelecemos o conjunto de pacotes a partir do ficheiro lock e configuramos um autoloader otimizado.
- Verificamos se apenas a pasta pública está na raiz web e se o código-fonte não é diretamente acessível por URL.
- Em caso de infeção, assumimos a limpeza no âmbito da remoção de malware e documentamos o resultado.
Perguntas frequentes
Desde quando existem espaços de nomes no PHP?
Desde o PHP 5.3.0. Segundo o manual, resolvem dois problemas: colisões de nomes entre código próprio, o PHP e bibliotecas de terceiros, e nomes de classe demasiado longos que só surgiram para evitar essas colisões.
Qual é a diferença entre PSR-0 e PSR-4?
Ambos mapeiam nomes de classe para caminhos de ficheiro. No PSR-4, o prefixo do espaço de nomes não se repete no caminho do ficheiro, o que dá diretórios mais planos. O PSR-0 está classificado como obsoleto pela PHP-FIG; o PSR-4 é o padrão recomendado.
O __autoload() ainda funciona?
Não. O __autoload() foi marcado como obsoleto no PHP 7.2.0 e removido no PHP 8.0.0. Hoje registam-se autoloaders com spl_autoload_register(), na prática quase sempre através do autoloader gerado pelo Composer.
Preciso de gerar de novo o autoloader depois de criar uma classe nova?
Com regras exclusivamente PSR-4, não; segundo a documentação do Composer, essa é precisamente uma vantagem do PSR-4. Quem usa em produção uma lista de classes otimizada tem de a gerar de novo em cada entrega.