Решаване на проблеми

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

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

Грешка 500 Сървърна грешка: Съжаляваме! …

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

Ако съобщението за грешка съдържа Tracy is unable to log error, разберете защо грешките не могат да бъдат регистрирани. Това може да стане например чрез превключване в режим за разработчици и извикване на Tracy\Debugger::log('hello'); след $configurator->enableTracy(...). Трейси ще ви каже защо не може да се регистрира. Обикновено причината е в недостатъчните права за запис в директорията log/.

Ако фразата Tracy is unable to log error липсва в съобщението за грешка (вече не е така), можете да откриете причината за грешката в директорията на дневника log/.

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

Грешка #[\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 или да зададете пътищата до директориите temp и log с правилния контекст за сигурност на SELinux. Директориите temp и log трябва да бъдат настроени на контекста httpd_sys_rw_content_t; за останалата част от приложението – главно за папката app – ще бъде достатъчен контекстът httpd_sys_content_t. Изпълнете изброените команди като root на сървъра:

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, е така наречената публична директория или коренът на проекта. Това е единствената директория, чието съдържание е достъпно за браузъра. Той съдържа файла index.php, който е входната точка, от която започва уеб приложението, написано на Nette.

За да стартирате приложението в хостинг услуга, трябва да зададете document-root в тази директория в конфигурацията на хостинга. Или ако на хостинга има готова папка за публичната директория с различно име (например web, public_html и т.н.), просто преименувайте www/.

Единственото** решение е да се “отървете” от папката www/, като използвате правила във файла .htaccess или в маршрутизатора. Ако хостингът ви не позволява да зададете корен на документа в поддиректория (т.е. да създавате директории едно ниво над публичната директория), потърсете друга. В противен случай поемате значителен риск за сигурността. Това е като да живееш в апартамент, в който не можеш да затвориш входната врата и тя винаги е отворена.

Как да настроя сървъра за красиви 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]

За да промените конфигурацията на Apache чрез файловете .htaccess, трябва да е разрешена директивата AllowOverride. Това е поведението по подразбиране за Apache.

nginx: В конфигурацията на сървъра трябва да се използва директивата try_files:

location / {
	try_files $uri $uri/ /index.php$is_args$args;  # $is_args$args важно
}

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

Nette генерира връзки със същия протокол, който използва текущата страница. Така връзките, започващи с https://foo и обратно. Ако се намирате зад обратен прокси сървър за стрийминг на 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 или имя хоста сервера/контейнера, на котором запущено приложение.
}

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

http:
	proxy: IP-proxy/IP-range

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

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

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

{is tag}
{ is not tag }
{l}is not tag{r}

Уведомление Presenter::getContext() is deprecated

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

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

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

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