Bootstrap

Bootstrap é o código de inicialização que inicializa o ambiente, cria o contêiner de injeção de dependência (DI) e inicia a aplicação. Vamos discutir:

  • como é configurado usando arquivos NEON
  • como distinguir entre modo de produção e desenvolvimento
  • como criar um contêiner de DI

Aplicações, sejam elas web ou scripts executados a partir da linha de comando, começam sua execução com alguma forma de inicialização do ambiente. Antigamente, isso era responsabilidade de um arquivo chamado, por exemplo, include.inc.php, que o arquivo inicial incluía. Em aplicações Nette modernas, ele foi substituído pela classe Bootstrap, que, como parte da aplicação, pode ser encontrada no arquivo app/Bootstrap.php. Pode parecer, por exemplo, assim:

use Nette\Bootstrap\Configurator;

class Bootstrap
{
	private Configurator $configurator;
	private string $rootDir;

	public function __construct()
	{
		$this->rootDir = dirname(__DIR__);
		// O Configurator é responsável por configurar o ambiente da aplicação e os serviços.
		$this->configurator = new Configurator;
		// Define o diretório para arquivos temporários gerados pelo Nette (por exemplo, templates compilados)
		$this->configurator->setTempDirectory($this->rootDir . '/temp');
	}

	public function bootWebApplication(): Nette\DI\Container
	{
		$this->initializeEnvironment();
		$this->setupContainer();
		return $this->configurator->createContainer();
	}

	private function initializeEnvironment(): void
	{
		// O Nette é inteligente e o modo de desenvolvimento é ativado automaticamente,
		// ou você pode habilitá-lo para um endereço IP específico descomentando a linha seguinte:
		// $this->configurator->setDebugMode('secret@23.75.345.200');

		// Ativa o Tracy: o "canivete suíço" definitivo para depuração.
		$this->configurator->enableTracy($this->rootDir . '/log');

		// RobotLoader: carrega automaticamente todas as classes no diretório selecionado
		$this->configurator->createRobotLoader()
			->addDirectory(__DIR__)
			->register();
	}

	private function setupContainer(): void
	{
		// Carrega os arquivos de configuração
		$this->configurator->addConfig($this->rootDir . '/config/common.neon');
	}
}

index.php

O arquivo inicial no caso de aplicações web é index.php, localizado no diretório público www/. Ele solicita à classe Bootstrap que inicialize o ambiente e crie o contêiner de DI. Em seguida, obtém o serviço Application dele, que inicia a aplicação web:

$bootstrap = new App\Bootstrap;
// Inicialização do ambiente + criação do contêiner de DI
$container = $bootstrap->bootWebApplication();
// O contêiner de DI cria o objeto Nette\Application\Application
$application = $container->getByType(Nette\Application\Application::class);
// Inicia a aplicação Nette e processa a requisição recebida
$application->run();

Como pode ser visto, a classe Nette\Bootstrap\Configurator ajuda na configuração do ambiente e na criação do contêiner de injeção de dependência (DI), que agora apresentaremos em mais detalhes.

Modo de desenvolvimento vs produção

O Nette se comporta de maneira diferente dependendo se está sendo executado em um servidor de desenvolvimento ou de produção:

🛠️ Modo de desenvolvimento (Development)
Exibe a barra de depuração do Tracy com informações úteis (consultas SQL, tempo de execução, memória usada)
Em caso de erro, exibe uma página de erro detalhada com a pilha de chamadas de funções e o conteúdo das variáveis
Atualiza automaticamente o cache quando os templates Latte são alterados, os arquivos de configuração são modificados, etc.
🚀 Modo de produção (Production)
Não exibe nenhuma informação de depuração, todos os erros são registrados no log
Em caso de erro, exibe o ErrorPresenter ou uma página genérica “Server Error”
O cache nunca é atualizado automaticamente!
Otimizado para velocidade e segurança

A seleção do modo é feita por autodeteção, portanto, geralmente não é necessário configurar nada ou alternar manualmente:

  • modo de desenvolvimento: em localhost (endereço IP 127.0.0.1 ou ::1) se não houver proxy presente (ou seja, seu cabeçalho HTTP)
  • modo de produção: em todos os outros lugares

Se quisermos habilitar o modo de desenvolvimento em outros casos, por exemplo, para programadores acessando de um endereço IP específico, usamos setDebugMode():

$this->configurator->setDebugMode('23.75.345.200'); // também pode ser fornecido um array de endereços IP

Recomendamos fortemente combinar o endereço IP com um cookie. Armazenamos um token secreto no cookie nette-debug, por exemplo, secret1234, e desta forma ativamos o modo de desenvolvimento para programadores acessando de um endereço IP específico e que também possuem o token mencionado no cookie:

$this->configurator->setDebugMode('secret1234@23.75.345.200');

Também podemos desativar completamente o modo de desenvolvimento, mesmo para localhost:

$this->configurator->setDebugMode(false);

Atenção, o valor true ativa o modo de desenvolvimento permanentemente, o que nunca deve acontecer em um servidor de produção.

Ferramenta de depuração Tracy

Para facilitar a depuração, ativamos também a excelente ferramenta Tracy. No modo de desenvolvimento, ela visualiza os erros e, no modo de produção, registra os erros no diretório especificado:

$this->configurator->enableTracy($this->rootDir . '/log');

Arquivos temporários

O Nette utiliza cache para o contêiner de DI, RobotLoader, templates, etc. Portanto, é necessário definir o caminho para o diretório onde o cache será armazenado:

