Nette Documentation Preview

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


Nette не работи, показва бяла страница .[#toc-nette-is-not-working-white-page-is-displayed]
-------------------------------------------------------------------------------------------
- Опитайте да поставите `ini_set('display_errors', '1'); error_reporting(E_ALL);` след `declare(strict_types=1);` във файла `index.php`, за да накарате грешките да се показват.
- Ако все още виждате бял екран, вероятно има грешка в конфигурацията на сървъра и причината ще откриете в дневника на сървъра. За да сте сигурни, проверете дали PHP изобщо работи, като се опитате да въведете нещо с помощта на `echo 'test';`.
- Ако видите грешка *Server Error: We're sorry! ...*, преминете към следващия раздел:


Грешка 500 *Сървърна грешка: Съжаляваме! ...* .[#toc-error-500-server-error-we-re-sorry]
----------------------------------------------------------------------------------------
Тази страница за грешка се показва от Nette в производствен режим. Ако я видите на машината си за разработка, [превключете на режим за разработчици |application:bootstrap#Development vs Production Mode] и Tracy ще се покаже с подробен отчет.

Винаги можете да намерите причината за грешката в директорията `log/`. Ако обаче съобщението за грешка показва фразата `Tracy is unable to log error`, първо определете защо грешките не могат да се регистрират. Можете да направите това, например, като временно [превключите |application:bootstrap#Development vs Production Mode] в режим на разработчик и позволите на Tracy да запише всичко след стартирането си:

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

Tracy ще ви информира защо не може да регистрира. Причината може да е [недостатъчно разрешение за |#Setting Directory Permissions] запис в директорията `log/`.

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


Грешка 404, маршрутизирането не работи .[#toc-error-404-routing-not-working]
----------------------------------------------------------------------------
Когато всички страници (с изключение на началната страница) връщат грешка 404, това изглежда като проблем с конфигурацията на сървъра за [красиви URL адреси |#How to Configure a Server for Nice URLs?].


Как да деактивирам кеша по време на разработка? .[#toc-how-to-disable-cache-during-development]
-----------------------------------------------------------------------------------------------
Nette е умен и не е необходимо да деактивирате кеширането в него. По време на разработката той автоматично актуализира кеша, когато има промяна в шаблона или конфигурацията на DI контейнера. Освен това режимът за разработка се активира чрез автоматично откриване, така че обикновено не е необходимо да конфигурирате нищо [или само IP адреса |application:bootstrap#development-vs-production-mode].

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


Грешка `#[\ReturnTypeWillChange] attribute should be used` .[#toc-error-returntypewillchange-attribute-should-be-used]
----------------------------------------------------------------------------------------------------------------------
Тази грешка се появява, ако сте обновили PHP до версия 8.1, но използвате Nette, която не е съвместима с нея. Решението е да се обнови Nette до по-нова версия с помощта на `composer update`. Nette поддържа PHP 8.1 от версия 3.0. Ако използвате по-стара версия (можете да разберете това, като погледнете `composer.json`), [обновете Nette |migrations:en] или останете с PHP 8.0.


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

```shell
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 на сървъра:

```shell
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`, тази настройка ще се запази при всички рестартирания.

```shell
setsebool -P httpd_can_network_connect_db on
```


Как да променя или премахна директорията `www` от URL адреса? .[#toc-how-to-change-or-remove-www-directory-from-url]
--------------------------------------------------------------------------------------------------------------------
Директорията `www/`, използвана в примерните проекти в Nette, е така наречената публична директория или коренът на проекта. Това е единствената директория, чието съдържание е достъпно за браузъра. Той съдържа файла `index.php`, който е входната точка, от която започва уеб приложението, написано на Nette.

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

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


Как да настроя сървъра за красиви URL адреси? .[#toc-how-to-configure-a-server-for-nice-urls]
---------------------------------------------------------------------------------------------
**Apache**: трябва да разрешите и зададете правила за mod_rewrite във файла `.htaccess`:

```apacheconf
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]
```

Ако се сблъскате с проблеми, уверете се, че:
- файлът `.htaccess` се намира в директорията с корена на документа (т.е. до файла `index.php` )
- [Apache обработва файловете .htaccess |#Test if .htaccess is working]
- [mod_rewrite е активиран |#Test if mod_rewrite is enabled]

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

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

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

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


Проверка дали `.htaccess` работи .[#toc-test-if-htaccess-is-working]
--------------------------------------------------------------------
Най-простият начин да тествате дали Apache използва или игнорира вашия файл `.htaccess` е да го нарушите умишлено. Поставете реда `Test` в началото на файла и сега, ако опресните страницата в браузъра си, трябва да видите *Internal Server Error* (Вътрешна грешка на сървъра).

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

Ако не видите *вътрешна грешка на сървъра*, вашата настройка на Apache игнорира файла `.htaccess`. Обикновено Apache го игнорира заради липсващата конфигурационна директива `AllowOverride All`.

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

```apacheconf
<Directory "/var/www/htdocs"> # path to your document root
    AllowOverride All
    ...
```

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


Проверка дали е активиран `mod_rewrite` .[#toc-test-if-mod-rewrite-is-enabled]
------------------------------------------------------------------------------
Ако сте проверили, че [`.htaccess` работи |#Test if .htaccess is working], можете да проверите дали разширението mod_rewrite е разрешено. Поставете реда `RewriteEngine On` в началото на файла `.htaccess` и опреснете страницата в браузъра си.
Ако видите *Internal Server Error* (Вътрешна грешка на сървъра), това означава, че разширението mod_rewrite не е разрешено. Има няколко начина да го активирате. Вижте Stack Overflow за различните начини, по които това може да се направи при различните настройки.


Връзките се генерират без `https:`. .[#toc-links-are-generated-without-https]
-----------------------------------------------------------------------------
Nette генерира връзки със същия протокол, който използва текущата страница. Така връзките, започващи с `https://foo` и обратно.
Ако се намирате зад обратен прокси сървър за стрийминг на HTTPS (напр. в Docker), трябва да настроите [прокси сървър |http:configuration#HTTP-Proxy] в конфигурацията, за да работи правилно дефиницията на протокола.

Ако използвате 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 обхвата на локалната мрежа, в която се изпълнява инфраструктурата:

```neon
http:
	proxy: IP-proxy/IP-range
```


Използване на знаци { } в JavaScript .[#toc-use-of-characters-in-javascript]
----------------------------------------------------------------------------
Символите `{` и `}` се използват за изписване на етикети Latte. Всичко (с изключение на бели интервали и кавички) след символа `{`, считается тегом. Если вам нужно вывести символ `{` (често срещан в JavaScript), можете да поставите интервал (или друг празен символ) непосредствено след `{`. По този начин се избягва тълкуването му като таг.

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

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


Уведомление `Presenter::getContext() is deprecated` .[#toc-notice-presenter-getcontext-is-deprecated]
-----------------------------------------------------------------------------------------------------

Nette определено е първата рамка на PHP, която преминава към инжектиране на зависимостите и принуждава програмистите да я използват последователно, като се започне от главния модул. Ако водещият иска зависимост, [той ще я поиска |dependency-injection:passing-dependencies].
За разлика от това, начинът, по който предаваме целия 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()` към основния презентатор и да заобиколите съобщението:

```php
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;
	}
}
```


{{leftbar: www:@menu-common}}

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

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 ще се покаже с подробен отчет.

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

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

Tracy ще ви информира защо не може да регистрира. Причината може да е недостатъчно разрешение за запис в директорията log/.

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

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

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

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

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 или да зададете пътищата до директориите 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]

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

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

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

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.

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

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

<Directory "/var/www/htdocs"> # path to your document root
    AllowOverride All
    ...

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

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

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

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;
	}
}