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;
+ }
+}