Reševanje težav

Nette mi ne deluje, prikazuje se bela stran

  • Poskusite v datoteko index.php takoj za declare(strict_types=1); vstaviti ini_set('display_errors', '1'); error_reporting(E_ALL);, s tem boste prisilili prikazovanje napak
  • Če še vedno vidite bel zaslon, je verjetno napaka v nastavitvah strežnika in razlog boste odkrili v dnevniku strežnika. Za vsak slučaj še preverite, ali PHP sploh deluje, tako da poskusite nekaj izpisati z echo 'test';
  • Če vidite napako Server Error: We're sorry! …, nadaljujte z naslednjim odsekom:

Napaka 500 Server Error: We're sorry! …

To stran z napako prikazuje Nette v produkcijskem načinu. Če se vam prikazuje na razvijalskem računalniku, preklopite v razvijalski način in prikazala se vam bo Tracy s podrobnim sporočilom.

Razlog napake vedno preberete v dnevniku v mapi log/. Če pa se v sporočilu o napaki pokaže stavek Tracy is unable to log error, najprej ugotovite, zakaj ni mogoče beležiti napak. To storite na primer tako, da se začasno preklopite v razvijalski način in pustite Tracy, da karkoli zabeleži po njenem zagonu:

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

Tracy vam bo sporočila, zakaj ne more beležiti. Vzrok so lahko nezadostna dovoljenja za pisanje v mapo log/.

Eden najpogostejših vzrokov napake 500 je zastarel predpomnilnik. Medtem ko Nette v razvijalskem načinu pametno samodejno posodablja predpomnilnik, se v produkcijskem načinu osredotoča na maksimizacijo zmogljivosti in brisanje predpomnilnika, po vsaki spremembi kode, je na vas. Poskusite izbrisati temp/cache.

Napaka 404, usmerjanje ne deluje

Če vse strani (razen domače strani) vračajo napako 404, kaže na težavo s konfiguracijo strežnika za lepe URL-je.

Spremembe v predlogah ali konfiguraciji se ne odražajo

“Uredil sem predlogo ali konfiguracijo, vendar spletno mesto še vedno prikazuje staro različico.” To vedenje se pojavi v produkcijskem načinu, ki zaradi zmogljivosti ne preverja sprememb v datotekah in ohranja enkrat generiran predpomnilnik.

Da vam na produkcijskem strežniku po vsaki spremembi ne bi bilo treba ročno brisati predpomnilnika, omogočite razvijalski način za vaš IP naslov v datoteki Bootstrap.php:

$this->configurator->setDebugMode('vas.ip.naslov');

Kako izklopiti predpomnilnik med razvojem?

Nette je pameten in v njem ni treba izklapljati predpomnjenja. Med razvojem namreč samodejno posodablja predpomnilnik ob vsaki spremembi predloge ali konfiguracije DI vsebnika. Razvijalski način se poleg tega vklopi s samodejnim zaznavanjem, zato običajno ni treba konfigurirati ničesar, ali samo IP naslov.

Pri razhroščevanju usmerjevalnika priporočamo izklop predpomnilnika v brskalniku, v katerem so lahko shranjene na primer preusmeritve: odprite Razvijalska orodja (Ctrl+Shift+I ali Cmd+Option+I) in v plošči Network (Omrežje) označite izklop predpomnilnika.

Napaka #[\ReturnTypeWillChange] attribute should be used

Ta napaka se pojavi, če ste posodobili PHP na različico 8.1, vendar uporabljate Nette, ki z njo ni združljiv. Rešitev je torej posodobitev Nette na novejšo različico z uporabo composer update. Nette podpira PHP 8.1 od različice 3.0. Če uporabljate starejšo različico (ugotovite s pogledom v composer.json), nadgradite Nette ali ostanite pri PHP 8.0.

Nastavitev pravic map

Če razvijate na macOS ali Linuxu (ali katerem koli drugem sistemu, ki temelji na Unixu), boste morali nastaviti pravice za pisanje spletnemu strežniku. Predpostavimo, da se vaša aplikacija nahaja v privzeti /var/www/html (Fedora, CentOS, RHEL).

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

Na nekaterih distribucijah Linuxa (Fedora, CentOS, …) je SELinux privzeto omogočen. Morali boste ustrezno prilagoditi politike SELinuxa in nastaviti pravilen varnostni kontekst SELinuxa za mape temp in log. Za temp in log bomo nastavili tip konteksta httpd_sys_rw_content_t, za preostanek aplikacije (in predvsem za mapo app) bo zadostoval httpd_sys_content_t. Na strežniku zaženite:

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/

Nato je treba omogočiti logično vrednost SELinuxa httpd_can_network_connect_db, ki je v privzeti nastavitvi izklopljena in ki bo Nette omogočila povezavo z bazo podatkov prek omrežja. Za to bomo uporabili ukaz setsebool in z možnostjo -P bomo spremembo naredili trajno, tj. po ponovnem zagonu strežnika nas ne bo čakalo neprijetno presenečenje:

setsebool -P httpd_can_network_connect_db on

Kako spremeniti ali odstraniti mapo www iz URL-ja?

Mapa www/, ki se uporablja v vzorčnih projektih v Nette, predstavlja tako imenovano javno mapo ali document-root projekta. To je edina mapa, katere vsebina je dostopna brskalniku. Vsebuje datoteko index.php, vstopno točko, ki zažene spletno aplikacijo, napisano v Nette.

Za zagon aplikacije na gostovanju je treba imeti pravilno konfiguriran document-root. Imate dve možnosti:

  1. V konfiguraciji gostovanja nastavite document-root na to mapo
  2. Če ima gostovanje vnaprej pripravljeno mapo (npr. public_html), preimenujte www/ v to ime

