alexeyshockov/pattern-matcher
Lightweight PHP pattern-matching utility by Alexey Shockov. Helps compare values against defined patterns and execute matching logic, enabling cleaner conditional flows than nested if/switch statements. Suitable for small libraries and framework-agnostic use.
Installation:
composer require alexeyshockov/pattern-matcher
No additional configuration is required—just autoload the package.
First Use Case: Match a simple string pattern (e.g., for validation or routing):
use AlexeyShockov\PatternMatcher\PatternMatcher;
$matcher = new PatternMatcher();
$result = $matcher->match('user/{id}', '/user/123'); // Returns ['id' => '123']
Where to Look First:
PatternMatcher class docs (if any) for syntax nuances.Dynamic Route Matching:
$routes = [
'user/{id}' => 'UserController@show',
'posts/{id}/comments/{comment_id}' => 'CommentController@show',
];
$matcher = new PatternMatcher();
foreach ($routes as $pattern => $handler) {
if ($matcher->match($pattern, $requestUri)) {
return $handler; // Resolve with captured params
}
}
Data Validation:
$matcher = new PatternMatcher();
$isValid = $matcher->match('email-{user}-{domain}.com', 'email-john@example.com');
// Returns ['user' => 'john', 'domain' => 'example'] if matched.
Template Rendering:
$template = 'Hello, {name}! Your ID is {id}.';
$data = ['name' => 'Alice', 'id' => 42];
$rendered = str_replace(
array_keys($data),
$matcher->extract($template, $data),
$template
);
Illuminate\Routing\Router to use PatternMatcher for custom route patterns:
$router->matchPattern('user/{id}', function ($id) {
return view('user.profile', compact('id'));
});
$matcher->match('YYYY-MM-DD', $userInputDate);
$matcher->match('api/v1/{resource}/{id}', $request->path());
Greedy Matching:
Patterns like {var*} (zero or more) may consume unintended segments. Use {var+} (one or more) or {var?} (optional) for precision.
// Avoid:
$matcher->match('file.{ext}', 'file.tar.gz'); // May match 'tar.gz' as {ext}
// Fix:
$matcher->match('file.{ext}', 'file.tar.gz', ['ext' => 'tar.gz']); // Explicit
Case Sensitivity:
The matcher is case-sensitive by default. Use strtolower() on inputs if needed:
$matcher->match(strtolower($pattern), strtolower($input));
Backslashes in Patterns:
Escape literal { or } with \:
$matcher->match('literal\\{curly\\}', '{curly}'); // Matches exactly
Performance: Avoid overusing complex patterns in loops (e.g., thousands of routes). Cache compiled patterns if possible.
Enable Verbose Output: Extend the class to log matches:
$matcher = new class extends PatternMatcher {
public function match($pattern, $input) {
$result = parent::match($pattern, $input);
logger()->debug("Pattern: $pattern | Input: $input | Result: " . print_r($result, true));
return $result;
}
};
Test Edge Cases:
/{} vs {}.!@#$% in patterns or inputs.Custom Matchers:
Extend PatternMatcher to add domain-specific rules:
class EmailMatcher extends PatternMatcher {
public function match($pattern, $input) {
if (!filter_var($input, FILTER_VALIDATE_EMAIL)) {
return false;
}
return parent::match($pattern, $input);
}
}
Pre/Post-Processing: Hook into the matching logic to transform inputs/outputs:
$matcher->match($pattern, preg_replace('/[^a-z0-9]/', '', $input));
Integration with Laravel: Publish a config file to centralize pattern rules:
// config/pattern-matcher.php
'reserved' => ['admin', 'api'],
Then validate against these in your matcher logic.
How can I help you explore Laravel packages today?