phlak/splat
Splat is a lightweight PHP library for matching strings against wildcard patterns. It provides simple, expressive helpers for route-style matching, capturing wildcard segments, and filtering paths or values, making it useful for routing, file matching, and other pattern-based tasks.
phlak/splat is a small, dependency-light, static utility for matching strings against glob-like patterns. It does not touch the filesystem — think fnmatch() / Laravel's Str::is(), not glob().
Install and verify
composer require phlak/splat
composer show phlak/splat # confirm PHP constraint and namespace
Where to look first
vendor/phlak/splat/README.md — the pattern-syntax table is the source of truth (wildcards, character classes, brace expansion).PHLAK\Splat\Splat: match(), filter(), and capture().vendor/phlak/splat/tests for edge-case behavior (anchoring, separators, escaping) before relying on it in production.First use case — filter or validate a list of filenames/keys:
use PHLAK\Splat\Splat;
Splat::match('*.php', 'index.php'); // true
Splat::match('*.php', 'index.txt'); // false
Splat::filter('*.md', [
'README.md',
'composer.json',
'CHANGELOG.md',
]); // ['README.md', 'CHANGELOG.md']
If your real problem is "find files on disk," pair Splat with Symfony Finder or Laravel's File facade for traversal, and use Splat only to match the resulting paths.
Custom Laravel validation rule
Wrap match() in a rule so pattern-based validation is reusable and testable:
use Illuminate\Contracts\Validation\Rule;
use PHLAK\Splat\Splat;
class MatchesPattern implements Rule
{
public function __construct(private string $pattern) {}
public function passes($attribute, $value): bool
{
return is_string($value) && Splat::match($this->pattern, $value);
}
public function message(): string
{
return 'The :attribute must match the pattern ' . $this->pattern;
}
}
Collection pipelines
Splat's filter() is convenient, but inside a larger chain it is often cleaner to treat match() as the predicate:
$matching = collect($files)
->filter(fn (string $f) => Splat::match($pattern, $f))
->values();
Extracting structured segments with capture()
Use this for filenames, cache keys, or route-like strings where the wildcard portion is the value you care about:
// Treat capture() as returning the wildcard substrings, in order.
[$slug] = Splat::capture('*.blade.php', 'welcome.blade.php');
// Structured names: 2024-12-06-report.pdf
[$year, $month, $day, $name] = Splat::capture(
'*-*-*-*.pdf',
'2024-12-06-report.pdf'
);
Always guard against a failed capture (empty array) before destructuring user input.
Middleware / path exclusion lists
A common day-to-day pattern is centralizing "skip these paths" logic:
protected array $except = ['health*', 'admin/**', '*.well-known*'];
public function handle($request, Closure $next)
{
foreach ($this->except as $pattern) {
if (Splat::match($pattern, ltrim($request->path(), '/'))) {
return $next($request);
}
}
// ...
}
Normalize paths once (leading slash, \ → /) before matching.
Configuration key matching
Useful for filtering flat config arrays exported from config() or .env-style maps:
$stripeKeys = Splat::filter('services.stripe.*', array_keys($flatConfig));
Encapsulate behind your own service
Splat is static-only and intentionally thin. If you use it in many places, wrap it:
final class PatternMatcher
{
public function matches(string $pattern, string $value): bool
{
return Splat::match($pattern, $value);
}
}
This gives you one place to swap in fnmatch() or Symfony's Glob later, and one place to normalize separators and case.
Integration tips
Splat::filter() for one-off array filtering and match() inside existing loops/pipelines.glob(). No filesystem access, no hidden-file rules, no directory walking. Dotfiles are matched by * because that is just a character; if you want shell-like dotfile exclusion, filter manually.*foo*. Confirm in the README/tests, and write a test for the exact behavior you depend on.* crosses / (and whether ** exists for recursive segments). If you're matching paths, normalize \ to / on Windows and test mixed separators explicitly.*, ?, {, }, [, ], and the escape character itself. Otherwise a crafted input can broaden a match unexpectedly. Do not treat patterns as security allowlists without understanding this.{a,b}{c,d}{e,f} expands multiplicatively. Keep brace sets small and avoid building patterns from unbounded user input.capture(). Patterns like *.* are ambiguous. Assert the exact capture shape with a test before using it to parse filenames.capture() can return an empty array or fewer elements than expected. Validate length before destructuring.../ is just characters to Splat. If patterns gate file access, resolve and validate the real path separately.composer.json and cover your usage with tests so an upstream change is visible.*, ?, one brace, one character class at a time). Most issues are anchoring, separator handling, or unescaped specials — not the library itself.How can I help you explore Laravel packages today?