brenoroosevelt / php-specifications
Implementation of Specification Pattern for PHP
dev-main
2021-10-15 13:50 UTC
Requires
- php: >=7.1
- respect/validation: ^2
Requires (Dev)
- phpstan/phpstan: ^0.12.90
- phpunit/phpunit: ^9.0
- squizlabs/php_codesniffer: 3.5.*
This package is auto-updated.
Last update: 2025-03-15 21:15:30 UTC
README
Implementation of Specification Pattern in PHP for general purpose.
Requirements
- PHP >= 7.1
Install
composer require brenoroosevelt/php-specifications
Definition
Specifications are classes that implements the interface Specification
:
<?php declare(strict_types=1); namespace BrenoRoosevelt\Specification; interface Specification { public function isSatisfiedBy($candidate): bool; }
Constraints
The functions below evaluate a candidate using the following specifications:
Function | Specification |
---|---|
isTrue() |
is true (using === true ) |
isFalse() |
is false (using === false ) |
isNull() |
is null (using is_null() ) |
isNotNull() |
is not null !is_null() |
isEmpty() |
is empty (using empty() ) |
isNotEmpty() |
not empty (using empty() ) |
isInstanceOf($classOrObject) |
is instance of (using instanceof ) |
isType($type) |
is type (using gettype() ) |
equals($value) |
equals to the given value (using == ) |
notEquals($value) |
not equals to the given value (using != ) |
same($value) |
identical to the given value (using === ) |
notSame($value) |
not identical to the given value (using !== ) |
greaterThan($value) |
greater the given value (using > ) |
greaterThanEqual($value) |
greater than or equal to the given value (using >= ) |
lessThan($value) |
less than to the given value (using < ) |
lessThanEqual($value) |
less than or equal to to the given value (using <= ) |
between($i, $f) |
>= and <= |
startsWith($prefix, bool $case = true) |
string starts with another given string |
endsWith($suffix, bool $case = true) |
string ends with another given string |
contains($value, bool $strict = true) |
evaluates if the candidate contains a given value (can be used for strings or arrays) |
in($values, bool $trict = true) |
evaluates if the candidate exists in a list |
true() |
Always true |
false() |
Always false |
spec(Specification $spec) |
proxy another specification |
rule($rule) |
callable or specification class name |
Operators
Logic | Operator |
---|---|
class Not(Specification $specification) |
NOT |
class AllOf(Specification ...$specification) |
AND |
class AnyOf(Specification ...$specification) |
OR |
class OneOf(Specification ...$specification) |
XOR |
class NoneOf(Specification ...$specification) |
check that none of the specifications match |
Chaining Specifications
Specifications can be chained using the following function:
Function | Operator | Example |
---|---|---|
allOf() |
AND |
allOf()->isNull()->isNotEmpty() ... |
anyOf() |
OR |
anyOf()->isNull()->equals(2) ... |
oneOf() |
XOR |
oneOf()->greaterThan(1)->isEmpty() ... |
noneOf() |
none of | noneOf()->isNotNull()->contains('active') ... |
Chained specifications will be evaluated with the corresponding operator.
Transverse Specifications for iterables
Often used with collections, these especial specifications iterate candidates and evaluate their elements.
Function | Operator | Example |
---|---|---|
all(Specification $spcification) |
Check that all of elements match with the specification | all(property('age', lessThan(5))) |
any(Specification $spcification) |
Check that any of elements match with the specification | any(method('getAge', lessThan(5))) |
atLeast(int $count, Specification $spcification) |
Check that at least the count number of elements match with the specification | atLeast(2, lessThan(5)) |
atMost(int $count, Specification $spcification) |
Check that at most the count number of elements match with the specification | atMost(5, length(equals(10))) |
exactly(int $count, Specification $spcification) |
Check that exactly the count number of elements match with the specification | exactly(3, key('age', lessThan(5))) |
none(Specification $spcification) |
Check that none of elements match with the specification | none(key('age', between(5, 15))) |
Selectors
These special specifications extract candidate values and then evaluate the specification.
Function | Operator | Example |
---|---|---|
key(string $key, Specification $spcification) |
Extract value using key/index | key('age', greaterThan(10) |
method(string/array $method, Specification $spcification) |
Extract value using method | method(['getPrice', $tax]', lessThan(200.50) |
property(string $property, Specification $spcification) |
Extract value from property | property('name', length(between(2, 10)) |
count(Specification $spcification) |
Extract count from countable candidates | count(equals(10)) |
length(Specification $spcification) |
Extract the length from string candidates | length(equals(10)) |
Creating Specifications
<?php class RecentUser implements Specification { private $daysAgo; public function __construct(int $daysAgo = 15) { $this->daysAgo = $daysAgo; } public function isSatisfiedBy($candidate): bool { $daysAgo = (new DateTimeImmutable())->modify(sprintf("-%s days", $this->daysAgo)); return $candidate instanceof User && $candidate->createdAt() >= $daysAgo; } }
<?php $user = new User(/** ... */); (new RecentUser(30))->isSatisfiedBy($user); // (bool) rule(RecentUser::class, 30)->isSatisfiedBy($user); // (bool) anyOf()->rule(RecentUser::class)->method('getCategory', in(['premium']))->isSatisfiedBy($user); // (bool)
Inline Specifications
<?php rule(fn($candidate) => $candidate->isActive());