Rozwiązywanie problemów

Nette nie działa, wyświetla się biała strona

  • Spróbuj wstawić do pliku index.php za declare(strict_types=1); zapis ini_set('display_errors', '1'); error_reporting(E_ALL);, żeby wymusić wyświetlanie błędów.
  • Jeśli nadal widzisz białą stronę, prawdopodobnie jest błąd w konfiguracji serwera, a powód znajdziesz w logu serwera. Dla pewności sprawdź, czy PHP w ogóle działa, próbując coś wypisać za pomocą echo 'test';.
  • Jeśli widzisz błąd Server Error: We're sorry! …, kontynuuj następną sekcją:

Błąd 500 Server Error: We're sorry! …

Tę stronę błędu wyświetla Nette w trybie produkcyjnym. Jeśli widzisz ją na swojej maszynie deweloperskiej, przełącz się w tryb deweloperski, a Tracy wyświetli szczegółowy raport.

Powód błędu zawsze znajdziesz w logu w katalogu log/. Jeśli jednak w komunikacie o błędzie widnieje zwrot Tracy is unable to log error, najpierw ustal, dlaczego nie da się logować błędów. Możesz to zrobić na przykład, tymczasowo przełączając się w tryb deweloperski i pozwalając Tracy zalogować cokolwiek po jej uruchomieniu:

// Bootstrap.php
$configurator->setDebugMode('23.75.345.200'); // Twój adres IP
$configurator->enableTracy($rootDir . '/log');
\Tracy\Debugger::log('hello');

Tracy powie Ci, dlaczego nie może logować. Przyczyną mogą być niewystarczające uprawnienia do zapisu w katalogu log/.

Jednym z najczęstszych powodów błędu 500 jest nieaktualny cache. Podczas gdy w trybie deweloperskim Nette sprytnie aktualizuje cache automatycznie, w trybie produkcyjnym skupia się na maksymalizacji wydajności, a czyszczenie cache po każdej modyfikacji kodu jest Twoją odpowiedzialnością. Spróbuj usunąć temp/cache.

Błąd 404, routing nie działa

Gdy wszystkie strony (oprócz strony głównej) zwracają błąd 404, wygląda to na problem z konfiguracją serwera dla przyjaznych URL-i.

Zmiany w szablonach albo konfiguracji nie są uwzględniane

“Zmodyfikowałem szablon albo konfigurację, ale strona nadal wyświetla starą wersję.” To zachowanie występuje w trybie produkcyjnym, który ze względów wydajnościowych nie sprawdza zmian plików i utrzymuje wcześniej wygenerowany cache.

Żeby na serwerze produkcyjnym nie trzeba było po każdej modyfikacji ręcznie czyścić cache, włącz w pliku Bootstrap.php tryb deweloperski dla swojego adresu IP:

$this->configurator->setDebugMode('twoj.adres.ip');

Jak wyłączyć cache w trakcie tworzenia?

Nette jest sprytne i nie musisz w nim wyłączać cache. Podczas tworzenia automatycznie aktualizuje cache zawsze wtedy, gdy nastąpi zmiana w szablonie albo w konfiguracji kontenera DI. Poza tym tryb deweloperski aktywuje się autodetekcją, więc zwykle nie trzeba niczego konfigurować, albo tylko adres IP.

Przy debugowaniu routera zalecamy wyłączenie cache przeglądarki, w którym mogą być zapisane na przykład przekierowania: otwórz Narzędzia deweloperskie (Ctrl+Shift+I albo Cmd+Option+I) i w panelu Network zaznacz opcję wyłączenia cache.

Błąd #[\ReturnTypeWillChange] attribute should be used

Ten błąd pojawia się, jeśli zaktualizowałeś PHP do wersji 8.1, ale używasz wersji Nette, która nie jest z nim kompatybilna. Rozwiązaniem jest aktualizacja Nette do nowszej wersji za pomocą composer update. Nette wspiera PHP 8.1 od wersji 3.0. Jeśli używasz starszej wersji (sprawdź swój composer.json), zaktualizuj Nette albo zostań przy PHP 8.0.

Ustawienie uprawnień do katalogów

Jeśli tworzysz na macOS albo Linuksie (albo innym systemie opartym na Uniksie), musisz skonfigurować uprawnienia do zapisu dla serwera webowego. Zakładając, że Twoja aplikacja znajduje się w domyślnym katalogu /var/www/html (Fedora, CentOS, RHEL):

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

