From 48b306b072e5dfff36c9b8bb66861ce16eb9cead Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 19 Oct 2026 09:24:22 +0200 Subject: [PATCH 1/2] Fix glob() not negating bracket expressions that start with "^" --- .../tests/file/glob_bracket_negation.phpt | 30 +++++++++++++++++++ main/php_glob.c | 4 +++ 2 files changed, 34 insertions(+) create mode 100644 ext/standard/tests/file/glob_bracket_negation.phpt diff --git a/ext/standard/tests/file/glob_bracket_negation.phpt b/ext/standard/tests/file/glob_bracket_negation.phpt new file mode 100644 index 000000000000..3f5ef513789e --- /dev/null +++ b/ext/standard/tests/file/glob_bracket_negation.phpt @@ -0,0 +1,30 @@ +--TEST-- +glob() negates a bracket expression that starts with "!" or "^" +--FILE-- + +--CLEAN-- + +--EXPECT-- +[!a]: ! ^ b c +[^a]: ! ^ b c +[!a-b]: ! ^ c +[^a-b]: ! ^ c +[!]a]: ! ^ b c +[^]a]: ! ^ b c +[a^]: ^ a diff --git a/main/php_glob.c b/main/php_glob.c index 8edfb8326da4..f7da643505a8 100644 --- a/main/php_glob.c +++ b/main/php_glob.c @@ -107,6 +107,7 @@ #include "charclass.h" +#define CIRCUMFLEX '^' #define DOLLAR '$' #define DOT '.' #define EOS '\0' @@ -566,6 +567,9 @@ static int glob0(const Char *pattern, php_glob_t *pglob, struct glob_lim *limitp switch (c) { case LBRACKET: c = *qpatnext; + /* Like fnmatch(), treat a leading '^' as '!' */ + if (c == CIRCUMFLEX) + c = NOT; if (c == NOT) ++qpatnext; if (*qpatnext == EOS || From 284ccfdbe8d02bfe9e6160f222a8d5e392c6f9d6 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 19 Oct 2026 17:05:19 +0200 Subject: [PATCH 2/2] Note that the leading '^' handling is not in OpenBSD's glob() --- main/php_glob.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/main/php_glob.c b/main/php_glob.c index f7da643505a8..af087c4530a6 100644 --- a/main/php_glob.c +++ b/main/php_glob.c @@ -567,7 +567,10 @@ static int glob0(const Char *pattern, php_glob_t *pglob, struct glob_lim *limitp switch (c) { case LBRACKET: c = *qpatnext; - /* Like fnmatch(), treat a leading '^' as '!' */ + /* + * Not in OpenBSD's glob(), keep it when syncing: like fnmatch(), + * treat a leading '^' as '!'. + */ if (c == CIRCUMFLEX) c = NOT; if (c == NOT)