diff --git a/composer.json b/composer.json index 7d637a60..abf44182 100644 --- a/composer.json +++ b/composer.json @@ -112,7 +112,7 @@ ], "cs": "phpcs", "cs:fix": "phpcbf", - "stan": "phpstan analyse module/*/src/ module/*/config config docker/config data/migrations --level=6", + "stan": "phpstan analyse module/*/src/ module/*/config config docker/config data/migrations --level=7", "test": [ "@test:unit", "@test:db", diff --git a/module/CLI/src/ApiKey/RoleResolver.php b/module/CLI/src/ApiKey/RoleResolver.php index 179fff53..c8cccfc6 100644 --- a/module/CLI/src/ApiKey/RoleResolver.php +++ b/module/CLI/src/ApiKey/RoleResolver.php @@ -8,6 +8,8 @@ use Shlinkio\Shlink\Core\Domain\DomainServiceInterface; use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition; use Symfony\Component\Console\Input\InputInterface; +use function is_string; + class RoleResolver implements RoleResolverInterface { public function __construct(private DomainServiceInterface $domainService) @@ -23,7 +25,7 @@ class RoleResolver implements RoleResolverInterface if ($author) { $roleDefinitions[] = RoleDefinition::forAuthoredShortUrls(); } - if ($domainAuthority !== null) { + if (is_string($domainAuthority)) { $domain = $this->domainService->getOrCreate($domainAuthority); $roleDefinitions[] = RoleDefinition::forDomain($domain); } diff --git a/module/CLI/src/Command/Util/AbstractWithDateRangeCommand.php b/module/CLI/src/Command/Util/AbstractWithDateRangeCommand.php index 51731e3a..9d7f5723 100644 --- a/module/CLI/src/Command/Util/AbstractWithDateRangeCommand.php +++ b/module/CLI/src/Command/Util/AbstractWithDateRangeCommand.php @@ -11,6 +11,7 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Throwable; +use function is_string; use function sprintf; abstract class AbstractWithDateRangeCommand extends BaseCommand @@ -49,7 +50,7 @@ abstract class AbstractWithDateRangeCommand extends BaseCommand private function getDateOption(InputInterface $input, OutputInterface $output, string $key): ?Chronos { $value = $this->getOptionWithDeprecatedFallback($input, $key); - if (empty($value)) { + if (empty($value) || ! is_string($value)) { return null; } diff --git a/module/CLI/test/ApiKey/RoleResolverTest.php b/module/CLI/test/ApiKey/RoleResolverTest.php index 5e4de2c3..76163348 100644 --- a/module/CLI/test/ApiKey/RoleResolverTest.php +++ b/module/CLI/test/ApiKey/RoleResolverTest.php @@ -68,6 +68,21 @@ class RoleResolverTest extends TestCase [RoleDefinition::forDomain($domain)], 1, ]; + yield 'false domain role' => [ + $buildInput([RoleResolver::DOMAIN_ONLY_PARAM => false]), + [], + 0, + ]; + yield 'true domain role' => [ + $buildInput([RoleResolver::DOMAIN_ONLY_PARAM => true]), + [], + 0, + ]; + yield 'string array domain role' => [ + $buildInput([RoleResolver::DOMAIN_ONLY_PARAM => ['foo', 'bar']]), + [], + 0, + ]; yield 'author role only' => [ $buildInput([RoleResolver::DOMAIN_ONLY_PARAM => null, RoleResolver::AUTHOR_ONLY_PARAM => true]), [RoleDefinition::forAuthoredShortUrls()], diff --git a/module/Core/src/Action/RobotsAction.php b/module/Core/src/Action/RobotsAction.php index b7fa7d42..12baa7b3 100644 --- a/module/Core/src/Action/RobotsAction.php +++ b/module/Core/src/Action/RobotsAction.php @@ -23,6 +23,7 @@ class RobotsAction implements RequestHandlerInterface, StatusCodeInterface public function handle(ServerRequestInterface $request): ResponseInterface { + // @phpstan-ignore-next-line The "Response" phpdoc is wrong return new Response(self::STATUS_OK, ['Content-type' => 'text/plain'], $this->buildRobots()); } diff --git a/module/Core/src/Model/ShortUrlsOrdering.php b/module/Core/src/Model/ShortUrlsOrdering.php index b59435ca..4184fcc6 100644 --- a/module/Core/src/Model/ShortUrlsOrdering.php +++ b/module/Core/src/Model/ShortUrlsOrdering.php @@ -49,7 +49,6 @@ final class ShortUrlsOrdering ]); } - /** @var string|array $orderBy */ if (! $isArray) { [$field, $dir] = array_pad(explode('-', $orderBy), 2, null); $this->orderField = $field; diff --git a/module/Core/src/Util/CocurSymfonySluggerBridge.php b/module/Core/src/Util/CocurSymfonySluggerBridge.php index a612068c..da60836e 100644 --- a/module/Core/src/Util/CocurSymfonySluggerBridge.php +++ b/module/Core/src/Util/CocurSymfonySluggerBridge.php @@ -7,8 +7,7 @@ namespace Shlinkio\Shlink\Core\Util; use Cocur\Slugify\SlugifyInterface; use Symfony\Component\String\AbstractUnicodeString; use Symfony\Component\String\Slugger\SluggerInterface; - -use function Symfony\Component\String\s; +use Symfony\Component\String\UnicodeString; class CocurSymfonySluggerBridge implements SluggerInterface { @@ -18,6 +17,6 @@ class CocurSymfonySluggerBridge implements SluggerInterface public function slug(string $string, string $separator = '-', ?string $locale = null): AbstractUnicodeString { - return s($this->slugger->slugify($string, $separator)); + return new UnicodeString($this->slugger->slugify($string, $separator)); } } diff --git a/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php b/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php index 59515242..8eb98153 100644 --- a/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php +++ b/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php @@ -17,8 +17,10 @@ class DropDefaultDomainFromRequestMiddleware implements MiddlewareInterface public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { + /** @var array $body */ + $body = $request->getParsedBody(); $request = $request->withQueryParams($this->sanitizeDomainFromPayload($request->getQueryParams())) - ->withParsedBody($this->sanitizeDomainFromPayload($request->getParsedBody())); + ->withParsedBody($this->sanitizeDomainFromPayload($body)); return $handler->handle($request); } diff --git a/module/Rest/src/Middleware/ShortUrl/OverrideDomainMiddleware.php b/module/Rest/src/Middleware/ShortUrl/OverrideDomainMiddleware.php index 6943f986..0f4fd75e 100644 --- a/module/Rest/src/Middleware/ShortUrl/OverrideDomainMiddleware.php +++ b/module/Rest/src/Middleware/ShortUrl/OverrideDomainMiddleware.php @@ -32,6 +32,7 @@ class OverrideDomainMiddleware implements MiddlewareInterface $domain = $this->domainService->getDomain($domainId); if ($requestMethod === RequestMethodInterface::METHOD_POST) { + /** @var array $payload */ $payload = $request->getParsedBody(); $payload[ShortUrlInputFilter::DOMAIN] = $domain->getAuthority();