Εάν αναπτύσσετε PHP τα τελευταία χρόνια, πιθανότατα γνωρίζετε τα προβλήματα με αυτήν τη γλώσσα. Μπορείτε συχνά να ακούσετε ότι είναι μια κατακερματισμένη γλώσσα, ένα εργαλείο χάκερ, ότι δεν έχει πραγματικές προδιαγραφές κ.λπ. Η πραγματικότητα είναι ότι η PHP έχει αναπτυχθεί πολύ τον τελευταίο καιρό. Η έκδοση PHP 5.4 το έφερε πιο κοντά στην ολοκλήρωση μοντέλο αντικειμένουκαι παρείχε πολλές νέες λειτουργίες.

Και όλα αυτά, φυσικά, είναι καλά, αλλά τι γίνεται με τα πλαίσια; Υπάρχουν πολλά από αυτά στην PHP. Αρκεί κανείς να αρχίσει να ψάχνει και θα καταλάβεις ότι δεν θα έχεις αρκετή ζωή για να τα μελετήσεις όλα, γιατί νέα πλαίσια εμφανίζονται συνεχώς και κάτι δικό τους επινοείται σε καθένα. Πώς λοιπόν μπορείτε να το μετατρέψετε σε κάτι που δεν αποξενώνει τους προγραμματιστές και καθιστά εύκολη τη μεταφορά της λειτουργικότητας από το ένα πλαίσιο στο άλλο;

Τι είναι το PHPFIG

Το PHP-FIG (PHP Framework Interop Group) είναι μια οργανωμένη ομάδα προγραμματιστών που στόχος τους είναι να βρουν τρόπους για να συνεργαστούν πολλά πλαίσια.

Απλώς φανταστείτε: αυτήν τη στιγμή υποστηρίζετε ένα έργο Zend Framework που χρειαζόταν μια ενότητα καλαθιού αγορών. Έχετε ήδη γράψει μια τέτοια ενότητα για ένα προηγούμενο έργο που ήταν στο Symphony. Γιατί να μην το ξανακάνω; Ευτυχώς, τόσο το ZendF όσο και το Symphony αποτελούν μέρος του PHP-FIG, επομένως είναι δυνατή η εισαγωγή μιας ενότητας από το ένα πλαίσιο στο άλλο. Δεν είναι υπέροχο;

Ας μάθουμε ποια πλαίσια περιλαμβάνονται στο PHP-FIG

Μέλη PHP-FIG

Οποιοσδήποτε προγραμματιστής μπορεί να προσθέσει το πλαίσιο του στη λίστα των συνεργατών του PHP-FIG. Ωστόσο, θα χρειαστεί να πληρώσετε ένα συγκεκριμένο ποσό για αυτό, οπότε αν δεν έχετε την υποστήριξη της κοινότητας, είναι απίθανο να συμφωνήσετε με αυτό. Αυτό γίνεται για να αποτραπεί η εγγραφή εκατομμυρίων μικροπλαισίων χωρίς καμία φήμη.

Τρέχοντα μέλη:

Τι είναι το PSR;

PSR (PHP Standards Recommendations) - τυπικές συστάσεις, το αποτέλεσμα του PHP-FIG. Ορισμένα μέλη της Ομάδας προτείνουν κανόνες για κάθε PSR, άλλα ψηφίζουν υπέρ αυτών των κανόνων ή για την κατάργησή τους. Η συζήτηση πραγματοποιείται στις Ομάδες Google και τα σύνολα PSR είναι διαθέσιμα στον επίσημο ιστότοπο PHP-FIG.

Ας δούμε μερικά PSR:

Το πρώτο βήμα προς την ενοποίηση των πλαισίων είναι να έχουμε μια κοινή δομή καταλόγου, γι' αυτό και υιοθετήθηκε ένα κοινό πρότυπο αυτόματης φόρτωσης.

  1. Ο χώρος ονομάτων (χώρος ονομάτων) και η κλάση πρέπει να έχουν τη δομή \\(\)*.
  2. Κάθε χώρος ονομάτων πρέπει να περιέχει ένα κενό ανώτατου επιπέδου ("Όνομα προμηθευτή").
  3. Κάθε χώρος ονομάτων μπορεί να έχει οποιοδήποτε αριθμό επιπέδων.
  4. Κάθε διαχωριστικό χώρου ονομάτων μετατρέπεται σε DIRECTORY_SEPARATOR κατά τη φόρτωση.
  5. Κάθε χαρακτήρας "_" στην CLASS NAME μετατρέπεται σε DIRECTORY_SEPARATOR.
  6. Ο πλήρως αναγνωρισμένος χώρος ονομάτων και η κλάση επισυνάπτονται με ".php" κατά τη φόρτωση.

Παράδειγμα λειτουργίας αυτόματης φόρτωσης:

PSR-1 - Βασικό πρότυπο κωδικοποίησης

Αυτά τα PSR ρυθμίζουν τα βασικά πρότυπα, η κύρια ιδέα του οποίου είναι ότι εάν όλοι οι προγραμματιστές χρησιμοποιούν τα ίδια πρότυπα, τότε η μεταφορά κώδικα μπορεί να γίνει χωρίς προβλήματα.

  1. Τα αρχεία πρέπει να χρησιμοποιούν μόνο ετικέτες
  2. Τα αρχεία πρέπει να χρησιμοποιούν μόνο UTF-8 χωρίς κωδικοποίηση BOM.
  3. Τα ονόματα και οι κλάσεις χώρου πρέπει να ακολουθούν το PSR-0.
  4. Τα ονόματα των κλάσεων πρέπει να δηλώνονται με συμβολισμό StudlyCaps.
  5. Οι σταθερές κλάσης πρέπει να δηλώνονται με κεφαλαία, διαχωρισμένα με κάτω παύλες.
  6. Οι μέθοδοι πρέπει να δηλώνονται με σημειογραφία camelCase.

PSR-2 - Οδηγός στυλ κωδικοποίησης

