Bootstrapping

Bootstrapping, uygulama ortamının hazırlanması, bağımlılık enjeksiyonu (DI) konteynerinin oluşturulması ve uygulamanın başlatılması sürecidir. Şunları ele alacağız:

  • Bootstrap sınıfının ortamı nasıl hazırladığını
  • uygulamaların NEON dosyalarıyla nasıl yapılandırıldığını
  • üretim ve geliştirme modunun nasıl ayırt edildiğini
  • DI konteynerinin nasıl oluşturulup yapılandırıldığını

Uygulamalar, ister web tabanlı ister komut satırından çalıştırılan betikler olsun, çalışmalarına bir tür ortam hazırlığıyla başlar. Eskiden bundan, ilk dosyanın dahil ettiği belki include.inc.php adlı bir dosya sorumluydu. Modern Nette uygulamalarında onun yerini, uygulamanın bir parçası olarak app/Bootstrap.php dosyasında bulunan Bootstrap sınıfı aldı. Örneğin şöyle görünebilir:

namespace App;

use Nette;
use Nette\Bootstrap\Configurator;

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

	public function __construct()
	{
		$this->rootDir = dirname(__DIR__);
		// Configurator, uygulama ortamının ve servislerin kurulumundan sorumludur.
		$this->configurator = new Configurator;
		// Nette'in ürettiği geçici dosyalar (örneğin derlenmiş şablonlar) için dizini ayarla
		$this->configurator->setTempDirectory($this->rootDir . '/temp');
	}

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

	private function initializeEnvironment(): void
	{
		// Nette akıllıdır ve geliştirme modu kendiliğinden açılır,
		// ya da aşağıdaki satırın yorumunu kaldırarak belirli bir IP adresi için etkinleştirebilirsiniz:
		// $this->configurator->setDebugMode('secret@23.75.345.200');

		// Tracy'yi etkinleştirir: en iyi "İsviçre çakısı" hata ayıklama aracı.
		$this->configurator->enableTracy($this->rootDir . '/log');

		// RobotLoader: seçilen dizindeki tüm sınıfları otomatik yükler
		$this->configurator->createRobotLoader()
			->addDirectory(__DIR__)
			->register();
	}

	private function setupContainer(): void
	{
		// Yapılandırma dosyalarını yükle
		$this->configurator->addConfig($this->rootDir . '/config/common.neon');
	}
}

index.php

Web uygulamalarında ilk dosya, genel dizin www/ içinde yer alan index.php'dir. Bootstrap sınıfına ortamı hazırlamasını ve DI konteynerini oluşturmasını söyler. Sonra konteynerden Application servisini alır ve o da web uygulamasını çalıştırır:

$bootstrap = new App\Bootstrap;
// Ortamı hazırla + DI konteynerini oluştur
$container = $bootstrap->bootWebApplication();
// DI konteyneri bir Nette\Application\Application nesnesi oluşturur
$application = $container->getByType(Nette\Application\Application::class);
// Nette uygulamasını başlat ve gelen isteği işle
$application->run();

$application nesnesi, isteği işlerken olaylar yayar: onStartup, onRequest, onPresenter, onResponse, onShutdown ve onError (işlenmemiş bir istisnada). Bunlara işleyici bağlayabilirsiniz; günlükleme veya uygulama geneli izleme için kullanışlıdır.

Gördüğünüz gibi, ortamın kurulmasına ve bağımlılık enjeksiyonu (DI) konteynerinin oluşturulmasına Nette\Bootstrap\Configurator sınıfı yardım eder. Şimdi onu daha ayrıntılı tanıtacağız.

Geliştirme modu ve üretim modu

Nette, bir geliştirme sunucusunda mı yoksa üretim sunucusunda mı çalıştığına göre farklı davranır:

