Skip to content

Commit 62b5ee9

Browse files
committed
build: increase PHPStan analysis level to 8
- Removed missingType.generics suppression by adding proper generic type annotations to AbstractFilter, AbstractProvider, ProviderInterface, BetweenFilter, and OnFilter - Added runtime instanceof ProviderInterface guard in Yasumi::create() and throw ProviderNotFoundException if not satisfied - Added is_string() guard after preg_replace() in getProviders() and narrow return type PHPDoc to array<string, string> - Fixed null-unsafe usage of getHoliday() result in examples/basic.php - Added test covering the new ProviderInterface guard in create() Signed-off-by: Sacha Telgenhof <me@sachatelgenhof.com>
1 parent 71b06e6 commit 62b5ee9

9 files changed

Lines changed: 46 additions & 25 deletions

File tree

examples/basic.php

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,20 @@
4343
// Get a holiday instance for Independence Day
4444
$independenceDay = $holidays->getHoliday('independenceDay');
4545

46-
// Show the localized name
47-
echo 'Name of the holiday : ' . $independenceDay->getName() . PHP_EOL;
46+
if (null !== $independenceDay) {
47+
// Show the localized name
48+
echo 'Name of the holiday : ' . $independenceDay->getName() . PHP_EOL;
4849

49-
// Show the date
50-
echo "Date of the holiday : {$independenceDay}" . PHP_EOL;
50+
// Show the date
51+
echo "Date of the holiday : {$independenceDay}" . PHP_EOL;
5152

52-
// Show the type of holiday
53-
echo 'Type of holiday : ' . $independenceDay->getType() . PHP_EOL;
53+
// Show the type of holiday
54+
echo 'Type of holiday : ' . $independenceDay->getType() . PHP_EOL;
55+
}
5456
echo PHP_EOL;
5557

5658
// Dump the holiday as a JSON object
5759
echo 'Holiday as a JSON object:' . PHP_EOL;
58-
echo json_encode($independenceDay, JSON_PRETTY_PRINT);
60+
echo json_encode($independenceDay ?? [], JSON_PRETTY_PRINT);
5961

6062
echo PHP_EOL;

phpstan.neon.dist

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
parameters:
2-
level: 6
2+
level: 8
33
paths:
44
- src
55
- examples
66
ignoreErrors:
77
-
88
message: '#Comparison operation "<=" between [0-9]+ and int<[0-9]+, max> is always true.#'
99
path: src/Yasumi/Provider/Turkey.php
10-
-
11-
identifier: missingType.generics
1210
reportUnmatchedIgnoredErrors: true # Do not allow outdated errors in the baseline
1311
treatPhpDocTypesAsCertain: false
1412
tipsOfTheDay: false

src/Yasumi/Filters/AbstractFilter.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717

1818
namespace Yasumi\Filters;
1919

20+
use Yasumi\Holiday;
2021
use Yasumi\SubstituteHoliday;
2122

