PHPackages                             bentools/specification - 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. bentools/specification

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

bentools/specification
======================

Provides an implementation of the Specification Pattern. PHP7+

3.0.1(8y ago)4189MITPHPPHP &gt;=7.1

Since Mar 14Pushed 8y ago1 watchersCompare

[ Source](https://github.com/bpolaszek/bentools-specification)[ Packagist](https://packagist.org/packages/bentools/specification)[ RSS](/packages/bentools-specification/feed)WikiDiscussions master Synced 4w ago

READMEChangelogDependencies (4)Versions (11)Used By (0)

[![Latest Stable Version](https://camo.githubusercontent.com/4f72a3748b3328b5f1d7ddadd2a9860b97d1b0c8b1b799c6d6158218ded02f8d/68747470733a2f2f706f7365722e707567782e6f72672f62656e746f6f6c732f73706563696669636174696f6e2f762f737461626c65)](https://packagist.org/packages/bentools/specification)[![License](https://camo.githubusercontent.com/f8d254afb97a2da11378f7ad5bbc527130c0cc1c75b4c070db1ec5e1efb882c7/68747470733a2f2f706f7365722e707567782e6f72672f62656e746f6f6c732f73706563696669636174696f6e2f6c6963656e7365)](https://packagist.org/packages/bentools/specification)[![Build Status](https://camo.githubusercontent.com/3ab9a3a26664706d6e7196b2b771e4cc87233ccdd4a13ee99b30bee7b153887b/68747470733a2f2f6170692e7472617669732d63692e6f72672f62706f6c61737a656b2f62656e746f6f6c732d73706563696669636174696f6e2e7376673f6272616e63683d6d6173746572)](https://scrutinizer-ci.com/g/bpolaszek/bentools-specification/build-status/master)[![Coverage Status](https://camo.githubusercontent.com/51d02fb57060bee2079e859e6b431d3a26df7b15ffa1e44c24757cb7aa41454c/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f62706f6c61737a656b2f62656e746f6f6c732d73706563696669636174696f6e2f62616467652e7376673f6272616e63683d6d6173746572)](https://coveralls.io/github/bpolaszek/bentools-specification?branch=master)[![Scrutinizer Code Quality](https://camo.githubusercontent.com/7b0d1bc3588f4269b467c492348ab1306e49443086da87674bf9a885edfd749d/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f62706f6c61737a656b2f62656e746f6f6c732d73706563696669636174696f6e2f6261646765732f7175616c6974792d73636f72652e706e673f623d6d6173746572)](https://scrutinizer-ci.com/g/bpolaszek/bentools-specification/?branch=master)[![Total Downloads](https://camo.githubusercontent.com/9032ede58486cd4216f0cfcd495b2ed3cbab08dec4f489a3db2851d74ae585eb/68747470733a2f2f706f7365722e707567782e6f72672f62656e746f6f6c732f73706563696669636174696f6e2f646f776e6c6f616473)](https://packagist.org/packages/bentools/specification)

bentools/specification
======================

[](#bentoolsspecification)

PHP7.1+ implementation of the Specification Pattern.

The goal
--------

[](#the-goal)

The Specification Pattern allows you to wrap some conditionnal structures into value objects. This may have no sense in your usual code, but it can be useful when the combination of your business rules reaches a high [Cyclomatic Complexity](https://en.wikipedia.org/wiki/Cyclomatic_complexity).

Thus, wrapping your conditions into named objects with a specific failure handling may help you to:

- Add new business rules to existing ones
- Have a better understanding of your conditionnal structures
- Find which condition invalidates the others
- Avoid unreadable lasagna-code

The principles of the Specification Pattern are borrowed from Domain Driven Design, but can be applied anywhere.

Overview
--------

[](#overview)

A specification is a kind of enhanced conditionnal structure, which can be chained to other specifications.

Here's how to create a specification:

```
require_once __DIR__ . '/vendor/autoload.php';

use function BenTools\Specification\spec;

$color = 'green';
$spec = spec('green' === $color);
$spec->validate(); // Hurray! Our specification has been validated.
```

As you can see, the `validate()` method is used to validate a specification is met. It returns `void` (nothing).

When a specification is unmet, the `validate()` method throws an `UnmetSpecificationException`:

```
use function BenTools\Specification\spec;

$color = 'green';
$size = 'small';
$spec = spec('green' === $color)->and('big' === $size);
$spec->validate(); // Oh no! An UnmetSpecificationException has been thrown.
```

When handling these exceptions, you can know which specification(s) failed. To identify them, you can name each specification:

```
use BenTools\Specification\Exception\UnmetSpecificationException;
use function BenTools\Specification\spec;

$color = 'green';
$size = 'small';

$spec = spec('green' === $color)->withLabel('Color specification')
            ->and('big' === $size)->withLabel('Size specification');
try {
    $spec->validate();
} catch (UnmetSpecificationException $e) {
    foreach ($e->getUnmetSpecifications() as $unmetSpecification) {
        if (null !== $unmetSpecification->getLabel()) {
            printf('%s failed.' . PHP_EOL, $unmetSpecification->getLabel());
        }
    }
}

// Outputs: Size specification failed.
```

#### Specification quick check

[](#specification-quick-check)

Instead of dealing with try/catch blocks, you can check wether or not a specification is met by calling `$spec->isSatisfied()`, which will handle this for you and return a boolean.

```
use BenTools\Specification\Exception\UnmetSpecificationException;
use function BenTools\Specification\spec;

$color = 'green';
$size = 'small';

$spec = spec('green' === $color)->withLabel('Color specification')
            ->and('big' === $size)->withLabel('Size specification');

var_dump($spec->isSatisfied()); // (bool) false
```

#### Create specifications

[](#create-specifications)

The `spec()`, `group()` and `not()` functions, and the `and()` and `or()` methods are Specification factories, that will return a `Specification` object. They accept as an argument:

- A boolean
- A callable that will resolve to a boolean
- An existing `Specification object`

Examples:

```
use function BenTools\Specification\spec;
use function BenTools\Specification\group;
use function BenTools\Specification\not;

spec(true); // Specification met
not(false); // Specification met
not(spec(function () {
    return false;
})); // Specification met
group(not(spec(false))->or(true)); // Specification met
```

#### Group and Chain specifications

[](#group-and-chain-specifications)

A `Specification` object contains `and` and `or` methods that can be used to create composite specifications. You can also use the `group()` function which will behave like parenthesis:

```
use function BenTools\Specification\spec;
use function BenTools\Specification\group;
use function BenTools\Specification\not;

spec(true)
    ->and(
        group(
            spec(true)->or(false)
        )
        ->or(
            not(false)
        )
    ); // Specification met
```

#### Create your own specifications

[](#create-your-own-specifications)

Since the Specification Pattern is intended to test your business rules, you can extend the abstract `Specification` class to create your own objects instead of relying on simple booleans:

```
use BenTools\Specification\Exception\UnmetSpecificationException;
use BenTools\Specification\Specification;

class SpecProductInStock extends Specification
{
    /**
     * @var Product
     */
    private $product;

    /**
     * SpecProductInStock constructor.
     * @param Product $product
     */
    public function __construct(Product $product)
    {
        $this->product = $product;
        $this->label = sprintf('Product %s in stock verification', $product->getName());
    }

    /**
     * Validate the specification.
     * If the specification is unmet the implementation MUST throw an UnmetSpecificationException.
     *
     * @throws UnmetSpecificationException
     */
    public function validate(): void
    {
        if (false === $this->product->isInStock()) {
            throw new UnmetSpecificationException($this);
        }
    }
}
```

Advanced Example
----------------

[](#advanced-example)

Since the Specification Pattern is intended to test your business rules, you should better implement your own `Specification` classes.

See our [example](doc/Example.md) to get started.

Installation
------------

[](#installation)

```
composer require bentools/specification ^3.0

```

License
-------

[](#license)

MIT

###  Health Score

30

—

LowBetter than 62% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity15

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity64

Established project with proven stability

 Bus Factor1

Top contributor holds 100% 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.

###  Release Activity

Cadence

Every ~50 days

Recently: every ~98 days

Total

9

Last Release

2996d ago

Major Versions

1.0 → 2.02017-03-14

1.1 → 2.12017-03-20

1.1.1 → 3.0.x-dev2017-12-21

PHP version history (3 changes)1.0PHP &gt;=5.6

2.0PHP &gt;=7.0

3.0.x-devPHP &gt;=7.1

### Community

Maintainers

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

---

Top Contributors

[![bpolaszek](https://avatars.githubusercontent.com/u/5569077?v=4)](https://github.com/bpolaszek "bpolaszek (36 commits)")

###  Code Quality

TestsPHPUnit

Code StylePHP\_CodeSniffer

### Embed Badge

![Health badge](/badges/bentools-specification/health.svg)

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

###  Alternatives

[psalm/plugin-symfony

Psalm Plugin for Symfony

24114.3M219](/packages/psalm-plugin-symfony)[gurmanalexander/laravel-metrics

Managable metrics

143.0k](/packages/gurmanalexander-laravel-metrics)

PHPackages © 2026

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