Fehlerbehebung
- Nette funktioniert nicht, eine weiße Seite wird angezeigt
- Fehler 500 Server Error: We're sorry! …
- Fehler 404, Routing funktioniert nicht
- Änderungen in Templates oder in der Konfiguration wirken sich nicht aus
- Wie schaltet man den Cache während der Entwicklung aus?
- Fehler #[\ReturnTypeWillChange] attribute should be used
- Einstellung der Verzeichnisberechtigungen
- Wie ändert oder entfernt man das Verzeichnis www aus der URL?
- Wie konfiguriert man den Server für schöne URLs?
- Überprüfung, ob .htaccess funktioniert
- Überprüfung, ob mod_rewrite aktiviert ist
- Links werden ohne https: generiert
- Verwendung der Zeichen { } in JavaScript
- Fehler Cannot modify header information - headers already sent
- Meldung Presenter::getContext() is deprecated
Nette funktioniert nicht, eine weiße Seite wird angezeigt
- Versuchen Sie, in der Datei
index.phphinterdeclare(strict_types=1);die Zeileini_set('display_errors', '1'); error_reporting(E_ALL);einzufügen, um die Anzeige von Fehlern zu erzwingen. - Sehen Sie weiterhin eine weiße Seite, liegt vermutlich ein Fehler in der Serverkonfiguration vor, und den Grund finden Sie im
Log des Servers. Prüfen Sie zur Sicherheit, ob PHP überhaupt funktioniert, indem Sie versuchen, mit
echo 'test';etwas auszugeben. - Sehen Sie den Fehler Server Error: We're sorry! …, machen Sie mit dem nächsten Abschnitt weiter:
Fehler 500 Server Error: We're sorry! …
Diese Fehlerseite zeigt Nette im Produktionsmodus an. Sehen Sie sie auf Ihrem Entwicklungsrechner, wechseln Sie in den Entwicklungsmodus, und Tracy zeigt Ihnen einen ausführlichen Bericht.
Den Grund für den Fehler finden Sie immer im Log im Verzeichnis log/. Enthält die Fehlermeldung jedoch die
Formulierung Tracy is unable to log error, stellen Sie zuerst fest, warum sich Fehler nicht protokollieren lassen.
Das können Sie zum Beispiel tun, indem Sie vorübergehend in den Entwicklungsmodus
wechseln und Tracy nach dem Start irgendetwas protokollieren lassen:
// Bootstrap.php
$configurator->setDebugMode('23.75.345.200'); // Ihre IP-Adresse
$configurator->enableTracy($rootDir . '/log');
\Tracy\Debugger::log('hello');
Tracy sagt Ihnen, warum es nicht protokollieren kann. Die Ursache können unzureichende Rechte zum Schreiben in das Verzeichnis
log/ sein.
Einer der häufigsten Gründe für einen Fehler 500 ist ein veralteter Cache. Während Nette den Cache im Entwicklungsmodus
klug automatisch aktualisiert, konzentriert es sich im Produktionsmodus auf maximale Leistung, und das Leeren des Caches nach
jeder Änderung des Codes liegt in Ihrer Verantwortung. Versuchen Sie, temp/cache zu löschen.
Fehler 404, Routing funktioniert nicht
Wenn alle Seiten (außer der Startseite) einen Fehler 404 zurückgeben, sieht das nach einem Problem mit der Serverkonfiguration für schöne URLs aus.
Änderungen in Templates oder in der Konfiguration wirken sich nicht aus
“Ich habe das Template oder die Konfiguration geändert, aber die Website zeigt weiterhin die alte Version.” Dieses Verhalten tritt im Produktionsmodus auf, der aus Leistungsgründen nicht auf Dateiänderungen prüft und den zuvor erzeugten Cache behält.
Damit Sie den Cache auf dem Produktionsserver nicht nach jeder Änderung von Hand leeren müssen, aktivieren Sie in der Datei
Bootstrap.php den Entwicklungsmodus für Ihre IP-Adresse:
$this->configurator->setDebugMode('your.ip.address');
Wie schaltet man den Cache während der Entwicklung aus?
Nette ist klug, und Sie müssen das Caching darin nicht abschalten. Während der Entwicklung aktualisiert es den Cache automatisch, sobald sich etwas am Template oder an der Konfiguration des DI-Containers ändert. Außerdem wird der Entwicklungsmodus per Autodetektion aktiviert, sodass meist nichts konfiguriert werden muss, höchstens die IP-Adresse.
Beim Debuggen des Routers empfehlen wir, den Cache des Browsers abzuschalten, in dem zum Beispiel Weiterleitungen gespeichert sein können: Öffnen Sie die Entwicklerwerkzeuge (Strg+Umschalt+I oder Cmd+Option+I) und aktivieren Sie im Panel Network das Kästchen zum Abschalten des Caches.
Fehler
#[\ReturnTypeWillChange] attribute should be used
Dieser Fehler tritt auf, wenn Sie PHP auf Version 8.1 aktualisiert haben, aber eine Version von Nette verwenden, die damit
nicht kompatibel ist. Die Lösung ist, Nette mit composer update auf eine neuere Version zu aktualisieren. Nette
unterstützt PHP 8.1 seit Version 3.0. Wenn Sie eine ältere Version verwenden (prüfen Sie Ihre composer.json), aktualisieren Sie Nette oder bleiben Sie bei PHP 8.0.
Einstellung der Verzeichnisberechtigungen
Wenn Sie unter macOS oder Linux (oder einem anderen unixbasierten System) entwickeln, müssen Sie Schreibrechte für den
Webserver einrichten. Nehmen wir an, Ihre Anwendung liegt im Standardverzeichnis /var/www/html (Fedora,
CentOS, RHEL):
cd /var/www/html/MY_PROJECT
chmod -R a+rw temp log
Auf manchen Linux-Systemen (Fedora, CentOS, …) ist SELinux möglicherweise standardmäßig aktiviert. Unter Umständen
müssen Sie die SELinux-Richtlinien aktualisieren oder für die Pfade der Verzeichnisse temp und log den
richtigen SELinux-Sicherheitskontext setzen. Für die Verzeichnisse temp und log sollte der Kontext
httpd_sys_rw_content_t gesetzt werden; für den Rest der Anwendung – vor allem den Ordner app –
genügt der Kontext httpd_sys_content_t. Führen Sie auf dem Server als root aus:
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/
Weiterhin muss der SELinux-Boolean httpd_can_network_connect_db aktiviert werden, damit Nette sich über das
Netzwerk mit der Datenbank verbinden darf. Standardmäßig ist er deaktiviert. Dafür lässt sich der Befehl
setsebool verwenden, und wenn die Option -P angegeben wird, bleibt diese Einstellung über Neustarts
hinweg erhalten:
setsebool -P httpd_can_network_connect_db on
Wie ändert oder entfernt man das Verzeichnis
www aus der URL?
Das in den Beispielprojekten von Nette verwendete Verzeichnis www/ stellt das öffentliche Verzeichnis bzw. den
Document-Root des Projekts dar. Es ist das einzige Verzeichnis, dessen Inhalt für den Browser zugänglich ist. Es enthält die
Datei index.php, den Einstiegspunkt, der die Nette-Webanwendung startet.
Um die Anwendung bei einem Hoster zu betreiben, müssen Sie den Document-Root korrekt einrichten. Sie haben zwei Möglichkeiten:
- Setzen Sie den Document-Root in der Konfiguration des Hostings auf dieses Verzeichnis.
- Hat das Hosting einen vorbereiteten Ordner (z. B.
public_html), benennen Siewww/in diesen Namen um.
Versuchen Sie niemals, Ihre Anwendung nur mit .htaccess oder Regeln des Routers abzusichern, um
den Zugriff auf andere Ordner zu verhindern.
Erlaubt der Hoster nicht, den Document-Root auf ein Unterverzeichnis zu setzen (also Verzeichnisse eine Ebene über dem öffentlichen Verzeichnis anzulegen), suchen Sie sich einen anderen Anbieter. Sonst gingen Sie ein erhebliches Sicherheitsrisiko ein. Das wäre, als würden Sie in einer Wohnung leben, deren Wohnungstür sich nicht schließen lässt und immer weit offen steht.
Wie konfiguriert man den Server für schöne URLs?
Apache: Sie müssen die mod_rewrite-Regeln in der Datei .htaccess aktivieren und konfigurieren:
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]
Wenn Sie auf Probleme stoßen, stellen Sie sicher, dass:
- die Datei
.htaccessim Verzeichnis des Document-Root liegt (also neben der Dateiindex.php) - Apache die
.htaccess-Dateien verarbeitet - mod_rewrite aktiviert ist
Richten Sie die Anwendung in einem Unterordner ein, müssen Sie eventuell die Zeile für die Einstellung
RewriteBase einkommentieren und auf den richtigen Ordner setzen.
nginx: Die Weiterleitung muss über die Direktive try_files innerhalb des Blocks location / in
der Serverkonfiguration eingerichtet werden.
location / {
try_files $uri $uri/ /index.php$is_args$args; # $is_args$args IST WICHTIG!
}
Der Block location darf innerhalb des Blocks server für jeden Dateisystempfad nur einmal vorkommen.
Wenn Sie in Ihrer Konfiguration bereits einen Block location / haben, ergänzen Sie die Direktive
try_files im bestehenden Block.
Überprüfung, ob .htaccess funktioniert
Am einfachsten prüfen Sie, ob Apache Ihre Datei .htaccess verwendet oder ignoriert, indem Sie sie absichtlich
kaputt machen. Setzen Sie an den Anfang der Datei die Zeile Test. Wenn Sie die Seite nun im Browser neu laden,
sollten Sie einen Internal Server Error sehen.
Wenn Sie diesen Fehler sehen, ist das eigentlich gut! Es bedeutet, dass Apache die Datei .htaccess verarbeitet und
auf den Fehler stößt, den wir dort hineingeschrieben haben. Entfernen Sie die Zeile Test.
Sehen Sie keinen Internal Server Error, ignoriert Ihre Apache-Einrichtung die Datei .htaccess. Meist
ignoriert Apache sie, weil die Konfigurationsdirektive AllowOverride All fehlt.
Wenn Sie selbst hosten, lässt sich das leicht beheben. Öffnen Sie Ihre httpd.conf oder apache.conf
in einem Texteditor, suchen Sie den passenden Abschnitt <Directory> und ergänzen bzw. ändern Sie diese
Direktive:
<Directory "/var/www/htdocs"> # Pfad zu Ihrem Document-Root
AllowOverride All
...
Wird Ihre Site anderswo gehostet, sehen Sie in Ihrem Control Panel nach, ob Sie .htaccess dort aktivieren können.
Wenn nicht, wenden Sie sich an Ihren Hoster, damit er es für Sie tut.
Überprüfung, ob mod_rewrite aktiviert ist
Wenn Sie geprüft haben, dass .htaccess funktioniert,
können Sie prüfen, ob die Extension mod_rewrite aktiviert ist. Setzen Sie an den Anfang der Datei .htaccess die
Zeile RewriteEngine On und laden Sie die Seite im Browser neu. Sehen Sie einen Internal Server Error,
bedeutet das, dass mod_rewrite nicht aktiviert ist. Es gibt mehrere Wege, es zu aktivieren. Auf Stack Overflow finden Sie
verschiedene Wege, wie das bei unterschiedlichen Einrichtungen geht.
Links werden ohne https: generiert
Nette erzeugt Links mit demselben Protokoll, das die aktuelle Seite verwendet. Auf einer Seite https://foo erzeugt
es also Links, die mit https: beginnen, und umgekehrt. Wenn Sie hinter einem Reverse Proxy stehen, der HTTPS
abstreift (zum Beispiel in Docker), müssen Sie in der Konfiguration einen Proxy einrichten, damit die Erkennung des Protokolls
richtig funktioniert.
Wenn Sie Nginx als Proxy verwenden, muss die Weiterleitung zum Beispiel so eingerichtet sein:
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 oder Hostname des Servers/Containers, auf dem die Anwendung läuft
}
Weiterhin müssen Sie in der Konfiguration die IP des Proxys und optional den IP-Bereich Ihres lokalen Netzwerks angeben, in dem Sie die Infrastruktur betreiben:
http:
proxy: IP-proxy/IP-range
Verwendung der Zeichen { } in JavaScript
Die Zeichen { und } dienen zum Schreiben von Latte-Tags. Alles, was auf das Zeichen {
folgt (außer einem Leerzeichen und einem Anführungszeichen), gilt als Tag. Wenn Sie das Zeichen { direkt ausgeben
müssen (häufig in JavaScript), können Sie direkt hinter { ein Leerzeichen (oder ein anderes Whitespace-Zeichen)
setzen. Damit wird es nicht als Tag interpretiert.
Müssen diese Zeichen in einer Situation ausgegeben werden, in der der Text als Tag interpretiert würde, können Sie spezielle
Tags zur Ausgabe dieser Zeichen verwenden – {l} für { und {r} für }.
{is a tag}
{ is not a tag }
{l}is not a tag{r}
Fehler
Cannot modify header information - headers already sent
Dieser Fehler tritt auf, wenn die Anwendung versucht, einen HTTP-Header zu senden (ein Cookie, eine Weiterleitung oder den Start einer Session), obwohl bereits eine Ausgabe an den Browser gegangen ist. Header müssen immer vor dem Body der Response kommen.
Es gibt zwei mögliche Ursachen: Entweder geht die Ausgabe zu früh raus, oder der Header wird zu spät gesendet.
Die Ausgabe geht meist wegen eines versehentlichen Leerzeichens oder einer Leerzeile vor <?php, nach dem
schließenden ?> oder wegen eines BOM zu früh raus, das der Editor am Anfang der Datei eingefügt hat und nicht
anzeigt. Beenden Sie PHP-Dateien deshalb niemals mit ?>. Um herauszufinden, welche Stelle zuerst etwas ausgegeben
hat, verwenden Sie Tracy\OutputDebugger.
Der Header wird typischerweise bei der Arbeit mit der Session zu spät gesendet. Nette startet die Session automatisch beim
ersten Lesen oder Schreiben, und wenn das erst beim Rendern des Templates passiert, ist die Ausgabe bereits unterwegs. Arbeiten
Sie mit der Session deshalb spätestens in der Methode beforeRender(), in Komponenten auch in den Methoden
handle<Signal>().
Versuchen Sie nicht, das Problem mit autoStart: true zu
lösen. Das startet die Session für jeden Besucher, auch für Robots, und legt unnötig eine riesige Zahl von Dateien auf der
Festplatte an. Der Standardwert smart startet die Session nur dann, wenn sie wirklich gebraucht wird.
Meldung Presenter::getContext() is deprecated
Nette war bei Weitem das erste PHP-Framework, das auf Dependency Injection umgestiegen ist und Programmierer angeleitet hat,
sie konsequent zu verwenden, gleich beginnend bei den Presentern. Braucht ein Presenter eine Abhängigkeit, fordert er sie an. Umgekehrt gilt es als
Antipattern, einer Klasse den gesamten DI-Container zu übergeben und sich die Abhängigkeiten direkt daraus zu holen (bekannt als
Service-Locator-Pattern). Dieser Ansatz wurde in Nette 0.x vor dem Aufkommen von Dependency Injection verwendet, und die seit
Langem als veraltet markierte Methode Presenter::getContext() ist ein Überbleibsel jener Zeit.
Wenn Sie eine sehr alte Nette-Anwendung portieren, kann es sein, dass sie diese Methode noch verwendet. Seit
nette/application in Version 3.1 begegnet Ihnen die Warnung
Nette\Application\UI\Presenter::getContext() is deprecated, use dependency injection, und seit Version 4.0 ein
Fehler, dass die Methode nicht existiert.
Die saubere Lösung ist natürlich, die Anwendung so umzubauen, dass die Abhängigkeiten per Dependency Injection übergeben
werden. Als Behelfslösung können Sie in Ihrem Basis-Presenter eine eigene Methode getContext() ergänzen, um die
Meldung zu umgehen:
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;
}
}