Отстраняване на неизправности

Nette не работи, показва се бяла страница

  • Опитайте да вмъкнете ini_set('display_errors', '1'); error_reporting(E_ALL); във файла index.php веднага след declare(strict_types=1);, това ще принуди показването на грешки
  • Ако все още виждате бял екран, вероятно има грешка в конфигурацията на сървъра и причината ще откриете в лога на сървъра. За всеки случай проверете дали PHP изобщо работи, като опитате да отпечатате нещо с помощта на echo 'test';
  • Ако виждате грешката Server Error: We're sorry! …, продължете към следващата секция:

Грешка 500 Server Error: We're sorry! …

Тази страница за грешки се показва от Nette в продукционен режим. Ако я виждате на компютъра си за разработка, превключете в режим на разработка и ще видите Tracy с подробно съобщение.

Причината за грешката винаги можете да прочетете в лога в директорията log/. Но ако в съобщението за грешка се показва изречението Tracy is unable to log error, първо разберете защо грешките не могат да бъдат логвани. Можете да направите това например, като временно превключите в режим на разработка и накарате Tracy да логне нещо след стартирането си:

// Bootstrap.php
$configurator->setDebugMode('23.75.345.200'); // вашият IP адрес
$configurator->enableTracy($rootDir . '/log');
\Tracy\Debugger::log('hello');

Tracy ще ви каже защо не може да логва. Причината може да е дефектна електронна лампа е-тринадесет от фабрика “Катода Оломоуц”, но по-вероятно са недостатъчни права за запис в директорията log/.

Една от най-честите причини за грешка 500 е остарял кеш. Докато Nette в режим на разработка интелигентно автоматично актуализира кеша, в продукционен режим той се фокусира върху максималната производителност и изчистването на кеша след всяка промяна на кода е ваша отговорност. Опитайте да изтриете temp/cache.

Грешка 404, маршрутизацията не работи

Когато всички страници (с изключение на началната) връщат грешка 404, изглежда има проблем с конфигурацията на сървъра за красиви URL адреси.

Промените в шаблоните или конфигурацията не се отразяват

“Редактирах шаблона или конфигурацията, но уебсайтът все още показва старата версия.” Това поведение се случва в продукционен режим, който поради съображения за производителност не проверява за промени във файловете и поддържа веднъж генериран кеш.

За да не се налага ръчно да изтривате кеша на продукционния сървър след всяка промяна, активирайте режима за разработка за вашия IP адрес във файла Bootstrap.php:

$this->configurator->setDebugMode('вашият IP адрес');

Как да изключите кеша по време на разработка?

Nette е интелигентен и не е необходимо да изключвате кеширането в него. По време на разработка той автоматично актуализира кеша при всяка промяна на шаблона или конфигурацията на DI контейнера. Освен това режимът за разработка се активира чрез автоматично откриване, така че обикновено не е необходимо да конфигурирате нищо, или само IP адреса.

При дебъгване на рутера препоръчваме да изключите кеша на браузъра, в който могат да бъдат съхранени например пренасочвания: отворете Developer Tools (Ctrl+Shift+I или Cmd+Option+I) и в панела Network (Мрежа) отметнете изключването на кеша.

Грешка #[\ReturnTypeWillChange] attribute should be used

Тази грешка се появява, ако сте актуализирали PHP до версия 8.1, но използвате Nette, която не е съвместима с нея. Решението е да актуализирате Nette до по-нова версия с помощта на composer update. Nette поддържа PHP 8.1 от версия 3.0. Ако използвате по-стара версия (можете да проверите в composer.json), надстройте Nette или останете с PHP 8.0.

Настройка на правата на директориите

Ако разработвате на macOS или Linux (или на всяка друга система, базирана на Unix), ще трябва да настроите права за запис за уеб сървъра. Да предположим, че вашето приложение се намира в директорията по подразбиране /var/www/html (Fedora, CentOS, RHEL).

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

На някои Linux дистрибуции (Fedora, CentOS, …) SELinux е активиран по подразбиране. Ще трябва съответно да модифицирате политиките на SELinux и да зададете правилния контекст на сигурност на SELinux за папките temp и log. За temp и log ще зададем тип на контекста httpd_sys_rw_content_t, за останалата част от приложението (и особено за папката app) ще бъде достатъчен httpd_sys_content_t. На сървъра изпълнете:

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/

Освен това е необходимо да се активира булевата променлива на SELinux httpd_can_network_connect_db, която е изключена по подразбиране и която ще позволи на Nette да се свърже с базата данни през мрежата. Ще използваме командата setsebool за това и с опцията -P ще направим промяната постоянна, т.е. след рестартиране на сървъра няма да ни очаква неприятна изненада:

setsebool -P httpd_can_network_connect_db on

Как да промените или премахнете директорията www от URL адреса?

Директорията www/, използвана в примерните проекти в Nette, представлява т.нар. публична директория или document-root на проекта. Това е единствената директория, чието съдържание е достъпно за браузъра. Тя съдържа файла index.php, входната точка, която стартира уеб приложението, написано в Nette.

За да стартирате приложението на хостинг, е необходимо да имате правилно конфигуриран document-root. Имате две възможности:

  1. В конфигурацията на хостинга задайте document-root на тази директория
  2. Ако хостингът има предварително подготвена папка (напр. public_html), преименувайте www/ на това име

