Rozwiązywanie problemów

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

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

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

Tę stronę błędu wyświetla Nette w trybie produkcyjnym. Jeśli wyświetla się na Twoim komputerze deweloperskim, przełącz się do trybu deweloperskiego, a wyświetli się Tracy ze szczegółowym komunikatem.

Przyczynę błędu zawsze znajdziesz w logu w katalogu log/. Jeśli jednak w komunikacie błędu pojawia się zdanie Tracy is unable to log error, najpierw ustal, dlaczego nie można logować błędów. Zrobisz to na przykład tak, że tymczasowo przełączysz się do trybu deweloperskiego i pozwolisz Tracy cokolwiek zalogować po jej uruchomieniu:

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

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

Jedną z najczęstszych przyczyn błędu 500 jest przestarzała pamięć podręczna (cache). Podczas gdy Nette w trybie deweloperskim sprytnie automatycznie aktualizuje cache, w trybie produkcyjnym skupia się na maksymalizacji wydajności, a czyszczenie cache po każdej modyfikacji kodu należy do Ciebie. Spróbuj usunąć temp/cache.

Błąd 404, routing nie działa

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

Zmiany w szablonach lub konfiguracji nie są widoczne

“Zmodyfikowałem szablon lub konfigurację, ale strona nadal wyświetla starą wersję.” To zachowanie występuje w trybie produkcyjnym, który ze względu na wydajność nie kontroluje zmian w plikach i utrzymuje raz wygenerowaną pamięć podręczną.

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

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

Jak wyłączyć cache podczas developmentu?

Nette jest sprytne i nie musisz w nim wyłączać buforowania. Podczas rozwoju bowiem automatycznie aktualizuje cache przy każdej zmianie szablonu lub konfiguracji kontenera DI. Tryb deweloperski jest ponadto włączany przez autodetekcję, więc zazwyczaj nie trzeba nic konfigurować, lub tylko adres IP.

Podczas debugowania routera zalecamy wyłączenie cache w przeglądarce, w której mogą być zapisane na przykład przekierowania: otwórz Narzędzia deweloperskie (Ctrl+Shift+I lub Cmd+Option+I) i w panelu Sieć (Network) zaznacz wyłączenie cache.

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

Ten błąd pojawi się, jeśli zaktualizowałeś PHP do wersji 8.1, ale używasz Nette, które nie jest z nią kompatybilne. Rozwiązaniem jest więc aktualizacja Nette do nowszej wersji za pomocą composer update. Nette obsługuje PHP 8.1 od wersji 3.0. Jeśli używasz wersji starszej (sprawdzisz w composer.json), zaktualizuj Nette lub pozostań przy PHP 8.0.

Ustawianie uprawnień do katalogów

Jeśli tworzysz oprogramowanie na macOS lub Linuksie (lub na jakimkolwiek innym systemie opartym na Uniksie), będziesz musiał ustawić uprawnienia zapisu dla serwera WWW do katalogów temp/ i log/. Załóżmy, że Twoja aplikacja znajduje się w domyślnym /var/www/html (Fedora, CentOS, RHEL).

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

Na niektórych dystrybucjach Linuksa (Fedora, CentOS, …) domyślnie włączony jest SELinux. Będziesz musiał odpowiednio zmodyfikować polityki SELinux i ustawić prawidłowy kontekst bezpieczeństwa SELinux dla folderów temp i log. Dla temp i log ustawimy typ kontekstu httpd_sys_rw_content_t, dla reszty aplikacji (a zwłaszcza dla folderu app) wystarczy httpd_sys_content_t. Na serwerze uruchom:

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/

Następnie należy włączyć flagę SELinux httpd_can_network_connect_db, która jest domyślnie wyłączona i która pozwoli Nette połączyć się z bazą danych przez sieć. Użyjemy do tego polecenia setsebool z opcją -P, aby zmiana była trwała, tzn. po restarcie serwera nie spotka nas niemiła niespodzianka:

setsebool -P httpd_can_network_connect_db on

Jak zmienić lub usunąć katalog www z adresu URL?

Katalog www/ używany w przykładowych projektach Nette reprezentuje tzw. katalog publiczny lub document-root projektu. Jest to jedyny katalog, którego zawartość jest dostępna dla przeglądarki. Zawiera on plik index.php, punkt wejściowy, który uruchamia aplikację internetową napisaną w Nette.

Aby uruchomić aplikację na hostingu, konieczne jest prawidłowe skonfigurowanie document-root. Masz dwie możliwości:

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

Nigdy nie próbuj rozwiązywać problemów z bezpieczeństwem tylko za pomocą .htaccess lub routera, które miałyby uniemożliwiać dostęp do pozostałych folderów.