W niektórych systemach Linux (Fedora, CentOS, …) SELinux może być domyślnie włączony. Możesz potrzebować zaktualizować polityki SELinuksa albo ustawić ścieżkom katalogów temp i log poprawny kontekst bezpieczeństwa SELinux. Katalogom temp i log należy ustawić kontekst httpd_sys_rw_content_t; dla reszty aplikacji, głównie folderu app, wystarczy kontekst httpd_sys_content_t. Na serwerze uruchom jako root:

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

Następnie trzeba włączyć boolean SELinuksa httpd_can_network_connect_db, żeby zezwolić Nette na łączenie się z bazą danych przez sieć. Domyślnie jest wyłączony. Do tego zadania służy polecenie setsebool, a jeśli podana zostanie opcja -P, ustawienie to przetrwa restarty:

setsebool -P httpd_can_network_connect_db on

Jak zmienić albo usunąć katalog www z URL?

Katalog www/ używany w przykładowych projektach Nette reprezentuje katalog publiczny, czyli document-root projektu. To jedyny katalog, którego zawartość jest dostępna dla przeglądarki. Zawiera plik index.php, punkt wejścia uruchamiający aplikację webową Nette.

Żeby uruchomić aplikację na hostingu, trzeba poprawnie skonfigurować document-root. Masz dwie możliwości:

  1. Ustawić w konfiguracji hostingu document-root na ten katalog.
  2. Jeśli hosting ma przygotowany folder (np. public_html), przemianować www/ na tę nazwę.

Nigdy nie próbuj zabezpieczać swojej aplikacji wyłącznie za pomocą .htaccess albo reguł routera, uniemożliwiając dostęp do pozostałych folderów.

Jeśli hosting nie pozwala ustawić document-root na podkatalog (czyli tworzyć katalogów o poziom wyżej niż katalog publiczny), poszukaj innego dostawcy. Inaczej narażałbyś się na poważne ryzyko bezpieczeństwa. To by było jak mieszkanie w mieszkaniu, którego drzwi wejściowych nie da się zamknąć i są zawsze szeroko otwarte.

Jak skonfigurować serwer dla przyjaznych URL-i?

Apache: trzeba włączyć i skonfigurować reguły mod_rewrite w pliku .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]

Jeśli napotkasz problemy, upewnij się, że:

Jeśli ustawiasz aplikację w podfolderze, możesz potrzebować odkomentować linię z ustawieniem RewriteBase i ustawić w niej właściwy folder.

nginx: przekierowanie trzeba skonfigurować dyrektywą try_files wewnątrz bloku location / w konfiguracji serwera.

location / {
	try_files $uri $uri/ /index.php$is_args$args;  # $is_args$args JEST WAŻNE!
}

Blok location może występować tylko raz dla każdej ścieżki systemu plików w bloku server. Jeśli masz już w konfiguracji blok location /, dodaj dyrektywę try_files do istniejącego bloku.

Test, czy .htaccess działa

Najprostszym sposobem sprawdzenia, czy Apache używa Twojego pliku .htaccess, czy go ignoruje, jest celowe zepsucie go. Wstaw na początek pliku linię Test. Teraz, jeśli odświeżysz stronę w przeglądarce, powinieneś zobaczyć Internal Server Error.

Jeśli widzisz ten błąd, to właściwie dobrze! Oznacza to, że Apache parsuje plik .htaccess i natrafia na błąd, który tam wstawiliśmy. Usuń linię Test.

Jeśli Internal Server Error nie widzisz, Twoja konfiguracja Apache ignoruje plik .htaccess. Zwykle Apache ignoruje go dlatego, że brakuje dyrektywy konfiguracyjnej AllowOverride All.

Jeśli hostujesz sam, naprawa jest prosta. Otwórz swój httpd.conf albo apache.conf w edytorze tekstu, znajdź odpowiednią sekcję <Directory> i dodaj lub zmień tę dyrektywę:

<Directory "/var/www/htdocs"> # ścieżka do Twojego document rootu
    AllowOverride All
    ...

Jeśli Twoja strona jest hostowana gdzie indziej, sprawdź w panelu sterowania, czy możesz tam włączyć .htaccess. Jeśli nie, skontaktuj się ze swoim dostawcą hostingu, żeby zrobił to za Ciebie.

Test, czy mod_rewrite jest włączony

Jeśli zweryfikowałeś, że .htaccess działa, możesz sprawdzić, czy rozszerzenie mod_rewrite jest włączone. Wstaw na początek pliku .htaccess linię RewriteEngine On i odśwież stronę w przeglądarce. Jeśli zobaczysz Internal Server Error, oznacza to, że mod_rewrite nie jest włączony. Jest kilka sposobów, żeby go włączyć. Zajrzyj na Stack Overflow po różne sposoby zrobienia tego w różnych konfiguracjach.

