Finder: αναζήτηση αρχείων

Χρειάζεστε να βρείτε αρχεία που ταιριάζουν σε μια συγκεκριμένη μάσκα; Ο Finder θα σας βοηθήσει με αυτό. Είναι ένα ευέλικτο και γρήγορο εργαλείο για τη διάσχιση της δομής καταλόγων.

Εγκατάσταση:

composer require nette/utils

Τα παραδείγματα προϋποθέτουν δημιουργημένο ψευδώνυμο:

use Nette\Utils\Finder;

Χρήση

Πρώτα θα δείξουμε πώς μπορείτε χρησιμοποιώντας το Nette\Utils\Finder να εκτυπώσετε τα ονόματα των αρχείων με τις επεκτάσεις .txt και .md στον τρέχοντα κατάλογο:

foreach (Finder::findFiles(['*.txt', '*.md']) as $name => $file) {
	echo $file;
}

Ο προεπιλεγμένος κατάλογος για αναζήτηση είναι ο τρέχων κατάλογος, αλλά μπορείτε να τον αλλάξετε χρησιμοποιώντας τις μεθόδους in() ή from(). Η μεταβλητή $file είναι ένα στιγμιότυπο της κλάσης FileInfo με πολλές χρήσιμες μεθόδους. Στο κλειδί $name βρίσκεται η διαδρομή προς το αρχείο ως string.

Τι να αναζητήσετε;

Εκτός από τη μέθοδο findFiles(), υπάρχει επίσης η findDirectories(), που αναζητά μόνο καταλόγους, και η find(), που αναζητά και τα δύο. Αυτές οι μέθοδοι είναι στατικές, έτσι μπορούν να κληθούν χωρίς τη δημιουργία στιγμιότυπου. Η παράμετρος με τη μάσκα είναι προαιρετική, αν δεν την καθορίσετε, θα αναζητηθούν όλα.

foreach (Finder::find() as $file) {
	echo $file; // nyní se vypíší všechny soubory i adresáře - τώρα θα εκτυπωθούν όλα τα αρχεία και οι κατάλογοι
}

Με τις μεθόδους files() και directories() μπορείτε να συμπληρώσετε τι άλλο να αναζητήσετε. Οι μέθοδοι μπορούν να κληθούν επανειλημμένα και ως παράμετρος μπορεί να δοθεί και ένας πίνακας μασκών:

Finder::findDirectories('vendor') // všechny adresáře - όλοι οι κατάλογοι
	->files(['*.php', '*.phpt']); // plus všechny PHP soubory - συν όλα τα αρχεία PHP

Μια εναλλακτική λύση στις στατικές μεθόδους είναι η δημιουργία ενός στιγμιότυπου με new Finder (ένα τέτοιο νεοδημιουργημένο αντικείμενο δεν αναζητά τίποτα) και ο καθορισμός του τι να αναζητήσετε χρησιμοποιώντας τις files() και directories():

(new Finder)
	->directories()      // všechny adresáře - όλοι οι κατάλογοι
	->files('*.php');    // plus všechny PHP soubory - συν όλα τα αρχεία PHP

