PHPackages                             slava-basko/specification-php - 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. slava-basko/specification-php

ActiveLibrary

slava-basko/specification-php
=============================

Encapsulate your business decisions for readable, clear, maintainable purposes.

2.0.1(8mo ago)02.0k↓30.8%MITPHPPHP &gt;=5.5|^7|^8CI passing

Since Sep 4Pushed 7mo ago1 watchersCompare

[ Source](https://github.com/slava-basko/specification-php)[ Packagist](https://packagist.org/packages/slava-basko/specification-php)[ RSS](/packages/slava-basko-specification-php/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (7)Dependencies (2)Versions (9)Used By (0)

Specification Pattern
=====================

[](#specification-pattern)

Encapsulate your business decisions for readable, clear, and maintainable purposes. In simpler words: encapsulate your business's IF's and ELSE's, and speak with clients on the same language.

Read it if you are not familiar with Specification pattern \[\].

This library has no dependencies on any external libs and works on PHP 5.5+.

Install
-------

[](#install)

```
composer require slava-basko/specification-php
```

Usage
-----

[](#usage)

Let's imagine that we have the specification of an Adult Person.

```
class AdultUserSpecification extends AbstractSpecification
{
    /**
     * @param User $candidate
     * @return bool
     */
    public function isSatisfiedBy($candidate)
    {
        return $candidate->getAge() >= 18;
    }
}
```

Now let's check if the user is actually an adult.

```
$adultUserSpecification = new AdultUserSpecification();
$adultUserSpecification->isSatisfiedBy(new User(14)); // false
$adultUserSpecification->isSatisfiedBy(new User(20)); // true
```

Use `TypedSpecification` decorator/wrapper if you want typed specification.

```
$adultUserSpecification = new TypedSpecification(new AdultUserSpecification(), User::class);
$adultUserSpecification->isSatisfiedBy(new User(20)); // true
$adultUserSpecification->isSatisfiedBy('blah'); // InvalidArgumentException will be thrown
```

#### `TypedSpecification` VS `public function isSatisfiedBy(User $candidate)`

[](#typedspecification-vs-public-function-issatisfiedbyuser-candidate)

Of course, you can create your own specification interfaces with type hinting in `isSatisfiedBy`, but sooner or later you will see a lot of interfaces that are similar by 99%.

```
interface UserSpecification
{
    public function isSatisfiedBy(User $user);
}

interface ProductSpecification
{
    public function isSatisfiedBy(Product $product);
}

interface CartSpecification
{
    public function isSatisfiedBy(Cart $cart);
}

interface ParcelSpecification
{
    public function isSatisfiedBy(Parcel $parcel);
}
// etc.
```

Or you can use `TypedSpecification` decorator to achieve the same goal.

```
new TypedSpecification(new SomeUserSpecification(), User::class);
new TypedSpecification(new SomeProductSpecification(), Product::class);
new TypedSpecification(new SomeCartSpecification(), Cart::class);
new TypedSpecification(new SomeParcelSpecification(), Parcel::class);
```

#### Autocompletion

[](#autocompletion)

Use the doc-block type hinting in your end specifications for autocompletion, like `@param User $candidate`.

```
/**
 * @param User $candidate
 * @return bool
 */
public function isSatisfiedBy($candidate)
{
    return $candidate->someMethodThatWillBeAutocompletedInYourIDE();
}
```

`TypedSpecification` guaranty that `$candidate` will be an instance of `User` class, and doc-block `@param User $candidate` helps your IDE to autocomplete `$candidate` methods.

#### Composition

[](#composition)

This lib provides useful builtin specifications like `NotSpecification`, `AndSpecification`, `OrSpecification`, etc. ([https://en.wikipedia.org/wiki/Logical\_connective](https://en.wikipedia.org/wiki/Logical_connective)) that helps you to group up your specifications and create a new one.

```
$adultPersonSpecification = new AndSpecification([
    new AdultSpecification(),
    new OrSpecification([
        new MaleSpecification(),
        new FemaleSpecification(),
    ])
]);

$adultPersonSpecification->isSatisfiedBy($adultAlien); // false
// because only AdultSpecification was satisfied; assume we know age, and we don't know alien sex.
```

Here is another example that shows how highly composable specifications could be.

```
// Card of spades and not (two or three of spades), or (card of hearts and not (two or three of hearts))
$spec = new OrSpecification([
    new AndSpecification([
        new SpadesSpecification(),
        new NorSpecification([
            new PlayingCardSpecification(PlayingCard::SUIT_SPADES, PlayingCard::RANK_2),
            new PlayingCardSpecification(PlayingCard::SUIT_SPADES, PlayingCard::RANK_3)
        ])
    ]),
    new AndSpecification([
        new HeartsSpecification(),
        new NorSpecification([
            new PlayingCardSpecification(PlayingCard::SUIT_HEARTS, PlayingCard::RANK_2),
            new PlayingCardSpecification(PlayingCard::SUIT_HEARTS, PlayingCard::RANK_3)
        ])
    ]),
]);

$spec->isSatisfiedBy(new PlayingCard(PlayingCard::SUIT_SPADES, PlayingCard::RANK_4)); // true
$spec->isSatisfiedBy(new PlayingCard(PlayingCard::SUIT_SPADES, PlayingCard::RANK_2)); // false
```

#### Remainders

[](#remainders)

Method `isSatisfiedBy` returns `bool`, and sometimes in case of `false` you want to know what exactly has gone wrong. Use `remainderUnsatisfiedBy` method for that. It returns a remainder of unsatisfied specifications.

```
$parcel = [
    'value' => 20,
    'destination' => 'CA'
];

$trackableParcelSpecification = new AndSpecification([
    new HighValueParcelSpecification(),
    new OrSpecification([
        new DestinationCASpecification(),
        new DestinationUSSpecification(),
    ])
]);

if ($trackableParcelSpecification->isSatisfiedBy($parcel)) {
    // do something
} else {
    $remainderSpecification = $trackableParcelSpecification->remainderUnsatisfiedBy($parcel);
    // do something with $remainderSpecification
}

// $remainderSpecification is equal to
//
// AndSpecification([
//      HighValueParcelSpecification
// ]);
//
// because only DestinationXX satisfied
```

You can use `Utils` to convert it to useful array.

```
$remainder = Utils::flatten(Utils::toSnakeCase($trackableParcel->remainderUnsatisfiedBy($parcel)));
// $remainder is equal to ['high_value_parcel']
```

For example, you can use strings inside `$remainder` like `high_value_parcel` as a translation key to show meaningful error message.

License
-------

[](#license)

Use as you want. No liability or warranty from me. Can be considered as MIT.

###  Health Score

36

—

LowBetter than 82% of packages

Maintenance61

Regular maintenance activity

Popularity21

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 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 ~104 days

Recently: every ~156 days

Total

8

Last Release

258d ago

Major Versions

1.5.0 → 2.0.02025-04-04

PHP version history (2 changes)1.0.0PHP ~5.5|~7|~8.2

1.4.0PHP &gt;=5.5|^7|^8

### Community

Maintainers

![](https://www.gravatar.com/avatar/6a8e37b4c52b1cfe4f5d64f420d21875ebdeb53954d6859d6d663038b95ef3cc?d=identicon)[slava-basko](/maintainers/slava-basko)

---

Top Contributors

[![slava-basko](https://avatars.githubusercontent.com/u/4376953?v=4)](https://github.com/slava-basko "slava-basko (16 commits)")

---

Tags

specificationpattern

###  Code Quality

TestsPHPUnit

Code StylePHP\_CodeSniffer

### Embed Badge

![Health badge](/badges/slava-basko-specification-php/health.svg)

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

###  Alternatives

[swagger-api/swagger-ui

 Swagger UI is a collection of HTML, Javascript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API.

28.8k45.4M99](/packages/swagger-api-swagger-ui)[darkaonline/l5-swagger

OpenApi or Swagger integration to Laravel

2.9k34.0M112](/packages/darkaonline-l5-swagger)[phpspec/phpspec

Specification-oriented BDD framework for PHP 7.1+

1.9k36.7M3.1k](/packages/phpspec-phpspec)[league/pipeline

A plug and play pipeline implementation.

1.0k16.0M74](/packages/league-pipeline)[kphoen/rulerz

Powerful implementation of the Specification pattern

8831.3M6](/packages/kphoen-rulerz)[darkaonline/swagger-lume

OpenApi or Swagger integration to Lumen

3372.3M3](/packages/darkaonline-swagger-lume)

PHPackages © 2026

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