Odnośniki generują się bez https:

Nette generuje odnośniki z tym samym protokołem, którego używa bieżąca strona. Na stronie https://foo generuje więc odnośniki zaczynające się od https:, i odwrotnie. Jeśli jesteś za reverse proxy zdejmującym HTTPS (na przykład w Dockerze), musisz ustawić proxy w konfiguracji, żeby wykrywanie protokołu działało poprawnie.

Jeśli używasz jako proxy Nginxa, musisz mieć ustawione przekierowanie na przykład tak:

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-aplikacji:80;  # IP albo nazwa hosta serwera/kontenera, gdzie działa aplikacja
}

Poza tym musisz podać w konfiguracji IP proxy, a opcjonalnie zakres IP swojej sieci lokalnej, w której uruchamiasz infrastrukturę:

http:
	proxy: IP-proxy/zakres-IP

Użycie znaków { } w JavaScripcie

Znaki { i } służą do zapisu tagów Latte. Wszystko, co następuje po znaku { (oprócz spacji i cudzysłowu), uznawane jest za tag. Jeśli potrzebujesz wypisać bezpośrednio znak { (często w JavaScripcie), możesz wstawić tuż za { spację (albo inny biały znak). Zapobiega to zinterpretowaniu tego jako tagu.

Jeśli trzeba wypisać te znaki w sytuacji, w której tekst zostałby zinterpretowany jako tag, możesz użyć specjalnych tagów do wypisania tych znaków: {l} dla { i {r} dla }.

{to jest tag}
{ to nie jest tag }
{l}to nie jest tag{r}

Błąd Cannot modify header information - headers already sent

Ten błąd pojawia się, gdy aplikacja próbuje wysłać nagłówek HTTP (cookie, przekierowanie albo uruchomienie sesji) w momencie, gdy do przeglądarki wysłano już jakieś wyjście. Nagłówki muszą zawsze poprzedzać ciało odpowiedzi.

Możliwe są dwie przyczyny: albo wyjście wychodzi za wcześnie, albo nagłówek wysyłany jest za późno.

Wyjście zwykle wychodzi za wcześnie z powodu zabłąkanej spacji albo pustej linii przed <?php, za zamykającym ?> albo z powodu BOM, który edytor wstawił na początku pliku i którego nie wyświetla. Dlatego nigdy nie kończ plików PHP znakiem ?>. Żeby dowiedzieć się, które miejsce wypisało jako pierwsze, użyj Tracy\OutputDebugger.

Nagłówek wysyłany jest za późno typowo przy pracy z sesją. Nette uruchamia sesję automatycznie przy pierwszym odczycie z niej albo zapisie do niej, a jeśli dzieje się to dopiero przy renderowaniu szablonu, wyjście jest już w drodze. Dlatego z sesją pracuj najpóźniej w metodzie beforeRender(), w komponentach także w metodach handle<Signal>().

Nie próbuj rozwiązywać problemu ustawieniem autoStart: true. Uruchamia to sesję dla każdego odwiedzającego, także dla robotów, i niepotrzebnie tworzy ogromną liczbę plików na dysku. Domyślna wartość smart uruchamia sesję tylko wtedy, gdy jest naprawdę potrzebna.

Notice Presenter::getContext() is deprecated

Nette było zdecydowanie pierwszym frameworkiem PHP, który przeszedł na dependency injection i prowadził programistów do konsekwentnego jego używania, poczynając od samych presenterów. Jeśli presenter potrzebuje zależności, prosi o nią. Odwrotnie, przekazywanie całego kontenera DI do klasy i pobieranie zależności bezpośrednio z niego uznawane jest za antywzorzec (znany jako wzorzec service locator). Takie podejście stosowano w Nette 0.x przed nadejściem dependency injection, a metoda Presenter::getContext(), od dawna oznaczona jako przestarzała, jest pozostałością tamtej epoki.

Jeśli przenosisz bardzo starą aplikację Nette, możesz odkryć, że nadal używa tej metody. Od wersji nette/application 3.1 natrafisz na ostrzeżenie Nette\Application\UI\Presenter::getContext() is deprecated, use dependency injection, a od wersji 4.0 na błąd mówiący, że metoda nie istnieje.

Czystym rozwiązaniem jest oczywiście przerobienie aplikacji tak, żeby przekazywała zależności przez dependency injection. Jako obejście możesz dodać do swojego bazowego presentera własną metodę getContext() i tym samym ominąć komunikat:

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;
	}
}
wersja: 4.x