Αυτές είναι εκτεταμένες οδηγίες για το PSR-1, που περιγράφουν κανόνες μορφοποίησης κώδικα.

  1. Ο κωδικός πρέπει να συμμορφώνεται με το PSR-1.
  2. Θα πρέπει να χρησιμοποιηθούν 4 κενά αντί για καρτέλες.
  3. Δεν πρέπει να υπάρχει αυστηρός περιορισμός στο μήκος της συμβολοσειράς, το προτεινόμενο μήκος είναι έως 80 χαρακτήρες.
  4. Πρέπει να υπάρχει μία κενή γραμμή μετά τη δήλωση χώρου ονομάτων.
  5. Οι παρενθέσεις για τις κλάσεις θα πρέπει να ανοίγουν στην επόμενη γραμμή μετά τη δήλωση και να κλείνουν μετά το σώμα της κλάσης (το ίδιο για τις μεθόδους).
  6. Πρέπει να καθοριστεί η ορατότητα των μεθόδων και των ιδιοτήτων (δημόσια, ιδιωτική).
  7. Οι βραχίονες ανοίγματος για τις κατασκευές ελέγχου πρέπει να βρίσκονται στην ίδια γραμμή, οι βραχίονες κλεισίματος πρέπει να βρίσκονται στην επόμενη γραμμή μετά το σώμα της κατασκευής.
  8. Δεν τοποθετούνται κενά μετά το άνοιγμα των παρενθέσεων των μεθόδων δομής ελέγχου και πριν από το κλείσιμο των παρενθέσεων.

PCR-3 - Διασύνδεση Logger

Η PCR-3 ρυθμίζει την υλοτομία, ιδιαίτερα τις εννέα κύριες μεθόδους.

  1. Το LoggerInterface παρέχει 8 μεθόδους για την καταγραφή οκτώ επιπέδων RFC 5424 (εντοπισμός σφαλμάτων, ειδοποίηση, προειδοποίηση, σφάλμα, κρίσιμο, ειδοποίηση, έκτακτη ανάγκη).
  2. Η μέθοδος ένατη log() παίρνει το επίπεδο προειδοποίησης ως πρώτη παράμετρό της. Η κλήση μιας μεθόδου με μια παράμετρο επιπέδου ειδοποίησης πρέπει να αποδίδει το ίδιο αποτέλεσμα με την κλήση μιας μεθόδου συγκεκριμένου επιπέδου καταγραφής (log(ALERT) == alert()). Η κλήση μιας μεθόδου με απροσδιόριστο επίπεδο προειδοποίησης πρέπει να δημιουργήσει ένα Psr\Log\InvalidArgumentException.

Όπως το PSR-0, το PSR-4 παρέχει βελτιωμένες μεθόδους αυτόματης φόρτωσης

  1. Ο όρος «κλάση» αναφέρεται σε κλάσεις, διεπαφές, χαρακτηριστικά και άλλες παρόμοιες δομές.
  2. Ένα πλήρες όνομα τάξης έχει την ακόλουθη μορφή: \ (\)*\
  3. Κατά τη φόρτωση ενός αρχείου που ταιριάζει με ένα πλήρως αναγνωρισμένο όνομα τάξης:
  • Μια συνεχόμενη σειρά ενός ή περισσότερων κυριοτέρων χώρων ονομάτων, εξαιρουμένου του κύριου διαχωριστικού χώρου ονομάτων, σε ένα πλήρως αναγνωρισμένο όνομα κλάσης αντιστοιχεί σε τουλάχιστον έναν "ριζικό κατάλογο".
  • Τα ονόματα των καταλόγων και των υποκαταλόγων πρέπει να ταιριάζουν με την περίπτωση του χώρου ονομάτων.
  • Η κατάληξη του ονόματος πλήρους κλάσης αντιστοιχεί στο όνομα του αρχείου με την κατάληξη .php. Η περίπτωση του ονόματος αρχείου πρέπει να ταιριάζει με την περίπτωση του τέλους του πλήρους ονόματος της τάξης.
  • Μια εφαρμογή αυτόματης φόρτωσης δεν πρέπει να δημιουργεί εξαιρέσεις, να δημιουργεί σφάλματα οποιουδήποτε επιπέδου και να μην επιστρέφει τιμή.

συμπέρασμα

Το PHP-FIG αλλάζει τον τρόπο με τον οποίο γράφονται τα πλαίσια, αλλά όχι τον τρόπο λειτουργίας τους. Οι πελάτες συχνά απαιτούν από εσάς να εργαστείτε με τον υπάρχοντα κώδικα μέσα σε ένα πλαίσιο ή να καθορίσετε με ποιο πλαίσιο θα πρέπει να εργαστείτε σε ένα έργο. Οι συστάσεις PSR κάνουν τη ζωή των προγραμματιστών πολύ πιο εύκολη από αυτή την άποψη, κάτι που είναι υπέροχο!

Γλώσσα προγραμματισμού PHPέχει διανύσει πολύ δρόμο από ένα εργαλείο για τη δημιουργία προσωπικών σελίδων σε μια γλώσσα γενικής χρήσης. Σήμερα είναι εγκατεστημένο σε εκατομμύρια διακομιστές σε όλο τον κόσμο, που χρησιμοποιούνται από εκατομμύρια προγραμματιστές που δημιουργούν μια μεγάλη ποικιλία έργων.

Είναι εύκολο στην εκμάθηση και εξαιρετικά δημοφιλές, ειδικά στους αρχάριους. Επομένως, μετά την ανάπτυξη της γλώσσας, ακολούθησε μια ισχυρή ανάπτυξη της κοινότητας γύρω από αυτήν. Ένας τεράστιος αριθμός σεναρίων, για όλες τις περιπτώσεις, διαφορετικές βιβλιοθήκες, πλαίσια. Η απουσία ομοιόμορφων προτύπων σχεδίασης και κωδικοποίησης οδήγησε στην εμφάνιση ενός τεράστιου επιπέδου προϊόντων πληροφοριών που βασίστηκαν στις αρχές του ίδιου του προγραμματιστή αυτού του προϊόντος. Αυτό ήταν ιδιαίτερα αισθητό όταν εργάζεστε με διάφορους Πλαίσια PHP, που για μεγάλο χρονικό διάστημα αντιπροσώπευε ένα κλειστό οικοσύστημα, μη συμβατό με άλλα πλαίσια, παρά το γεγονός ότι οι εργασίες που επιλύουν είναι συχνά παρόμοιες.

