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

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 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 или в роутере. Если хостинг не позволяет вам установить 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]

Чтобы изменить конфигурацию 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: и наоборот. Если вы находитесь за 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;
	}
}