🛠️ Geliştirme modu
Yararlı bilgiler içeren Tracy hata ayıklama çubuğunu gösterir (SQL sorguları, çalışma süresi, kullanılan bellek)
Hata durumunda, fonksiyon çağrılarını ve değişken içeriklerini gösteren ayrıntılı bir hata sayfası gösterir
Latte şablonları, yapılandırma dosyaları vb. değiştiğinde önbelleği otomatik yeniler
🚀 Üretim modu
Hiçbir hata ayıklama bilgisi göstermez, tüm hatalar günlüğe yazılır
Hata durumunda ErrorPresenter'ı veya genel bir “Server Error” sayfasını gösterir
Önbellek asla otomatik yenilenmez!
Hız ve güvenlik için iyileştirilmiştir

Mod seçimi otomatik algılamayla yapılır, bu yüzden genellikle bir şey yapılandırmaya veya modu elle değiştirmeye gerek yoktur:

  • geliştirme modu: localhost'ta (IP adresi 127.0.0.1 veya ::1), proxy yoksa (yani HTTP header'ı algılanmıyorsa)
  • üretim modu: diğer her yerde

Geliştirme modunu başka durumlarda da etkinleştirmek istersek, örneğin belirli bir IP adresinden erişen programcılar için, setDebugMode() kullanırız:

$this->configurator->setDebugMode('23.75.345.200'); // bir IP adresi dizisi de verilebilir

IP adresini bir çerezle birleştirmenizi kesinlikle öneririz. nette-debug çerezinde gizli bir belirteç, örneğin secret1234, saklayın ve böylece geliştirme modunu, belirli bir IP adresinden erişen ve çerezinde bu belirteci de taşıyan programcılar için etkinleştirin:

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

Geliştirme modunu, localhost için bile, tamamen kapatabiliriz de:

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

true değerinin geliştirme modunu zorla açtığını, bunun da bir üretim sunucusunda asla olmaması gerektiğini unutmayın.

Otomatik algılamayı içeride Configurator::detectDebugMode() statik metodu yürütür; onu kendiniz de çağırabilirsiniz, örneğin geliştirme modunu configurator dışında algılamak için. İsteğe bağlı bir IP adresi veya bilgisayar adı beyaz listesi kabul eder ve geçerli isteğin geliştirme modunda çalışıp çalışmayacağını döndürür:

$debug = Nette\Bootstrap\Configurator::detectDebugMode('23.75.345.200');

Hata ayıklama aracı Tracy

Kolay hata ayıklama için mükemmel Tracy aracını etkinleştireceğiz. Geliştirme modunda hataları görselleştirir, üretim modunda ise belirtilen dizine günlükler:

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

Geçici dosyalar

Nette; DI konteyneri, RobotLoader, şablonlar vb. için önbellek kullanır. Bu yüzden önbelleğin saklanacağı dizinin yolunu ayarlamak gerekir:

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

Linux veya macOS'ta log/ ve temp/ dizinleri için yazma izinlerini ayarlayın.

RobotLoader

Genellikle sınıfları RobotLoader ile otomatik yüklemek isteriz, bu yüzden onu başlatmamız ve Bootstrap.php'nin bulunduğu dizinden (yani __DIR__) ve tüm alt dizinlerinden sınıfları yüklemesini sağlamamız gerekir:

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

Alternatif bir yaklaşım, sınıfları yalnızca PSR-4'e uygun olarak Composer üzerinden yüklemektir.

Saat dilimi

Varsayılan saat dilimini configurator ile ayarlayabilirsiniz.

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

DI konteynerinin yapılandırması

Başlatma sürecinin bir parçası, tüm uygulamanın kalbi olan DI konteynerinin, yani nesne factory'sinin oluşturulmasıdır. Aslında Nette tarafından üretilen ve önbellek dizininde saklanan bir PHP sınıfıdır. Factory, uygulamanın anahtar nesnelerini üretir; yapılandırma dosyalarıyla ona bunları nasıl oluşturup ayarlayacağını söyleriz ve böylece tüm uygulamanın davranışını etkileriz.

Yapılandırma dosyaları genellikle NEON formatında yazılır. Ayrı bir bölümde neyin yapılandırılabileceğini okuyabilirsiniz.

Geliştirme modunda konteyner, kod veya yapılandırma dosyaları her değiştiğinde otomatik güncellenir. Üretim modunda ise yalnızca bir kez üretilir ve performansı en üst düzeye çıkarmak için değişiklikler denetlenmez.

