aklump / glob
File and directory path globbing.
Requires
- php: >=7.3
- aklump/gitignore: ^0.0
- crysalead/dir: ^2.0
- psr/simple-cache: ^1.0
- symfony/filesystem: ^4 || ^5.4 || ^6.4
Requires (Dev)
- aklump/phpswap: ^0.0.17
- phpunit/phpunit: ^9.6
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A replacement for PHP's
glob()that understands**, for recursive path matching without a shell call.
Summary
PHP's own glob() stops at one directory level. There is no **, so finding every .php file under src/ means writing a recursive walk or shelling out to find. This package adds the missing piece: \AKlump\Glob\Glob::glob() takes the patterns you already write and reads ** as "any number of directories". Patterns without ** are handed straight to the native glob(), so the common case costs nothing extra. Matches come back absolute and sorted, directories carry a trailing slash, and a matched symlink brings its target along with it. It exists because the other glob-replacement packages available at the time returned unexpected results.
Quick Start
Install the package:
composer require aklump/glob:^0.0
Then match across directory levels:
<?php require 'vendor/autoload.php'; use AKlump\Glob\Glob; print_r(Glob::glob('src/**/*.php'));
A relative pattern is resolved against the current working directory, and the matches come back absolute and sorted:
Array
(
[0] => /path/to/project/src/Filesystem/Path.php
[1] => /path/to/project/src/Helpers/Cache.php
[2] => /path/to/project/src/Helpers/GetConcretePaths.php
)
Requirements
PHP 7.3 or newer. The unit tests are run against 7.3, 7.4, 8.0, 8.1, 8.2, 8.3, 8.4 and 8.5 by bin/run-multi-phpunit-tests.sh.
Everything else arrives through Composer: aklump/gitignore for pattern matching, plus symfony/filesystem, crysalead/dir and psr/simple-cache. No PHP extension has to be enabled and there is no binary to install. Symfony's Path class did not exist before symfony/filesystem 5.4, so this package ships its own and aliases it in when Symfony's is absent; ^4 works as well as ^6.4.
Installation
composer require aklump/glob:^0.0
The package is autoloaded under the AKlump\Glob\ namespace by PSR-4, so Composer's autoloader is the whole of the setup.
To work on the package itself:
git clone https://github.com/aklump/glob.git
cd glob
composer install
./vendor/bin/phpunit -c ./tests_unit/phpunit.xml
OK (14 tests, 32 assertions)
bin/run-phpunit-tests.sh runs the same suite and writes an HTML coverage report to reports/html, which needs Xdebug or PCOV. bin/run-multi-phpunit-tests.sh runs it under every supported PHP version and needs aklump/phpswap.
Usage
One call at a time, or many
Glob::glob() builds a new instance for every call, and each instance builds its own file-list cache the first time it runs. That makes the static call safe by default: it can never hand you a stale listing.
When you are about to run several patterns against a tree that is not changing, reuse one instance and the cache is built once:
$glob = new \AKlump\Glob\Glob(); $php = $glob('src/**/*.php'); // This second call reads the cache the first call built. $md = $glob('docs/**/*.md');
The trade is yours to manage. An instance keeps its cache for as long as it lives, so if the file system changes underneath it, discard it and make a new one.
Testing a path against a pattern
Glob::match() answers the same question without touching the disk:
\AKlump\Glob\Glob::match('src/Helpers/Cache.php', 'src/**/*.php'); // TRUE \AKlump\Glob\Glob::match('/foo/lorem', '/*/bar'); // FALSE
What the patterns mean
Patterns follow gitignore's pattern format, which is where ** comes from.
*matches within a single directory level;**crosses any number of them.- A relative pattern is made absolute against the current working directory before matching, so every returned path is absolute.
- A directory in the results ends in a slash, so you can tell it from a file without a second
is_dir()call. - When a match is a symlink, both the link and the path it resolves to appear in the results.
