Αντιμετώπιση προβλημάτων

Το 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 είναι η παλιά cache. Ενώ το Nette σε κατάσταση ανάπτυξης ενημερώνει έξυπνα αυτόματα την cache, σε κατάσταση παραγωγής εστιάζει στη μεγιστοποίηση της απόδοσης και η διαγραφή της cache, μετά από κάθε τροποποίηση κώδικα, εξαρτάται από εσάς. Δοκιμάστε να διαγράψετε το temp/cache.

Σφάλμα 404, η δρομολόγηση δεν λειτουργεί

Όταν όλες οι σελίδες (εκτός από την αρχική) επιστρέφουν σφάλμα 404, φαίνεται να υπάρχει πρόβλημα με τη διαμόρφωση του διακομιστή για όμορφα URLs.

Οι αλλαγές στα πρότυπα ή στη διαμόρφωση δεν εφαρμόζονται

“Τροποποίησα το πρότυπο ή τη διαμόρφωση, αλλά ο ιστότοπος εξακολουθεί να εμφανίζει την παλιά έκδοση.” Αυτή η συμπεριφορά συμβαίνει σε κατάσταση παραγωγής, η οποία για λόγους απόδοσης δεν ελέγχει τις αλλαγές στα αρχεία και διατηρεί την cache που δημιουργήθηκε μία φορά.

Για να μην χρειάζεται να διαγράφετε χειροκίνητα την cache στον διακομιστή παραγωγής μετά από κάθε τροποποίηση, ενεργοποιήστε την κατάσταση ανάπτυξης για τη διεύθυνση IP σας στο αρχείο Bootstrap.php:

$this->configurator->setDebugMode('η.δικη.σας.ip.διευθυνση');

Πώς να απενεργοποιήσετε την cache κατά την ανάπτυξη?

Το Nette είναι έξυπνο και δεν χρειάζεται να απενεργοποιήσετε την προσωρινή αποθήκευση σε αυτό. Κατά την ανάπτυξη, ενημερώνει αυτόματα την cache με κάθε αλλαγή στο πρότυπο ή στη διαμόρφωση του DI container. Η κατάσταση ανάπτυξης ενεργοποιείται επιπλέον με αυτόματη ανίχνευση, οπότε συνήθως δεν χρειάζεται να διαμορφώσετε τίποτα, ή μόνο τη διεύθυνση IP.

Κατά τον εντοπισμό σφαλμάτων του router, συνιστούμε να απενεργοποιήσετε την cache στον περιηγητή, στην οποία μπορεί να είναι αποθηκευμένες, για παράδειγμα, ανακατευθύνσεις: ανοίξτε τα Developer Tools (Ctrl+Shift+I ή Cmd+Option+I) και στον πίνακα Network (Δίκτυο) επιλέξτε την απενεργοποίηση της 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), θα χρειαστεί να ρυθμίσετε τα δικαιώματα εγγραφής για τον web server. Ας υποθέσουμε ότι η εφαρμογή σας βρίσκεται στον προεπιλεγμένο κατάλογο /var/www/html (Fedora, CentOS, RHEL).

cd /var/www/html/MY_PROJECT
chmod -R a+rw temp log

Σε ορισμένα Linux (Fedora, CentOS, …), το SELinux είναι ενεργοποιημένο από προεπιλογή. Θα χρειαστεί να τροποποιήσετε κατάλληλα τις πολιτικές SELinux και να ορίσετε το σωστό πλαίσιο ασφαλείας SELinux για τους φακέλους temp και log. Για τα temp και log θα ορίσουμε τον τύπο πλαισίου httpd_sys_rw_content_t, για την υπόλοιπη εφαρμογή (και κυρίως για τον φάκελο app) θα αρκεί το httpd_sys_content_t. Στον διακομιστή εκτελέστε:

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 boolean httpd_can_network_connect_db, το οποίο είναι απενεργοποιημένο από προεπιλογή και το οποίο επιτρέπει στο Nette να συνδεθεί στη βάση δεδομένων μέσω δικτύου. Θα χρησιμοποιήσουμε γι' αυτό την εντολή setsebool και με την επιλογή -P θα κάνουμε την αλλαγή μόνιμη, δηλαδή μετά την επανεκκίνηση του διακομιστή δεν θα μας περιμένει δυσάρεστη έκπληξη:

setsebool -P httpd_can_network_connect_db on

Πώς να αλλάξετε ή να αφαιρέσετε τον κατάλογο www από το URL?

Ο κατάλογος www/ που χρησιμοποιείται στα παραδείγματα έργων στο Nette αντιπροσωπεύει τον λεγόμενο δημόσιο κατάλογο ή document-root του έργου. Είναι ο μόνος κατάλογος του οποίου το περιεχόμενο είναι προσβάσιμο από τον περιηγητή. Και περιέχει το αρχείο index.php, το σημείο εισόδου που εκκινεί την διαδικτυακή εφαρμογή γραμμένη σε Nette.

