Introduction

PHP's glob() function with GLOB_BRACE expands brace patterns like *.{php,inc,twig} to match multiple file extensions. However, GLOB_BRACE is implemented by the system's libc glob() function, and some systems (notably Alpine Linux with musl libc) do not support brace expansion, causing glob() to return an empty array or the literal brace pattern. This is a silent failure -- no error is raised, just no results -- making it particularly dangerous in file scanning, template loading, and build tool code.

Symptoms

```php // On Alpine Linux / musl libc $files = glob('src/*.{php,inc}', GLOB_BRACE); var_dump($files); // array(0) { } -- Empty! No files found

// On the same system, simple glob works $files = glob('src/*.php'); // array(45) { ... } -- Works fine ```

Common Causes

  • musl libc does not support GLOB_BRACE: Alpine Linux, some Docker images
  • GLOB_BRACE constant not defined: Very old PHP versions or minimal builds
  • Pattern syntax error: Spaces in brace pattern not handled correctly
  • No files match any pattern: Empty result is correct, not a bug
  • Directory path incorrect: Base path wrong, no files to match
  • Special characters in path: Spaces or special chars in directory name

Step-by-Step Fix

Step 1: Detect GLOB_BRACE support

```php function globBraceSupported(): bool { // Test if GLOB_BRACE works on this system $testFile = tempnam(sys_get_temp_dir(), 'glob_test_'); rename($testFile, $testFile . '.php');

$pattern = dirname($testFile) . '/*.{php,txt}'; $result = glob($pattern, GLOB_BRACE);

unlink($testFile . '.php');

return is_array($result) && count($result) > 0; } ```

Step 2: Implement cross-platform brace expansion

```php function globBrace(string $pattern, int $flags = 0): array { // Check if pattern contains braces if (strpos($pattern, '{') === false || strpos($pattern, '}') === false) { return glob($pattern, $flags) ?: []; }

// Extract brace content preg_match('/\{([^}]+)\}/', $pattern, $matches); if (!$matches) { return glob($pattern, $flags) ?: []; }

// Expand brace patterns manually $extensions = explode(',', $matches[1]); $results = [];

foreach ($extensions as $ext) { $expanded = str_replace($matches[0], $ext, $pattern); $files = glob($expanded, $flags); if ($files) { $results = array_merge($results, $files); } }

return array_values(array_unique($results)); }

// Usage - works on all platforms $files = globBrace('src/*.{php,inc,twig}'); ```

Step 3: Use Symfony Finder for robust file matching

```php use Symfony\Component\Finder\Finder;

$finder = new Finder(); $finder->files() ->in('src/') ->name('*.php') ->name('*.inc') ->name('*.twig') ->sortByName();

$files = []; foreach ($finder as $file) { $files[] = $file->getPathname(); }

// Symfony Finder works cross-platform, no GLOB_BRACE issues ```

Prevention

  • Never rely on GLOB_BRACE in code that must run on Alpine Linux
  • Use globBrace() helper function for cross-platform brace expansion
  • Consider Symfony Finder or similar library for complex file matching
  • Test file scanning code on the same OS/platform as production
  • Always handle empty glob results explicitly
  • Add a test that verifies glob returns expected files on the target platform
  • Document platform requirements if GLOB_BRACE is used