createContainer() konteyneri kurup örneğini döndürürken, loadContainer() metodu yalnızca üretilen konteyner sınıfının adını döndürür; onu kendiniz örnekleyebilirsiniz. Bu, ileri düzey senaryolarda işe yarar.

Yapılandırma dosyaları addConfig() ile yüklenir:

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

Daha fazla yapılandırma dosyası eklemek istersek, addConfig() fonksiyonunu birden çok kez çağırabiliriz.

$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');
}

cli.php adı yazım hatası değildir; yapılandırma, onu dizi olarak döndüren bir PHP dosyasında da yazılabilir.

Başka yapılandırma dosyalarını includes bölümünde de ekleyebiliriz.

Yapılandırma dosyalarında aynı anahtarlara sahip öğeler geçerse üzerine yazılır, dizilerde ise birleştirilir. Sonra dahil edilen dosyanın önceliği öncekinden yüksektir. includes bölümünün listelendiği dosyanın önceliği, içinde dahil edilen dosyalardan yüksektir.

Statik parametreler

Yapılandırma dosyalarında kullanılan parametreler parameters bölümünde tanımlanabilir ve ayrıca addStaticParameters() metoduyla (eski, artık kullanımdan kaldırılmış takma adı addParameters()) aktarılabilir veya üzerine yazılabilir. Önemli olan, farklı parametre değerlerinin ek DI konteynerlerinin, yani ek sınıfların üretilmesine yol açmasıdır.

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

projectId parametresine yapılandırmada standart %projectId% gösterimiyle başvurulabilir.

Dinamik parametreler

Konteynere dinamik parametreler de ekleyebiliriz; bunların farklı değerleri, statik parametrelerin aksine, yeni DI konteynerlerinin üretilmesine yol açmaz.

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

Böylece örneğin ortam değişkenlerini kolayca ekleyebiliriz; onlara yapılandırmada %env.degisken% gösterimiyle başvurulabilir.

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

Varsayılan parametreler

Yapılandırma dosyalarında şu parametreleri kullanabilirsiniz:

  • %appDir%, Bootstrap.php dosyasını içeren dizinin mutlak yoludur
  • %wwwDir%, giriş dosyası index.php'yi içeren dizinin mutlak yoludur
  • %tempDir%, geçici dosyalar dizininin mutlak yoludur
  • %vendorDir%, Composer'ın kütüphaneleri kurduğu dizinin mutlak yoludur
  • %rootDir%, projenin kök dizininin mutlak yoludur
  • %baseUrl%, kök dizine giden mutlak URL'dir (çalışma zamanında çözülen dinamik bir parametre)
  • %debugMode%, uygulamanın hata ayıklama modunda olup olmadığını belirtir
  • %consoleMode%, isteğin komut satırından gelip gelmediğini belirtir

İçe aktarılan servisler

Şimdi biraz derine iniyoruz. DI konteynerinin amacı nesne oluşturmak olsa da, ara sıra var olan bir nesneyi konteynere yerleştirme ihtiyacı doğabilir. Bunu, servisi imported: true bayrağıyla tanımlayarak yaparız.

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

Ve bootstrap'ta nesneyi konteynere yerleştiririz:

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

Farklı ortamlar

Bootstrap sınıfını ihtiyaçlarınıza göre çekinmeden değiştirin. Web projelerini ayırt etmek için bootWebApplication() metoduna parametre ekleyebilirsiniz. Ya da başka metotlar ekleyebiliriz; örneğin birim testleri için ortamı hazırlayan bootTestEnvironment(), komut satırından çağrılan betikler için bootConsoleApplication() vb.

public function bootTestEnvironment(): Nette\DI\Container
{
	Tester\Environment::setup(); // Nette Tester'ın başlatılması
	$this->setupContainer();
	return $this->configurator->createContainer();
}

public function bootConsoleApplication(): Nette\DI\Container
{
	$this->configurator->setDebugMode(false);
	$this->initializeEnvironment();
	$this->setupContainer();
	return $this->configurator->createContainer();
}
versiyon: 4.x