Το 2009, οι προγραμματιστές πολλών πλαισίων συμφώνησαν να δημιουργήσουν μια κοινότητα PHP Framework Interop Group (PHP-FIG), το οποίο θα αναπτύξει συστάσεις για προγραμματιστές. Είναι σημαντικό να τονίσουμε ότι δεν μιλάμε Πρότυπα ISO, είναι πιο σωστό να μιλάμε για συστάσεις. Αλλά αφού αυτοί που δημιούργησαν php-figΗ κοινότητα των προγραμματιστών αντιπροσωπεύει σημαντικά πλαίσια, οι συστάσεις τους έχουν σοβαρό βάρος. Υποστήριξη Πρότυπα PSR (πρότυπη σύσταση PHP).επιτρέπει τη διαλειτουργικότητα, η οποία διευκολύνει και επιταχύνει την ανάπτυξη του τελικού προϊόντος.

Συνολικά, τη στιγμή της σύνταξης αυτού του άρθρου, υπάρχουν 17 πρότυπα, και 9 από αυτά έχουν εγκριθεί, 8 βρίσκονται στο στάδιο του σχεδίου, συζητούνται ενεργά, 1 πρότυπο δεν συνιστάται για χρήση.

Τώρα ας πάμε απευθείας στην περιγραφή κάθε προτύπου. Σημειώστε ότι δεν θα αναλύσω κάθε πρότυπο λεπτομερώς εδώ, αλλά μάλλον μια μικρή εισαγωγή. Επίσης, το άρθρο θα εξετάσει μόνο αυτά Πρότυπα PSR, τα οποία γίνονται επίσημα αποδεκτά, δηλ. είναι στην κατάσταση Αποδεκτό.

PSR-1. Κύριο πρότυπο κωδικοποίησης

Αντιπροσωπεύει τους πιο γενικούς κανόνες, όπως, για παράδειγμα, τη χρήση Ετικέτες PHP, κωδικοποίηση αρχείου, διαχωρισμός του τόπου δήλωσης μιας συνάρτησης, κλάσης και τόπος χρήσης τους, ονομασία κλάσεων, μέθοδοι.

PSR-2. Οδηγός στυλ κώδικα

Αποτελεί συνέχεια του πρώτου προτύπου και ρυθμίζει τη χρήση καρτελών στον κώδικα, αλλαγές γραμμής, το μέγιστο μήκος των γραμμών κώδικα, τους κανόνες σχεδιασμού δομών ελέγχου κ.λπ.

PSR-3. Διασύνδεση καταγραφής.

Αυτό το πρότυπο έχει σχεδιαστεί για να επιτρέπει τη σύνδεση (σύνδεση) σε εφαρμογές γραμμένες PHP.

PSR-4. Standard Autoload

Αυτό είναι ίσως το πιο σημαντικό και απαραίτητο πρότυπο, το οποίο θα καλυφθεί σε ξεχωριστό, λεπτομερές άρθρο. Τάξεις που υλοποιούν PSR-4, μπορεί να φορτωθεί με ένα μόνο πρόγραμμα αυτόματης φόρτωσης, επιτρέποντας εξαρτήματα και στοιχεία από ένα πλαίσιο ή βιβλιοθήκη να χρησιμοποιηθούν σε άλλα έργα.

PSR-6. διασύνδεση προσωρινής αποθήκευσης

Η προσωρινή αποθήκευση χρησιμοποιείται για τη βελτίωση της απόδοσης του συστήματος. Και PSR-6επιτρέπει την τυπική αποθήκευση και ανάκτηση δεδομένων από τη μνήμη cache χρησιμοποιώντας μια ενοποιημένη διεπαφή.

PSR-7. Διεπαφή μηνυμάτων HTTP

Όταν γράφεις περισσότερο ή λιγότερο περίπλοκη ιστοσελίδες στην PHP, σχεδόν πάντα πρέπει να δουλεύουμε Κεφαλίδες HTTP. Φυσικά, Γλώσσα PHPμας παρέχει έτοιμες επιλογές για εργασία μαζί τους, όπως π.χ υπερσφαιρικός πίνακας $_SERVER, λειτουργίες επί κεφαλής(), setcookie()κ.λπ., ωστόσο, η μη αυτόματη ανάλυσή τους είναι γεμάτη λάθη και δεν είναι πάντα δυνατό να ληφθούν υπόψη όλες οι αποχρώσεις της εργασίας μαζί τους. Και έτσι, προκειμένου να διευκολυνθεί το έργο του προγραμματιστή, καθώς και να γίνει η διεπαφή αλληλεπίδρασης με Πρωτόκολλο HTTPαυτό το πρότυπο έχει υιοθετηθεί. Θα μιλήσω για αυτό το πρότυπο με περισσότερες λεπτομέρειες σε ένα από τα ακόλουθα άρθρα.

PSR-11. Διεπαφή κοντέινερ

Όταν γράφετε Προγράμματα PHPΣυχνά πρέπει να χρησιμοποιείτε στοιχεία τρίτων. Και για να μην χαθούμε σε αυτό το δάσος εξαρτήσεων, επινοήθηκαν διάφορες μέθοδοι διαχείρισης εξαρτήσεων κώδικα, συχνά ασυμβίβαστες μεταξύ τους, κάτι που αυτό το πρότυπο οδηγεί σε έναν κοινό παρονομαστή.

PSR-13. Σύνδεσμοι υπερμέσων

Αυτή η διεπαφή έχει σχεδιαστεί για να διευκολύνει την ανάπτυξη και χρήση διεπαφών προγραμματισμού εφαρμογών ( API).

PSR-14. Απλή διεπαφή προσωρινής αποθήκευσης

Είναι συνέχεια και βελτίωση του προτύπου PSR-6

Έτσι, σήμερα εξετάσαμε Πρότυπα PSR. Για ενημερωμένες πληροφορίες σχετικά με την κατάσταση των προτύπων, επικοινωνήστε

16.09.2016

Ας προσπαθήσουμε να προσδιορίσουμε πώς να βελτιώσουμε την απόδοση του διακομιστή εφαρμογών που βασίζεται σε php-fpm, καθώς και να δημιουργήσουμε μια λίστα ελέγχου για τον έλεγχο της διαμόρφωσης fpm της διαδικασίας.