Jeśli hosting nie pozwala na ustawienie document-root na podkatalog (tj. tworzenie katalogów o poziom wyżej nad katalogiem publicznym), poszukaj innego. W przeciwnym razie naraziłbyś się na znaczne ryzyko bezpieczeństwa. Byłoby to jak mieszkanie w mieszkaniu, w którym nie da się zamknąć drzwi wejściowych i są one stale otwarte.

Jak skonfigurować serwer dla przyjaznych adresów URL?

Apache: należy 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 konfigurujesz aplikację w podkatalogu, być może będziesz musiał odkomentować linię do ustawienia RewriteBase i ustawić ją na właściwy folder.

nginx: należy skonfigurować przekierowanie za pomocą dyrektywy 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 dla każdej ścieżki systemu plików może występować w bloku server tylko raz. Jeśli już masz w konfiguracji location /, dodaj do niego dyrektywę try_files.

Sprawdzenie, czy .htaccess działa

Najprostszym sposobem na przetestowanie, czy Apache używa czy ignoruje Twój plik .htaccess, jest celowe jego uszkodzenie. Wstaw na początek pliku linię Test i teraz, jeśli odświeżysz stronę w przeglądarce, powinieneś zobaczyć Internal Server Error.

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

Jeśli nie zobaczysz Internal Server Error, Twoja konfiguracja Apache ignoruje plik .htaccess. Ogólnie Apache ignoruje go z powodu brakującej dyrektywy konfiguracyjnej AllowOverride All.

Jeśli hostujesz go sam, można to łatwo naprawić. Otwórz plik httpd.conf lub apache.conf w edytorze tekstu, znajdź odpowiednią sekcję <Directory> i dodaj/zmień tę dyrektywę:

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

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

Sprawdzenie, czy mod_rewrite jest włączony

Jeśli masz pewność, ż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. Istnieje kilka sposobów, aby go włączyć. Różne sposoby, jak to można zrobić w różnych konfiguracjach, znajdziesz na Stack Overflow.

Linki generują się bez https:

Nette generuje linki z tym samym protokołem, jaki ma sama strona. Czyli na stronie https://foo generuje linki zaczynające się od https: i odwrotnie. Jeśli jesteś za odwrotnym serwerem proxy, który usuwa HTTPS (na przykład w Dockerze), należy w konfiguracji ustawić proxy, aby detekcja protokołu działała poprawnie.

Jeśli używasz Nginx jako proxy, musisz mieć ustawione przekierowanie np. w ten sposób:

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 lub nazwa hosta serwera/kontenera, na którym działa aplikacja
}

Następnie należy w konfiguracji podać IP proxy i ewentualnie zakres IP Twojej sieci lokalnej, w której działa infrastruktura:

http:
	proxy: IP-proxy/IP-range

Użycie znaków { } w JavaScript

Znaki { i } są używane do zapisu tagów Latte. Jako tag traktowane jest wszystko, co następuje po znaku { z wyjątkiem spacji i cudzysłowu. Jeśli więc potrzebujesz wypisać bezpośrednio znak { (często na przykład w JavaScript), możesz po znaku { wstawić spację (lub inny biały znak). W ten sposób unikniesz interpretacji jako tagu.

Jeśli konieczne jest wypisanie tych znaków w sytuacji, gdy tekst byłby interpretowany jako tag, możesz wykorzystać specjalne tagi do wypisania tych znaków – {l} dla { i {r} dla }.

{jest tagiem}
{ nie jest tagiem }
{l}nie jest tagiem{r}

Komunikat Presenter::getContext() is deprecated

Nette jest zdecydowanie pierwszym frameworkiem PHP, który przeszedł na wstrzykiwanie zależności i prowadził programistów do jego konsekwentnego używania, już od samych prezenterów. Jeśli prezenter potrzebuje jakiejś zależności, zgłasza się po nią. Natomiast droga, w której do klasy przekazujemy cały kontener DI, a ta wyciąga z niego zależności bezpośrednio, uważana jest za antywzorzec (nazywa się service locator). Ten sposób był używany w Nette 0.x jeszcze przed pojawieniem się wstrzykiwania zależności, a jego pozostałością jest metoda Presenter::getContext(), dawno oznaczona jako przestarzała (deprecated).

Jeśli przenosisz bardzo starą aplikację do Nette, może się zdarzyć, że ta metoda nadal jest używana. Od nette/application wersji 3.1 spotkasz się więc z ostrzeżeniem Nette\Application\UI\Presenter::getContext() is deprecated, use dependency injection, od wersji 4.0 z błędem, że metoda nie istnieje.

Czystym rozwiązaniem jest oczywiście przerobienie aplikacji tak, aby przekazywała zależności za pomocą wstrzykiwania zależności. Jako obejście (workaround) możesz do swojego podstawowego prezentera dodać własną metodę getContext() i w ten sposób obejść komunikat:

abstract class 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;
	}
}
wersja: 4.0