Skip to content

Commit 0d1280c

Browse files
authored
Merge pull request #2064 from hydephp/replace-glob-brace
[1.x] Replace `GLOB_BRACE` with a more robust solution
2 parents ab30be4 + b41253e commit 0d1280c

File tree

18 files changed

+658
-229
lines changed

18 files changed

+658
-229
lines changed

RELEASE_NOTES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This serves two purposes:
1414
- Registered the `cache:clear` command to make it easier to clear the cache in https://github.com/hydephp/develop/pull/1881
1515
- Added a new `Hyperlinks::isRemote()` helper method to check if a URL is remote in https://github.com/hydephp/develop/pull/1882
1616
- All page types now support the `description` front matter field (used in page metadata) in https://github.com/hydephp/develop/pull/1884
17+
- Added a new `Filesystem::findFiles()` method to find files in a directory in https://github.com/hydephp/develop/pull/2064
1718

1819
### Changed
1920
- Changed the `Hyde` facade to use a `@mixin` annotation instead of single method annotations in https://github.com/hydephp/develop/pull/1919
@@ -24,6 +25,7 @@ This serves two purposes:
2425
- The `torchlight:install` command is now hidden from the command list as it's already installed in https://github.com/hydephp/develop/pull/1879
2526
- Updated the home page fallback link in the 404 template to lead to the site root in https://github.com/hydephp/develop/pull/1880 (fixes https://github.com/hydephp/develop/issues/1781)
2627
- Normalized remote URL checks so that protocol relative URLs `//` are consistently considered to be remote in all places in https://github.com/hydephp/develop/pull/1882 (fixes https://github.com/hydephp/develop/issues/1788)
28+
- Replaced internal usages of glob functions with our improved file finder in https://github.com/hydephp/develop/pull/2064
2729
- Updated to HydeFront v3.4 in https://github.com/hydephp/develop/pull/1803
2830
- Realtime Compiler: Virtual routes are now managed through the service container in https://github.com/hydephp/develop/pull/1858
2931
- Realtime Compiler: Improved the dashboard layout in https://github.com/hydephp/develop/pull/1866
@@ -44,6 +46,8 @@ This serves two purposes:
4446
- Fixed routing issues with nested 404 pages where an index page does not exist https://github.com/hydephp/develop/issues/1781 in https://github.com/hydephp/develop/pull/1880
4547
- Fixed URL metadata for blog posts not using customized post output directories in https://github.com/hydephp/develop/pull/1889
4648
- Improved printed documentation views in https://github.com/hydephp/develop/pull/2005
49+
- Fixed "BuildService finding non-existent files to copy in Debian" https://github.com/hydephp/framework/issues/662 in https://github.com/hydephp/develop/pull/2064
50+
- Fixed "Undefined constant `Hyde\Foundation\Kernel\GLOB_BRACE`" https://github.com/hydephp/hyde/issues/270 in https://github.com/hydephp/develop/pull/2064
4751
- Realtime Compiler: Updated the exception handler to match HTTP exception codes when sending error responses in https://github.com/hydephp/develop/pull/1853
4852
- Realtime Compiler: Improved routing for nested index pages in https://github.com/hydephp/develop/pull/1852
4953
- Realtime Compiler: Improved the dashboard https://github.com/hydephp/develop/pull/1866 fixing https://github.com/hydephp/realtime-compiler/issues/22 and https://github.com/hydephp/realtime-compiler/issues/29

monorepo/HydeStan/HydeStan.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ final class HydeStan
1515
private const FILE_ANALYSERS = [
1616
NoFixMeAnalyser::class,
1717
UnImportedFunctionAnalyser::class,
18+
NoGlobBraceAnalyser::class,
1819
];
1920

2021
private const TEST_FILE_ANALYSERS = [
@@ -370,6 +371,27 @@ public function run(string $file, string $contents): void
370371
}
371372
}
372373

374+
class NoGlobBraceAnalyser extends FileAnalyser
375+
{
376+
public function run(string $file, string $contents): void
377+
{
378+
$lines = explode("\n", $contents);
379+
380+
foreach ($lines as $lineNumber => $line) {
381+
AnalysisStatisticsContainer::analysedExpression();
382+
383+
if (str_contains($line, 'GLOB_BRACE')) {
384+
$this->fail(sprintf('Usage of `GLOB_BRACE` found in %s at line %d. This feature is not supported on all systems and should be avoided.',
385+
realpath(BASE_PATH.'/'.$file),
386+
$lineNumber + 1
387+
));
388+
389+
HydeStan::addActionsMessage('error', $file, $lineNumber + 1, 'HydeStan: NoGlobBraceError', '`GLOB_BRACE` is not supported on all systems. Consider refactoring to avoid it.');
390+
}
391+
}
392+
}
393+
}
394+
373395
class NoTestReferenceAnalyser extends LineAnalyser
374396
{
375397
public function run(string $file, int $lineNumber, string $line): void

packages/framework/src/Facades/Filesystem.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,21 @@ public static function smartGlob(string $pattern, int $flags = 0): Collection
6969
return self::kernel()->filesystem()->smartGlob($pattern, $flags);
7070
}
7171