Πρώτα απ 'όλα, αξίζει να προσδιορίσετε τη θέση του αρχείου διαμόρφωσης της πισίνας. Εάν εγκαταστήσατε το php-fpm από το χώρο αποθήκευσης του συστήματος, τότε η διαμόρφωση της πισίνας wwwθα βρίσκεται γύρω από το /etc/php5/fpm/pool.d/www.conf . Εάν χρησιμοποιείτε τη δική σας έκδοση ή άλλο λειτουργικό σύστημα (όχι το debian), θα πρέπει να αναζητήσετε τη θέση του αρχείου στην τεκμηρίωση ή να το καθορίσετε με μη αυτόματο τρόπο.

Ας προσπαθήσουμε να εξετάσουμε τη διαμόρφωση με περισσότερες λεπτομέρειες.

Μετάβαση σε υποδοχές UNIX

Πιθανώς το πρώτο πράγμα που πρέπει να προσέξετε είναι ο τρόπος με τον οποίο τα δεδομένα περνούν από τον διακομιστή ιστού στον php σας. Αυτό αντικατοπτρίζεται στην οδηγία ακρόασης:

ακρόαση = 127.0.0.1:9000

Εάν έχει οριστεί διεύθυνση:port, τότε τα δεδομένα περνούν από τη στοίβα TCP και αυτό μάλλον δεν είναι πολύ καλό. Εάν υπάρχει μια διαδρομή προς την πρίζα, για παράδειγμα:

listen = /var/run/php5-fpm.sock

τότε τα δεδομένα περνούν από την υποδοχή unix και μπορείτε να παραλείψετε αυτήν την ενότητα.

Γιατί αξίζει ακόμα να μεταβείτε σε πρίζα unix; Το UDS (unix domain socket), σε αντίθεση με την επικοινωνία μέσω της στοίβας TCP, έχει σημαντικά πλεονεκτήματα:

  • δεν απαιτείται διακόπτης περιβάλλοντος, το UDS χρησιμοποιεί το netisr)
  • Το datagram UDS είναι γραμμένο απευθείας στην υποδοχή προορισμού
  • Η αποστολή ενός datagram UDS απαιτεί λιγότερες λειτουργίες (χωρίς αθροίσματα ελέγχου, χωρίς κεφαλίδες TCP, χωρίς δρομολόγηση)

Μέσος χρόνος καθυστέρησης TCP: 6 ΗΠΑ UDS Μέση καθυστέρηση: 2 ΗΠΑ Μέση καθυστέρηση PIPE: 2 ΗΠΑ Μέση ροή TCP: 253702 msg/s Μέση ροή UDS: 1733874 msg/s Μέση ροή PIPE: 18 msg/s

Έτσι, το UDS έχει καθυστέρηση από ~66% λιγότεροκαι απόδοσης μέσα 7 φορές περισσότερο TCP. Επομένως, πιθανότατα αξίζει να μεταβείτε στο UDS. Στην περίπτωσή μου, η υποδοχή θα βρίσκεται στο /var/run/php5-fpm.sock .

; σχολιάστε το - ακούστε = 127.0.0.1:9000 ακούστε = /var/run/php5-fpm.sock

Θα πρέπει επίσης να βεβαιωθείτε ότι ο διακομιστής web (ή οποιαδήποτε άλλη διαδικασία που χρειάζεται να επικοινωνήσει) έχει πρόσβαση ανάγνωσης/εγγραφής στην υποδοχή σας. Υπάρχουν ρυθμίσεις για αυτό. ακούω.ομάδακαι λειτουργία ακρόασηςΟ ευκολότερος τρόπος είναι να εκτελέσετε και τις δύο διεργασίες από τον ίδιο χρήστη ή ομάδα, στην περίπτωσή μας php-fpm και ο διακομιστής web θα ξεκινήσει με την ομάδα www-data:

listen.owner=www-data listen.group=www-data listen.mode=0660

Έλεγχος του επιλεγμένου μηχανισμού χειρισμού συμβάντων

Για να εργαστείτε με αποτελεσματική εργασία με I/O (είσοδος-έξοδος, περιγραφείς αρχείου / συσκευής / υποδοχής), αξίζει να ελέγξετε εάν η ρύθμιση είναι σωστή γεγονότα.μηχανισμός. Εάν το php-fpm είναι εγκατεστημένο από το αποθετήριο συστήματος, πιθανότατα όλα είναι εντάξει εκεί - είτε δεν καθορίζεται (εγκαθίσταται αυτόματα), είτε έχει καθοριστεί σωστά.

Η σημασία του εξαρτάται από το λειτουργικό σύστημα, για το οποίο υπάρχει μια υπόδειξη στην τεκμηρίωση:

; - epoll (linux >= 2.5.44) ; - kqueue (FreeBSD >= 4.1, OpenBSD >= 2.9, NetBSD >= 2.0) ; - /dev/poll (Solaris >= 7) ; - λιμάνι (Solaris >= 10)

Για παράδειγμα, εάν εργαζόμαστε σε μια σύγχρονη διανομή linux, χρειαζόμαστε epool:

γεγονότα.μηχανισμός = επολ

Επιλογή τύπου πισίνας - δυναμική / στατική / κατ' απαίτηση

Επίσης, θα πρέπει να προσέξετε τις ρυθμίσεις του διαχειριστή διεργασιών (pm). Στην πραγματικότητα, αυτή είναι η κύρια διαδικασία (κύριος διεργασία), η οποία θα διαχειρίζεται όλα τα θυγατρικά (που εκτελούν τον κώδικα της εφαρμογής) σύμφωνα με μια συγκεκριμένη λογική, η οποία στην πραγματικότητα περιγράφεται στο αρχείο ρυθμίσεων.

Διατίθενται συνολικά 3 σχήματα ελέγχου διαδικασίας:

  • δυναμικός
  • στατικός
  • κατα παραγγελια

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

δυναμικόςη πισίνα θα λύσει αυτό το πρόβλημα, ρυθμίζει τον αριθμό των θυγατρικών διεργασιών με βάση τις τιμές του αρχείου διαμόρφωσης, αλλάζοντας τις προς τα πάνω ή προς τα κάτω, ανάλογα με το φορτίο. Αυτό το pool είναι πιο κατάλληλο για έναν διακομιστή εφαρμογών που χρειάζεται γρήγορη απόκριση σε ένα αίτημα, εργασία με αιχμή και απαιτεί εξοικονόμηση πόρων (με τη μείωση των θυγατρικών διεργασιών όταν είναι αδρανής).

