PHPackages                             bishopb/pattern - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. bishopb/pattern

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

bishopb/pattern
===============

A string-matching PHP library sporting a consistent, fluent API.

4282[1 issues](https://github.com/bishopb/pattern/issues)PHPCI failing

Since Dec 29Pushed 5y agoCompare

[ Source](https://github.com/bishopb/pattern)[ Packagist](https://packagist.org/packages/bishopb/pattern)[ RSS](/packages/bishopb-pattern/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependenciesVersions (1)Used By (0)

Pattern
=======

[](#pattern)

[![Latest Stable Version](https://camo.githubusercontent.com/1b6f8120a3b1d709b5251002eaf97a625f4a8178a27175251b87b36c991d1caa/68747470733a2f2f706f7365722e707567782e6f72672f626973686f70622f7061747465726e2f762f737461626c652e706e67)](https://packagist.org/packages/bishopb/pattern) [![Build Status](https://camo.githubusercontent.com/0aebd02f6f95fee027b55a029101b95d6124f53fab6a557fcb76bc89faba615d/68747470733a2f2f7472617669732d63692e6f72672f626973686f70622f7061747465726e2e706e673f6272616e63683d6d6173746572)](https://travis-ci.org/bishopb/pattern) [![Coverage Status](https://camo.githubusercontent.com/0ab9745facc83c91fd8e3693c6a79ca1d52aff086ec476d24af9037f7eb0ce31/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f626973686f70622f7061747465726e2f62616467652e706e673f6272616e63683d6d6173746572)](https://coveralls.io/r/bishopb/pattern?branch=master)[![Monthly Downloads](https://camo.githubusercontent.com/15eec24102f606f51ab988a749d2805af2bb740322ef914900a57b8eee33538f/68747470733a2f2f706f7365722e707567782e6f72672f626973686f70622f7061747465726e2f642f6d6f6e74686c792e706e67)](https://packagist.org/packages/bishopb/pattern)

**Warning: This API does not exist. After implementing it, and measuring the performance, I decided the overhead did not meet my goals and abandoned that code. The situation may be different in PHP 8. If you like this API, and wnat to see it come alive, please implement it and submit a PR.**

Pattern is a string-matching PHP library sporting a consistent, fluent API. Pattern unifies the API for `strcmp` and family, `fnmatch`, `preg_match`, and `version_compare`, while also offering convenience methods for common string-matching operations.

Pattern might be for you if:

- You want readable string-matching code that clearly describes your intent.
- You're frustrated that there's no simple, built-in implementation to find if a string *ends* with another.
- You want to avoid silly off-by-one errors when doing simple string checks.
- You want the best performing algorithm for a particular kind of string check, regardless of whether that's `strstr` or `strpos`.
- You're tired of referring to the PHP user manual for the argument order of `strpos` and friends.

Quickstart
----------

[](#quickstart)

Install with [Composer](http://getcomposer.org/): `composer require bishopb/pattern:dev-master`

Use:

```
use BishopB\Pattern;

// common matching API regardless of pattern language
$subjects = array ( 'Capable', 'Enabler', 'Able', );
$patterns = array (
    new Pattern\Literal('Able'),
    new Pattern\Wildcard('*able*')
);
foreach ($subjects as $subject) {
    foreach ($patterns as $pattern) {
        $pattern->matches($subject) and "$pattern matches $subject";
    }
}

// literal matching sugar
$able = new Pattern\Literal('Able')->fold();
$able->foundIn('tablet');
$able->begins('Abletic');
$able->ends('Parable');
$able->sorts->before('active');
$able->sorts->after('aardvark');

// version matching sugar
$stable = new Pattern\Version('1.0.0');
$stable->matches('1.0.0');
$stable->before('1.0.1');
$stable->after('0.9.9');
```

Motivation
----------

[](#motivation)

In PHP, developers have four common ways to match strings to patterns:

KindExample CodeExample PatternNeedle-in-Haystack`0 === strcmp($pattern, $input)``'foobar'`Versiony`0 === version_compare($pattern, '7.0.0')``'7.0.0RC1'`Shell-style wildcards`true === fnmatch($pattern, $input)``'foo*bar?'`PCRE regular expressions`true === preg_match($pattern, $input)``'^\s*#'`There are few problems with these API:

- `$pattern` is a plain old string, which means you can make probable mistakes like: `fnmatch('^foo.*bar', $input)`
- `strcmp` and family return an orderable result that doesn't encourage intenional programming. Consider: `if (! strcasecmp('foo', $input)) { echo 'pop quiz: matches foo?'; }`
- Functions to perform literal comparisons are scattered all over the place: `strcmp`, `strcasecmp`, `strpos`, `stripos`, etc.
- Both `strcasecmp` and `==` are [dangerous](https://bugs.php.net/bug.php?id=64069) ways to compare strings.
- Can be difficult to remember which argument is pattern and which is subject (compare `strpos` and `preg_match`).
- How one specifies "case-insensitive" various widely amongst the comparison functions.
- If your code initially accepts literal matches, then you want to support regular expressions, you have to re-write your code.
- Not every platform supports `fnmatch`.

This library provides a fast, thin abstraction over the built-in pattern matching functions to mitigate these problems.

Performance
-----------

[](#performance)

This package's philosophy is simple: to deliver syntactic sugar with minimal run-time fat. API calls are a thin facade over the fastest implementation of the requested match. Space is conserved as much as possible.

### Run-time benchmarks

[](#run-time-benchmarks)

Meaurements for different tests in operations per second.

BenchmarkNative PHPThis Library% Diffstrcmp\_single\_char\_matchstrcmp\_tiny\_string\_match3,798.21683strcmp\_small\_string\_matchstrcmp\_medium\_string\_match32,991.49158strcmp\_large\_string\_match71.65056strcmp\_tiny\_to\_large\_mismatch54,822.84512strcasecmp\_single\_char\_match90,357.87224strcasecmp\_tiny\_string\_match75,753.89895strcasecmp\_small\_string\_match30,056.59029strcasecmp\_medium\_string\_match436.80003strcasecmp\_large\_string\_match1.55844### Peak-memory consumption benchmarks

[](#peak-memory-consumption-benchmarks)

BenchmarkNative PHPThis Library% Diff*Note*: All benchmarks run a minimum of 1,000 times on a small, unloaded EC2 instance using PHP 5.3. Refer to `tests/*Event.php` for actual code. Refer to the [Travis CI](https://travis-ci.org/bishopb/pattern) builds for run times on different PHP versions.

Advanced usage
--------------

[](#advanced-usage)

### Manipulating the search subjects

[](#manipulating-the-search-subjects)

Typically methods in the pattern classes (`Literal`, `Wildcard`, and `Pcre`) take strings. However, you can also pass instances of `Subject`, which is a lightweight string class fit with methods common to string comparison:

```
use BishopB\Pattern;

$device  = new Literal('Tablet')->fold();
$version = new Version('8.1');
$subject = new Subject('    Microsoft Tablet running Windows 8.1.0RC242.')-trim();

$device->matches(
    $subject->
    column(' ', 1) // explode at space and get the 1st index (0-based)
);
$version->after(
    $subject->
    column(' ', -1)-> // explode at space and get the last index (nth-from last)
    substring(0, 4)   // only the first 5 characters
);
```

### Faster searching of big text or with repeated searches

[](#faster-searching-of-big-text-or-with-repeated-searches)

When your subject text is long, or you expect to compare your literal pattern to many different subjects, it's worth it to "study" the literal pattern for improved performance.

```
// notice the use of study()
// without this, searching would be much slower
$zebra = new Literal('zebra')->fold()->study();
$words = file_get_contents('/usr/share/dict/words') or die('No dictionary');
$zebra->foundIn($words);
```

You may be wondering: how many characters is "long"? Or, how many iterations is "many"? Well, I suppose it depends. But, a long time ago, some PHP internals [benchmarking](http://grokbase.com/t/php/php-internals/0869z2aemb/algorithm-optimizations-string-search#20080611g4vev3qwk7sj0sdwmgjtg7pjyc) suggested a length of 5000+ or more would make studying worth it.

FAQ
---

[](#faq)

### Why not just use the built-ins?

[](#why-not-just-use-the-built-ins)

For the reasons mentioned above. Personally, I wrote this library because I kept referring to the official docs on the argument order for the built-ins and because common use cases aren't handled concisely. In summary, this library lets me write less code and be more clear in meaning.

For example, I see a lot of code following this pattern:

```
if (! strcmp($actual, $expected)) {
    $this->doSomething();
} else {
    throw new \RuntimeException('Actual does not match expected');
}
```

It's technically right. But, to me, it looks wrong. I find this much easier to read:

```
if ($actual->matches($expected)) {
    $this->doSomething();
} else {
    throw new \RuntimeException('Actual does not match expected');
}
```

There is a related side benefit. In weak-mode PHP, functions that receive an invalid parameter emit a warning and return `null`. Since `null` evaluates falsey, the example above runs `doSomething` unexpectedly. Consider:

```
// ?password=[]
if (! strcmp($_GET['password'], $user->password)) {
    $this->login($user);
} else {
    throw new \RuntimeException('Invalid password');
}
```

Why? Because `true === (! (null === strcmp(array (), '******')))`. In this library, an exception is raised if you try to match against an array.

### Why not add more stringy methods, like `length()`, to `Subject`?

[](#why-not-add-more-stringy-methods-like-length-to-subject)

The package overall aims to support pattern matching in the lightest weight possible. Bulking up `Subject` with methods unrelated to pattern matches conflicts with this goal.

###  Health Score

18

—

LowBetter than 8% of packages

Maintenance15

Infrequent updates — may be unmaintained

Popularity13

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity31

Early-stage or recently created project

 Bus Factor1

Top contributor holds 97.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/f7d1b2950f32597fbcd30215950167fb1390c6bdd7da5f8820b3e0c7f7c69a64?d=identicon)[bishopb](/maintainers/bishopb)

---

Top Contributors

[![bishopb](https://avatars.githubusercontent.com/u/6844145?v=4)](https://github.com/bishopb "bishopb (43 commits)")[![waknauss](https://avatars.githubusercontent.com/u/20912626?v=4)](https://github.com/waknauss "waknauss (1 commits)")

### Embed Badge

![Health badge](/badges/bishopb-pattern/health.svg)

```
[![Health](https://phpackages.com/badges/bishopb-pattern/health.svg)](https://phpackages.com/packages/bishopb-pattern)
```

###  Alternatives

[creatuity/magento2-interceptors

Creatuity Compiled Interceptors Module

136116.5k1](/packages/creatuity-magento2-interceptors)[alexcrawford/lexorank-php

PHP Implementation of JIRA's LexoRank algorithm.

2879.1k1](/packages/alexcrawford-lexorank-php)[unionco/calendarize

A calendar field type providing functionality for recurrence.

1827.2k](/packages/unionco-calendarize)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
