├── LICENSE ├── README.md ├── composer.json └── src ├── AndSpecification.php ├── AnyOfSpecification.php ├── NoneOfSpecification.php ├── NotSpecification.php ├── OneOfSpecification.php ├── OrSpecification.php └── Specification.php /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Hirofumi Tanigami 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Specification 2 | 3 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/tanigami/specification-php/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/tanigami/specification-php/?branch=master) 4 | [![Code Coverage](https://scrutinizer-ci.com/g/tanigami/specification-php/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/tanigami/specification-php/?branch=master) 5 | [![Build Status](https://scrutinizer-ci.com/g/tanigami/specification-php/badges/build.png?b=master)](https://scrutinizer-ci.com/g/tanigami/specification-php/build-status/master) 6 | 7 | Basic classes for [Specification pattern](https://en.wikipedia.org/wiki/Specification_pattern) in PHP. On top of the typical set of `and`, `or` and `not` specificaitons, `anyOf`, `oneOf`, `noneOf` specifications are proposed. 8 | 9 | This package is based on the implementation in [carlosbuenosvinos/ddd](https://github.com/dddinphp/ddd). 10 | 11 | ## Installation 12 | 13 | ``` 14 | $ composer require tanigami/specification 15 | ``` 16 | 17 | ## Usage example 18 | 19 | ```php 20 | isShipped(); 48 | } 49 | } 50 | 51 | class PaidOrderSpecification extends Specification 52 | { 53 | public function isSatisfiedBy($order): bool 54 | { 55 | return $order->isPaid(); 56 | } 57 | } 58 | 59 | class CancelledOrderSpecification extends Specification 60 | { 61 | public function isSatisfiedBy($order): bool 62 | { 63 | return $order->isCancelled(); 64 | } 65 | } 66 | 67 | $paid = new PaidOrderSpecification; 68 | $unshipped = new UnshippedOrderSpecification; 69 | $cancelled = new CancelledOrderSpecification; 70 | 71 | 72 | $paid->and($unshipped)->isSatisfiedBy(new Order); // => true 73 | (new OneOfSpecification($paid, $unshipped, $cancelled))->isSatisfiedBy(new Order); // => true 74 | ``` 75 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tanigami/specification", 3 | "description": "Basic classes for Specification pattern in PHP.", 4 | "type": "library", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Hirofumi Tanigami", 9 | "email": "tanigami@gmail.com", 10 | "homepage": "https://tanigami.wtf", 11 | "role": "Developer" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=7.0" 16 | }, 17 | "require-dev": { 18 | "phpunit/phpunit": "^6.4", 19 | "squizlabs/php_codesniffer": "^3.1" 20 | }, 21 | "autoload": { 22 | "psr-4": { 23 | "Tanigami\\Specification\\": "src/" 24 | } 25 | }, 26 | "autoload-dev": { 27 | "psr-4": { 28 | "Tanigami\\Specification\\": "tests/" 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/AndSpecification.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | class AndSpecification extends Specification 10 | { 11 | /** 12 | * @var Specification 13 | */ 14 | private $one; 15 | 16 | /** 17 | * @var Specification 18 | */ 19 | private $other; 20 | 21 | /** 22 | * @param Specification $one 23 | * @param Specification $other 24 | */ 25 | public function __construct(Specification $one, Specification $other) 26 | { 27 | $this->one = $one; 28 | $this->other = $other; 29 | } 30 | 31 | /** 32 | * @param T $object 33 | */ 34 | public function isSatisfiedBy($object): bool 35 | { 36 | return $this->one->isSatisfiedBy($object) && $this->other->isSatisfiedBy($object); 37 | } 38 | 39 | public function whereExpression(string $alias): string 40 | { 41 | return sprintf( 42 | '(%s) AND (%s)', 43 | $this->one()->whereExpression($alias), 44 | $this->other()->whereExpression($alias) 45 | ); 46 | } 47 | 48 | /** 49 | * @return Specification 50 | */ 51 | public function one(): Specification 52 | { 53 | return $this->one; 54 | } 55 | 56 | /** 57 | * @return Specification 58 | */ 59 | public function other(): Specification 60 | { 61 | return $this->other; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/AnyOfSpecification.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | class AnyOfSpecification extends Specification 10 | { 11 | /** 12 | * @var Specification[] 13 | */ 14 | private $specifications; 15 | 16 | /** 17 | * @param Specification ...$specifications 18 | */ 19 | public function __construct(Specification ...$specifications) 20 | { 21 | $this->specifications = $specifications; 22 | } 23 | 24 | /** 25 | * @param T $object 26 | */ 27 | public function isSatisfiedBy($object): bool 28 | { 29 | foreach ($this->specifications as $specification) { 30 | if (!$specification->isSatisfiedBy($object)) { 31 | return false; 32 | } 33 | } 34 | 35 | return true; 36 | } 37 | 38 | public function whereExpression(string $alias): string 39 | { 40 | return implode(' AND ', array_map( 41 | static function (Specification $specification) use ($alias) { 42 | return '(' . $specification->whereExpression($alias) . ')'; 43 | }, 44 | $this->specifications 45 | )); 46 | } 47 | 48 | /** 49 | * @return Specification[] 50 | */ 51 | public function specifications(): array 52 | { 53 | return $this->specifications; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/NoneOfSpecification.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | class NoneOfSpecification extends Specification 10 | { 11 | /** 12 | * @var Specification[] 13 | */ 14 | private $specifications; 15 | 16 | /** 17 | * @param Specification ...$specifications 18 | */ 19 | public function __construct(Specification ...$specifications) 20 | { 21 | $this->specifications = $specifications; 22 | } 23 | 24 | /** 25 | * @param T $object 26 | */ 27 | public function isSatisfiedBy($object): bool 28 | { 29 | foreach ($this->specifications as $specification) { 30 | if ($specification->isSatisfiedBy($object)) { 31 | return false; 32 | } 33 | } 34 | 35 | return true; 36 | } 37 | 38 | /** 39 | * @return Specification[] 40 | */ 41 | public function specifications(): array 42 | { 43 | return $this->specifications; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/NotSpecification.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | class NotSpecification extends Specification 10 | { 11 | /** 12 | * @var Specification 13 | */ 14 | private $specification; 15 | 16 | /** 17 | * @param Specification $specification 18 | */ 19 | public function __construct(Specification $specification) 20 | { 21 | $this->specification = $specification; 22 | } 23 | 24 | /** 25 | * @param T $object 26 | */ 27 | public function isSatisfiedBy($object): bool 28 | { 29 | return !$this->specification->isSatisfiedBy($object); 30 | } 31 | 32 | /** 33 | * @return Specification 34 | */ 35 | public function specification(): Specification 36 | { 37 | return $this->specification; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/OneOfSpecification.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | class OneOfSpecification extends Specification 10 | { 11 | /** 12 | * @var Specification[] 13 | */ 14 | private $specifications; 15 | 16 | /** 17 | * @param Specification ...$specifications 18 | */ 19 | public function __construct(Specification ...$specifications) 20 | { 21 | $this->specifications = $specifications; 22 | } 23 | 24 | /** 25 | * @param T $object 26 | */ 27 | public function isSatisfiedBy($object): bool 28 | { 29 | foreach ($this->specifications as $specification) { 30 | if ($specification->isSatisfiedBy($object)) { 31 | return true; 32 | } 33 | } 34 | 35 | return false; 36 | } 37 | 38 | public function whereExpression(string $alias): string 39 | { 40 | return implode(' OR ', array_map( 41 | static function (Specification $specification) use ($alias) { 42 | return '(' . $specification->whereExpression($alias) . ')'; 43 | }, 44 | $this->specifications 45 | )); 46 | } 47 | 48 | /** 49 | * @return Specification[] 50 | */ 51 | public function specifications(): array 52 | { 53 | return $this->specifications; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/OrSpecification.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | class OrSpecification extends Specification 10 | { 11 | /** 12 | * @var Specification 13 | */ 14 | private $one; 15 | 16 | /** 17 | * @var Specification 18 | */ 19 | private $other; 20 | 21 | /** 22 | * @param Specification $one 23 | * @param Specification $other 24 | */ 25 | public function __construct(Specification $one, Specification $other) 26 | { 27 | $this->one = $one; 28 | $this->other = $other; 29 | } 30 | 31 | /** 32 | * @param T $object 33 | */ 34 | public function isSatisfiedBy($object): bool 35 | { 36 | return $this->one->isSatisfiedBy($object) || $this->other->isSatisfiedBy($object); 37 | } 38 | 39 | public function whereExpression(string $alias): string 40 | { 41 | return sprintf( 42 | '(%s) OR (%s)', 43 | $this->one()->whereExpression($alias), 44 | $this->other()->whereExpression($alias) 45 | ); 46 | } 47 | 48 | /** 49 | * @return Specification 50 | */ 51 | public function one(): Specification 52 | { 53 | return $this->one; 54 | } 55 | 56 | /** 57 | * @return Specification 58 | */ 59 | public function other(): Specification 60 | { 61 | return $this->other; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Specification.php: -------------------------------------------------------------------------------- 1 | $specification 24 | * @return AndSpecification 25 | */ 26 | public function and(Specification $specification): AndSpecification 27 | { 28 | return new AndSpecification($this, $specification); 29 | } 30 | 31 | /** 32 | * @param Specification $specification 33 | * @return OrSpecification 34 | */ 35 | public function or(Specification $specification): OrSpecification 36 | { 37 | return new OrSpecification($this, $specification); 38 | } 39 | 40 | /** 41 | * @return NotSpecification 42 | */ 43 | public function not(): NotSpecification 44 | { 45 | return new NotSpecification($this); 46 | } 47 | } 48 | --------------------------------------------------------------------------------