23+
/** @extends \FilterIterator<string, Holiday, \Iterator<string, Holiday>> */
2224
abstract class AbstractFilter extends \FilterIterator implements \Countable
2325
{
2426
/**

src/Yasumi/Filters/BetweenFilter.php

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717

1818
namespace Yasumi\Filters;
1919

20-
use Yasumi\ProviderInterface;
21-
2220
/**
2321
* BetweenFilter is a class used for filtering holidays based on given date range.
2422
*
@@ -40,11 +38,11 @@ class BetweenFilter extends AbstractFilter
4038
/**
4139
* Construct the Between FilterIterator Object.
4240
*
43-
* @param \Iterator<ProviderInterface> $iterator Iterator object of the Holidays Provider
44-
* @param \DateTimeInterface $startDate Start date of the time frame to check against
45-
* @param \DateTimeInterface $endDate End date of the time frame to check against
46-
* @param bool $equal Indicate whether the start and end dates should be included in the
47-
* comparison
41+
* @param \Iterator<string, \Yasumi\Holiday> $iterator Iterator object of the Holidays Provider
42+
* @param \DateTimeInterface $startDate Start date of the time frame to check against
43+
* @param \DateTimeInterface $endDate End date of the time frame to check against
44+
* @param bool $equal Indicate whether the start and end dates should be included in the
45+
* comparison
4846
*/
4947
public function __construct(
5048
\Iterator $iterator,

src/Yasumi/Filters/OnFilter.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717

1818
namespace Yasumi\Filters;
1919

20-
use Yasumi\ProviderInterface;
21-
2220
/**
2321
* OnFilter is a class used for filtering holidays based on a given date.
2422
*
@@ -36,8 +34,8 @@ class OnFilter extends AbstractFilter
3634
/**
3735
* Construct the On FilterIterator Object.
3836
*
39-
* @param \Iterator<ProviderInterface> $iterator Iterator object of the Holidays Provider
40-
* @param \DateTimeInterface $date Start date of the time frame to check against
37+
* @param \Iterator<string, \Yasumi\Holiday> $iterator Iterator object of the Holidays Provider
38+
* @param \DateTimeInterface $date Start date of the time frame to check against
4139
*/
4240
public function __construct(
4341
\Iterator $iterator,

src/Yasumi/Provider/AbstractProvider.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828

2929
/**
3030
* Class AbstractProvider.
31+
*
32+
* @implements \IteratorAggregate<string, Holiday>
3133
*/
3234
abstract class AbstractProvider implements \Countable, ProviderInterface, \IteratorAggregate
3335
{
@@ -236,6 +238,7 @@ public function between(
236238
return new BetweenFilter($this->getIterator(), $startDate, $endDate, $equals ?? true);
237239
}
238240

241+
/** @return \ArrayIterator<string, Holiday> */
239242
public function getIterator(): \ArrayIterator
240243
{
241244
$this->ensureSorted();

src/Yasumi/ProviderInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public function isHoliday(\DateTimeInterface $date): bool;
8989
/**
9090
* Get an iterator for the holidays.
9191
*
92-
* @return \ArrayIterator iterator for the holidays of this calendar
92+
* @return \ArrayIterator<string, Holiday> iterator for the holidays of this calendar
9393
*/
9494
public function getIterator(): \ArrayIterator;
9595

src/Yasumi/Yasumi.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,13 @@ public static function create(string $class, int $year = self::YEAR_LOWER_BOUND,
150150
throw new UnknownLocaleException(sprintf('Locale "%s" is not a valid locale.', $locale));
151151
}
152152

153-
return new $providerClass($year, $locale, self::$globalTranslations);
153+
$instance = new $providerClass($year, $locale, self::$globalTranslations);
154+
155+
if (! $instance instanceof ProviderInterface) {
156+
throw new ProviderNotFoundException(sprintf('Holiday provider "%s" does not implement ProviderInterface.', $class));
157+
}
158+
159+
return $instance;
154160
}
155161

156162
/**
@@ -200,7 +206,7 @@ public static function createByISO3166_2(
200206
/**
201207
* Returns a list of available holiday providers.
202208
*
203-
* @return array<string, array<array-key, string>|string|null> list of available holiday providers
209+
* @return array<string, string> list of available holiday providers
204210
*
205211
* @throws \ReflectionException
206212
*/
@@ -238,7 +244,13 @@ public static function getProviders(): array
238244
$quotedDs = preg_quote(\DIRECTORY_SEPARATOR, '');
239245
$provider = preg_replace("#^.+{$quotedDs}Provider{$quotedDs}(.+)\\.php$#", '$1', $file->getPathName());
240246

241-
$class = new \ReflectionClass(sprintf('Yasumi\Provider\%s', str_replace('/', '\\', $provider)));
247+
if (! is_string($provider)) {
248+
continue;
249+
}
250+
251+
/** @var class-string $providerClass */
252+
$providerClass = sprintf('Yasumi\Provider\%s', str_replace('/', '\\', $provider));
253+
$class = new \ReflectionClass($providerClass);
242254

243255
$key = 'ID';
244256

tests/Base/YasumiTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ public function testCreateWithAbstractClassProvider(): void
6161
Yasumi::create('AbstractProvider');
6262
}
6363

64+
public function testCreateWithClassNotImplementingProviderInterface(): void
65+
{
66+
$this->expectException(ProviderNotFoundException::class);
67+
68+
$class = new class {};
69+
Yasumi::create($class::class, self::YEAR_LOWER_BOUND);
70+
}
71+
6472
public function testCreateWithAbstractExtension(): void
6573
{
6674
$class = YasumiExternalProvider::class;

0 commit comments

Comments
 (0)