κατα παραγγελιαη πισίνα μοιάζει πολύ με στατικός, αλλά δεν ξεκινά τις θυγατρικές διεργασίες όταν ξεκινά η κύρια διαδικασία. Μόνο όταν φτάσει το πρώτο αίτημα, θα δημιουργηθεί η πρώτη θυγατρική διαδικασία και μετά από ένα ορισμένο χρονικό όριο (που καθορίζεται στη διαμόρφωση) θα σβήσει. Επομένως, είναι σχετικό με διακομιστές με περιορισμένους πόρους ή τη λογική που δεν απαιτεί γρήγορη απόκριση.

Διαρροές μνήμης και δολοφόνος OOM

Θα πρέπει να δώσετε προσοχή στην ποιότητα των εφαρμογών που θα εκτελεστούν από θυγατρικές διεργασίες. Εάν η ποιότητα της εφαρμογής δεν είναι πολύ υψηλή ή χρησιμοποιούνται πολλές βιβλιοθήκες τρίτων, τότε πρέπει να σκεφτείτε πιθανές διαρροές μνήμης και να ορίσετε τις τιμές σε τέτοιες μεταβλητές:

  • pm.max_requests
  • request_terminate_timeout

pm.max_requestsΑυτός είναι ο μέγιστος αριθμός αιτημάτων που θα επεξεργαστεί η επεξεργασία του παιδιού προτού σκοτωθεί. Με τη βίαιη θανάτωση μιας διαδικασίας αποφεύγεται μια κατάσταση στην οποία η μνήμη της θυγατρικής διαδικασίας «διογκώνεται» λόγω διαρροών (επειδή η διαδικασία συνεχίζει να λειτουργεί μετά από αίτημα μετά από αίτημα). Από την άλλη πλευρά, μια τιμή που είναι πολύ μικρή θα έχει ως αποτέλεσμα συχνές επανεκκινήσεις, με αποτέλεσμα απώλειες απόδοσης. Αξίζει να ξεκινήσετε με μια τιμή 1000 και στη συνέχεια να μειώσετε ή να αυξήσετε αυτήν την τιμή.

request_terminate_timeoutορίζει το μέγιστο χρόνο που μπορεί να εκτελεστεί μια θυγατρική διεργασία πριν από τη θανάτωση. Αυτό αποφεύγει μεγάλα ερωτήματα εάν για κάποιο λόγο έχει αλλάξει η τιμή max_execution_time στις ρυθμίσεις του διερμηνέα. Η τιμή θα πρέπει να οριστεί με βάση τη λογική των εφαρμογών που υποβάλλονται σε επεξεργασία, ας πούμε δεκαετία του '60(1 λεπτό).

Διαμόρφωση δυναμικής πισίνας

Για τον κύριο διακομιστή εφαρμογών, λόγω των προφανών πλεονεκτημάτων, επιλέγεται συχνά το δυναμικό pool. Η λειτουργία του περιγράφεται από τις ακόλουθες ρυθμίσεις:

  • μ.μ.max_children- μέγιστος αριθμός θυγατρικών διεργασιών
  • pm.start_servers- αριθμός διαδικασιών κατά την εκκίνηση
  • pm.min_spare_servers- ο ελάχιστος αριθμός διαδικασιών που περιμένουν σύνδεση (αιτήματα για επεξεργασία)
  • pm.max_spare_servers- ο μέγιστος αριθμός διαδικασιών που περιμένουν σύνδεση (αιτήματα για επεξεργασία)

Για να ορίσετε σωστά αυτές τις τιμές, είναι απαραίτητο να λάβετε υπόψη:

  • πόση μνήμη καταναλώνει κατά μέσο όρο μια παιδική διαδικασία
  • διαθέσιμη μνήμη RAM

Μπορείτε να μάθετε τη μέση τιμή μνήμης ανά διεργασία php-fpm σε μια εφαρμογή που ήδη εκτελείται χρησιμοποιώντας τον προγραμματιστή:

# ps -ylC php-fpm --sort:rss S UID PID PPID C PRI NI RSS SZ WCHAN TTY TIME CMD S 0 1445 1 0 80 0 9552 42588 ep_pol ? 00:00:00 php5-fpm

Χρειαζόμαστε τη μέση τιμή στη στήλη RSS (μέγεθος μόνιμης μνήμης σε kilobyte). Στην περίπτωσή μου είναι ~20 Mb. Σε περίπτωση που δεν υπάρχει φόρτωση σε εφαρμογές, μπορείτε να χρησιμοποιήσετε το Apache Benchmark για να δημιουργήσετε το απλούστερο φορτίο σε php-fpm.

Η ποσότητα της συνολικής / διαθέσιμης / χρησιμοποιημένης μνήμης μπορεί να προβληθεί με Ελεύθερος:

# δωρεάν -m συνολικά χρησιμοποιημένο δωρεάν ... Μνήμη: 4096 600 3496

Συνολικές μέγιστες διεργασίες = (Συνολική μνήμη Ram - (Χρησιμοποιημένη Ram + Buffer)) / (Μνήμη ανά διεργασία php) Συνολική μνήμη RAM: 4 GB RAM Χρησιμοποιούμενη: 1000 MB Buffer ασφαλείας: 400 MB Μνήμη ανά παιδί διεργασία php-fpm (μέσος όρος): 30 MB Μέγιστος δυνατός αριθμός διεργασίες = (4096 - (1000 + 400)) / 30 = 89 Ζυγός αριθμός: 89 στρογγυλοποιείται προς τα κάτω στο 80

Η τιμή των υπόλοιπων οδηγιών μπορεί να οριστεί με βάση το αναμενόμενο φορτίο στην εφαρμογή και επίσης να ληφθεί υπόψη τι άλλο κάνει ο διακομιστής εκτός από php-fpm (ας πούμε, το DBMS απαιτεί επίσης πόρους). Εάν υπάρχουν πολλές εργασίες στον διακομιστή, αξίζει να μειώσετε τον αριθμό των δύο αρχικών / μέγιστων διεργασιών.

Για παράδειγμα, ας λάβουμε υπόψη ότι υπάρχουν 2 pools www1 και www2 στον διακομιστή (για παράδειγμα, 2 πόροι Ιστού), τότε η διαμόρφωση καθενός από αυτούς μπορεί να μοιάζει με αυτό:

