From a7b8d875b63cfa736f05ae1fed552cfd2477e6cf Mon Sep 17 00:00:00 2001 From: PurHur Date: Wed, 8 Jul 2026 19:19:03 +0000 Subject: [PATCH] Fix glob('/dir/.*') to include . and .. dot entries (#17456) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the basename pattern explicitly requests dotfiles, stop unconditionally skipping . and .. before fnmatch — matches php-src ext/standard/dir.c. Co-authored-by: Cursor --- ext/standard/VmFsGlobPure.php | 8 +++----- .../cases/stdlib/glob_dot_entries.phpt | 18 ++++++++++++++++++ test/repro/maintainer_gap_glob_dot_entries.php | 13 +++++++++++++ test/unit/VmFsGlobTest.php | 17 +++++++++++++++++ 4 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 test/compliance/cases/stdlib/glob_dot_entries.phpt create mode 100644 test/repro/maintainer_gap_glob_dot_entries.php diff --git a/ext/standard/VmFsGlobPure.php b/ext/standard/VmFsGlobPure.php index 2784858809f..93f0178a173 100644 --- a/ext/standard/VmFsGlobPure.php +++ b/ext/standard/VmFsGlobPure.php @@ -97,11 +97,9 @@ private static function globSingle(string $pattern, int $libcFlags, bool $onlyDi $matches = []; $skipDotEntries = '' !== $filePattern && '.' !== $filePattern[0]; foreach ($entries as $entry) { - if ('.' === $entry || '..' === $entry) { - continue; - } - // php-src ext/standard/dir.c — omit dotfiles unless pattern explicitly requests them. - if ($skipDotEntries && isset($entry[0]) && '.' === $entry[0]) { + // php-src ext/standard/dir.c — omit dot entries unless pattern explicitly requests them. + if ($skipDotEntries && ('.' === $entry || '..' === $entry + || (isset($entry[0]) && '.' === $entry[0]))) { continue; } if (!VmFnmatch::match($filePattern, $entry, self::fnmatchFlagsFromGlob($libcFlags))) { diff --git a/test/compliance/cases/stdlib/glob_dot_entries.phpt b/test/compliance/cases/stdlib/glob_dot_entries.phpt new file mode 100644 index 00000000000..c4c159c4efc --- /dev/null +++ b/test/compliance/cases/stdlib/glob_dot_entries.phpt @@ -0,0 +1,18 @@ +--TEST-- +glob() dotfile pattern includes . and .. entries (#17456, ext/standard/dir.c) +--FILE-- +assertFalse($result); } + /** Issue #17456 — glob('/dir/.*') includes . and .. when pattern requests dotfiles. */ + public function testVmFsGlobDotPatternIncludesDotEntries(): void + { + $tmp = sys_get_temp_dir(); + if (!is_dir($tmp)) { + $this->markTestSkipped('sys_get_temp_dir() is not a directory'); + } + $matches = \PHPCompiler\ext\standard\VmFsGlob::glob($tmp.'/.*'); + $this->assertIsArray($matches); + $this->assertContains($tmp.'/.', $matches); + $this->assertContains($tmp.'/..', $matches); + $star = \PHPCompiler\ext\standard\VmFsGlob::glob($tmp.'/*'); + $this->assertIsArray($star); + $this->assertNotContains($tmp.'/.', $star); + $this->assertNotContains($tmp.'/..', $star); + } + /** Issue #14881 — absolute directory path without double leading slash. */ public function testVmFsGlobAbsoluteDirNormalizesLeadingSlash(): void {