diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8c36dab --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +/.gitattributes export-ignore +/.gitignore export-ignore +/.scrutinizer.yml export-ignore +/tests export-ignore +/phpcs.xml export-ignore +/phpunit.xml.dist export-ignore \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..82ee382 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/build/ +/vendor/ +/composer.lock +/phpunit.xml +.idea +.DS_Store \ No newline at end of file diff --git a/.scrutinizer.yml b/.scrutinizer.yml new file mode 100644 index 0000000..22d659d --- /dev/null +++ b/.scrutinizer.yml @@ -0,0 +1,15 @@ +checks: + php: + code_rating: true + duplication: true +build: + tests: + override: + - + command: 'vendor/bin/phpunit --coverage-clover=build/coverage/log/coverage.xml' + coverage: + file: 'build/coverage/log/coverage.xml' + format: 'clover' + - phpcs-run --ignore=*/tests/*,*/vendor/* +filter: + paths: ['src/'] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a2d8979 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Hirofumi Tanigami + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index c45c154..2e9e490 100644 --- a/README.md +++ b/README.md @@ -1 +1,58 @@ -specification-php +# PHP Specification + +[![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) +[![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) +[![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) + +Basic classes for [Specification pattern](https://en.wikipedia.org/wiki/Specification_pattern) in PHP. + +This package is based on the implementation in [carlosbuenosvinos/ddd](https://github.com/dddinphp/ddd). + +# Installation + +``` +$ composer require tanigami/specification +``` + +## Usage example + +```php +isShipped(); + } +} + +class PaidOrderSpecification extends Specification +{ + public function isSatisfiedBy($order): bool + { + return $order->isPaid(); + } +} + +$paid = new PaidOrderSpecification; +$unshipped = new UnshippedOrderSpecification; + +$paid->and($unshipped)->isSatisfiedBy(new Order); // => true + +``` diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..ff59150 --- /dev/null +++ b/composer.json @@ -0,0 +1,30 @@ +{ + "name": "tanigami/specification", + "description": "Basic classes for Specification pattern in PHP.", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Hirofumi Tanigami", + "email": "tanigami@gmail.com", + "homepage": "https://tanigami.wtf", + "role": "Developer" + } + ], + "require": { + "squizlabs/php_codesniffer": "^3.1" + }, + "require-dev": { + "phpunit/phpunit": "^6.4" + }, + "autoload": { + "psr-4": { + "Tanigami\\Specification\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Tanigami\\Specification\\": "tests/" + } + } +} diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..40d0531 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,206 @@ + + + The PSR-2 coding standard. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..c76eb59 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,22 @@ + + + + + ./tests/ + + + + + ./src/ + + + \ No newline at end of file diff --git a/src/AndSpecification.php b/src/AndSpecification.php new file mode 100644 index 0000000..a8de441 --- /dev/null +++ b/src/AndSpecification.php @@ -0,0 +1,34 @@ +one = $one; + $this->other = $other; + } + + /** + * {@inheritdoc} + */ + public function isSatisfiedBy($object): bool + { + return $this->one->isSatisfiedBy($object) && $this->other->isSatisfiedBy($object); + } +} diff --git a/src/NotSpecification.php b/src/NotSpecification.php new file mode 100644 index 0000000..c7df84a --- /dev/null +++ b/src/NotSpecification.php @@ -0,0 +1,28 @@ +specification = $specification; + } + + /** + * @param mixed $object + * @return bool + */ + public function isSatisfiedBy($object): bool + { + return !$this->specification->isSatisfiedBy($object); + } +} diff --git a/src/OrSpecification.php b/src/OrSpecification.php new file mode 100644 index 0000000..9ad7b58 --- /dev/null +++ b/src/OrSpecification.php @@ -0,0 +1,34 @@ +one = $one; + $this->other = $other; + } + + /** + * {@inheritdoc} + */ + public function isSatisfiedBy($object): bool + { + return $this->one->isSatisfiedBy($object) || $this->other->isSatisfiedBy($object); + } +} diff --git a/src/Specification.php b/src/Specification.php new file mode 100644 index 0000000..7249808 --- /dev/null +++ b/src/Specification.php @@ -0,0 +1,38 @@ +assertTrue($trueSpec->isSatisfiedBy(new stdClass)); + $this->assertFalse($falseSpec->isSatisfiedBy(new stdClass)); + } + + public function testNotSpecification() + { + $trueSpec = new FakeSpecification(true); + $falseSpec = new FakeSpecification(false); + $notTrueSpec = $trueSpec->not(); + $notFalseSpec = $falseSpec->not(); + $this->assertFalse($notTrueSpec->isSatisfiedBy(new stdClass)); + $this->assertTrue($notFalseSpec->isSatisfiedBy(new stdClass)); + } + + public function testAndSpecification() + { + $trueSpec = new FakeSpecification(true); + $falseSpec = new FakeSpecification(false); + $trueAndTrueSpec = $trueSpec->and($trueSpec); + $trueAndFalseSpec = $trueSpec->and($falseSpec); + $this->assertTrue($trueAndTrueSpec->isSatisfiedBy(new stdClass)); + $this->assertFalse($trueAndFalseSpec->isSatisfiedBy(new stdClass)); + } + + public function testOrSpecification() + { + $trueSpec = new FakeSpecification(true); + $falseSpec = new FakeSpecification(false); + $trueOrTrueSpec = $trueSpec->or($trueSpec); + $trueOrFalseSpec = $trueSpec->or($falseSpec); + $this->assertTrue($trueOrTrueSpec->isSatisfiedBy(new stdClass)); + $this->assertTrue($trueOrFalseSpec->isSatisfiedBy(new stdClass)); + } +} + +class FakeSpecification extends Specification +{ + private $bool; + + public function __construct(bool $bool) + { + $this->bool = $bool; + } + + public function isSatisfiedBy($object): bool + { + return $this->bool; + } +}