diff --git a/Behavioral/ChainOfResponsibilities/Handler.php b/Behavioral/ChainOfResponsibilities/Handler.php index 679734e..fe4e18b 100644 --- a/Behavioral/ChainOfResponsibilities/Handler.php +++ b/Behavioral/ChainOfResponsibilities/Handler.php @@ -2,77 +2,42 @@ namespace DesignPatterns\Behavioral\ChainOfResponsibilities; -/** - * Handler is a generic handler in the chain of responsibilities. - * - * Yes you could have a lighter CoR with a simpler handler but if you want your CoR - * to be extendable and decoupled, it's a better idea to do things like that in real - * situations. Usually, a CoR is meant to be changed everytime and evolves, that's - * why we slice the workflow in little bits of code. - */ +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + abstract class Handler { /** - * @var Handler + * @var Handler|null */ private $successor = null; - /** - * Append a responsibility to the end of chain. - * - * A prepend method could be done with the same spirit - * - * You could also send the successor in the constructor but in PHP that is a - * bad idea because you have to remove the type-hint of the parameter because - * the last handler has a null successor. - * - * And if you override the constructor, that Handler can no longer have a - * successor. One solution is to provide a NullObject (see pattern). - * It is more preferable to keep the constructor "free" to inject services - * you need with the DiC of symfony2 for example. - * - * @param Handler $handler - */ - final public function append(Handler $handler) + public function __construct(Handler $handler = null) { - if (is_null($this->successor)) { - $this->successor = $handler; - } else { - $this->successor->append($handler); - } + $this->successor = $handler; } /** - * Handle the request. - * * This approach by using a template method pattern ensures you that - * each subclass will not forget to call the successor. Besides, the returned - * boolean value indicates you if the request have been processed or not. + * each subclass will not forget to call the successor * - * @param Request $req + * @param RequestInterface $request * - * @return bool + * @return string|null */ - final public function handle(Request $req) + final public function handle(RequestInterface $request) { - $req->forDebugOnly = get_called_class(); - $processed = $this->processing($req); - if (!$processed) { + $processed = $this->processing($request); + + if ($processed === null) { // the request has not been processed by this handler => see the next - if (!is_null($this->successor)) { - $processed = $this->successor->handle($req); + if ($this->successor !== null) { + $processed = $this->successor->handle($request); } } return $processed; } - /** - * Each concrete handler has to implement the processing of the request. - * - * @param Request $req - * - * @return bool true if the request has been processed - */ - abstract protected function processing(Request $req); + abstract protected function processing(RequestInterface $request); } diff --git a/Behavioral/ChainOfResponsibilities/README.rst b/Behavioral/ChainOfResponsibilities/README.rst index b3f47b9..a3e3b66 100644 --- a/Behavioral/ChainOfResponsibilities/README.rst +++ b/Behavioral/ChainOfResponsibilities/README.rst @@ -33,12 +33,6 @@ Code You can also find these code on `GitHub`_ -Request.php - -.. literalinclude:: Request.php - :language: php - :linenos: - Handler.php .. literalinclude:: Handler.php diff --git a/Behavioral/ChainOfResponsibilities/Request.php b/Behavioral/ChainOfResponsibilities/Request.php deleted file mode 100644 index 68bf24e..0000000 --- a/Behavioral/ChainOfResponsibilities/Request.php +++ /dev/null @@ -1,19 +0,0 @@ -data = $data; - } - - protected function processing(Request $req) - { - if ('get' === $req->verb) { - if (array_key_exists($req->key, $this->data)) { - // the handler IS responsible and then processes the request - $req->response = $this->data[$req->key]; - // instead of returning true, I could return the value but it proves - // to be a bad idea. What if the value IS "false" ? - return true; - } - } - - return false; - } -} diff --git a/Behavioral/ChainOfResponsibilities/Responsible/HttpInMemoryCacheHandler.php b/Behavioral/ChainOfResponsibilities/Responsible/HttpInMemoryCacheHandler.php new file mode 100644 index 0000000..9391ceb --- /dev/null +++ b/Behavioral/ChainOfResponsibilities/Responsible/HttpInMemoryCacheHandler.php @@ -0,0 +1,45 @@ +data = $data; + } + + /** + * @param RequestInterface $request + * + * @return string|null + */ + protected function processing(RequestInterface $request) + { + $key = sprintf( + '%s?%s', + $request->getUri()->getPath(), + $request->getUri()->getQuery() + ); + + if ($request->getMethod() == 'GET' && isset($this->data[$key])) { + return $this->data[$key]; + } + + return null; + } +} diff --git a/Behavioral/ChainOfResponsibilities/Responsible/SlowDatabaseHandler.php b/Behavioral/ChainOfResponsibilities/Responsible/SlowDatabaseHandler.php new file mode 100644 index 0000000..975ed42 --- /dev/null +++ b/Behavioral/ChainOfResponsibilities/Responsible/SlowDatabaseHandler.php @@ -0,0 +1,21 @@ +data = $data; - } - - protected function processing(Request $req) - { - if ('get' === $req->verb) { - if (array_key_exists($req->key, $this->data)) { - $req->response = $this->data[$req->key]; - - return true; - } - } - - return false; - } -} diff --git a/Behavioral/ChainOfResponsibilities/Tests/ChainTest.php b/Behavioral/ChainOfResponsibilities/Tests/ChainTest.php index 62d1172..296a0c3 100644 --- a/Behavioral/ChainOfResponsibilities/Tests/ChainTest.php +++ b/Behavioral/ChainOfResponsibilities/Tests/ChainTest.php @@ -2,80 +2,50 @@ namespace DesignPatterns\Behavioral\ChainOfResponsibilities\Tests; -use DesignPatterns\Behavioral\ChainOfResponsibilities\Request; -use DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible; -use DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\FastStorage; -use DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\SlowStorage; +use DesignPatterns\Behavioral\ChainOfResponsibilities\Handler; +use DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\HttpInMemoryCacheHandler; +use DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\SlowDatabaseHandler; -/** - * ChainTest tests the CoR. - */ class ChainTest extends \PHPUnit_Framework_TestCase { /** - * @var FastStorage + * @var Handler */ - protected $chain; + private $chain; protected function setUp() { - $this->chain = new FastStorage(array('bar' => 'baz')); - $this->chain->append(new SlowStorage(array('bar' => 'baz', 'foo' => 'bar'))); - } - - public function makeRequest() - { - $request = new Request(); - $request->verb = 'get'; - - return array( - array($request), + $this->chain = new HttpInMemoryCacheHandler( + ['/foo/bar?index=1' => 'Hello In Memory!'], + new SlowDatabaseHandler() ); } - /** - * @dataProvider makeRequest - */ - public function testFastStorage($request) + public function testCanRequestKeyInFastStorage() { - $request->key = 'bar'; - $ret = $this->chain->handle($request); + $uri = $this->createMock('Psr\Http\Message\UriInterface'); + $uri->method('getPath')->willReturn('/foo/bar'); + $uri->method('getQuery')->willReturn('index=1'); - $this->assertTrue($ret); - $this->assertObjectHasAttribute('response', $request); - $this->assertEquals('baz', $request->response); - // despite both handle owns the 'bar' key, the FastStorage is responding first - $className = 'DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\FastStorage'; - $this->assertEquals($className, $request->forDebugOnly); + $request = $this->createMock('Psr\Http\Message\RequestInterface'); + $request->method('getMethod') + ->willReturn('GET'); + $request->method('getUri')->willReturn($uri); + + $this->assertEquals('Hello In Memory!', $this->chain->handle($request)); } - /** - * @dataProvider makeRequest - */ - public function testSlowStorage($request) + public function testCanRequestKeyInSlowStorage() { - $request->key = 'foo'; - $ret = $this->chain->handle($request); + $uri = $this->createMock('Psr\Http\Message\UriInterface'); + $uri->method('getPath')->willReturn('/foo/baz'); + $uri->method('getQuery')->willReturn(''); - $this->assertTrue($ret); - $this->assertObjectHasAttribute('response', $request); - $this->assertEquals('bar', $request->response); - // FastStorage has no 'foo' key, the SlowStorage is responding - $className = 'DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\SlowStorage'; - $this->assertEquals($className, $request->forDebugOnly); - } + $request = $this->createMock('Psr\Http\Message\RequestInterface'); + $request->method('getMethod') + ->willReturn('GET'); + $request->method('getUri')->willReturn($uri); - /** - * @dataProvider makeRequest - */ - public function testFailure($request) - { - $request->key = 'kurukuku'; - $ret = $this->chain->handle($request); - - $this->assertFalse($ret); - // the last responsible : - $className = 'DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\SlowStorage'; - $this->assertEquals($className, $request->forDebugOnly); + $this->assertEquals('Hello World!', $this->chain->handle($request)); } } diff --git a/composer.json b/composer.json index 3bac6dc..416f1cc 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,8 @@ ], "minimum-stability": "stable", "require": { - "php": ">=7.0" + "php": ">=7.0", + "psr/http-message": "^1.0" }, "require-dev": { "phpunit/phpunit": ">=5.5.4", diff --git a/composer.lock b/composer.lock index 2f6aac1..93cae18 100644 --- a/composer.lock +++ b/composer.lock @@ -4,9 +4,60 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "fc06bf31eb6cf4b21a7923e186abfb9d", - "content-hash": "c829a84cbe749d776139a3e9ebdd3094", - "packages": [], + "hash": "61109e150871360cd44739b2a6bea702", + "content-hash": "53848b7fc8e374b8e407799bb47d6917", + "packages": [ + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06 14:39:51" + } + ], "packages-dev": [ { "name": "doctrine/instantiator", @@ -558,24 +609,24 @@ }, { "name": "phpunit/phpunit", - "version": "5.5.4", + "version": "5.5.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "3e6e88e56c912133de6e99b87728cca7ed70c5f5" + "reference": "a57126dc681b08289fef6ac96a48e30656f84350" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3e6e88e56c912133de6e99b87728cca7ed70c5f5", - "reference": "3e6e88e56c912133de6e99b87728cca7ed70c5f5", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a57126dc681b08289fef6ac96a48e30656f84350", + "reference": "a57126dc681b08289fef6ac96a48e30656f84350", "shasum": "" }, "require": { "ext-dom": "*", "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", "myclabs/deep-copy": "~1.3", "php": "^5.6 || ^7.0", "phpspec/prophecy": "^1.3.1", @@ -597,7 +648,12 @@ "conflict": { "phpdocumentor/reflection-docblock": "3.0.2" }, + "require-dev": { + "ext-pdo": "*" + }, "suggest": { + "ext-tidy": "*", + "ext-xdebug": "*", "phpunit/php-invoker": "~1.1" }, "bin": [ @@ -632,7 +688,7 @@ "testing", "xunit" ], - "time": "2016-08-26 07:11:44" + "time": "2016-09-21 14:40:13" }, { "name": "phpunit/phpunit-mock-objects",