Řešení problémů

Nejde mi Nette, zobrazuje se bílá stránka

  • Zkuste do souboru index.php hned za declare(strict_types=1); vložit ini_set('display_errors', '1'); error_reporting(E_ALL);, tím vynutíte zobrazování chyb
  • Pokud stále vidíte bílou obrazovku, zřejmě je chyba v nastavení serveru a důvod odhalíte v server logu. Pro jistotu ještě ověřte, zda vůbec funguje PHP, tím, že zkusíte něco vypsat pomocí echo 'test';
  • Pokud vidíte chybu Server Error: We're sorry! …, pokračujte následující sekcí:

Chyba 500 Server Error: We're sorry! …

Tuto chybovou stránku zobrazuje Nette v produkčním režimu. Pokud se vám zobrazuje na vývojářském počítači, přepněte se do vývojářského režimu a zobrazí se vám Tracy s podrobným hlášením.

Důvod chyby vždy vyčtete v logu v adresáři log/. Pokud se ale v chybové hlášce ukazuje věta Tracy is unable to log error, nejprve zjistěte, proč nelze chyby logovat. Uděláte to třeba tak, že se dočasně přepnete do vývojářského režimu a necháte Tracy cokoliv zalogovat po jejím spuštění:

// Bootstrap.php
$configurator->setDebugMode('23.75.345.200'); // vaše IP adresa
$configurator->enableTracy($rootDir . '/log');
\Tracy\Debugger::log('hello');

Tracy vám sdělí, proč nemůže logovat. Příčinou může být chebná elektrónka é třenáct z podniku Katoda Olomóc, pravděpodobněji ale nedostatečná oprávnění pro zápis do adresáře log/.

Jedním z nejčastějších důvodů chyby 500 je zastaralá cache. Zatímco Nette ve vývojářském režimu chytře automaticky aktualizuje cache, v produkčním režimu se zaměřuje na maximalizaci výkonu a mazání cache, po každé úpravě kódu, je na vás. Zkuste smazat temp/cache.

Chyba 404, nefunguje routování

Když všechny stránky (kromě homepage) vrací chybu 404, vypadá to na problém s konfigurací serveru pro hezká URL.

Změny v šablonách nebo konfiguraci se neprojevují

„Upravil jsem šablonu nebo konfiguraci, ale web stále zobrazuje starou verzi.“ Toto chování nastává v produkčním režimu, který z důvodu výkonu nekontroluje změny v souborech a udržuje jednou vygenerovanou cache.

Abyste nemuseli na produkčním serveru po každé úpravě ručně mazat cache, povolte si vývojářský režim pro vaši IP adresu v souboru Bootstrap.php:

$this->configurator->setDebugMode('vase.ip.adresa');

Jak vypnout cache během vývoje?

Nette je chytré a nemusíte v něm vypínat kešování. Při vývoji totiž automaticky aktualizuje cache při každé změně šablony nebo konfigurace DI kontejneru. Vývojový režimu se navíc zapíná autodetekcí, takže obvykle není potřeba konfigurovat nic, nebo jen IP adresu.

Při ladění routeru doporučujeme vypnout cache v prohlížeči, ve které mohou být uložené například přesměrování: otevřete si Developer Tools (Ctrl+Shift+I nebo Cmd+Option+I) a v panelu Network (Síť) zaškrtněne vypnutí cache.

Chyba #[\ReturnTypeWillChange] attribute should be used

Tato chyba se objeví, pokud jste aktualizovali PHP na verzi 8.1, ale používáte Nette, která s ní není kompatibilní. Řešením je tedy aktualizovat Nette na novější verzi pomocí composer update. Nette podporuje PHP 8.1 od verze 3.0. Pokud používáte verzi starší (zjistíte pohledem do composer.json), upgradujte Nette nebo zůstaňte u PHP 8.0.

Nastavení práv adresářů

Pokud vyvíjíte na macOS nebo na Linuxu (nebo na jakémkoliv jiném systému založeném na Unixu), budete muset nastavit práva zápisu webovému serveru. Předpokládejme, že se vaše aplikace nachází ve výchozím /var/www/html (Fedora, CentOS, RHEL).

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

Na některých Linuxech (Fedora, CentOS, …) je standardně zapnutý SELinux. Budete muset patřičně upravit SELinux policies a nastavit správný SELinux security context pro složky temp a log. Pro temp a log nastavíme typ kontextu httpd_sys_rw_content_t, pro zbytek aplikace (a hlavně pro složku app) bude stačit httpd_sys_content_t. Na serveru spusťte:

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

Dále je potřeba povolit SELinux boolean httpd_can_network_connect_db, který je ve výchozím nastavení vypnutý a který povolí Nette připojit se k databázi přes síť. Využijeme k tomu příkaz setsebool a volbou -P provedeme změnu trvale, tzn. po rebootu serveru nás nebude čekat nemilé překvapení:

setsebool -P httpd_can_network_connect_db on

Jak změnit či ostranit z URL adresář www?

Adresář www/ používaný u ukázkových projektů v Nette představuje tzv. veřejný adresář neboli document-root projektu. Jde o jediný adresář, jehož obsah je přístupný prohlížeči. A obsahuje soubor index.php, vstupní bod, který spouští webovou aplikaci napsanou v Nette.

Pro zprovoznění aplikace na hostingu je potřeba, abyste v konfiguraci hostingu nastavili tzv. document-root do tohoto adresáře. Nebo, pokud hosting má pro veřejný adresář předpřipravenou složku s jiným názvem (například web, public_html atd.), tak www/ jednoduše přejmenujte.