Στη μάσκα μπορείτε να χρησιμοποιήσετε χαρακτήρες μπαλαντέρ *, **, ? και [...]. Μπορείτε ακόμη να καθορίσετε και σε καταλόγους, για παράδειγμα το src/*.php αναζητά όλα τα αρχεία PHP στον κατάλογο src.

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

Πού να ψάξετε;

Ο προεπιλεγμένος κατάλογος για αναζήτηση είναι ο τρέχων κατάλογος. Θα τον αλλάξετε χρησιμοποιώντας τις μεθόδους in() και from(). Όπως είναι εμφανές από τα ονόματα των μεθόδων, η in() αναζητά μόνο στον δεδομένο κατάλογο, ενώ η from() αναζητά και στους υποκαταλόγους του (αναδρομικά). Αν θέλετε να αναζητήσετε αναδρομικά στον τρέχοντα κατάλογο, μπορείτε να χρησιμοποιήσετε το from('.').

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

Finder::findFiles('*.php')
	->in(['src', 'tests']) // hledá přímo v src/ a tests/ - αναζητά απευθείας στο src/ και tests/
	->from('vendor');      // hledá i v podadresářích vendor/ - αναζητά και στους υποκαταλόγους vendor/

Οι σχετικές διαδρομές είναι σχετικές με τον τρέχοντα κατάλογο. Μπορούν φυσικά να δοθούν και απόλυτες διαδρομές:

Finder::findFiles('*.php')
	->in('/var/www/html');

Στη διαδρομή είναι δυνατό να χρησιμοποιηθούν χαρακτήρες μπαλαντέρ χαρακτήρες μπαλαντέρ *, **, ?. Μπορείτε έτσι, για παράδειγμα, χρησιμοποιώντας τη διαδρομή src/*/*.php να αναζητήσετε όλα τα αρχεία PHP σε καταλόγους δεύτερου επιπέδου στον κατάλογο src. Ο χαρακτήρας ** που ονομάζεται globstar είναι ένα ισχυρό ατού, επειδή επιτρέπει την αναζήτηση και σε υποκαταλόγους: χρησιμοποιώντας το src/**/tests/*.php αναζητάτε όλα τα αρχεία PHP στον κατάλογο tests που βρίσκεται στο src ή σε οποιονδήποτε υποκατάλογό του.

Αντίθετα, οι χαρακτήρες μπαλαντέρ [...] στη διαδρομή δεν υποστηρίζονται, δηλαδή δεν έχουν ειδική σημασία, για να αποφευχθεί η ανεπιθύμητη συμπεριφορά σε περίπτωση που ψάχνετε για παράδειγμα in(__DIR__) και τυχαία στη διαδρομή υπάρχουν χαρακτήρες [].

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

Χαρακτήρες Μπαλαντέρ

Στη μάσκα μπορείτε να χρησιμοποιήσετε αρκετούς ειδικούς χαρακτήρες:

  • * – αντικαθιστά οποιονδήποτε αριθμό οποιωνδήποτε χαρακτήρων (εκτός από /)
  • ** – αντικαθιστά οποιονδήποτε αριθμό οποιωνδήποτε χαρακτήρων συμπεριλαμβανομένου του / (δηλ. μπορεί να γίνει αναζήτηση σε πολλαπλά επίπεδα)
  • ? – αντικαθιστά έναν οποιονδήποτε χαρακτήρα (εκτός από /)
  • [a-z] – αντικαθιστά έναν χαρακτήρα από τη λίστα χαρακτήρων στις αγκύλες
  • [!a-z] – αντικαθιστά έναν χαρακτήρα εκτός της λίστας χαρακτήρων στις αγκύλες

Παραδείγματα χρήσης:

  • img/?.png – αρχεία με μονογράμματο όνομα 0.png, 1.png, x.png, κ.λπ.
  • logs/[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9].log – αρχεία καταγραφής σε μορφή YYYY-MM-DD
  • src/**/tests/* – αρχεία στον κατάλογο src/tests, src/foo/tests, src/foo/bar/tests και ούτω καθεξής.
  • docs/**.md – όλα τα αρχεία με την επέκταση .md σε όλους τους υποκαταλόγους του καταλόγου docs

Εξαίρεση

Με τη μέθοδο exclude() μπορείτε να εξαιρέσετε αρχεία και καταλόγους από την αναζήτηση. Καθορίζετε τη μάσκα στην οποία το αρχείο δεν πρέπει να ταιριάζει. Παράδειγμα αναζήτησης αρχείων *.txt εκτός από αυτά που περιέχουν το γράμμα X στο όνομα:

Finder::findFiles('*.txt')
	->exclude('*X*');

Για να παραλείψετε τους διασχιζόμενους υποκαταλόγους, χρησιμοποιήστε το exclude():

Finder::findFiles('*.php')
	->from($dir)
	->exclude('temp', '.git')

Φιλτράρισμα

Ο Finder προσφέρει διάφορες μεθόδους για το φιλτράρισμα των αποτελεσμάτων (δηλ. τη μείωσή τους). Μπορείτε να τα συνδυάσετε και να τα καλέσετε επανειλημμένα.

Χρησιμοποιώντας το size() φιλτράρουμε ανάλογα με το μέγεθος του αρχείου. Έτσι βρίσκουμε αρχεία με μέγεθος μεταξύ 100 και 200 byte:

Finder::findFiles('*.php')
	->size('>=', 100)
	->size('<=', 200);

Η μέθοδος date() φιλτράρει με βάση την ημερομηνία τελευταίας τροποποίησης του αρχείου. Οι τιμές μπορεί να είναι απόλυτες ή σχετικές με την τρέχουσα ημερομηνία και ώρα, για παράδειγμα, έτσι βρίσκουμε αρχεία που τροποποιήθηκαν τις τελευταίες δύο εβδομάδες:

Finder::findFiles('*.php')
	->date('>', '-2 weeks')
	->from($dir)

Και οι δύο συναρτήσεις κατανοούν τους τελεστές >, >=, <, <=, =, !=, <>.

Ο Finder επιτρέπει επίσης το φιλτράρισμα των αποτελεσμάτων με προσαρμοσμένες συναρτήσεις. Η συνάρτηση λαμβάνει ως παράμετρο ένα αντικείμενο Nette\Utils\FileInfo και πρέπει να επιστρέψει true, για να συμπεριληφθεί το αρχείο στα αποτελέσματα.

Παράδειγμα: αναζήτηση αρχείων PHP που περιέχουν το string Nette (ανεξαρτήτως κεφαλαίων-πεζών):

Finder::findFiles('*.php')
	->filter(fn($file) => strcasecmp($file->read(), 'Nette') === 0);

Φιλτράρισμα σε βάθος

Κατά την αναδρομική αναζήτηση, μπορείτε να ορίσετε το μέγιστο βάθος διάσχισης χρησιμοποιώντας τη μέθοδο limitDepth(). Αν ορίσετε limitDepth(1), διασχίζονται μόνο οι πρώτοι υποκατάλογοι, το limitDepth(0) απενεργοποιεί τη διάσχιση σε βάθος και η τιμή –1 ακυρώνει το όριο.

Ο Finder επιτρέπει τη λήψη αποφάσεων με προσαρμοσμένες συναρτήσεις για το σε ποιον κατάλογο να εισέλθετε κατά τη διάσχιση. Η συνάρτηση λαμβάνει ως παράμετρο ένα αντικείμενο Nette\Utils\FileInfo και πρέπει να επιστρέψει true, για να εισέλθετε στον κατάλογο:

Finder::findFiles('*.php')
	->descentFilter($file->getBasename() !== 'temp');

Ταξινόμηση

Ο Finder προσφέρει επίσης αρκετές συναρτήσεις για την ταξινόμηση των αποτελεσμάτων.

Η μέθοδος sortByName() ταξινομεί τα αποτελέσματα με βάση τα ονόματα των αρχείων. Η ταξινόμηση είναι φυσική, δηλαδή χειρίζεται σωστά τους αριθμούς στα ονόματα και επιστρέφει π.χ. το foo1.txt πριν από το foo10.txt.

Ο Finder επιτρέπει επίσης την ταξινόμηση με προσαρμοσμένη συνάρτηση. Αυτή λαμβάνει ως παράμετρο δύο αντικείμενα Nette\Utils\FileInfo και πρέπει να επιστρέψει το αποτέλεσμα της σύγκρισης με τον τελεστή <=>, δηλαδή -1, 0 ή 1. Για παράδειγμα, έτσι ταξινομούμε τα αρχεία κατά μέγεθος:

$finder->sortBy(fn($a, $b) => $a->getSize() <=> $b->getSize());

Πολλαπλές διαφορετικές αναζητήσεις

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

($finder = new Finder) // do proměnné $finder si uložíme první Finder! - στη μεταβλητή $finder αποθηκεύουμε τον πρώτο Finder!
	->files('*.php')   // v src/ hledáme soubory *.php - στο src/ αναζητούμε αρχεία *.php
	->from('src')
	->append()
	->files('*.md')    // v docs/ hledáme soubory *.md - στο docs/ αναζητούμε αρχεία *.md
	->from('docs')
	->append()
	->files('*.json'); // v aktuální složce hledáme soubory *.json - στον τρέχοντα φάκελο αναζητούμε αρχεία *.json

Εναλλακτικά, μπορείτε να χρησιμοποιήσετε τη μέθοδο append() για να προσθέσετε ένα συγκεκριμένο αρχείο (ή έναν πίνακα αρχείων). Τότε επιστρέφει το ίδιο αντικείμενο Finder:

$finder = Finder::findFiles('*.txt')
	->append(__FILE__);

FileInfo

Η Nette\Utils\FileInfo είναι μια κλάση που αντιπροσωπεύει ένα αρχείο ή κατάλογο στα αποτελέσματα αναζήτησης. Είναι μια επέκταση της κλάσης SplFileInfo, η οποία παρέχει πληροφορίες όπως το μέγεθος του αρχείου, η ημερομηνία τελευταίας τροποποίησης, το όνομα, η διαδρομή, κ.λπ.

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

foreach (Finder::findFiles('*.jpg')->from('.') as $file) {
	$absoluteFilePath = $file->getRealPath();
	$relativeFilePath = $file->getRelativePathname();
}

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

foreach ($finder as $file) {
    $contents = $file->read();
    // ...
    $file->write($contents);
}

Επιστροφή αποτελεσμάτων ως πίνακας

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

Μπορείτε επίσης να λάβετε τα αποτελέσματα ως πίνακα αντικειμένων Nette\Utils\FileInfo, και αυτό με τη μέθοδο collect(). Ο πίνακας δεν είναι συσχετιστικός, αλλά αριθμητικός.

$array = $finder->findFiles('*.php')->collect();
έκδοση: 4.0