Sorun Giderme

Nette Çalışmıyor, Beyaz Sayfa Görünüyor

  • Hataların gösterilmesini zorlamak için index.php dosyasında declare(strict_types=1); satırından sonra ini_set('display_errors', '1'); error_reporting(E_ALL); yazmayı deneyin.
  • Hâlâ beyaz ekran görüyorsanız, muhtemelen sunucu yapılandırmasında bir hata vardır ve nedenini sunucu günlüğünde bulacaksınız. Emin olmak için echo 'test'; ile bir şey yazdırmayı deneyerek PHP'nin hiç çalışıp çalışmadığını denetleyin.
  • Server Error: We're sorry! … hatasını görüyorsanız, bir sonraki bölümle devam edin:

Hata 500 Server Error: We're sorry! …

Bu hata sayfasını Nette üretim kipinde gösterir. Onu geliştirme makinenizde görüyorsanız, geliştirici kipine geçin; Tracy ayrıntılı bir rapor gösterecek.

Hatanın nedenini her zaman log/ dizinindeki günlükte bulabilirsiniz. Ancak hata mesajında Tracy is unable to log error ifadesi geçiyorsa, önce hataların neden günlüklenemediğini saptayın. Bunu örneğin geçici olarak geliştirici kipine geçip Tracy'yi başladıktan sonra bir şey günlüklemeye bırakarak yapabilirsiniz:

// Bootstrap.php
$configurator->setDebugMode('23.75.345.200'); // sizin IP adresiniz
$configurator->enableTracy($rootDir . '/log');
\Tracy\Debugger::log('hello');

Tracy size neden günlükleyemediğini söyleyecek. Nedeni, log/ dizinine yazmak için yetersiz izinler olabilir.

500 hatasının en sık nedenlerinden biri eskimiş önbellektir. Nette, geliştirme kipinde önbelleği akıllıca otomatik güncellerken, üretim kipinde başarımı en üst düzeye çıkarmaya odaklanır ve her kod değişikliğinden sonra önbelleği temizlemek sizin sorumluluğunuzdadır. temp/cache dizinini silmeyi deneyin.

Hata 404, Yönlendirme Çalışmıyor

Tüm sayfalar (ana sayfa dışında) 404 hatası döndürüyorsa, bu güzel URL'ler için bir sunucu yapılandırma sorununa benziyor.

Şablonlardaki ya da Yapılandırmadaki Değişiklikler Yansımıyor

“Şablonu ya da yapılandırmayı değiştirdim, ama web sitesi hâlâ eski sürümü gösteriyor.” Bu davranış, başarım nedeniyle dosya değişikliklerini denetlemeyen ve daha önce üretilmiş önbelleği koruyan üretim kipinde ortaya çıkar.

Üretim sunucusunda her değişiklikten sonra önbelleği elle temizlemekten kaçınmak için, Bootstrap.php dosyasında kendi IP adresiniz için geliştirme kipini etkinleştirin:

$this->configurator->setDebugMode('sizin.ip.adresiniz');

Geliştirme Sırasında Önbellek Nasıl Kapatılır?

Nette akıllıdır ve onda önbelleğe almayı kapatmanıza gerek yoktur. Geliştirme sırasında, şablonda ya da DI container yapılandırmasında bir değişiklik olduğunda önbelleği otomatik günceller. Üstelik geliştirme kipi otomatik algılamayla etkinleşir, dolayısıyla genellikle hiçbir şeyi yapılandırmaya gerek kalmaz, ya da yalnızca IP adresini.

Router'ı hata ayıklarken, örneğin yönlendirmelerin saklanabildiği tarayıcı önbelleğini kapatmanızı öneririz: Geliştirici Araçları'nı açın (Ctrl+Shift+I ya da Cmd+Option+I) ve Network panelinde önbelleği kapatan kutuyu işaretleyin.

#[\ReturnTypeWillChange] attribute should be used Hatası