72+
/**
73+
* Find files in the project's directory, with optional filtering by extension and recursion.
74+
*
75+
* The returned collection will be a list of paths relative to the project root.
76+
*
77+
* @param string $directory
78+
* @param string|array<string>|false $matchExtensions The file extension(s) to match, or false to match all files.
79+
* @param bool $recursive Whether to search recursively or not.
80+
* @return \Illuminate\Support\Collection<int, string>
81+
*/
82+
public static function findFiles(string $directory, string|array|false $matchExtensions = false, bool $recursive = false): Collection
83+
{
84+
return self::kernel()->filesystem()->findFiles($directory, $matchExtensions, $recursive);
85+
}
86+
7287
/**
7388
* Touch one or more files in the project's directory.
7489
*

packages/framework/src/Foundation/Kernel/FileCollection.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ protected function runExtensionHandlers(): void
5959
protected function discoverFilesFor(string $pageClass): void
6060
{
6161
// Scan the source directory, and directories therein, for files that match the model's file extension.
62-
foreach (Filesystem::smartGlob($pageClass::sourcePath('{*,**/*}'), GLOB_BRACE) as $path) {
62+
foreach (Filesystem::findFiles($pageClass::sourceDirectory(), $pageClass::fileExtension(), true) as $path) {
6363
if (! str_starts_with(basename((string) $path), '_')) {
6464
$this->addFile(SourceFile::make($path, $pageClass));
6565
}

packages/framework/src/Foundation/Kernel/Filesystem.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Hyde\Foundation\HydeKernel;
99
use Hyde\Foundation\PharSupport;
1010
use Illuminate\Support\Collection;
11+
use Hyde\Framework\Actions\Internal\FileFinder;
1112

1213
use function collect;
1314
use function Hyde\normalize_slashes;
@@ -183,4 +184,16 @@ public function smartGlob(string $pattern, int $flags = 0): Collection
183184

184185
return $files->map(fn (string $path): string => $this->pathToRelative($path));
185186
}
187+
188+
/**
189+
* @param string|array<string>|false $matchExtensions
190+
* @return \Illuminate\Support\Collection<int, string>
191+
*/
192+
public function findFiles(string $directory, string|array|false $matchExtensions = false, bool $recursive = false): Collection
193+
{
194+
/** @var \Hyde\Framework\Actions\Internal\FileFinder $finder */
195+
$finder = app(FileFinder::class);
196+
197+
return $finder->handle($directory, $matchExtensions, $recursive);
198+
}
186199
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Hyde\Framework\Actions\Internal;
6+
7+
use Hyde\Facades\Filesystem;
8+
use Hyde\Hyde;
9+
use Illuminate\Support\Collection;
10+
use SplFileInfo;
11+
use Symfony\Component\Finder\Finder;
12+
13+
/**
14+
* @interal This class is used internally by the framework and is not part of the public API, unless that is requested on GitHub with a valid use case.
15+
*/
16+
class FileFinder
17+
{
18+
/**
19+
* @param array<string>|string|false $matchExtensions
20+
* @return \Illuminate\Support\Collection<int, string>
21+
*/
22+
public static function handle(string $directory, array|string|false $matchExtensions = false, bool $recursive = false): Collection
23+
{
24+
if (! Filesystem::isDirectory($directory)) {
25+
return collect();
26+
}
27+
28+
$finder = Finder::create()->files()->in(Hyde::path($directory));
29+
30+
if ($recursive === false) {
31+
$finder->depth('== 0');
32+
}
33+
34+
if ($matchExtensions !== false) {
35+
$finder->name(static::buildFileExtensionPattern((array) $matchExtensions));
36+
}
37+
38+
return collect($finder)->map(function (SplFileInfo $file): string {
39+
return Hyde::pathToRelative($file->getPathname());
40+
})->sort()->values();
41+
}
42+
43+
/** @param array<string> $extensions */
44+
protected static function buildFileExtensionPattern(array $extensions): string
45+
{
46+
$extensions = self::expandCommaSeparatedValues($extensions);
47+
48+
return '/\.('.self::normalizeExtensionForRegexPattern($extensions).')$/i';
49+
}
50+
51+
/** @param array<string> $extensions */
52+
private static function expandCommaSeparatedValues(array $extensions): array
53+
{
54+
return array_merge(...array_map(function (string $item): array {
55+
return array_map(fn (string $item): string => trim($item), explode(',', $item));
56+
}, $extensions));
57+
}
58+
59+
/** @param array<string> $extensions */
60+
private static function normalizeExtensionForRegexPattern(array $extensions): string
61+
{
62+
return implode('|', array_map(function (string $extension): string {
63+
return preg_quote(ltrim($extension, '.'), '/');
64+
}, $extensions));
65+
}
66+
}