pm.max_children = 40 ; 80 / 2 pm.start_servers = 15 pm.min_spare_servers = 15 pm.max_spare_servers = 25

1. ΟΜΑΔΟΠΟΙΗΣΤΕ ΑΠΟ ένα κλειδί

Αυτή η συνάρτηση λειτουργεί ως GROUP BY για πίνακα, αλλά με έναν σημαντικό περιορισμό: Είναι δυνατή μόνο μία "στήλη" ομαδοποίησης ($identifier).

Συνάρτηση arrayUniqueByIdentifier( array $array, string $identifier) ​​( $ids = array_column($array, $identifier); $ids = array_unique($ids); $array = array_filter($array, function ($key, $value) ) χρήση ($ids) ( return in_array($value, array_keys($ids)); ), ARRAY_FILTER_USE_BOTH); επιστροφή $array; )

2. Ανίχνευση των μοναδικών σειρών για έναν πίνακα (δισδιάστατος πίνακας)

Αυτή η λειτουργία είναι για το φιλτράρισμα "γραμμών". Αν πούμε ότι ένας δισδιάστατος πίνακας είναι ένας πίνακας, τότε κάθε στοιχείο του είναι μια σειρά. Έτσι, μπορούμε να αφαιρέσουμε τις διπλότυπες σειρές με αυτήν τη συνάρτηση. Δύο σειρές (στοιχεία της πρώτης διάστασης) είναι ίσες, αν όλες οι στήλες τους (στοιχεία της δεύτερης διάστασης) είναι ίσες. Για τη σύγκριση των τιμών "στήλης" ισχύει: Εάν μια τιμή είναι απλού τύπου, η ίδια η τιμή θα χρησιμοποιηθεί κατά τη σύγκριση. Διαφορετικά θα χρησιμοποιηθεί ο τύπος του (πίνακας, αντικείμενο, πόρος, άγνωστος τύπος).

Η στρατηγική είναι απλή: Δημιουργήστε από τον αρχικό πίνακα έναν ρηχό πίνακα, όπου τα στοιχεία είναι εμπλουτισμένα d "στήλες" του αρχικού πίνακα. στη συνέχεια εφαρμόστε το array_unique(...) σε αυτό. και ως τελευταία χρησιμοποιήστε τα αναγνωριστικά που εντοπίστηκαν για το φιλτράρισμα του αρχικού πίνακα.

Συνάρτηση arrayUniqueByRow(array $table = , string $implodeSeparator) ( $elementStrings = ; foreach ($table ως $row) ( // Για να αποφύγετε ειδοποιήσεις όπως "Μετατροπή πίνακα σε συμβολοσειρά". $elementPreparedForImplode = array_map(function ($field) $valueType = gettype($field); $simpleTypes = ["boolean", "integer", "double", "float", "string", "NULL"]; $field = in_array($valueType, $simpleTypes) ? $field: $valueType; επιστροφή $field; ), $row); $elementStrings = implode($implodeSeparator, $elementPreparedForIplode); ) $elementStringsUnique = array_unique ($elementStrings); $table = array_intersect_key ($table, $elementStringsUni); επιστροφή $table;)

Είναι επίσης δυνατό να βελτιωθεί η κατηγορία σύγκρισης, ανιχνεύοντας την τιμή "στήλης", εάν ο τύπος της είναι αντικείμενο .

Το $implodeSeparator θα πρέπει να είναι περισσότερο ή λιγότερο πολύπλοκο, z.B. spl_object_hash($this) .

3. Ανίχνευση γραμμών με μοναδικές στήλες αναγνωριστικού για έναν πίνακα (δισδιάστατος πίνακας)

Αυτή η λύση βασίζεται στη 2η. Τώρα η πλήρης "σειρά" δεν χρειάζεται να είναι μοναδική. Δύο "σειρές" (στοιχεία της πρώτης διάστασης) είναι ίσες τώρα, αν όλες σχετικόΤα "πεδία" (στοιχεία της δεύτερης διάστασης) της μίας "σειράς" είναι ίσα με τα αντίστοιχα "πεδία" (στοιχεία με το ίδιο κλειδί).

Τα "σχετικά" "πεδία" είναι τα "πεδία" (στοιχεία της δεύτερης διάστασης), τα οποία έχουν κλειδί, που ισούται με ένα από τα στοιχεία των περασμένων "αναγνωριστικών".

Λειτουργία ArrayUniqueByMultIpleDidiers (πίνακας Array $, αναγνωριστικά Array $, String $ implodeSeparator = null) = πίνακας ($table, $arrayUniqueByRow); επιστροφή $arrayUniqueByMultipleIdentifiers; ) συνάρτηση removeArrayColumns( πίνακας $table, πίνακας $columnNames, bool $isWhitelist = false) ( foreach ($table ως $rowKey => είναι $rray) (αν (αν $row )) ( if ($isWhitelist) ( foreach ($row as $fieldName => $fieldValue) (if (!in_array($fieldName, $columnNames)) ( unset($table[$rowKey][$fieldName) ]); ) ) ) else (για τα $row ως $fieldName => $fieldValue) (if (in_array($fieldName, $columnNames)) ( unset($table[$rowKey][$fieldName]); ) ) ) ) ) επιστροφή $ πίνακα;)

Δεδομένου ότι η ανάπτυξη της τεχνολογίας έχει οδηγήσει στο γεγονός ότι κάθε προγραμματιστής έχει πλέον τον δικό του υπολογιστή, ως παρενέργεια έχουμε χιλιάδες διαφορετικές βιβλιοθήκες, πλαίσια, υπηρεσίες, API κ.λπ. για όλες τις περιπτώσεις. Αλλά όταν έρθει αυτή η περίπτωση ζωής, προκύπτει το πρόβλημα - τι να τα χρησιμοποιήσετε και τι να κάνετε αν δεν ταιριάζουν - ξαναγράψτε, γράψτε τη δική σας από την αρχή ή βιδώστε πολλές λύσεις για διαφορετικές περιπτώσεις χρήσης.

Νομίζω ότι πολλοί έχουν παρατηρήσει ότι συχνά η δημιουργία ενός έργου δεν εξαρτάται τόσο από τον προγραμματισμό όσο τη σύνταξη του κώδικα για την ενσωμάτωση πολλών έτοιμων λύσεων. Μερικές φορές τέτοιοι συνδυασμοί μετατρέπονται σε νέες λύσεις που μπορούν να χρησιμοποιηθούν επανειλημμένα σε επόμενα προβλήματα.

