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 *Ошибка сервера: We're sorry! ...* .[#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` находится в каталоге document-root (т.е. рядом с файлом `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`.

Если вы не видите *Internal Server Error*, значит, ваша установка 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:` и наоборот.
Если вы находитесь за HTTPS-стриминговым обратным прокси (например, в Docker), то вам нужно [set up a proxy|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()`, давно помеченный как deprecated.

Если вы портируете очень старое приложение 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 Ошибка сервера: We're sorry! …

Эта страница ошибки отображается 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.

Если вы не видите Internal Server Error, значит, ваша установка 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: и наоборот. Если вы находитесь за HTTPS-стриминговым обратным прокси (например, в Docker), то вам нужно set up a 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 диапазон вашей локальной сети, где вы запускаете инфраструктуру:

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(), давно помеченный как deprecated.

Если вы портируете очень старое приложение 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;
	}
}