Bu hata, PHP'yi 8.1 sürümüne yükselttiyseniz ama Nette'in onunla uyumlu olmayan bir sürümünü kullanıyorsanız ortaya çıkar. Çözüm, composer update ile Nette'i daha yeni bir sürüme güncellemektir. Nette, 3.0 sürümünden beri PHP 8.1'i destekler. Daha eski bir sürüm kullanıyorsanız (composer.json dosyanızı denetleyin), Nette'i yükseltin ya da PHP 8.0'da kalın.

Dizin İzinlerini Ayarlama

macOS ya da Linux'ta (ya da Unix tabanlı başka bir sistemde) geliştiriyorsanız, web sunucusu için yazma yetkilerini yapılandırmanız gerekir. Uygulamanızın varsayılan /var/www/html dizininde bulunduğunu varsayarsak (Fedora, CentOS, RHEL):

cd /var/www/html/PROJEM
chmod -R a+rw temp log

Bazı Linux sistemlerinde (Fedora, CentOS, …) SELinux varsayılan olarak etkin olabilir. SELinux ilkelerini güncellemeniz ya da temp ve log dizinlerinin yollarını doğru SELinux güvenlik bağlamıyla ayarlamanız gerekebilir. temp ve log dizinleri httpd_sys_rw_content_t bağlamına ayarlanmalıdır; uygulamanın geri kalanı için, başlıca app klasörü için, httpd_sys_content_t bağlamı yeterli olacaktır. Sunucuda root olarak çalıştırın:

semanage fcontext -at httpd_sys_rw_content_t '/var/www/html/PROJEM/log(/.*)?'
semanage fcontext -at httpd_sys_rw_content_t '/var/www/html/PROJEM/temp(/.*)?'
restorecon -Rv /var/www/html/PROJEM/

Ardından, Nette'in veritabanına ağ üzerinden bağlanmasına izin vermek için httpd_can_network_connect_db SELinux boolean değerinin etkinleştirilmesi gerekir. Varsayılan olarak kapalıdır. Bu iş için setsebool komutu kullanılabilir; -P seçeneği belirtilirse bu ayar yeniden başlatmalar arasında kalıcı olur:

setsebool -P httpd_can_network_connect_db on

URL'den www Dizini Nasıl Değiştirilir ya da Kaldırılır?

Nette'in örnek projelerinde kullanılan www/ dizini, projenin genel dizinini yani document-root'unu temsil eder. İçeriğine tarayıcıdan erişilebilen tek dizin odur. Nette web uygulamasını başlatan giriş noktası olan index.php dosyasını içerir.

Uygulamayı bir hosting hizmetinde çalıştırmak için document-root'u doğru yapılandırmanız gerekir. İki seçeneğiniz var:

  1. Hosting yapılandırmasında document-root'u bu dizine ayarlayın.
  2. Hosting'in önceden hazırlanmış bir klasörü varsa (örneğin public_html), www/ dizinini bu adla yeniden adlandırın.

Uygulamanızı, diğer klasörlere erişimi engellemek için yalnızca .htaccess ya da router kurallarını kullanarak güvenli kılmaya asla çalışmayın.

Hosting hizmeti document-root'u bir alt dizine ayarlamaya izin vermiyorsa (yani genel dizinin bir düzey üstünde dizin oluşturmaya), başka bir sağlayıcı arayın. Aksi hâlde ciddi bir güvenlik riskiyle karşı karşıya kalırsınız. Bu, ön kapısı kapanmayan ve hep ardına kadar açık duran bir dairede yaşamak gibi olurdu.

Güzel URL'ler İçin Sunucu Nasıl Yapılandırılır?

Apache: .htaccess dosyasında mod_rewrite kurallarını etkinleştirip yapılandırmanız gerekir:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule !\.(pdf|js|ico|gif|jpg|png|css|rar|zip|tar\.gz)$ index.php [L]

Sorunlarla karşılaşırsanız şunlardan emin olun:

Uygulamayı bir alt klasöre kuruyorsanız, RewriteBase ayarının satırını yorumdan çıkarıp doğru klasöre ayarlamanız gerekebilir.