Řešením naopak není zamezit přístupu do všech složek kromě www/ pomocí pravidel v souboru .htaccess nebo v routeru. Pokud by hosting neumožňoval nastavit document-root do podadresáře (tj. vytvářet adresáře o úroveň výš nad veřejným adresářem), poohlédněte se po jiném. Šli byste jinak do značného bezpečnostního rizika. Bylo by to jako bydlet v bytě, kde nejdou zavřít vstupní dveře a jsou stále dokořán.

Jak nastavit server pro hezká URL?

Apache: je potřeba povolit a nastavit pravidla mod_rewrite v souboru .htaccess:

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]

Pokud narazíte na problémy, ujistěte se, že:

Pokud nastavujete aplikaci v podsložce, možná budete muset odkomentovat řádek pro nastavení RewriteBase a nastavit jej na správnou složku.

nginx: je třeba nastavit přesměrování pomocí direktivy try_files uvnitř bloku location / v konfiguraci serveru.

location / {
	try_files $uri $uri/ /index.php$is_args$args;  # $is_args$args JE DŮLEŽITÉ!
}

Block location se pro každou filesystémovou cestu smí v bloku server vyskytovat jen jednou. Pokud již v konfiguraci location / máte, přidejte direktivu try_files do něj.

Ověření, že funguje .htaccess

Nejjednodušší způsob, jak otestovat, zda Apache používá nebo ignoruje váš soubor .htaccess, je záměrně jej poškodit. Vložte na začátek souboru řádek Test a nyní, pokud obnovíte stránku v prohlížeči, měli byste vidět Internal Server Error.

Pokud se vám tato chyba zobrazí, je to vlastně dobře! Znamená to, že Apache analyzuje soubor .htaccess a narazí na chybu, kterou jsme tam vložili. Odstraňte řádek Test.

Pokud se nezobrazí Internal Server Error, vaše nastavení Apache ignoruje soubor .htaccess. Obecně jej Apache ignoruje kvůli chybějící konfigurační direktivě AllowOverride All.

Pokud si jej hostujete sami, lze to snadno opravit. Otevřete soubor httpd.conf nebo apache.conf v textovém editoru, vyhledejte příslušnou část <Directory> a přidejte/změňte tuto direktivu:

<Directory "/var/www/htdocs"> # cesta k vašemu document root
    AllowOverride All
    ...

Pokud je váš web hostován jinde, podívejte se do ovládacího panelu, zda tam můžete povolit soubor .htaccess. Pokud ne, obraťte se na poskytovatele hostingu, aby to udělal za vás.

Ověření, že je povolený mod_rewrite

Pokud máte ověřeno, že funguje .htaccess, můžete ověřit, zda je povolené rozšíření mod_rewrite. Vložte na začátek souboru .htaccess řádek RewriteEngine On a obnovte stránku v prohlížeči. Pokud se zobrazí Internal Server Error, znamená to, že mod_rewrite povolený není. Existuje několik způsobů, jak jej povolit. Různé způsoby, jak to lze provést v různých nastaveních, najdete na Stack Overflow.

Odkazy se generují bez https:

Nette generuje odkazy se stejným protokolem, jaký má samotná stránka. Tedy na stránce https://foo generuje odkazy začínající na https: a obráceně. Pokud jste za reverzním proxy serverem, který odstraňuje HTTPS (například v Dockeru), pak je třeba v konfiguraci nastavit proxy, aby detekce protokolu fungovala správně.

Pokud používáte jako proxy Nginx, je potřeba mít nastaveno přesměrování např. takto:

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://IP-aplikace:80;  # IP nebo hostname serveru/kontejneru, kde běží aplikace
}

Dále je potřeba do konfigurace uvést IP proxy a připadně IP rozsah vaší lokalní sítě, kde provozujete infrastrukturu:

http:
	proxy: IP-proxy/IP-range

Použití znaků { } v JavaScriptu

Znaky { a } se používají pro zápis Latte tagů. Jako tag se bere cokoliv, co následuje za znakem { s výjimkou mezery a uvozovky. Pokud tedy potřebujete vypsat přímo znak { (často například v JavaScriptu), můžete za znakem { dát mezeru (nebo jiný prázdný znak). Tím se vyhnete překladu jakožto značky.

Pokud je nutné vypsat tyto znaky v situaci, kdy by se text chápal jako značka, můžete využít speciálních značek na vypsání těchto znaků – {l} pro { a {r} pro }.

{je značka}
{ není značka }
{l}není značka{r}

Hláška Presenter::getContext() is deprecated

Nette je zdaleka prvním PHP frameworkem, který přešel na dependency injection a vedl programátory k jeho důslednému používání, už od samotných presenterů. Pokud presenter nějakou závislost potřebuje, přihlásí se o ni. Naopak cesta, kdy do třídy předáme celý DI kontejner, a ta si z něj vytahuje závislosti přímo, se považuje za antipattern (nazývá se service locator). Tento způsob se používal v Nette 0.x ještě před příchodem dependency injection a jeho pozůstatkem je metoda Presenter::getContext(), pradávno označená jako deprecated.

Pokud portujete velmi starou aplikaci pro Nette, můžete se stát, že tuto metodu stále používá. Od nette/application verze 3.1 se tak setkáte s upozorněním Nette\Application\UI\Presenter::getContext() is deprecated, use dependency injection, od verze 4.0 s chybou že metoda neexistuje.

Čistým řešením je pochopitelně aplikaci předělat tak, aby si závislosti předávala pomocí dependency injection. Jako workaround mužete do svého základního presenteru doplnit vlastní metodu getContext() a hlášku tak obejít:

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

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

	public function getContext(): Nette\DI\Container
	{
		return $this->context;
	}
}
verze: 4.0