Skip to content

Commit daa59ab

Browse files
authored
Add PHPStan in CI (#19)
* Add PHPStan * Apply PHPStan fixes * Update php.yml * Update README.md
1 parent 9f9078f commit daa59ab

File tree

8 files changed

+76
-69
lines changed

8 files changed

+76
-69
lines changed

.github/workflows/php.yml

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,37 @@ on:
77
branches: [ master ]
88

99
jobs:
10-
build:
11-
10+
test:
1211
runs-on: ubuntu-latest
13-
1412
strategy:
1513
fail-fast: false
1614
matrix:
1715
php-version: ['7.4', '8.0', '8.1', '8.2']
18-
1916
steps:
2017
- uses: actions/checkout@v2
21-
2218
- name: Use PHP ${{ matrix.php-version }}
2319
uses: shivammathur/setup-php@v2
2420
with:
2521
php-version: ${{ matrix.php-version }}
2622
extensions: curl
27-
2823
- name: Validate composer.json and composer.lock
2924
run: composer validate
30-
3125
- name: Install dependencies
3226
run: composer update --prefer-dist --no-progress
33-
3427
- name: Run test suite
3528
run: composer run-script test
29+
30+
phpstan:
31+
runs-on: ubuntu-latest
32+
name: PHPStan
33+
steps:
34+
- uses: actions/checkout@v3
35+
- name: Use PHP 8.2
36+
uses: shivammathur/setup-php@v2
37+
with:
38+
php-version: 8.2
39+
extensions: curl
40+
- name: Install dependencies
41+
run: composer install --no-progress
42+
- name: Run PHPStan
43+
run: composer run-script analyse

README.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![Monthly Downloads](https://poser.pugx.org/geocoder-php/provider-integration-tests/d/monthly.png)](https://packagist.org/packages/geocoder-php/provider-integration-tests)
66
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
77

8-
This repository contains integration tests to make sure your implementation of a Geocoder Provider is correct.
8+
This repository contains integration tests to make sure your implementation of a Geocoder Provider is correct.
99

1010
### Install
1111

@@ -15,7 +15,7 @@ composer require --dev geocoder-php/provider-integration-tests:dev-master
1515

1616
### Use
1717

18-
Create a test that looks like this:
18+
Create a test that looks like this:
1919

2020
```php
2121
use Geocoder\IntegrationTest\ProviderIntegrationTest;
@@ -28,16 +28,15 @@ class IntegrationTest extends ProviderIntegrationTest
2828
{
2929
return new GoogleMaps($httpClient);
3030
}
31-
32-
protected function getCacheDir()
31+
32+
protected function getCacheDir(): string;
3333
{
3434
return dirname(__DIR__).'/.cached_responses';
3535
}
3636

37-
protected function getApiKey()
37+
protected function getApiKey(): string;
3838
{
3939
return env('GOOLE_API_KEY');
4040
}
4141
}
4242
```
43-

composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
},
2525
"require-dev": {
2626
"geocoder-php/nominatim-provider": "^5.3",
27-
"php-http/curl-client": "^2.2"
27+
"php-http/curl-client": "^2.2",
28+
"phpstan/phpstan": "^1.10"
2829
},
2930
"autoload": {
3031
"psr-4": {
@@ -45,6 +46,7 @@
4546
"sort-packages": true
4647
},
4748
"scripts": {
49+
"analyse": "vendor/bin/phpstan analyse",
4850
"test": "vendor/bin/phpunit"
4951
}
5052
}

phpstan.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
parameters:
2+
level: 8
3+
paths:
4+
- src
5+
- tests

src/BaseTestCase.php

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,8 @@ abstract protected function getCacheDir();
3030

3131
/**
3232
* Get a real HTTP client. If a cache dir is set to a path it will use cached responses.
33-
*
34-
* @return ClientInterface
3533
*/
36-
protected function getHttpClient($apiKey = null)
34+
protected function getHttpClient(string $apiKey = null): ClientInterface
3735
{
3836
if (null !== $cacheDir = $this->getCacheDir()) {
3937
return new CachedResponseClient(new HttplugClient(), $cacheDir, $apiKey);
@@ -44,13 +42,8 @@ protected function getHttpClient($apiKey = null)
4442

4543
/**
4644
* Get a mocked HTTP client that never do calls over the internet. Use this is you want to control the response data.
47-
*
48-
* @param string|null $body
49-
* @param int $statusCode
50-
*
51-
* @return ClientInterface
5245
*/
53-
protected function getMockedHttpClient($body = null, $statusCode = 200)
46+
protected function getMockedHttpClient(string $body = null, int $statusCode = 200): ClientInterface
5447
{
5548
$client = new MockedHttpClient();
5649
$client->addResponse(new Response($statusCode, [], $body));
@@ -60,13 +53,8 @@ protected function getMockedHttpClient($body = null, $statusCode = 200)
6053

6154
/**
6255
* Get a mocked HTTP client where you may do tests on the request.
63-
*
64-
* @param string|null $body
65-
* @param int $statusCode
66-
*
67-
* @return ClientInterface
6856
*/
69-
protected function getMockedHttpClientCallback(callable $requestCallback)
57+
protected function getMockedHttpClientCallback(callable $requestCallback): ClientInterface
7058
{
7159
$client = $this->getMockBuilder(ClientInterface::class)->getMock();
7260

src/CachedResponseClient.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ public function sendRequest(RequestInterface $request): ResponseInterface
7575
}
7676

7777
$file = sprintf('%s/%s_%s', $this->cacheDir, $host, sha1($cacheKey));
78-
if (is_file($file) && is_readable($file)) {
79-
return new Response(200, [], (new HttplugFactory())->createStream(unserialize(file_get_contents($file))));
78+
if (is_file($file) && is_readable($file) && ($content = file_get_contents($file)) !== false) {
79+
return new Response(200, [], (new HttplugFactory())->createStream(unserialize($content)));
8080
}
8181

8282
$response = $this->delegate->sendRequest($request);

src/ProviderIntegrationTest.php

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
namespace Geocoder\IntegrationTest;
1212

13+
use Exception;
1314
use Geocoder\Collection;
1415
use Geocoder\Exception\InvalidCredentials;
1516
use Geocoder\Exception\InvalidServerResponse;
@@ -24,6 +25,7 @@
2425
use Geocoder\Query\ReverseQuery;
2526
use Http\Discovery\Psr18ClientDiscovery;
2627
use Nyholm\Psr7\Response;
28+
use PHPUnit\Framework\MockObject\MockObject;
2729
use PHPUnit\Framework\TestCase;
2830
use Psr\Http\Client\ClientInterface;
2931
use Psr\Http\Message\ResponseInterface;
@@ -34,15 +36,15 @@
3436
abstract class ProviderIntegrationTest extends TestCase
3537
{
3638
/**
37-
* @var array with functionName => reason
39+
* @var array<string,string> with functionName => reason
3840
*/
39-
protected $skippedTests = [];
41+
protected array $skippedTests = [];
4042

41-
protected $testAddress = true;
42-
protected $testReverse = true;
43-
protected $testIpv4 = true;
44-
protected $testIpv6 = true;
45-
protected $testHttpProvider = true;
43+
protected bool $testAddress = true;
44+
protected bool $testReverse = true;
45+
protected bool $testIpv4 = true;
46+
protected bool $testIpv6 = true;
47+
protected bool $testHttpProvider = true;
4648

4749
/**
4850
* @return Provider that is used in the tests.
@@ -52,17 +54,17 @@ abstract protected function createProvider(ClientInterface $httpClient);
5254
/**
5355
* @return string the directory where cached responses are stored
5456
*/
55-
abstract protected function getCacheDir();
57+
abstract protected function getCacheDir(): string;
5658

5759
/**
5860
* @return string the API key or substring to be removed from cache.
5961
*/
60-
abstract protected function getApiKey();
62+
abstract protected function getApiKey(): string;
6163

6264
/**
6365
* @param ResponseInterface $response
6466
*
65-
* @return \PHPUnit_Framework_MockObject_MockObject|ClientInterface
67+
* @return ClientInterface&MockObject
6668
*/
6769
private function getHttpClient(ResponseInterface $response)
6870
{
@@ -78,10 +80,8 @@ private function getHttpClient(ResponseInterface $response)
7880

7981
/**
8082
* This client will make real request if cache was not found.
81-
*
82-
* @return CachedResponseClient
8383
*/
84-
private function getCachedHttpClient()
84+
private function getCachedHttpClient(): CachedResponseClient
8585
{
8686
try {
8787
$client = Psr18ClientDiscovery::find();
@@ -97,7 +97,7 @@ private function getCachedHttpClient()
9797
return new CachedResponseClient($client, $this->getCacheDir(), $this->getApiKey());
9898
}
9999

100-
public function testGeocodeQuery()
100+
public function testGeocodeQuery(): void
101101
{
102102
if (isset($this->skippedTests[__FUNCTION__])) {
103103
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
@@ -111,18 +111,20 @@ public function testGeocodeQuery()
111111
$result = $provider->geocodeQuery($query);
112112
$this->assertWellFormattedResult($result);
113113

114-
// Check Downing Street
114+
/** @var Location $location */
115115
$location = $result->first();
116-
$this->assertEqualsWithDelta(51.5033, $location->getCoordinates()->getLatitude(), 0.1, 'Latitude should be in London');
117-
$this->assertEqualsWithDelta(-0.1276, $location->getCoordinates()->getLongitude(), 0.1, 'Longitude should be in London');
116+
/** @var Coordinates|null $coordinates */
117+
$coordinates = $location->getCoordinates();
118+
$this->assertNotNull($coordinates, 'Coordinates should not be null');
119+
$this->assertEqualsWithDelta(51.5033, $coordinates->getLatitude(), 0.1, 'Latitude should be in London');
120+
$this->assertEqualsWithDelta(-0.1276, $coordinates->getLongitude(), 0.1, 'Longitude should be in London');
121+
$this->assertNotNull($location->getStreetName(), 'Street name should not be null');
118122
$this->assertStringContainsString('Downing', $location->getStreetName(), 'Street name should contain "Downing St"');
119-
120-
if (null !== $streetNumber = $location->getStreetNumber()) {
121-
$this->assertStringContainsString('10', $streetNumber, 'Street number should contain "10"');
122-
}
123+
$this->assertNotNull($location->getStreetNumber(), 'Street number should not be null');
124+
$this->assertStringContainsString('10', (string) $location->getStreetNumber(), 'Street number should contain "10"');
123125
}
124126

125-
public function testGeocodeQueryWithNoResults()
127+
public function testGeocodeQueryWithNoResults(): void
126128
{
127129
if (isset($this->skippedTests[__FUNCTION__])) {
128130
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
@@ -138,7 +140,7 @@ public function testGeocodeQueryWithNoResults()
138140
$this->assertEquals(0, $result->count());
139141
}
140142

141-
public function testReverseQuery()
143+
public function testReverseQuery(): void
142144
{
143145
if (isset($this->skippedTests[__FUNCTION__])) {
144146
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
@@ -154,7 +156,7 @@ public function testReverseQuery()
154156
$this->assertWellFormattedResult($result);
155157
}
156158

157-
public function testReverseQueryWithNoResults()
159+
public function testReverseQueryWithNoResults(): void
158160
{
159161
if (isset($this->skippedTests[__FUNCTION__])) {
160162
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
@@ -170,7 +172,7 @@ public function testReverseQueryWithNoResults()
170172
$this->assertEquals(1, $result->count());
171173
}
172174

173-
public function testGeocodeIpv4()
175+
public function testGeocodeIpv4(): void
174176
{
175177
if (isset($this->skippedTests[__FUNCTION__])) {
176178
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
@@ -185,7 +187,7 @@ public function testGeocodeIpv4()
185187
$this->assertWellFormattedResult($result);
186188
}
187189

188-
public function testGeocodeIpv6()
190+
public function testGeocodeIpv6(): void
189191
{
190192
if (isset($this->skippedTests[__FUNCTION__])) {
191193
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
@@ -204,11 +206,11 @@ public function testGeocodeIpv6()
204206
* @dataProvider exceptionDataProvider
205207
*
206208
* @param GeocodeQuery|ReverseQuery $query
207-
* @param string $exceptionClass
209+
* @param class-string<Exception> $exceptionClass
208210
* @param ResponseInterface|null $response
209211
* @param string $message
210212
*/
211-
public function testExceptions($query, string $exceptionClass, ResponseInterface $response = null, string $message = '')
213+
public function testExceptions($query, string $exceptionClass, ResponseInterface $response = null, string $message = ''): void
212214
{
213215
if (isset($this->skippedTests[__FUNCTION__])) {
214216
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
@@ -228,7 +230,10 @@ public function testExceptions($query, string $exceptionClass, ResponseInterface
228230
}
229231
}
230232

231-
public function exceptionDataProvider()
233+
/**
234+
* @return array<array{GeocodeQuery|ReverseQuery, class-string<Exception>, Response, string}>
235+
*/
236+
public function exceptionDataProvider(): array
232237
{
233238
$testData = [];
234239

@@ -281,7 +286,7 @@ public function exceptionDataProvider()
281286
*
282287
* @param $result
283288
*/
284-
private function assertWellFormattedResult(Collection $result)
289+
private function assertWellFormattedResult(Collection $result): void
285290
{
286291
$this->assertInstanceOf(
287292
Collection::class,
@@ -335,7 +340,7 @@ private function assertWellFormattedResult(Collection $result)
335340
}
336341

337342
// Check country
338-
if (null !== $country = $location->getCountry()) {
343+
if (null !== ($country = $location->getCountry())) {
339344
$this->assertInstanceOf(
340345
Country::class,
341346
$country,
@@ -345,14 +350,14 @@ private function assertWellFormattedResult(Collection $result)
345350

346351
if (null !== $country->getCode()) {
347352
$this->assertNotEmpty(
348-
$location->getCountry()->getCode(),
353+
$country->getCode(),
349354
'The Country should not have an empty code.'
350355
);
351356
}
352357

353358
if (null !== $country->getName()) {
354359
$this->assertNotEmpty(
355-
$location->getCountry()->getName(),
360+
$country->getName(),
356361
'The Country should not have an empty name.'
357362
);
358363
}

tests/NominatimTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,20 @@
1919
*/
2020
class NominatimTest extends ProviderIntegrationTest
2121
{
22-
protected $testIpv4 = false;
23-
protected $testIpv6 = false;
22+
protected bool $testIpv4 = false;
23+
protected bool $testIpv6 = false;
2424

2525
protected function createProvider(ClientInterface $httpClient)
2626
{
2727
return Nominatim::withOpenStreetMapServer($httpClient, 'Geocoder PHP/Nominatim Provider/Nominatim Test');
2828
}
2929

30-
protected function getCacheDir()
30+
protected function getCacheDir(): string
3131
{
3232
return dirname(__DIR__).'/.cached_responses';
3333
}
3434

35-
protected function getApiKey()
35+
protected function getApiKey(): string
3636
{
3737
return '';
3838
}

0 commit comments

Comments
 (0)