トラブルシューティング

Nette が動かない、白いページが表示される

  • エラーを必ず表示させるために、index.phpdeclare(strict_types=1); のうしろに ini_set('display_errors', '1'); error_reporting(E_ALL); を置いてみてください。
  • それでも白い画面が見えるなら、たいていサーバーの設定に誤りがあり、その理由はサーバーのログにあります。念のため、echo 'test'; で何かを出力してみて、そもそも PHP が動いているか確かめてください。
  • 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 を使っている場合に出ます。解は composer update で Nette を新しい版に更新することです。Nette はバージョン 3.0 から PHP 8.1 に対応しています。それより古い版を使っているなら(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 のポリシーを更新するか、templog のディレクトリのパスに正しい SELinux の安全の文脈を設定する必要があるかもしれません。templog のディレクトリには 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/

さらに、Nette がネットワーク越しにデータベースへつなげるように、SELinux の真偽値 httpd_can_network_connect_db を有効にする必要があります。既定では無効です。これには setsebool のコマンドが使え、-P のオプションを付ければこの設定は再起動しても残ります。

setsebool -P httpd_can_network_connect_db on

URL から www のディレクトリを変えたり取り除いたりするには

Nette の見本のプロジェクトで使われている www/ ディレクトリは、プロジェクトの公開のディレクトリ、つまり document-root を表します。中身がブラウザから触れる唯一のディレクトリです。そこには index.php があり、これが Nette のウェブアプリケーションを立ち上げる入口です。

ホスティングでアプリケーションを動かすには、document-root を正しく設定する必要があります。方法は 2 つあります。

  1. ホスティングの設定で document-root をこのディレクトリにします。
  2. ホスティングにあらかじめ用意されたフォルダ(たとえば public_html)があるなら、www/ をその名前に変えます。

.htaccess やルーターの決まりだけでほかのフォルダへのアクセスを防ぎ、それでアプリケーションを守ろうとしては決していけません。

ホスティングが document-root を下位のディレクトリに設定できない(つまり公開ディレクトリのひとつ上の階層にディレクトリを作れない)なら、別の業者を探してください。さもないと大きなセキュリティリスクを背負うことになります。それは玄関の扉が閉まらず、いつも開けっぱなしの家に住むようなものです。

きれいな URL のためにサーバーを設定するには

Apache: .htaccess ファイルで mod_rewrite の決まりを有効にして設定する必要があります。

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: サーバーの設定の location / のブロックの中で、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.confapache.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 のヘッダー(クッキー、リダイレクト、セッションの開始)を送ろうとしたときに起こります。ヘッダーはいつもレスポンスの本文より前になければなりません。

原因は 2 つ考えられます。出力が早く出すぎているか、ヘッダーが遅く送られすぎているかです。

出力が早く出すぎるのは、たいてい <?php の前や閉じる ?> のあとの紛れ込んだ空白や空行のせい、あるいはエディタがファイルの先頭に入れて表示もしない BOM のせいです。ですから PHP のファイルを ?> で終わらせては決していけません。どこが最初に出力したかを突き止めるには Tracy\OutputDebuggerを使ってください。

ヘッダーが遅く送られすぎるのは、たいていセッションを扱うときです。Nette は最初にセッションから読むか書き込むときに自動的にセッションを始めるので、それがテンプレートを描いている最中にだけ起きるなら、出力はもう流れ出しています。ですからセッションは遅くとも beforeRender() メソッドで、コンポーネントでは handle<Signal>() メソッドでも扱ってください。

この問題を autoStart: trueの設定で解こうとしないでください。それはロボットも含むすべての訪問者にセッションを始め、必要もないのに大量のファイルをディスクに作ります。既定値の smart は、本当に必要なときにだけセッションを始めます。

通知 Presenter::getContext() is deprecated

Nette は PHP のフレームワークの中でずば抜けて早く dependency injection へ移り、プレゼンターを手はじめに、それを一貫して使うようプログラマーを導きました。プレゼンターが依存関係を必要とするなら、それを求めます。逆に DI コンテナ全体をクラスに渡し、そこから依存関係を直接引き出させるのはアンチパターンと見なされます(service locator のパターンとして知られています)。この手は dependency injection が現れる前の Nette 0.x で使われていて、ずっと前から非推奨と印を付けられている Presenter::getContext() メソッドは、その時代の名残です。

とても古い Nette のアプリケーションを移しているなら、そこでまだこのメソッドが使われているのを見つけるかもしれません。nette/application のバージョン 3.1 からは警告 Nette\Application\UI\Presenter::getContext() is deprecated, use dependency injection が出て、バージョン 4.0 からはそのメソッドが存在しないというエラーになります。

もちろんきれいな解は、dependency injection で依存関係を渡すようにアプリケーションを整理し直すことです。その場しのぎとしては、このメッセージを避けるために、基底のプレゼンターに自分の 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