Никога не се опитвайте да решавате сигурността само с помощта на .htaccess или рутера, които биха ограничили достъпа до други папки.

Ако хостингът не позволява задаването на document-root в поддиректория (т.е. създаване на директории едно ниво над публичната директория), потърсете друг. В противен случай бихте поели значителен риск за сигурността. Би било като да живеете в апартамент, където входната врата не може да се затвори и винаги е отворена.

Как да настроите сървъра за красиви URL адреси?

Apache: необходимо е да активирате и настроите правилата на mod_rewrite във файла .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]

Ако срещнете проблеми, уверете се, че:

Ако настройвате приложението в подпапка, може да се наложи да разкоментирате реда за настройка на RewriteBase и да го зададете на правилната папка.

nginx: необходимо е да настроите пренасочването с помощта на директивата try_files вътре в блока location / в конфигурацията на сървъра.

location / {
	try_files $uri $uri/ /index.php$is_args$args;  # $is_args$args Е ВАЖНО!
}

Блокът location може да се среща само веднъж за всеки път във файловата система в блока server. Ако вече имате location / в конфигурацията, добавете директивата try_files към него.

Проверка дали .htaccess работи

Най-лесният начин да тествате дали Apache използва или игнорира вашия файл .htaccess е умишлено да го повредите. Поставете ред Test в началото на файла и сега, ако обновите страницата в браузъра, трябва да видите Internal Server Error.

Ако видите тази грешка, всъщност е добре! Това означава, че Apache анализира файла .htaccess и среща грешката, която сме поставили там. Премахнете реда Test.

Ако не се покаже Internal Server Error, вашата конфигурация на Apache игнорира файла .htaccess. Обикновено Apache го игнорира поради липсваща конфигурационна директива AllowOverride All.

Ако хоствате сами, това може лесно да се поправи. Отворете файла httpd.conf или apache.conf в текстов редактор, намерете съответната секция <Directory> и добавете/променете тази директива:

<Directory "/var/www/htdocs"> # път до вашия document root
    AllowOverride All
    ...

Ако вашият уебсайт се хоства другаде, проверете в контролния панел дали можете да активирате файла .htaccess там. Ако не, свържете се с вашия хостинг доставчик, за да го направи вместо вас.

Проверка дали mod_rewrite е активиран

Ако сте проверили, че .htaccess работи, можете да проверите дали разширението mod_rewrite е активирано. Поставете ред RewriteEngine On в началото на файла .htaccess и обновете страницата в браузъра. Ако се покаже Internal Server Error, това означава, че mod_rewrite не е активиран. Има няколко начина да го активирате. Различни начини за това в различни конфигурации можете да намерите в Stack Overflow.

Връзките се генерират без https:

Nette генерира връзки със същия протокол като самата страница. Тоест, на страница https://foo генерира връзки, започващи с https: и обратно. Ако сте зад обратен прокси сървър, който премахва HTTPS (например в Docker), тогава трябва да настроите проксито в конфигурацията, за да работи правилно откриването на протокола.

Ако използвате Nginx като прокси, е необходимо да имате настроено пренасочване например по следния начин:

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 или hostname на сървъра/контейнера, където работи приложението
}

Освен това е необходимо да посочите IP адреса на проксито и евентуално IP обхвата на вашата локална мрежа, където работи инфраструктурата, в конфигурацията:

http:
	proxy: IP-proxy/IP-range

Използване на знаците { } в JavaScript

Знаците { и } се използват за записване на Latte тагове. Като таг се приема всичко, което следва знака {, с изключение на интервал и кавичка. Следователно, ако трябва да отпечатате директно знака { (често например в JavaScript), можете да поставите интервал (или друг празен знак) след знака {. По този начин ще избегнете превода му като таг.

Ако е необходимо да отпечатате тези знаци в ситуация, в която текстът би се разбрал като таг, можете да използвате специални тагове за отпечатване на тези знаци – {l} за { и {r} за }.

{е таг}
{ не е таг }
{l}не е таг{r}

Съобщение Presenter::getContext() is deprecated

Nette е далеч първият PHP framework, който премина към инжектиране на зависимости и насочи програмистите към последователното му използване, още от самите presenter-и. Ако presenter-ът се нуждае от някаква зависимост, той я заявява. Обратно, подходът, при който предаваме целия DI контейнер на класа и той директно извлича зависимости от него, се счита за антипатърн (нарича се service locator). Този метод се използваше в Nette 0.x още преди появата на инжектирането на зависимости и негов остатък е методът Presenter::getContext(), отдавна означен като deprecated.

Ако пренасяте много старо приложение към Nette, може да се окаже, че то все още използва този метод. От версия 3.1 на nette/application ще срещнете предупреждението Nette\Application\UI\Presenter::getContext() is deprecated, use dependency injection, а от версия 4.0 – грешка, че методът не съществува.

Чистото решение, разбира се, е да преработите приложението така, че да предава зависимостите чрез инжектиране на зависимости. Като заобиколно решение можете да добавите собствен метод getContext() към вашия базов presenter и така да заобиколите съобщението:

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;
	}
}
версия: 4.0