Ας προχωρήσουμε σε μια συγκεκριμένη εργασία "εκτελούμενη" - ένα επίπεδο αντικειμένου για εργασία με βάσεις δεδομένων στην PHP. Υπάρχουν πολλές λύσεις, που κυμαίνονται από ΠΟΠ έως πολυεπίπεδες (και, κατά τη γνώμη μου, όχι απόλυτα κατάλληλες σε PHP) μηχανές ORM.

Οι περισσότερες από αυτές τις λύσεις έχουν μετεγκατασταθεί στην PHP από άλλες πλατφόρμες. Συχνά όμως οι συγγραφείς δεν λαμβάνουν υπόψη τα χαρακτηριστικά της PHP, κάτι που θα απλοποιούσε σημαντικά τόσο τη γραφή όσο και τη χρήση φορητών κατασκευών.
Μία από τις κοινές αρχιτεκτονικές για αυτήν την κατηγορία εργασιών είναι το μοτίβο Active Record. Συγκεκριμένα, σύμφωνα με αυτό το μοτίβο κατασκευάζονται οι λεγόμενες οντότητες, οι οποίες χρησιμοποιούνται με τη μια ή την άλλη μορφή σε διάφορες πλατφόρμες, που κυμαίνονται από τα persistent beans στο EJB3 έως το EF στο .NET.

Λοιπόν, ας δημιουργήσουμε μια παρόμοια κατασκευή για την PHP. Ας συνδυάσουμε δύο ωραία πράγματα - την έτοιμη βιβλιοθήκη ADODB και τις ασθενώς πληκτρολογημένες και δυναμικές ιδιότητες των αντικειμένων στη γλώσσα PHP.
Ένα από τα πολλά χαρακτηριστικά του ADODB είναι η λεγόμενη αυτόματη δημιουργία ερωτημάτων SQL για εισαγωγή (INSERT) και ενημέρωση (UPDATE) εγγραφών που βασίζονται σε συσχετιστικούς πίνακες με δεδομένα.
Στην πραγματικότητα, δεν υπάρχει τίποτα στρατιωτικό για τη λήψη ενός πίνακα, όπου τα κλειδιά είναι τα ονόματα των πεδίων και οι τιμές είναι, αντίστοιχα, τα δεδομένα και δημιουργούν μια συμβολοσειρά ερωτήματος SQL. Αλλά το ADODB το κάνει πιο έξυπνα. Το ερώτημα χτίζεται με βάση τη δομή του πίνακα, η οποία διαβάζεται προκαταρκτικά από το σχήμα της βάσης δεδομένων. Ως αποτέλεσμα, πρώτον, μόνο τα υπάρχοντα πεδία μπαίνουν στη sql και όχι όλα στη σειρά, και δεύτερον, λαμβάνεται υπόψη ο τύπος του πεδίου - προστίθενται εισαγωγικά για συμβολοσειρές, οι μορφές ημερομηνίας μπορούν να διαμορφωθούν με βάση τη χρονική σήμανση εάν το δει το ADODB αντί για συμβολοσειρά στην μεταδιδόμενη τιμή κ.λπ. .

Τώρα ας πάμε από την πλευρά της PHP.
Φανταστείτε μια τέτοια τάξη (απλοποιημένη).

Οντότητα κλάσης( προστατευμένα $fields = array(); δημόσια τελική συνάρτηση __set($name, $value) ($this->fields[$name] = $value; ) δημόσια τελική συνάρτηση __get($name) (επιστροφή $ this- >πεδία[$name]; ) )

Περνώντας τον εσωτερικό πίνακα στη βιβλιοθήκη ADODB, μπορούμε να δημιουργήσουμε αυτόματα ερωτήματα SQL για να ενημερώσουμε μια εγγραφή στη βάση δεδομένων με αυτό το αντικείμενο. Ταυτόχρονα, δυσκίνητες κατασκευές αντιστοίχισης πεδίων των πινάκων βάσης δεδομένων σε πεδία ενός αντικειμένου οντότητας που βασίζεται σε XML και τα παρόμοια δεν χρειάζονται. Είναι απαραίτητο μόνο το όνομα του πεδίου να ταιριάζει με την ιδιότητα του αντικειμένου. Δεδομένου ότι ο τρόπος με τον οποίο ονομάζονται τα πεδία στη βάση δεδομένων και τα πεδία του αντικειμένου δεν έχει σημασία για τον υπολογιστή, δεν υπάρχει λόγος να μην ταιριάζουν.

Ας δείξουμε πώς λειτουργεί στην τελική έκδοση.
Ο κωδικός για την ολοκληρωμένη τάξη βρίσκεται στο Gist. Αυτή είναι μια αφηρημένη κλάση που περιέχει το ελάχιστο απαραίτητο για την εργασία με τη βάση δεδομένων. Σημειώνω ότι αυτή η τάξη είναι μια απλοποιημένη έκδοση της λύσης που έχει εκπονηθεί σε αρκετές δεκάδες έργα.

Ας φανταστούμε ότι έχουμε έναν πίνακα σαν αυτό:

ΔΗΜΙΟΥΡΓΙΑ ΠΙΝΑΚΑ «χρήστες» («όνομα χρήστη» varchar(255), ημερομηνία «δημιουργίας», «αναγνωριστικό_χρήστη» int(11) ΟΧΙ NULL AUTO_INCREMENT, ΚΥΡΙΟ ΚΛΕΙΔΙ («user_id»))
Ο τύπος της βάσης δεδομένων δεν έχει σημασία - το ADODB παρέχει φορητότητα για όλους τους κοινούς διακομιστές βάσεων δεδομένων.

Ας δημιουργήσουμε μια κατηγορία οντότητας User, με βάση την κλάση Entity

/** * @table=users * @keyfield=user_id */ class Ο χρήστης επεκτείνει την οντότητα( )

Στην πραγματικότητα, αυτό είναι όλο.
Χρησιμοποιείται απλά:

$user = νέος χρήστης(); $user->username="Vasya Pupkin"; $user->created=time(); $user->save(); //save to storage //load again $thesameuser = User::load($user->user_id); echo $thesameuser ->όνομα χρήστη;

