From a18568aac660af35a9993bbeb8c98a42202b2771 Mon Sep 17 00:00:00 2001 From: John Koster Date: Tue, 30 Jan 2024 15:38:40 -0600 Subject: [PATCH] [4.x] Improve collection Stache watcher performance (#9302) --- src/Stache/Stores/CollectionsStore.php | 2 ++ src/Stache/Stores/Store.php | 5 ++-- src/Stache/Traverser.php | 33 ++++++++++++++++++++++++-- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/Stache/Stores/CollectionsStore.php b/src/Stache/Stores/CollectionsStore.php index e43f8da720..b3cf9ebe6d 100644 --- a/src/Stache/Stores/CollectionsStore.php +++ b/src/Stache/Stores/CollectionsStore.php @@ -12,6 +12,8 @@ class CollectionsStore extends BasicStore { + protected $traverseRecursively = false; + public function key() { return 'collections'; diff --git a/src/Stache/Stores/Store.php b/src/Stache/Stores/Store.php index 41dc66bc9f..d336ee768d 100644 --- a/src/Stache/Stores/Store.php +++ b/src/Stache/Stores/Store.php @@ -22,6 +22,7 @@ abstract class Store protected $storeIndexes = []; protected $usedIndexes; protected $fileChangesHandled = false; + protected $traverseRecursively = true; protected $paths; protected $fileItems; protected $shouldCacheFileItems = false; @@ -180,7 +181,7 @@ public function handleFileChanges() $existing = collect(Cache::get($cacheKey, [])); // Get the files and timestamps from the filesystem right now. - $files = Traverser::filter([$this, 'getItemFilter'])->traverse($this); + $files = Traverser::filter([$this, 'getItemFilter'])->traverse($this, $this->traverseRecursively); // Cache the files and timestamps, ready for comparisons on the next request. // We'll do it now since there are multiple early returns coming up. @@ -292,7 +293,7 @@ public function paths() return $this->paths = collect($paths); } - $files = Traverser::filter([$this, 'getItemFilter'])->traverse($this); + $files = Traverser::filter([$this, 'getItemFilter'])->traverse($this, $this->traverseRecursively); $fileItems = $files->map(function ($timestamp, $path) { return [ diff --git a/src/Stache/Traverser.php b/src/Stache/Traverser.php index 84723f0534..9a1fb6bcc4 100644 --- a/src/Stache/Traverser.php +++ b/src/Stache/Traverser.php @@ -2,8 +2,13 @@ namespace Statamic\Stache; +use DirectoryIterator; +use FilesystemIterator; use Illuminate\Filesystem\Filesystem; +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; use Statamic\Facades\Path; +use Symfony\Component\Finder\SplFileInfo; class Traverser { @@ -15,7 +20,7 @@ public function __construct(Filesystem $filesystem) $this->filesystem = $filesystem; } - public function traverse($store) + public function traverse($store, $recursive = true) { if (! $dir = $store->directory()) { throw new \Exception("Store [{$store->key()}] does not have a directory defined."); @@ -27,7 +32,7 @@ public function traverse($store) return collect(); } - $files = collect($this->filesystem->allFiles($dir)); + $files = collect($this->getFiles($dir, $recursive)); if ($this->filter) { $files = $files->filter($this->filter); @@ -45,4 +50,28 @@ public function filter($filter) return $this; } + + private function getFiles($dir, $recursive) + { + $files = []; + + if ($recursive) { + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS | FilesystemIterator::CURRENT_AS_SELF), + RecursiveIteratorIterator::CHILD_FIRST + ); + } else { + $iterator = new DirectoryIterator($dir); + } + + foreach ($iterator as $fileInfo) { + if ($fileInfo->isDir() || $fileInfo->getFilename()[0] === '.') { + continue; + } + + $files[] = new SplFileInfo($fileInfo->getPathname(), $fileInfo->getPath(), $fileInfo->getFilename()); + } + + return $files; + } }