Rozwiązywanie problemów
Nette nie działa, wyświetla się biała strona
- Spróbuj wstawić do pliku
index.phpzadeclare(strict_types=1);zapisini_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:
- Ustawić w konfiguracji hostingu document-root na ten katalog.
- 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:
- plik
.htaccessznajduje się w katalogu document-root (czyli obok plikuindex.php) - Apache przetwarza pliki
.htaccess - mod_rewrite jest włączony
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;
}
}