Καθορίζουμε τον πίνακα και το πεδίο κλειδιού σε ψευδο-σχολιασμούς.
Μπορούμε επίσης να καθορίσουμε μια προβολή (για παράδειγμα, view = userview) εάν, όπως συμβαίνει συχνά, η οντότητα επιλέγεται βάσει του πίνακα της με συνημμένα ή υπολογισμένα πεδία. Σε αυτήν την περίπτωση, τα δεδομένα θα επιλεγούν από την προβολή και ο πίνακας θα ενημερωθεί. Όσοι δεν τους αρέσουν τέτοιοι σχολιασμοί μπορούν να παρακάμψουν τη μέθοδο getMetatada() και να καθορίσουν τις παραμέτρους του πίνακα στον πίνακα που επιστρέφεται.

Τι άλλο είναι χρήσιμο για την κλάση Entity σε αυτήν την υλοποίηση;

Για παράδειγμα, μπορούμε να παρακάμψουμε τη μέθοδο init(), η οποία καλείται μετά τη δημιουργία μιας παρουσίας οντότητας, για να αρχικοποιήσουμε την προεπιλεγμένη ημερομηνία δημιουργίας.
Ή υπερφορτώνετε τη μέθοδο afterLoad(), η οποία καλείται αυτόματα μετά τη φόρτωση της οντότητας από τη βάση δεδομένων, για να μετατρέψετε την ημερομηνία σε χρονική σήμανση για περαιτέρω πιο βολική χρήση.
Ως αποτέλεσμα, έχουμε μια όχι πολύ πιο περίπλοκη δομή.

/** * @table=users * @view=usersview * @keyfield=user_id */ class Ο χρήστης επεκτείνει την οντότητα( προστατευμένη συνάρτηση init() ( $this->created = time(); ) προστατευμένη συνάρτηση afterLoad() ( $this ->created = strtotime($this->created); ) )

Μπορείτε επίσης να υπερφορτώσετε τις μεθόδους πριν από την αποθήκευση και πριν από τη Διαγραφή και άλλα συμβάντα του κύκλου ζωής όπου μπορείτε, για παράδειγμα, να εκτελέσετε επικύρωση πριν από την αποθήκευση ή κάποιες άλλες ενέργειες - για παράδειγμα, να αφαιρέσετε εικόνες από τη μεταφόρτωση όταν ο χρήστης διαγραφεί.

Φορτώνουμε τη λίστα των οντοτήτων με το κριτήριο (στην πραγματικότητα, τις προϋποθέσεις για το WHERE).
$users = Χρήστης::load("όνομα χρήστη όπως "Pupkin" ");
Επίσης, η κλάση Entity σάς επιτρέπει να εκτελέσετε ένα αυθαίρετο, "εγγενές" ερώτημα SQL, ας πούμε έτσι. Για παράδειγμα, θέλουμε να επιστρέψουμε μια λίστα χρηστών με ορισμένες ομαδοποιήσεις σύμφωνα με στατιστικά στοιχεία. Δεν έχει σημασία ποια συγκεκριμένα πεδία θα επιστραφούν (το κύριο πράγμα είναι ότι υπάρχει ένα user_id εάν υπάρχει ανάγκη περαιτέρω χειρισμού της οντότητας), πρέπει μόνο να γνωρίζετε τα ονόματά τους για να αποκτήσετε πρόσβαση στα επιλεγμένα πεδία. Κατά την αποθήκευση μιας οντότητας, όπως είναι προφανές από τα παραπάνω, δεν είναι επίσης απαραίτητο να συμπληρώσετε όλα τα πεδία που θα υπάρχουν στο αντικείμενο οντότητας, θα πάνε στη βάση δεδομένων. Δηλαδή, δεν χρειάζεται να δημιουργήσουμε επιπλέον κλάσεις για αυθαίρετες επιλογές. Περίπου όπως οι ανώνυμες δομές κατά την ανάκτηση σε EF, μόνο εδώ είναι η ίδια κατηγορία οντοτήτων με όλες τις μεθόδους επιχειρηματικής λογικής.

Αυστηρά μιλώντας, οι παραπάνω μέθοδοι για τη λήψη λιστών είναι κάπως εκτός του μοτίβου AR. Βασικά, είναι εργοστασιακές μέθοδοι. Αλλά όπως κληροδότησε ο γέρος του Ockham, δεν θα παράγουμε οντότητες πέρα ​​από αυτό που είναι απαραίτητο και θα περιφράξουμε ένα ξεχωριστό Entity Manager ή κάτι τέτοιο.

Σημειώστε ότι τα παραπάνω είναι απλώς κλάσεις PHP και μπορούν να επεκταθούν και να τροποποιηθούν όπως θέλετε, προσθέτοντας ιδιότητες και μεθόδους επιχειρηματικής λογικής στην οντότητα (ή στη βασική κλάση Entity). Δηλαδή, δεν παίρνουμε απλώς ένα αντίγραφο μιας γραμμής πίνακα βάσης δεδομένων, αλλά μια επιχειρηματική οντότητα ως μέρος της αρχιτεκτονικής αντικειμένων της εφαρμογής.

Ποιος μπορεί να ωφεληθεί από αυτό; Φυσικά, όχι για προγραμματιστές που δεν αντλούνται, που πιστεύουν ότι η χρήση κάτι απλούστερου από ένα δόγμα δεν είναι σταθερή, και όχι για τους τελειομανείς, που είναι σίγουροι ότι εάν μια λύση δεν αποφέρει ένα δισεκατομμύριο προσβάσεις DB ανά δευτερόλεπτο, τότε αυτό είναι όχι λύση. Κρίνοντας από τα φόρουμ, πολλοί απλοί προγραμματιστές που εργάζονται σε συνηθισμένα έργα (εκ των οποίων το 99,9%) αντιμετωπίζουν αργά ή γρήγορα το πρόβλημα της εύρεσης ενός απλού και βολικού αντικειμενικού τρόπου πρόσβασης στη βάση δεδομένων. Αλλά έρχονται αντιμέτωποι με το γεγονός ότι οι περισσότερες από τις λύσεις είτε είναι αδικαιολόγητα φανταχτερές είτε αποτελούν μέρος ενός πλαισίου.

ΥΣΤΕΡΟΓΡΑΦΟ. Πήρε μια απόφαση από το πλαίσιο ως ξεχωριστό έργο