Устранение неполадок

Nette не работает, отображается белая страница

  • Попробуйте вставить в файл index.php после declare(strict_types=1); строку ini_set('display_errors', '1'); error_reporting(E_ALL);, чтобы принудительно включить вывод ошибок.
  • Если вы по-прежнему видите белый экран, вероятно, дело в ошибке настройки сервера, и причину вы найдёте в его логе. На всякий случай проверьте, работает ли 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.

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

“Я изменил шаблон или конфигурацию, но сайт по-прежнему показывает старую версию.” Такое поведение возникает в продакшн-режиме, который из соображений производительности не проверяет изменения файлов и держит ранее порождённый кеш.

Чтобы после каждой правки не очищать кеш на боевом сервере вручную, включите в файле Bootstrap.php режим разработки для своего IP-адреса:

$this->configurator->setDebugMode('your.ip.address');

Как отключить кеш при разработке?

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

При отладке маршрутизатора мы рекомендуем отключить кеш браузера, в котором могут храниться, например, перенаправления: откройте инструменты разработчика (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

Как изменить или убрать из URL каталог www?

Каталог 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. Вставьте в начало файла .htaccess строку RewriteEngine On и обновите страницу в браузере. Если вы увидите 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 или имя хоста сервера либо контейнера, где работает приложение
}

Кроме того, в конфигурации нужно указать IP прокси и, возможно, диапазон IP вашей локальной сети, где вы держите инфраструктуру:

http:
	proxy: IP-proxy/IP-range

Использование символов { } в JavaScript

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

Если эти символы нужно вывести в ситуации, когда текст был бы истолкован как тег, можно использовать особые теги для вывода этих символов: {l} для { и {r} для }.

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

Ошибка Cannot modify header information - headers already sent

Эта ошибка возникает, когда приложение пытается отправить HTTP-заголовок (cookie, перенаправление или запуск сессии) в момент, когда в браузер уже отправлен какой-то вывод. Заголовки всегда должны предшествовать телу ответа.

Возможных причин две: либо вывод уходит слишком рано, либо заголовок отправляется слишком поздно.

Вывод обычно уходит слишком рано из-за случайного пробела или пустой строки перед <?php, после закрывающего ?> либо из-за BOM, который редактор вставил в начало файла и не показывает. Поэтому никогда не заканчивайте PHP-файлы через ?>. Чтобы выяснить, какое место вывело первым, используйте Tracy\OutputDebugger.

Заголовок отправляется слишком поздно обычно при работе с сессией. Nette запускает сессию автоматически при первом чтении из неё или записи в неё, и если это происходит только при отрисовке шаблона, вывод уже в пути. Поэтому работайте с сессией самое позднее в методе beforeRender(), а в компонентах ещё и в методах handle<Signal>().

Не пытайтесь решить проблему заданием autoStart: true. Это запускает сессию для каждого посетителя, включая роботов, и без надобности создаёт огромное количество файлов на диске. Значение по умолчанию smart запускает сессию, только когда она действительно нужна.

Предупреждение Presenter::getContext() is deprecated

Nette была безусловно первым PHP-фреймворком, перешедшим на внедрение зависимостей и приучавшим программистов последовательно его использовать, начиная прямо с презентеров. Если презентеру нужна зависимость, он о ней просит. Наоборот, передача всего DI-контейнера в класс, чтобы тот доставал зависимости сам, считается антишаблоном (известным как service locator). Такой подход применялся в Nette 0.x до появления внедрения зависимостей, и метод Presenter::getContext(), давно помеченный как устаревший, – пережиток той эпохи.

Если вы переносите очень старое приложение на Nette, вы можете обнаружить, что оно ещё использует этот метод. Начиная с версии nette/application 3.1 вы столкнётесь с предупреждением Nette\Application\UI\Presenter::getContext() is deprecated, use dependency injection, а начиная с версии 4.0 – с ошибкой о том, что метода не существует.

Чистое решение, разумеется, – переработать приложение так, чтобы зависимости передавались через внедрение зависимостей. В качестве обходного пути вы можете добавить в свой базовый презентер собственный метод getContext(), чтобы обойти сообщение:

abstract class BasePresenter extends Nette\Application\UI\Presenter
{
	private Nette\DI\Container $context;

	public function injectContext(Nette\DI\Container $context): void
	{
		$this->context = $context;
	}

	public function getContext(): Nette\DI\Container
	{
		return $this->context;
	}
}
версия: 4.x