Για τη λειτουργία της εφαρμογής σε hosting, είναι απαραίτητο να έχετε σωστά διαμορφωμένο το document-root. Έχετε δύο επιλογές:

  1. Στη διαμόρφωση του hosting, ορίστε το document-root σε αυτόν τον κατάλογο (www/)
  2. Εάν το hosting έχει έναν προετοιμασμένο φάκελο για δημόσια αρχεία (π.χ. public_html, htdocs, httpdocs), απλώς μετονομάστε το www/ σε αυτό το όνομα.

Ποτέ μην προσπαθείτε να λύσετε την ασφάλεια μόνο με .htaccess ή router, που θα εμπόδιζαν την πρόσβαση στους υπόλοιπους φακέλους.

Αν το hosting δεν επιτρέπει τον ορισμό του document-root σε υποκατάλογο (δηλ. τη δημιουργία καταλόγων ένα επίπεδο πάνω από τον δημόσιο κατάλογο), αναζητήστε άλλο. Διαφορετικά θα διατρέχατε σημαντικό κίνδυνο ασφαλείας. Θα ήταν σαν να ζείτε σε διαμέρισμα όπου η εξώπορτα δεν κλείνει και είναι πάντα ορθάνοιχτη.

Πώς να ρυθμίσετε τον διακομιστή για όμορφα URLs?

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: και αντίστροφα. Αν βρίσκεστε πίσω από έναν reverse proxy server που αφαιρεί το HTTPS (για παράδειγμα στο Docker), τότε πρέπει στη διαμόρφωση να ρυθμίσετε το proxy, ώστε η ανίχνευση πρωτοκόλλου να λειτουργεί σωστά.

Αν χρησιμοποιείτε το Nginx ως proxy, είναι απαραίτητο να έχετε ρυθμισμένη την ανακατεύθυνση π.χ. ως εξής:

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 ή hostname του διακομιστή/κοντέινερ όπου εκτελείται η εφαρμογή
}

Περαιτέρω, είναι απαραίτητο να αναφέρετε στη διαμόρφωση την IP του proxy και ενδεχομένως το εύρος IP του τοπικού σας δικτύου, όπου λειτουργεί η υποδομή σας:

http:
	proxy: IP-proxy/IP-range # π.χ. 1.2.3.4 ή 1.2.3.0/24

Χρήση χαρακτήρων { } στην JavaScript

Οι χαρακτήρες { και } χρησιμοποιούνται για τη γραφή ετικετών Latte. Ως ετικέτα θεωρείται οτιδήποτε ακολουθεί τον χαρακτήρα { με εξαίρεση το κενό και τα εισαγωγικά. Αν λοιπόν χρειαστεί να εκτυπώσετε απευθείας τον χαρακτήρα { (συχνά για παράδειγμα στην JavaScript), μπορείτε μετά τον χαρακτήρα { να βάλετε ένα κενό (ή άλλο κενό χαρακτήρα). Με αυτόν τον τρόπο αποφεύγετε τη μετάφραση ως ετικέτα.

Αν είναι απαραίτητο να εκτυπώσετε αυτούς τους χαρακτήρες σε μια κατάσταση όπου το κείμενο θα ερμηνευόταν ως ετικέτα, μπορείτε να χρησιμοποιήσετε ειδικές ετικέτες για την εκτύπωση αυτών των χαρακτήρων – {l} για { και {r} για }.

{je značka}     {* είναι ετικέτα *}
{ není značka } {* δεν είναι ετικέτα *}
{l}není značka{r} {* δεν είναι ετικέτα *}

Μήνυμα Presenter::getContext() is deprecated

Το Nette είναι μακράν το πρώτο PHP framework που πέρασε στο dependency injection και οδήγησε τους προγραμματιστές στη συνεπή χρήση του, ήδη από τους ίδιους τους presenters. Αν ένας presenter χρειάζεται κάποια εξάρτηση, τη ζητά. Αντίθετα, ο τρόπος όπου περνάμε ολόκληρο τον DI container στην κλάση, και αυτή αντλεί τις εξαρτήσεις απευθείας από αυτόν, θεωρείται antipattern (ονομάζεται service locator). Αυτός ο τρόπος χρησιμοποιούνταν στο Nette 0.x πριν την έλευση του dependency injection και κατάλοιπό του είναι η μέθοδος Presenter::getContext(), προ πολλού χαρακτηρισμένη ως deprecated.

Αν μεταφέρετε μια πολύ παλιά εφαρμογή για το Nette, μπορεί να τύχει να χρησιμοποιεί ακόμα αυτή τη μέθοδο. Από την έκδοση nette/application 3.1 θα συναντήσετε έτσι την προειδοποίηση Nette\Application\UI\Presenter::getContext() is deprecated, use dependency injection, από την έκδοση 4.0 το σφάλμα ότι η μέθοδος δεν υπάρχει.

Η καθαρή λύση είναι φυσικά να ξαναγράψετε την εφαρμογή ώστε να περνά τις εξαρτήσεις χρησιμοποιώντας dependency injection. Ως λύση ανάγκης, μπορείτε να προσθέσετε στον βασικό σας presenter τη δική σας μέθοδο 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.0