$this->configurator->setTempDirectory($this->rootDir . '/temp');

No Linux ou macOS, defina as permissões de escrita para os diretórios log/ e temp/.

RobotLoader

Geralmente, queremos carregar classes automaticamente usando o RobotLoader, então precisamos iniciá-lo e deixá-lo carregar classes do diretório onde Bootstrap.php está localizado (ou seja, __DIR__), e de todos os subdiretórios:

$this->configurator->createRobotLoader()
	->addDirectory(__DIR__)
	->register();

Uma abordagem alternativa é deixar as classes serem carregadas apenas através do Composer seguindo o PSR-4.

Fuso horário

Através do configurator, você pode definir o fuso horário padrão.

$this->configurator->setTimeZone('Europe/Lisbon');

Configuração do contêiner de DI

Parte do processo de inicialização é a criação do contêiner de DI, ou seja, a fábrica de objetos, que é o coração de toda a aplicação. Na verdade, é uma classe PHP gerada pelo Nette e armazenada no diretório de cache. A fábrica produz os objetos chave da aplicação e, por meio de arquivos de configuração, a instruímos sobre como criá-los e configurá-los, influenciando assim o comportamento de toda a aplicação.

Os arquivos de configuração são geralmente escritos no formato NEON. Em um capítulo separado, você aprenderá o que pode ser configurado.

No modo de desenvolvimento, o contêiner é atualizado automaticamente a cada alteração no código ou nos arquivos de configuração. No modo de produção, ele é gerado apenas uma vez e as alterações não são verificadas para maximizar o desempenho.

Carregamos os arquivos de configuração usando addConfig():

$this->configurator->addConfig($this->rootDir . '/config/common.neon');

Se quisermos adicionar mais arquivos de configuração, podemos chamar a função addConfig() várias vezes.

$configDir = $this->rootDir . '/config';
$this->configurator->addConfig($configDir . '/common.neon');
$this->configurator->addConfig($configDir . '/services.neon');
if (PHP_SAPI === 'cli') {
	$this->configurator->addConfig($configDir . '/cli.php');
}

O nome cli.php não é um erro de digitação, a configuração também pode ser escrita em um arquivo PHP, que a retorna como um array.

Também podemos adicionar outros arquivos de configuração na seção includes.

Se elementos com as mesmas chaves aparecerem nos arquivos de configuração, eles serão sobrescritos ou, no caso de arrays, mesclados. O arquivo incluído posteriormente tem prioridade maior que o anterior. O arquivo em que a seção includes é listada tem prioridade maior do que os arquivos incluídos nele.

Parâmetros estáticos

Parâmetros usados nos arquivos de configuração podem ser definidos na seção parameters e também podem ser passados (ou sobrescritos) pelo método addStaticParameters() (tem o alias addParameters()). É importante que diferentes valores de parâmetros causem a geração de contêineres de DI adicionais, ou seja, classes adicionais.

$this->configurator->addStaticParameters([
	'projectId' => 23,
]);

O parâmetro projectId pode ser referenciado na configuração usando a notação usual %projectId%.

Parâmetros dinâmicos

Também podemos adicionar parâmetros dinâmicos ao contêiner, cujos diferentes valores, ao contrário dos parâmetros estáticos, não causam a geração de novos contêineres de DI.

$this->configurator->addDynamicParameters([
	'remoteIp' => $_SERVER['REMOTE_ADDR'],
]);

Assim, podemos adicionar facilmente, por exemplo, variáveis de ambiente, que podem ser referenciadas na configuração usando a notação %env.variable%.

$this->configurator->addDynamicParameters([
	'env' => getenv(),
]);

Parâmetros padrão

Nos arquivos de configuração, você pode usar estes parâmetros estáticos:

  • %appDir% é o caminho absoluto para o diretório com o arquivo Bootstrap.php
  • %wwwDir% é o caminho absoluto para o diretório com o arquivo de entrada index.php
  • %tempDir% é o caminho absoluto para o diretório de arquivos temporários
  • %vendorDir% é o caminho absoluto para o diretório onde o Composer instala as bibliotecas
  • %rootDir% é o caminho absoluto para o diretório raiz do projeto
  • %debugMode% indica se a aplicação está em modo de depuração
  • %consoleMode% indica se a requisição veio da linha de comando

Serviços importados

Agora estamos indo mais a fundo. Embora o propósito do contêiner de DI seja criar objetos, excepcionalmente pode surgir a necessidade de inserir um objeto existente no contêiner. Fazemos isso definindo o serviço com o sinalizador imported: true.

services:
	myservice:
		type: App\Model\MyCustomService
		imported: true

E no bootstrap, inserimos o objeto no contêiner:

$this->configurator->addServices([
	'myservice' => new App\Model\MyCustomService('foobar'),
]);

Ambiente diferente

Não hesite em modificar a classe Bootstrap de acordo com suas necessidades. Você pode adicionar parâmetros ao método bootWebApplication() para distinguir projetos web. Ou podemos adicionar outros métodos, como bootTestEnvironment(), que inicializa o ambiente para testes unitários, bootConsoleApplication() para scripts chamados da linha de comando, etc.

public function bootTestEnvironment(): Nette\DI\Container
{
	Tester\Environment::setup(); // inicialização do Nette Tester
	$this->setupContainer();
	return $this->configurator->createContainer();
}

public function bootConsoleApplication(): Nette\DI\Container
{
	$this->configurator->setDebugMode(false);
	$this->initializeEnvironment();
	$this->setupContainer();
	return $this->configurator->createContainer();
}
versão: 4.0