Nikoli se ne poskušajte zanašati samo na .htaccess ali usmerjevalnik za zaščito, ki bi preprečeval dostop do drugih map.

Če gostovanje ne omogoča nastavitve document-root v podmapo (tj. ustvarjanja map eno raven višje nad javno mapo), poiščite drugega ponudnika. Sicer bi tvegali znatno varnostno tveganje. Bilo bi kot živeti v stanovanju, kjer vhodnih vrat ni mogoče zapreti in so vedno na stežaj odprta.

Kako nastaviti strežnik za lepe URL-je?

Apache: potrebno je omogočiti in nastaviti pravila mod_rewrite v datoteki .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]

Če naletite na težave, se prepričajte, da:

Če nastavljate aplikacijo v podmapi, boste morda morali odkomentirati vrstico za nastavitev RewriteBase in jo nastaviti na pravilno mapo.

nginx: potrebno je nastaviti preusmeritev z uporabo direktive try_files znotraj bloka location / v konfiguraciji strežnika.

location / {
	try_files $uri $uri/ /index.php$is_args$args;  # $is_args$args JE POMEMBNO!
}

Blok location se za vsako datotečno sistemsko pot sme v bloku server pojaviti samo enkrat. Če že imate v konfiguraciji location /, dodajte direktivo try_files vanj.

Preverjanje, ali .htaccess deluje

Najlažji način, da preizkusite, ali Apache uporablja ali ignorira vašo datoteko .htaccess, je, da jo namerno poškodujete. Na začetek datoteke vstavite vrstico Test in zdaj, če osvežite stran v brskalniku, bi morali videti Internal Server Error.

Če se vam ta napaka prikaže, je to pravzaprav dobro! Pomeni, da Apache analizira datoteko .htaccess in naleti na napako, ki smo jo vstavili. Odstranite vrstico Test.

Če se ne prikaže Internal Server Error, vaša nastavitev Apache ignorira datoteko .htaccess. Na splošno jo Apache ignorira zaradi manjkajoče konfiguracijske direktive AllowOverride All.

Če gostujete sami, lahko to enostavno popravite. Odprite datoteko httpd.conf ali apache.conf v urejevalniku besedil, poiščite ustrezen del <Directory> in dodajte/spremenite to direktivo:

<Directory "/var/www/htdocs"> # pot do vašega document root
    AllowOverride All
    ...

Če vaše spletno mesto gostuje drugje, preverite v nadzorni plošči, ali lahko tam omogočite datoteko .htaccess. Če ne, se obrnite na ponudnika gostovanja, da to stori za vas.

Preverjanje, ali je omogočen mod_rewrite

Če ste preverili, da htaccess deluje, lahko preverite, ali je omogočena razširitev mod_rewrite. Na začetek datoteke .htaccess vstavite vrstico RewriteEngine On in osvežite stran v brskalniku. Če se prikaže Internal Server Error, pomeni, da mod_rewrite ni omogočen. Obstaja več načinov, kako ga omogočiti. Različne načine, kako to storiti v različnih nastavitvah, najdete na Stack Overflow.

Povezave se generirajo brez https:

Nette generira povezave z istim protokolom, kot ga ima sama stran. Torej na strani https://foo generira povezave, ki se začnejo s https:, in obratno. Če ste za povratnim proxy strežnikom, ki odstranjuje HTTPS (na primer v Dockerju), potem je treba v konfiguraciji nastaviti proxy, da zaznavanje protokola deluje pravilno.

Če kot proxy uporabljate Nginx, je treba imeti nastavljeno preusmeritev npr. takole:

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 ali ime gostitelja strežnika/kontejnerja, kjer teče aplikacija
}

Nato je treba v konfiguracijo vnesti IP proxyja in po možnosti IP obseg vašega lokalnega omrežja, kjer upravljate infrastrukturo:

http:
	proxy: IP-proxy/IP-range

Uporaba znakov { } v JavaScriptu

Znaka { in } se uporabljata za zapis Latte značk. Kot značka se šteje karkoli, kar sledi znaku {, razen presledka in narekovaja. Če torej morate izpisati neposredno znak { (pogosto na primer v JavaScriptu), lahko za znakom { dodate presledek (ali drug prazen znak). S tem se izognete prevajanju kot značke.

Če je treba te znake izpisati v situaciji, ko bi se besedilo razumelo kot značka, lahko uporabite posebne značke za izpis teh znakov – {l} za { in {r} za }.

{je značka}
{ ni značka }
{l}ni značka{r}

Sporočilo Presenter::getContext() is deprecated

Nette je daleč prvi PHP framework, ki je prešel na vbrizgavanje odvisnosti in vodil programerje k njegovi dosledni uporabi, že od samih presenterjev. Če presenter potrebuje kakšno odvisnost, se zanjo prijavi. Nasprotno pa se pot, ko v razred predamo celoten DI vsebnik, in ta si iz njega neposredno jemlje odvisnosti, šteje za antipattern (imenuje se service locator). Ta način se je uporabljal v Nette 0.x še pred prihodom vbrizgavanja odvisnosti in njegov ostanek je metoda Presenter::getContext(), že davno označena kot zastarela.

Če prenašate zelo staro aplikacijo za Nette, se lahko zgodi, da to metodo še vedno uporablja. Od nette/application različice 3.1 se boste tako srečali z opozorilom Nette\Application\UI\Presenter::getContext() is deprecated, use dependency injection, od različice 4.0 pa z napako, da metoda ne obstaja.

Čista rešitev je seveda predelati aplikacijo tako, da si odvisnosti predaja z uporabo vbrizgavanja odvisnosti. Kot obhodno rešitev lahko v svoj osnovni presenter dodate lastno metodo getContext() in tako obidete sporočilo:

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;
	}
}
različica: 4.0