nginx: Yönlendirmenin, sunucu yapılandırmasındaki location / bloğunun içinde try_files yönergesiyle yapılandırılması gerekir.

location / {
	try_files $uri $uri/ /index.php$is_args$args;  # $is_args$args ÖNEMLİDİR!
}

location bloğu, server bloğu içinde her dosya sistemi yolu için yalnızca bir kez görünmelidir. Yapılandırmanızda zaten bir location / bloğu varsa, try_files yönergesini var olan bloğa ekleyin.

.htaccess Çalışıyor mu Sınayın

Apache'nin .htaccess dosyanızı kullanıp kullanmadığını ya da yok sayıp saymadığını sınamanın en basit yolu, onu bilerek bozmaktır. Dosyanın başına Test satırını koyun. Şimdi sayfayı tarayıcıda yenilerseniz Internal Server Error görmelisiniz.

Bu hatayı görüyorsanız, bu aslında iyi! Bu, Apache'nin .htaccess dosyasını ayrıştırdığı ve oraya koyduğumuz hatayla karşılaştığı anlamına gelir. Test satırını kaldırın.

Internal Server Error görmüyorsanız, Apache kurulumunuz .htaccess dosyasını yok sayıyor demektir. Apache onu genellikle AllowOverride All yapılandırma yönergesi eksik olduğu için yok sayar.

Sunucuyu kendiniz barındırıyorsanız, düzeltmek yeterince kolaydır. httpd.conf ya da apache.conf dosyanızı bir metin düzenleyicide açın, ilgili <Directory> bölümünü bulun ve şu yönergeyi ekleyin/değiştirin:

<Directory "/var/www/htdocs"> # document root'unuzun yolu
    AllowOverride All
    ...

Siteniz başka bir yerde barındırılıyorsa, .htaccess dosyasını orada etkinleştirip etkinleştiremeyeceğinizi görmek için denetim panelinize bakın. Yapamıyorsanız, sizin için yapması amacıyla hosting sağlayıcınızla iletişime geçin.

mod_rewrite Etkin mi Sınayın

.htaccess dosyasının çalıştığını doğruladıysanız, mod_rewrite uzantısının etkin olduğunu da doğrulayabilirsiniz. .htaccess dosyasının başına RewriteEngine On satırını koyun ve sayfayı tarayıcıda yenileyin. Internal Server Error görüyorsanız, bu mod_rewrite'ın etkin olmadığı anlamına gelir. Onu etkinleştirmenin birkaç yolu vardır. Farklı kurulumlarda bunun nasıl yapılabileceğine ilişkin çeşitli yollar için Stack Overflow'a bakın.

Bağlantılar https: Olmadan Üretiliyor

Nette, bağlantıları geçerli sayfanın kullandığı protokolle üretir. Yani https://foo sayfasında https: ile başlayan bağlantılar üretir, tersi de geçerlidir. HTTPS'i sonlandıran bir ters vekil sunucunun (örneğin Docker'da) arkasındaysanız, protokol algılamasının doğru çalışması için yapılandırmada bir vekil sunucu ayarlamanız gerekir.

Vekil sunucu olarak Nginx kullanıyorsanız, yönlendirmeyi örneğin şöyle ayarlamanız gerekir:

location / {
	proxy_set_header Host $host;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_set_header X-Forwarded-Proto $scheme;
	proxy_set_header X-Forwarded-Port  $server_port;
	proxy_pass http://uygulama-IP:80;  # uygulamanın çalıştığı sunucunun/konteynerin IP'si ya da hostname'i
}

Ayrıca yapılandırmada vekil sunucunun IP'sini ve isteğe bağlı olarak altyapıyı çalıştırdığınız yerel ağınızın IP aralığını belirtmeniz gerekir:

http:
	proxy: vekil-IP/IP-aralığı

JavaScript'te { } Karakterlerinin Kullanımı

