From 2f8f2300ec759fbab81ed346969e74a9f3435009 Mon Sep 17 00:00:00 2001 From: Michelangelo van Dam Date: Sat, 6 Apr 2019 20:10:33 +0200 Subject: [PATCH 01/10] Preparing for PSR-7 and PSR-18 compliance Adding `psr/http-message` ([PSR-7](https://www.php-fig.org/psr/psr-7/)) and `psr/http-client` ([PSR-18](https://www.php-fig.org/psr/psr-18/)) as requirements in `composer.json`. --- composer.json | 4 +++- composer.lock | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 71f5107..e31396a 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,9 @@ "type": "library", "require": { "php": ">= 7.2", - "guzzlehttp/guzzle": "^6.3" + "guzzlehttp/guzzle": "^6.3", + "psr/http-message": "^1.0", + "psr/http-client": "^1.0" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 1c671b6..6e7b924 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c317ead1b5a78777df764d1d4a8c3b83", + "content-hash": "c19b2b959fe3c148eac9f4f0ecbe8e5f", "packages": [ { "name": "guzzlehttp/guzzle", @@ -187,6 +187,55 @@ ], "time": "2017-03-20T17:10:46+00:00" }, + { + "name": "psr/http-client", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "496a823ef742b632934724bf769560c2a5c7c44e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/496a823ef742b632934724bf769560c2a5c7c44e", + "reference": "496a823ef742b632934724bf769560c2a5c7c44e", + "shasum": "" + }, + "require": { + "php": "^7.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "time": "2018-10-30T23:29:13+00:00" + }, { "name": "psr/http-message", "version": "1.0.1", From 94699c103ae2c46baabc123021b832a4f2f19a98 Mon Sep 17 00:00:00 2001 From: Michelangelo van Dam Date: Sun, 7 Apr 2019 00:17:36 +0200 Subject: [PATCH 02/10] More PSR additions plus automation in composer.json - For Guzzle we use `ricardofiorani/guzzle-psr18-adapter` to allow us using it as a PSR-18 compliant HttpClient - We also implement PSR-17 with `psr/http-factory` for when we need it - We also provide a couple of scripts that will execute `phpcs`, `phpunit` and `infection` in one go: `composer check` --- composer.json | 18 +++++++- composer.lock | 111 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 126 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index e31396a..b05b418 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,9 @@ "php": ">= 7.2", "guzzlehttp/guzzle": "^6.3", "psr/http-message": "^1.0", - "psr/http-client": "^1.0" + "psr/http-client": "^1.0", + "ricardofiorani/guzzle-psr18-adapter": "^1.0", + "psr/http-factory": "^1.0" }, "autoload": { "psr-4": { @@ -30,5 +32,17 @@ "email": "dragonbe+github@gmail.com" } ], - "minimum-stability": "stable" + "minimum-stability": "stable", + "scripts": { + "check": [ + "@cs-check", + "@test", + "@infection" + ], + "cs-check": "phpcs", + "cs-fix": "phpcbf", + "test": "phpunit --colors=always", + "test-coverage": "phpunit --colors=always --coverage-clover clover.xml", + "infection": "infection --only-covered" + } } diff --git a/composer.lock b/composer.lock index 6e7b924..974f3ad 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c19b2b959fe3c148eac9f4f0ecbe8e5f", + "content-hash": "f92dd5bec8f0701992a6e12390dbf559", "packages": [ { "name": "guzzlehttp/guzzle", @@ -236,6 +236,58 @@ ], "time": "2018-10-30T23:29:13+00:00" }, + { + "name": "psr/http-factory", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "378bfe27931ecc54ff824a20d6f6bfc303bbd04c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/378bfe27931ecc54ff824a20d6f6bfc303bbd04c", + "reference": "378bfe27931ecc54ff824a20d6f6bfc303bbd04c", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.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 interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "time": "2018-07-30T21:54:04+00:00" + }, { "name": "psr/http-message", "version": "1.0.1", @@ -285,6 +337,63 @@ "response" ], "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "ricardofiorani/guzzle-psr18-adapter", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/ricardofiorani/guzzle-psr18-adapter.git", + "reference": "380e1ee4a4efcfd31f5defd2f21217cc3def6f59" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ricardofiorani/guzzle-psr18-adapter/zipball/380e1ee4a4efcfd31f5defd2f21217cc3def6f59", + "reference": "380e1ee4a4efcfd31f5defd2f21217cc3def6f59", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.3", + "php": "^7.1", + "psr/http-client": "^1.0" + }, + "provide": { + "psr/http-client": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.3", + "spryker/code-sniffer": "^0.12.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "RicardoFiorani\\GuzzlePsr18Adapter\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ricardo Fiorani", + "email": "ricardo.fiorani@gmail.com", + "homepage": "https://github.com/ricardofiorani", + "role": "Developer" + } + ], + "description": "A Guzzle PSR-18 adapter", + "homepage": "https://github.com/ricardofiorani/guzzle-psr18-adapter", + "keywords": [ + "Guzzle", + "psr-18" + ], + "time": "2018-12-11T09:10:08+00:00" } ], "packages-dev": [ From 03c6e82c82167b105fda5fcf65a4de3afc22c05a Mon Sep 17 00:00:00 2001 From: Michelangelo van Dam Date: Sun, 7 Apr 2019 00:18:20 +0200 Subject: [PATCH 03/10] Added HibpInterface We had no Interface yet :facepalm: --- src/HibpInterface.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/HibpInterface.php diff --git a/src/HibpInterface.php b/src/HibpInterface.php new file mode 100644 index 0000000..ea52091 --- /dev/null +++ b/src/HibpInterface.php @@ -0,0 +1,17 @@ + Date: Sun, 7 Apr 2019 00:20:54 +0200 Subject: [PATCH 04/10] Preparing tests for PSR compliance Ensuring we're implementing the right PSR's before we move forward --- tests/HibpTest.php | 45 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/tests/HibpTest.php b/tests/HibpTest.php index d867d0f..3fb8921 100644 --- a/tests/HibpTest.php +++ b/tests/HibpTest.php @@ -5,14 +5,16 @@ use Dragonbe\Hibp\Hibp; use Dragonbe\Hibp\HibpFactory; -use GuzzleHttp\Client; -use GuzzleHttp\ClientInterface; -use GuzzleHttp\Exception\ConnectException; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; use PHPUnit\Framework\TestCase; +use Psr\Http\Client\ClientInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use RicardoFiorani\GuzzlePsr18Adapter\Client; +use RicardoFiorani\GuzzlePsr18Adapter\Exception\ClientException; class HibpTest extends TestCase { @@ -54,14 +56,20 @@ public function testClassThrowsTypeErrorWhenWrongArgumentIsProvided() public function testExceptionIsThrownWhenServiceNotAvailable() { $mockHandler = new MockHandler([ - new ConnectException("Error Communicating with Server", new Request('GET', 'test')) + new ClientException( + 'Error Communicating with Server', + new Request('GET', 'test'), + new Response() + ) ]); $handlerStack = HandlerStack::create($mockHandler); $client = new Client(['handler' => $handlerStack]); + $request = new Request('GET', 'test'); + $response = new Response(); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Cannot connect to HIBP API'); - $hibp = new Hibp($client); + $hibp = new Hibp($client, $request, $response); $hibp->isPwnedPassword('foo'); $this->fail('Expected exception was not thrown'); } @@ -79,7 +87,7 @@ public function testExceptionIsThrownWhenRateLimitIsReached() $passwordFile = 'hit_rate_limit.txt'; $password = 'password'; $hibp = $this->createHibpWithMockedClientResponse(__DIR__ . '/_files/' . $passwordFile); - $this->expectException(\DomainException::class); + $this->expectException(\RuntimeException::class); $hibp->isPwnedPassword($password); $this->fail('Expected exception for hit rate was not triggered'); } @@ -97,7 +105,7 @@ public function testExceptionIsThrownWhenApiNotFound() $passwordFile = 'not_found.txt'; $password = 'password'; $hibp = $this->createHibpWithMockedClientResponse(__DIR__ . '/_files/' . $passwordFile); - $this->expectException(\DomainException::class); + $this->expectException(\RuntimeException::class); $hibp->isPwnedPassword($password); $this->fail('Expected exception for hit rate was not triggered'); } @@ -146,9 +154,15 @@ public function testPasswordHashRangeReturnsString() $client = $this->getMockBuilder(ClientInterface::class) ->getMockForAbstractClass(); + $request = $this->getMockBuilder(RequestInterface::class) + ->getMockForAbstractClass(); + + $response = $this->getMockBuilder(ResponseInterface::class) + ->getMockForAbstractClass(); + $password = 'foobar'; $hash = sha1($password); - $range = $getHashRange->invokeArgs(new Hibp($client), [$hash]); + $range = $getHashRange->invokeArgs(new Hibp($client, $request, $response), [$hash]); $this->assertTrue(is_string($range)); $this->assertSame(Hibp::HIBP_RANGE_LENGTH, strlen($range)); $this->assertSame( @@ -285,6 +299,21 @@ public function testCanNotFindGoodPasswordAsSha1Hash(string $strongPassword, str $this->assertFalse($resultSet); } + /** + * Testing that we cannot send a plain text password while we have set the + * hash flag to TRUE + * + * @covers \Dragonbe\Hibp\Hibp::isPwnedPassword() + */ + public function testErrorIsThrownWhenPlainPasswordIsPassedWithHashFlagEnabled() + { + $this->expectException(\InvalidArgumentException::class); + $hibp = HibpFactory::createTestClient([]); + $password = 'foo'; + $hibp->isPwnedPassword($password, true); + $this->fail('Expected Exception Was Not thrown for providing a plain text password with hash flag on'); + } + /** * Creates a mock response for GuzzleHttp Client and creates * a Hibp instance with these mocked responses. From d15dca31e105a7da036f0d38950ecdb2c8aa9414 Mon Sep 17 00:00:00 2001 From: Michelangelo van Dam Date: Sun, 7 Apr 2019 00:22:02 +0200 Subject: [PATCH 05/10] Updating our code to implement PSR-7, PSR-17 and PSR-18 Removing dependencies on GuzzleHttp client and more on PSR interfaces --- src/Hibp.php | 52 +++++++++++++++++++++++++++++++-------------- src/HibpFactory.php | 26 ++++++++++++++++++++--- 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/src/Hibp.php b/src/Hibp.php index 35a9eca..741f2c3 100644 --- a/src/Hibp.php +++ b/src/Hibp.php @@ -3,11 +3,13 @@ namespace Dragonbe\Hibp; -use GuzzleHttp\ClientInterface; -use GuzzleHttp\Exception\ClientException; -use GuzzleHttp\Exception\ConnectException; +use GuzzleHttp\Psr7\Request; +use Psr\Http\Client\ClientInterface; +use Psr\Http\Client\ClientExceptionInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; -class Hibp implements \Countable +class Hibp implements HibpInterface, \Countable { const HIBP_API_URI = 'https://api.pwnedpasswords.com'; const HIBP_API_TIMEOUT = 300; @@ -22,6 +24,16 @@ class Hibp implements \Countable */ protected $client; + /** + * @var RequestInterface + */ + protected $request; + + /** + * @var ResponseInterface + */ + protected $response; + /** * @var int */ @@ -31,33 +43,41 @@ class Hibp implements \Countable * Hibp constructor. * * @param ClientInterface $client + * @param RequestInterface $request + * @param ResponseInterface $response */ - public function __construct(ClientInterface $client) - { + public function __construct( + ClientInterface $client, + RequestInterface $request, + ResponseInterface $response + ) { $this->client = $client; + $this->request = $request; + $this->response = $response; } /** - * Checks a password against HIBP service and checks - * if the password is matching in the resultset - * - * @param string $password - * @param bool $isShaHash - * @return bool + * @inheritDoc */ public function isPwnedPassword(string $password, bool $isShaHash = false): bool { if (! $isShaHash) { $password = sha1($password); } + if (40 !== strlen($password) && $isShaHash) { + throw new \InvalidArgumentException( + 'Password does not appear to be a SHA1 hashed password, please verify your input' + ); + } $password = strtoupper($password); $range = $this->getHashRange($password); + + $request = new Request('GET', '/range/' . $range); + try { - $response = $this->client->get('/range/' . $range); - } catch (ConnectException $connectException) { + $response = $this->client->sendRequest($request); + } catch (ClientExceptionInterface $connectException) { throw $this->exception(\RuntimeException::class, 'Cannot connect to HIBP API'); - } catch (ClientException $clientException) { - throw $this->exception(\DomainException::class, $clientException->getMessage()); } $resultStream = (string) $response->getBody(); return $this->passwordInResponse($password, $resultStream); diff --git a/src/HibpFactory.php b/src/HibpFactory.php index 9a4ff87..c109f70 100644 --- a/src/HibpFactory.php +++ b/src/HibpFactory.php @@ -3,9 +3,11 @@ namespace Dragonbe\Hibp; -use GuzzleHttp\Client; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; +use GuzzleHttp\Psr7\Request; +use GuzzleHttp\Psr7\Response; +use RicardoFiorani\GuzzlePsr18Adapter\Client; class HibpFactory { @@ -40,7 +42,16 @@ private static function createRealClient(array $config): Hibp 'Accept' => Hibp::HIBP_CLIENT_ACCEPT, ] ], $config)); - return new Hibp($client); + $request = new Request( + 'GET', + Hibp::HIBP_API_URI, + [ + 'User-Agent' => Hibp::HIBP_CLIENT_UA, + 'Accept' => Hibp::HIBP_CLIENT_ACCEPT, + ] + ); + $response = new Response(); + return new Hibp($client, $request, $response); } /** @@ -56,6 +67,15 @@ public static function createTestClient(array $mockArray = []): Hibp $mock = new MockHandler($mockArray); $handler = HandlerStack::create($mock); $client = new Client(['handler' => $handler]); - return new Hibp($client); + $request = new Request( + 'GET', + Hibp::HIBP_API_URI, + [ + 'User-Agent' => Hibp::HIBP_CLIENT_UA, + 'Accept' => Hibp::HIBP_CLIENT_ACCEPT, + ] + ); + $response = new Response(); + return new Hibp($client, $request, $response); } } From ee76d9feefac5c621bc68a3ecbf66ddbc7df600c Mon Sep 17 00:00:00 2001 From: Michelangelo van Dam Date: Sun, 7 Apr 2019 23:24:10 +0200 Subject: [PATCH 06/10] Default configuration factory To make things very simple, we now have a factory method that will provide a default configuration, but can be overridden with custom settings or additional settings for the HTTP client. --- src/HibpFactory.php | 35 ++++++++++++-------- tests/HibpFactoryTest.php | 67 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 13 deletions(-) diff --git a/src/HibpFactory.php b/src/HibpFactory.php index c109f70..9010bb3 100644 --- a/src/HibpFactory.php +++ b/src/HibpFactory.php @@ -25,23 +25,36 @@ public static function create(array $config = []): Hibp } /** - * Creates a real HTTP client for using in your applications - * and make calls to the outside world. - * - * @param array $config GuzzleHttp\Client configuration settings. + * Factory method to create a basic configuration with the + * option to provide your own settings to override default + * configuration options. * - * @return Hibp + * @param array $config + * @return array */ - private static function createRealClient(array $config): Hibp + public static function createConfig(array $config = []): array { - $client = new Client(array_replace_recursive([ + return array_replace_recursive([ 'base_uri' => Hibp::HIBP_API_URI, 'timeout' => Hibp::HIBP_API_TIMEOUT, 'headers' => [ 'User-Agent' => Hibp::HIBP_CLIENT_UA, 'Accept' => Hibp::HIBP_CLIENT_ACCEPT, ] - ], $config)); + ], $config); + } + + /** + * Creates a real HTTP client for using in your applications + * and make calls to the outside world. + * + * @param array $config GuzzleHttp\Client configuration settings. + * + * @return Hibp + */ + private static function createRealClient(array $config): Hibp + { + $client = new Client(self::createConfig($config)); $request = new Request( 'GET', Hibp::HIBP_API_URI, @@ -69,11 +82,7 @@ public static function createTestClient(array $mockArray = []): Hibp $client = new Client(['handler' => $handler]); $request = new Request( 'GET', - Hibp::HIBP_API_URI, - [ - 'User-Agent' => Hibp::HIBP_CLIENT_UA, - 'Accept' => Hibp::HIBP_CLIENT_ACCEPT, - ] + '/' ); $response = new Response(); return new Hibp($client, $request, $response); diff --git a/tests/HibpFactoryTest.php b/tests/HibpFactoryTest.php index 5fa3cfa..48d9251 100644 --- a/tests/HibpFactoryTest.php +++ b/tests/HibpFactoryTest.php @@ -6,6 +6,7 @@ use Dragonbe\Hibp\Hibp; use Dragonbe\Hibp\HibpFactory; use PHPUnit\Framework\TestCase; +use PHPUnit\Runner\Version; class HibpFactoryTest extends TestCase { @@ -34,4 +35,70 @@ public function testFactoryGeneratesHibpObjectForTesting() $hibp = HibpFactory::createTestClient(); $this->assertInstanceOf(Hibp::class, $hibp); } + + /** + * Testing that we can generate our default configuration + * with expected results. + * + * @covers \Dragonbe\Hibp\HibpFactory::createConfig() + */ + public function testFactoryCreationOfDefaultConfig() + { + $hibpConfig = HibpFactory::createConfig(); + $expectedConfig = [ + 'base_uri' => Hibp::HIBP_API_URI, + 'timeout' => Hibp::HIBP_API_TIMEOUT, + 'headers' => [ + 'User-Agent' => Hibp::HIBP_CLIENT_UA, + 'Accept' => Hibp::HIBP_CLIENT_ACCEPT, + ] + ]; + $this->assertSame($expectedConfig, $hibpConfig); + } + + /** + * Testing that we can generate our custom configuration + * when providing different configuration settings + * + * @covers \Dragonbe\Hibp\HibpFactory::createConfig() + */ + public function testFactoryCreationWithOverridingConfiguration() + { + $hibpConfig = HibpFactory::createConfig([ + 'timeout' => 250, + 'headers' => [ + 'User-Agent' => 'phpunit/7.3.5', + ], + ]); + $expectedConfig = [ + 'base_uri' => Hibp::HIBP_API_URI, + 'timeout' => 250, + 'headers' => [ + 'User-Agent' => 'phpunit/7.3.5', + 'Accept' => Hibp::HIBP_CLIENT_ACCEPT, + ] + ]; + $this->assertSame($expectedConfig, $hibpConfig); + } + + /** + * Testing that we can add additional configuration settings + * by just providing the "new" configuration options. + * + * @covers \Dragonbe\Hibp\HibpFactory::createConfig() + */ + public function testFactoryConfigAddsNotDefinedConfigurationOptions() + { + $hibpConfig = HibpFactory::createConfig(['foo' => 'bar']); + $expectedConfig = [ + 'base_uri' => Hibp::HIBP_API_URI, + 'timeout' => Hibp::HIBP_API_TIMEOUT, + 'headers' => [ + 'User-Agent' => Hibp::HIBP_CLIENT_UA, + 'Accept' => Hibp::HIBP_CLIENT_ACCEPT, + ], + 'foo' => 'bar', + ]; + $this->assertSame($expectedConfig, $hibpConfig); + } } From 58f879f1415480dd6500126e7d3f550d0bdeb969 Mon Sep 17 00:00:00 2001 From: Michelangelo van Dam Date: Sun, 7 Apr 2019 23:26:25 +0200 Subject: [PATCH 07/10] Improving code quality We remove the hardcoded length for SHA1 hashes and we are using the provided Request object class to create a new request object for checking the provided password hash against the HIBP service. --- src/Hibp.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Hibp.php b/src/Hibp.php index 741f2c3..11d6879 100644 --- a/src/Hibp.php +++ b/src/Hibp.php @@ -18,6 +18,7 @@ class Hibp implements HibpInterface, \Countable const HIBP_RANGE_LENGTH = 5; const HIBP_RANGE_BASE = 0; const HIBP_COUNT_BASE = 0; + const SHA1_LENGTH = 40; /** * @var ClientInterface @@ -64,7 +65,7 @@ public function isPwnedPassword(string $password, bool $isShaHash = false): bool if (! $isShaHash) { $password = sha1($password); } - if (40 !== strlen($password) && $isShaHash) { + if (self::SHA1_LENGTH !== strlen($password) && $isShaHash) { throw new \InvalidArgumentException( 'Password does not appear to be a SHA1 hashed password, please verify your input' ); @@ -72,7 +73,8 @@ public function isPwnedPassword(string $password, bool $isShaHash = false): bool $password = strtoupper($password); $range = $this->getHashRange($password); - $request = new Request('GET', '/range/' . $range); + $requestClass = get_class($this->request); + $request = new $requestClass('GET', '/range/' . $range); try { $response = $this->client->sendRequest($request); From 9d1bff908eac74bd8cac0604dcc9a6e3ce442f30 Mon Sep 17 00:00:00 2001 From: Michelangelo van Dam Date: Sun, 7 Apr 2019 23:27:29 +0200 Subject: [PATCH 08/10] Improving example code We have improved the example code with additional comments and usage of namespaces. --- examples/hibp-count.php | 21 +++++++++++++++++++-- examples/hibp.php | 16 +++++++++++++++- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/examples/hibp-count.php b/examples/hibp-count.php index a8e5aeb..aabf6c9 100644 --- a/examples/hibp-count.php +++ b/examples/hibp-count.php @@ -1,8 +1,25 @@ isPwnedPassword($password); diff --git a/examples/hibp.php b/examples/hibp.php index c44a348..92de7e9 100644 --- a/examples/hibp.php +++ b/examples/hibp.php @@ -1,8 +1,22 @@ isPwnedPassword('password') ? 'Pwned' : 'OK') . PHP_EOL; echo 'Password "NVt3MpvQ": ' . ($hibp->isPwnedPassword('NVt3MpvQ') ? 'Pwned' : 'OK') . PHP_EOL; From 1c4f084d0274de105480186f9415ef4dec49da61 Mon Sep 17 00:00:00 2001 From: Michelangelo van Dam Date: Sun, 7 Apr 2019 23:29:31 +0200 Subject: [PATCH 09/10] Adding an example for PSR18 HTTP Clients With the recent acceptance of [PSR18](https://www.php-fig.org/psr/psr-18/) for HTTP Client interfaces, we have now provided an example how to use a HTTP client impelementing PSR18. --- examples/hibp-psr18.php | 44 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 examples/hibp-psr18.php diff --git a/examples/hibp-psr18.php b/examples/hibp-psr18.php new file mode 100644 index 0000000..ee726ed --- /dev/null +++ b/examples/hibp-psr18.php @@ -0,0 +1,44 @@ +isPwnedPassword('password') ? 'Pwned' : 'OK') . PHP_EOL; +echo 'Password "NVt3MpvQ": ' . ($hibp->isPwnedPassword('NVt3MpvQ') ? 'Pwned' : 'OK') . PHP_EOL; From 7ca6577d90e3abddd57b79a1318012ea88c9b06b Mon Sep 17 00:00:00 2001 From: Michelangelo van Dam Date: Sun, 7 Apr 2019 23:39:39 +0200 Subject: [PATCH 10/10] Improving CI mutation check We only want to test for mutations on covered code (and ignoring Interfaces as they provide no value in mutation testing) --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6696b22..93e9660 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -77,4 +77,4 @@ jobs: - run: name: Execute mutation testing - command: ./vendor/bin/infection --configuration=infection.json.dist --coverage=build/logs/coverage + command: ./vendor/bin/infection --configuration=infection.json.dist --coverage=build/logs/coverage --only-covered