packages/framework/src/Framework/Actions/PreBuildTasks/CleanSiteDirectory.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
use Hyde\Framework\Features\BuildTasks\PreBuildTask;
1111

1212
use function basename;
13-
use function glob;
1413
use function in_array;
1514
use function sprintf;
1615

@@ -21,7 +20,7 @@ class CleanSiteDirectory extends PreBuildTask
2120
public function handle(): void
2221
{
2322
if ($this->isItSafeToCleanOutputDirectory()) {
24-
Filesystem::unlink(glob(Hyde::sitePath('*.{html,json}'), GLOB_BRACE));
23+
Filesystem::unlink(Filesystem::findFiles(Hyde::sitePath(), ['html', 'json'])->all());
2524
Filesystem::cleanDirectory(Hyde::siteMediaPath());
2625
}
2726
}

packages/framework/src/Support/DataCollections.php

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@
1212
use Illuminate\Support\Collection;
1313
use Illuminate\Support\Str;
1414

15-
use function implode;
15+
use function Hyde\path_join;
1616
use function json_decode;
17-
use function sprintf;
1817
use function Hyde\unslash;
1918
use function str_starts_with;
2019

@@ -102,9 +101,7 @@ public static function json(string $name, bool $asArray = false): static
102101

103102
protected static function findFiles(string $name, array|string $extensions): Collection
104103
{
105-
return Filesystem::smartGlob(sprintf('%s/%s/*.{%s}',
106-
static::$sourceDirectory, $name, implode(',', (array) $extensions)
107-
), GLOB_BRACE);
104+
return Filesystem::findFiles(path_join(static::$sourceDirectory, $name), $extensions);
108105
}
109106

110107
protected static function makeIdentifier(string $path): string

packages/framework/src/Support/Filesystem/MediaFile.php

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Hyde\Support\Filesystem;
66

7+
use Hyde\Facades\Filesystem;
78
use Hyde\Hyde;
89
use Hyde\Facades\Config;
910
use Hyde\Framework\Exceptions\FileNotFoundException;
@@ -14,12 +15,9 @@
1415
use function array_merge;
1516
use function array_keys;
1617
use function filesize;
17-
use function implode;
1818
use function pathinfo;
1919
use function collect;
2020
use function is_file;
21-
use function sprintf;
22-
use function glob;
2321

2422
/**
2523
* File abstraction for a project media file.
@@ -104,15 +102,18 @@ protected static function discoverMediaAssetFiles(): array
104102
})->all();
105103
}
106104

105+
/** @return array<string> */
107106
protected static function getMediaAssetFiles(): array
108107
{
109-
return glob(Hyde::path(static::getMediaGlobPattern()), GLOB_BRACE) ?: [];
108+
return Filesystem::findFiles(Hyde::getMediaDirectory(), static::getMediaFileExtensions(), true)->all();
110109
}
111110

112-
protected static function getMediaGlobPattern(): string
111+
/** @return array<string>|string */
112+
protected static function getMediaFileExtensions(): array|string
113113
{
114-
return sprintf(Hyde::getMediaDirectory().'/{*,**/*,**/*/*}.{%s}', implode(',',
115-
Config::getArray('hyde.media_extensions', self::EXTENSIONS)
116-
));
114+
/** @var array<string>|string $config */
115+
$config = Config::get('hyde.media_extensions', self::EXTENSIONS);
116+
117+
return $config;
117118
}
118119
}

packages/framework/tests/Feature/DiscoveryServiceTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ public function testMediaAssetExtensionsCanBeAddedByCommaSeparatedValues()
130130

131131
$this->assertSame([], MediaFile::files());
132132

133-
self::mockConfig(['hyde.media_extensions' => ['1,2,3']]);
133+
self::mockConfig(['hyde.media_extensions' => '1,2,3']);
134134
$this->assertSame(['test.1', 'test.2', 'test.3'], MediaFile::files());
135135
}
136136

0 commit comments

Comments
 (0)