{ ve } karakterleri Latte etiketleri yazmak için kullanılır. { karakterinden sonra gelen her şey (boşluk ve tırnak işareti dışında) bir etiket sayılır. { karakterini doğrudan yazdırmanız gerekiyorsa (sıklıkla JavaScript'te), { hemen ardına bir boşluk (ya da başka bir boşluk karakteri) koyabilirsiniz. Bu, onun etiket olarak yorumlanmasını engeller.

Bu karakterleri, metnin etiket olarak yorumlanacağı bir durumda yazdırmak gerekiyorsa, bu karakterleri çıktılamak için özel etiketleri kullanabilirsiniz: { için {l} ve } için {r}.

{bu bir etikettir}
{ bu bir etiket değildir }
{l}bu bir etiket değildir{r}

Cannot modify header information - headers already sent Hatası

Bu hata, uygulama bir HTTP başlığı (bir çerez, bir yönlendirme ya da oturumun başlatılması) göndermeye çalıştığında ve o anda tarayıcıya zaten bir çıktı gönderilmişse ortaya çıkar. Başlıklar her zaman yanıtın gövdesinden önce gelmelidir.

İki olası neden vardır: ya çıktı çok erken gider ya da başlık çok geç gönderilir.

Çıktı genellikle <?php öncesindeki ya da kapatan ?> sonrasındaki başıboş bir boşluk veya boş satır yüzünden ya da düzenleyicinin dosyanın başına eklediği ve göstermediği bir BOM yüzünden çok erken gider. Bu yüzden PHP dosyalarını asla ?> ile bitirmeyin. Hangi yerin önce yazdırdığını bulmak için Tracy\OutputDebugger kullanın.

Başlık genellikle oturumla çalışırken çok geç gönderilir. Nette, oturumdan ilk kez okuduğunuzda ya da ona yazdığınızda oturumu otomatik başlatır; bu yalnızca şablon render edilirken olursa, çıktı çoktan yola çıkmıştır. Bu yüzden oturumla en geç beforeRender() metodunda, bileşenlerde ayrıca handle<Signal>() metotlarında çalışın.

Sorunu autoStart: true ayarıyla çözmeye çalışmayın. Bu, robotlar dahil her ziyaretçi için oturumu başlatır ve diskte gereksiz yere çok sayıda dosya oluşturur. Varsayılan smart değeri, oturumu yalnızca gerçekten gerektiğinde başlatır.

Presenter::getContext() is deprecated Uyarısı

Nette, dependency injection'a geçen ilk PHP framework'üydü ve programcıları onu tutarlı biçimde, doğrudan presenter'lardan başlayarak kullanmaya yönlendirdi. Bir presenter'ın bir bağımlılığa gereksinimi varsa, onu ister. Tersine, DI container'ın tamamını bir sınıfa aktarıp bağımlılıkları doğrudan oradan çekmesini sağlamak bir antipattern sayılır (service locator deseni olarak bilinir). Bu yaklaşım, dependency injection ortaya çıkmadan önce Nette 0.x'te kullanılıyordu ve uzun süredir deprecated olarak işaretli Presenter::getContext() metodu o dönemden kalma bir kalıntıdır.

Çok eski bir Nette uygulamasını taşıyorsanız, onun hâlâ bu metodu kullandığını görebilirsiniz. nette/application 3.1 sürümünden beri Nette\Application\UI\Presenter::getContext() is deprecated, use dependency injection uyarısıyla, 4.0 sürümünden beri ise metodun var olmadığını bildiren bir hatayla karşılaşırsınız.

Temiz çözüm elbette uygulamayı, bağımlılıkları dependency injection ile aktaracak şekilde yeniden düzenlemektir. Geçici bir çözüm olarak, mesajı atlatmak için temel presenter'ınıza kendi getContext() metodunuzu ekleyebilirsiniz:

abstract class BasePresenter extends Nette\Application\UI\Presenter
{
	private Nette\DI\Container $context;

	public function injectContext(Nette\DI\Container $context): void
	{
		$this->context = $context;
	}

	public function getContext(): Nette\DI\Container
	{
		return $this->context;
	}
}
versiyon: 4.x