From dce290433bc40e2cf4a813aedf30567bf78766dd Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 30 Jul 2026 13:03:02 +0200 Subject: [PATCH 1/2] Fixed some baseline analysis errors --- mago-analyze-baseline.toml | 294 ++---------------- mago.toml | 9 + src/Config/ArrayShlinkConfig.php | 58 ++-- src/Domains/Model/Domain.php | 7 + src/Domains/Model/DomainRedirects.php | 3 + src/Http/Exception/HttpException.php | 4 +- src/Model/ListEndpointIterator.php | 2 +- src/RedirectRules/Model/RedirectCondition.php | 3 + src/ShortUrls/Model/ShortUrlsList.php | 2 +- src/Tags/Model/TagsWithStatsList.php | 4 +- src/Tags/TagsClient.php | 7 +- src/Utils/JsonDecoder.php | 3 +- src/Visits/Model/VisitsList.php | 6 +- test/ShortUrls/Model/ShortUrlCreationTest.php | 4 +- test/ShortUrls/Model/ShortUrlEditionTest.php | 5 +- test/Tags/TagsClientTest.php | 2 - 16 files changed, 101 insertions(+), 312 deletions(-) diff --git a/mago-analyze-baseline.toml b/mago-analyze-baseline.toml index db8f732..63f8ffc 100644 --- a/mago-analyze-baseline.toml +++ b/mago-analyze-baseline.toml @@ -37,16 +37,16 @@ message = 'Could not infer a precise return type for function `Shlinkio\Shlink\S count = 1 [[issues]] -file = "src/Config/ArrayShlinkConfig.php" +file = "src/Config/EnvShlinkConfig.php" code = "mixed-argument" message = "Invalid argument type for argument #1 of `trim`: expected `string`, but found `nonnull`." count = 3 [[issues]] -file = "src/Config/EnvShlinkConfig.php" -code = "mixed-argument" -message = "Invalid argument type for argument #1 of `trim`: expected `string`, but found `nonnull`." -count = 3 +file = "src/Config/ShlinkConfig.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Config\ArrayShlinkConfig::fromArray`: expected `array{'apiKey'?: string, 'baseUrl'?: string, 'version'?: string}`, but possibly received `array`.''' +count = 1 [[issues]] file = "src/Domains/DomainsClient.php" @@ -57,7 +57,7 @@ count = 1 [[issues]] file = "src/Domains/DomainsClient.php" code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Domains\Model\Domain::fromArray`: expected `array`, but found `mixed`.' +message = '''Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Domains\Model\Domain::fromArray`: expected `array{'domain'?: string, 'isDefault'?: bool, 'redirects'?: array{'baseUrlRedirect'?: string, 'invalidShortUrlRedirect'?: string, 'regular404Redirect'?: string}}`, but found `mixed`.''' count = 1 [[issues]] @@ -72,6 +72,12 @@ code = "mixed-assignment" message = "Assigning `nonnull` type to a variable may lead to unexpected behavior." count = 1 +[[issues]] +file = "src/Domains/DomainsClient.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Domains\Model\DomainRedirects::fromArray`: expected `array{'baseUrlRedirect'?: string, 'invalidShortUrlRedirect'?: string, 'regular404Redirect'?: string}`, but possibly received `array`.''' +count = 1 + [[issues]] file = "src/Domains/Exception/DomainNotFoundException.php" code = "mixed-argument" @@ -84,42 +90,6 @@ code = "mixed-assignment" message = "Assigning `nonnull` type to a variable may lead to unexpected behavior." count = 1 -[[issues]] -file = "src/Domains/Model/Domain.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Domains\Model\Domain::__construct`: expected `string`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/Domains/Model/Domain.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Domains\Model\DomainRedirects::fromArray`: expected `array`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/Domains/Model/Domain.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `Shlinkio\Shlink\SDK\Domains\Model\Domain::__construct`: expected `bool`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/Domains/Model/DomainRedirects.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Domains\Model\DomainRedirects::__construct`: expected `null|string`, but found `mixed`.' -count = 1 - -[[issues]] -file = "src/Domains/Model/DomainRedirects.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `Shlinkio\Shlink\SDK\Domains\Model\DomainRedirects::__construct`: expected `null|string`, but found `mixed`.' -count = 1 - -[[issues]] -file = "src/Domains/Model/DomainRedirects.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #3 of `Shlinkio\Shlink\SDK\Domains\Model\DomainRedirects::__construct`: expected `null|string`, but found `mixed`.' -count = 1 - [[issues]] file = "src/Exception/InvalidDataException.php" code = "mixed-argument" @@ -210,36 +180,6 @@ code = "mixed-return-statement" message = 'Could not infer a precise return type for function `Shlinkio\Shlink\SDK\Model\ListEndpointIterator::count`. Saw type `nonnull`.' count = 1 -[[issues]] -file = "src/RedirectRules/Model/RedirectCondition.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\RedirectRules\Model\RedirectConditionType::tryFrom`: expected `string`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/RedirectRules/Model/RedirectCondition.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `Shlinkio\Shlink\SDK\RedirectRules\Model\RedirectCondition::__construct`: expected `null|string`, but found `mixed`.' -count = 1 - -[[issues]] -file = "src/RedirectRules/Model/RedirectCondition.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #3 of `Shlinkio\Shlink\SDK\RedirectRules\Model\RedirectCondition::__construct`: expected `null|string`, but found `mixed`.' -count = 1 - -[[issues]] -file = "src/RedirectRules/Model/RedirectCondition.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #4 of `Shlinkio\Shlink\SDK\RedirectRules\Model\RedirectCondition::__construct`: expected `null|string`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/RedirectRules/Model/RedirectCondition.php" -code = "mixed-assignment" -message = "Assigning `nonnull` type to a variable may lead to unexpected behavior." -count = 1 - [[issues]] file = "src/RedirectRules/Model/RedirectRule.php" code = "mixed-argument" @@ -249,7 +189,7 @@ count = 1 [[issues]] file = "src/RedirectRules/Model/RedirectRule.php" code = "mixed-argument" -message = "Invalid argument type for argument #2 of `array_map`: expected `array<('K.array_map() extends array-key), array>`, but found `nonnull`." +message = "Invalid argument type for argument #2 of `array_map`: expected `array<('K.array_map() extends array-key), array{'matchKey'?: string, 'matchValue'?: string, 'type'?: string}>`, but found `nonnull`." count = 1 [[issues]] @@ -459,13 +399,13 @@ count = 1 [[issues]] file = "src/ShortUrls/Model/ShortUrlsList.php" code = "less-specific-nested-argument-type" -message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Model\ListEndpointIterator::__construct`: expected `(closure(int, int): array{0: array, 1: array})`, but provided type `(closure(...mixed=): mixed)` is less specific.' +message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Model\ListEndpointIterator::__construct`: expected `(closure(int, int): list{array, array})`, but provided type `(closure(...mixed=): mixed)` is less specific.' count = 1 [[issues]] file = "src/ShortUrls/ShortUrlsClient.php" code = "less-specific-nested-argument-type" -message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrlsList::forTupleLoader`: expected `(closure(int, int): array{0: array, 1: array})`, but provided type `(closure(int, int): list{nonnull, nonnull})` is less specific.' +message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrlsList::forTupleLoader`: expected `(closure(int, int): list{list, list})`, but provided type `(closure(int, int): list{nonnull, nonnull})` is less specific.' count = 1 [[issues]] @@ -525,19 +465,7 @@ count = 1 [[issues]] file = "src/Tags/Model/TagsWithStatsList.php" code = "less-specific-nested-argument-type" -message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Model\ListEndpointIterator::__construct`: expected `(closure(int, int): array{0: array, 1: array})`, but provided type `(closure(...mixed=): mixed)` is less specific.' -count = 1 - -[[issues]] -file = "src/Tags/TagsClient.php" -code = "less-specific-nested-argument-type" -message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Tags\Model\TagsWithStatsList::forNonPaginatedTupleLoader`: expected `(closure(int, int): array{0: array, 1: array})`, but provided type `(closure(int, int): list{nonnull, nonnull})` is less specific.' -count = 1 - -[[issues]] -file = "src/Tags/TagsClient.php" -code = "less-specific-nested-argument-type" -message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Tags\Model\TagsWithStatsList::forTupleLoader`: expected `(closure(int, int): array{0: array, 1: array})`, but provided type `(closure(int, int): list{nonnull, nonnull})` is less specific.' +message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Model\ListEndpointIterator::__construct`: expected `(closure(int, int): list{array, array})`, but provided type `(closure(...mixed=): mixed)` is less specific.' count = 1 [[issues]] @@ -546,12 +474,6 @@ code = "mixed-return-statement" message = 'Could not infer a precise return type for function `Shlinkio\Shlink\SDK\Tags\TagsClient::listTagsWithFilter`. Saw type `nonnull`.' count = 1 -[[issues]] -file = "src/Utils/JsonDecoder.php" -code = "mixed-return-statement" -message = 'Could not infer a precise return type for function `Shlinkio\Shlink\SDK\Utils\JsonDecoder::decode`. Saw type `mixed`.' -count = 1 - [[issues]] file = "src/Visits/Model/OrphanVisit.php" code = "mixed-argument" @@ -681,13 +603,13 @@ count = 1 [[issues]] file = "src/Visits/VisitsClient.php" code = "less-specific-nested-argument-type" -message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsList::forOrphanVisitsTupleLoader`: expected `(closure(int, int): array{0: array, 1: array})`, but provided type `(closure(...mixed=): mixed)` is less specific.' +message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsList::forOrphanVisitsTupleLoader`: expected `(closure(int, int): list{array, array})`, but provided type `(closure(...mixed=): mixed)` is less specific.' count = 1 [[issues]] file = "src/Visits/VisitsClient.php" code = "less-specific-nested-argument-type" -message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsList::forTupleLoader`: expected `(closure(int, int): array{0: array, 1: array})`, but provided type `(closure(...mixed=): mixed)` is less specific.' +message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsList::forTupleLoader`: expected `(closure(int, int): list{array, array})`, but provided type `(closure(...mixed=): mixed)` is less specific.' count = 4 [[issues]] @@ -696,12 +618,6 @@ code = "mixed-argument" message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsOverview::fromArray`: expected `array`, but found `nonnull`.' count = 1 -[[issues]] -file = "test-integration/ShortUrls/ShortUrlsClientTest.php" -code = "string-member-selector" -message = "This member selector uses a non-literal string type (`string`); its specific value cannot be statically determined." -count = 1 - [[issues]] file = "test-integration/TestCase/AbstractTestCase.php" code = "mixed-return-statement" @@ -715,175 +631,7 @@ message = 'Could not infer a precise return type for function `ShlinkioIntegrati count = 1 [[issues]] -file = "test/Builder/ShlinkClientBuilderTest.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 2 - -[[issues]] -file = "test/Builder/ShlinkClientBuilderTest.php" -code = "string-member-selector" -message = "This member selector uses a non-literal string type (`string`); its specific value cannot be statically determined." -count = 2 - -[[issues]] -file = "test/Builder/SingletonShlinkClientBuilderTest.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 5 - -[[issues]] -file = "test/Builder/SingletonShlinkClientBuilderTest.php" -code = "string-member-selector" -message = "This member selector uses a non-literal string type (`non-empty-string`); its specific value cannot be statically determined." -count = 5 - -[[issues]] -file = "test/Config/EnvShlinkConfigTest.php" -code = "mixed-argument" -message = "Invalid argument type for argument #3 of `sprintf`: expected `Stringable|null|scalar`, but found `mixed`." -count = 1 - -[[issues]] -file = "test/Config/EnvShlinkConfigTest.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." +file = "test/Config/ArrayShlinkConfigTest.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Config\ArrayShlinkConfig::fromArray`: expected `array{'apiKey'?: string, 'baseUrl'?: string, 'version'?: string}`, but possibly received `array`.''' count = 1 - -[[issues]] -file = "test/Domains/DomainsClientTest.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 1 - -[[issues]] -file = "test/RedirectRules/Model/RedirectRuleDataTest.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `PHPUnit\Framework\Assert::assertCount`: expected `Countable|iterable`, but found `mixed`.' -count = 3 - -[[issues]] -file = "test/ShortUrls/Model/ShortUrlCreationTest.php" -code = "invalid-method-access" -message = "Attempting to access a method on a non-object type (`false`)." -count = 1 - -[[issues]] -file = "test/ShortUrls/Model/ShortUrlCreationTest.php" -code = "possibly-false-argument" -message = 'Argument #1 of method `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrlPayloadTrait::validSince` is possibly `false`, but parameter type `DateTimeInterface` does not accept it.' -count = 1 - -[[issues]] -file = "test/ShortUrls/Model/ShortUrlEditionTest.php" -code = "invalid-method-access" -message = "Attempting to access a method on a non-object type (`false`)." -count = 1 - -[[issues]] -file = "test/ShortUrls/Model/ShortUrlEditionTest.php" -code = "possibly-false-argument" -message = 'Argument #1 of method `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrlPayloadTrait::validUntil` is possibly `false`, but parameter type `DateTimeInterface` does not accept it.' -count = 1 - -[[issues]] -file = "test/ShortUrls/Model/ShortUrlMetaTest.php" -code = "invalid-method-access" -message = "Attempting to access a method on a non-object type (`false`)." -count = 1 - -[[issues]] -file = "test/ShortUrls/Model/ShortUrlMetaTest.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 1 - -[[issues]] -file = "test/ShortUrls/Model/ShortUrlTest.php" -code = "invalid-method-access" -message = "Attempting to access a method on a non-object type (`false`)." -count = 1 - -[[issues]] -file = "test/ShortUrls/Model/ShortUrlTest.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 1 - -[[issues]] -file = "test/ShortUrls/ShortUrlsClientTest.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 2 - -[[issues]] -file = "test/ShortUrls/ShortUrlsClientTest.php" -code = "mixed-operand" -message = "Invalid middle operand: type `mixed` cannot be reliably used in string concatenation." -count = 4 - -[[issues]] -file = "test/ShortUrls/ShortUrlsClientTest.php" -code = "mixed-operand" -message = "Left operand in binary operation has type `mixed`." -count = 2 - -[[issues]] -file = "test/Tags/TagsClientTest.php" -code = "invalid-array-element-key" -message = "Cannot use spread operator on an iterable with key type `mixed`." -count = 2 - -[[issues]] -file = "test/Tags/TagsClientTest.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 2 - -[[issues]] -file = "test/Tags/TagsClientTest.php" -code = "redundant-docblock-type" -message = "Redundant docblock type for variable `$item`." -count = 1 - -[[issues]] -file = "test/Visits/Model/OrphanVisitTest.php" -code = "invalid-method-access" -message = "Attempting to access a method on a non-object type (`false`)." -count = 1 - -[[issues]] -file = "test/Visits/Model/OrphanVisitTest.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 1 - -[[issues]] -file = "test/Visits/Model/VisitTest.php" -code = "invalid-method-access" -message = "Attempting to access a method on a non-object type (`false`)." -count = 1 - -[[issues]] -file = "test/Visits/Model/VisitTest.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 1 - -[[issues]] -file = "test/Visits/VisitsClientTest.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 3 - -[[issues]] -file = "test/Visits/VisitsClientTest.php" -code = "mixed-operand" -message = "Invalid middle operand: type `mixed` cannot be reliably used in string concatenation." -count = 4 - -[[issues]] -file = "test/Visits/VisitsClientTest.php" -code = "mixed-operand" -message = "Left operand in binary operation has type `mixed`." -count = 2 diff --git a/mago.toml b/mago.toml index bf8e08d..64e32d4 100644 --- a/mago.toml +++ b/mago.toml @@ -21,3 +21,12 @@ loop-does-not-iterate = { exclude = ["test"] } [analyzer] baseline = "mago-analyze-baseline.toml" +ignore = [ + { code = "mixed-assignment", in = ["test/", "test-integration/"] }, + { code = "mixed-argument", in = ["test/", "test-integration/"] }, + { code = "mixed-operand", in = ["test/", "test-integration/"] }, + { code = "invalid-method-access", in = ["test/", "test-integration/"] }, + { code = "string-member-selector", in = ["test/", "test-integration/"] }, + { code = "invalid-array-element-key", in = ["test/", "test-integration/"] }, + { code = "possibly-false-argument", in = ["test/", "test-integration/"] }, +] diff --git a/src/Config/ArrayShlinkConfig.php b/src/Config/ArrayShlinkConfig.php index c43ef8e..ba2cc63 100644 --- a/src/Config/ArrayShlinkConfig.php +++ b/src/Config/ArrayShlinkConfig.php @@ -11,40 +11,54 @@ final readonly class ArrayShlinkConfig implements ShlinkConfigInterface { - public const BASE_URL_PROP = 'baseUrl'; - public const API_KEY_PROP = 'apiKey'; - public const VERSION_PROP = 'version'; + public const string BASE_URL_PROP = 'baseUrl'; + public const string API_KEY_PROP = 'apiKey'; + public const string VERSION_PROP = 'version'; private function __construct(private ShlinkConfigInterface $wrapped) {} /** + * @param array{ + * ArrayShlinkConfig::BASE_URL_PROP?: string, + * ArrayShlinkConfig::API_KEY_PROP?: string, + * ArrayShlinkConfig::VERSION_PROP?: string, + * } $config * @throws InvalidConfigException */ public static function fromArray(array $config): self { - return new self(ShlinkConfig::fromRawConfig(new class($config) implements RawConfigInterface { - public function __construct(private readonly array $config) {} + return new self(ShlinkConfig::fromRawConfig( + new readonly class($config) implements RawConfigInterface { + /** + * @param array{ + * ArrayShlinkConfig::BASE_URL_PROP?: string, + * ArrayShlinkConfig::API_KEY_PROP?: string, + * ArrayShlinkConfig::VERSION_PROP?: string, + * } $config + */ + public function __construct(private array $config) {} - public function baseUrl(): string - { - return trim($this->config[ArrayShlinkConfig::BASE_URL_PROP] ?? ''); - } + public function baseUrl(): string + { + return trim($this->config[ArrayShlinkConfig::BASE_URL_PROP] ?? ''); + } - public function apiKey(): string - { - return trim($this->config[ArrayShlinkConfig::API_KEY_PROP] ?? ''); - } + public function apiKey(): string + { + return trim($this->config[ArrayShlinkConfig::API_KEY_PROP] ?? ''); + } - public function version(): string - { - return trim($this->config[ArrayShlinkConfig::VERSION_PROP] ?? ''); - } + public function version(): string + { + return trim($this->config[ArrayShlinkConfig::VERSION_PROP] ?? ''); + } - public function missingConfigException(): InvalidConfigException - { - return InvalidConfigException::forInvalidConfig(); - } - })); + public function missingConfigException(): InvalidConfigException + { + return InvalidConfigException::forInvalidConfig(); + } + }, + )); } public function baseUrl(): string diff --git a/src/Domains/Model/Domain.php b/src/Domains/Model/Domain.php index bb24d9b..a2c2501 100644 --- a/src/Domains/Model/Domain.php +++ b/src/Domains/Model/Domain.php @@ -8,6 +8,13 @@ { private function __construct(public string $domain, public bool $isDefault, public DomainRedirects $redirects) {} + /** + * @param array{ + * domain?: string, + * isDefault?: bool, + * redirects?: array{baseUrlRedirect?: string, regular404Redirect?: string, invalidShortUrlRedirect?: string} + * } $payload + */ public static function fromArray(array $payload): self { return new self( diff --git a/src/Domains/Model/DomainRedirects.php b/src/Domains/Model/DomainRedirects.php index 319d9f0..64c8e1d 100644 --- a/src/Domains/Model/DomainRedirects.php +++ b/src/Domains/Model/DomainRedirects.php @@ -12,6 +12,9 @@ private function __construct( public string|null $invalidShortUrlRedirect, ) {} + /** + * @param array{baseUrlRedirect?: string, regular404Redirect?: string, invalidShortUrlRedirect?: string} $payload + */ public static function fromArray(array $payload): self { return new self( diff --git a/src/Http/Exception/HttpException.php b/src/Http/Exception/HttpException.php index c1f307f..488f795 100644 --- a/src/Http/Exception/HttpException.php +++ b/src/Http/Exception/HttpException.php @@ -18,7 +18,7 @@ class HttpException extends RuntimeException implements ExceptionInterface { - private const STANDARD_PROBLEM_DETAILS_PROPS = ['type', 'title', 'detail', 'status']; + private const array STANDARD_PROBLEM_DETAILS_PROPS = ['type', 'title', 'detail', 'status']; private function __construct( public readonly ErrorType $type, @@ -39,7 +39,7 @@ public static function fromPayload(array $payload): self { $additional = array_filter( $payload, - static fn (string $key) => !in_array($key, self::STANDARD_PROBLEM_DETAILS_PROPS, true), + static fn (string $key) => !in_array($key, self::STANDARD_PROBLEM_DETAILS_PROPS, strict: true), ARRAY_FILTER_USE_KEY, ); diff --git a/src/Model/ListEndpointIterator.php b/src/Model/ListEndpointIterator.php index 9a0a5dd..1565e6d 100644 --- a/src/Model/ListEndpointIterator.php +++ b/src/Model/ListEndpointIterator.php @@ -19,7 +19,7 @@ abstract class ListEndpointIterator implements IteratorAggregate, Countable private array $currentPagination = []; /** - * @param Closure(int $page, int $itemsPerPage): array{array, array} $pageLoader + * @param Closure(int $page, int $itemsPerPage): list{array, array} $pageLoader * @param Closure(mixed): mixed $itemMapper */ public function __construct( diff --git a/src/RedirectRules/Model/RedirectCondition.php b/src/RedirectRules/Model/RedirectCondition.php index 0b12c92..65b5046 100644 --- a/src/RedirectRules/Model/RedirectCondition.php +++ b/src/RedirectRules/Model/RedirectCondition.php @@ -77,6 +77,9 @@ public static function forBrowser(Browser $browser): self return new self(RedirectConditionType::BROWSER, $browser->value); } + /** + * @param array{type?: string, matchValue?: string, matchKey?: string} $payload + */ public static function fromArray(array $payload): self { $originalType = $payload['type'] ?? ''; diff --git a/src/ShortUrls/Model/ShortUrlsList.php b/src/ShortUrls/Model/ShortUrlsList.php index aab2b91..6be75fa 100644 --- a/src/ShortUrls/Model/ShortUrlsList.php +++ b/src/ShortUrls/Model/ShortUrlsList.php @@ -20,7 +20,7 @@ private function __construct(Closure $pageLoader) } /** - * @param Closure(int $page, int $itemsPerPage): array{array, array} $pageLoader + * @param Closure(int $page, int $itemsPerPage): list{list, list} $pageLoader */ public static function forTupleLoader(Closure $pageLoader): self { diff --git a/src/Tags/Model/TagsWithStatsList.php b/src/Tags/Model/TagsWithStatsList.php index 0198537..941962f 100644 --- a/src/Tags/Model/TagsWithStatsList.php +++ b/src/Tags/Model/TagsWithStatsList.php @@ -21,7 +21,7 @@ private function __construct(Closure $pageLoader, int $itemsPerPage) } /** - * @param Closure(int $page, int $itemsPerPage): array{array, array} $pageLoader + * @param Closure(int $page, int $itemsPerPage): list{array, array} $pageLoader */ public static function forTupleLoader(Closure $pageLoader): self { @@ -29,7 +29,7 @@ public static function forTupleLoader(Closure $pageLoader): self } /** - * @param Closure(int $page, int $itemsPerPage): array{array, array} $pageLoader + * @param Closure(int $page, int $itemsPerPage): list{array, array} $pageLoader */ public static function forNonPaginatedTupleLoader(Closure $pageLoader): self { diff --git a/src/Tags/TagsClient.php b/src/Tags/TagsClient.php index d26bd8e..ec909d1 100644 --- a/src/Tags/TagsClient.php +++ b/src/Tags/TagsClient.php @@ -51,7 +51,12 @@ public function listTagsWithStatsWithFilter(TagsFilter $filter): TagsWithStatsLi }; $tupleLoader = function (int $page, int $itemsPerPage) use ($buildQueryWithPage): array { $payload = $this->httpClient->getFromShlink('/tags/stats', $buildQueryWithPage($page, $itemsPerPage)); - return [$payload['tags']['data'] ?? [], $payload['tags']['pagination'] ?? []]; + /** @var array $data */ + $data = $payload['tags']['data'] ?? []; + /** @var array $pagination */ + $pagination = $payload['tags']['pagination'] ?? []; + + return [$data, $pagination]; }; return $filter->shouldPaginateRequest() diff --git a/src/Utils/JsonDecoder.php b/src/Utils/JsonDecoder.php index 55cc0d2..407bf94 100644 --- a/src/Utils/JsonDecoder.php +++ b/src/Utils/JsonDecoder.php @@ -17,6 +17,7 @@ class JsonDecoder */ public static function decode(string $data): array { - return json_decode($data, true, 512, JSON_THROW_ON_ERROR); + // @mago-expect analysis:mixed-return-statement + return json_decode($data, associative: true, flags: JSON_THROW_ON_ERROR); } } diff --git a/src/Visits/Model/VisitsList.php b/src/Visits/Model/VisitsList.php index b66ed8e..0341655 100644 --- a/src/Visits/Model/VisitsList.php +++ b/src/Visits/Model/VisitsList.php @@ -16,7 +16,7 @@ final class VisitsList extends ListEndpointIterator private const ITEMS_PER_PAGE = 1000; /** - * @param Closure(int $page, int $itemsPerPage): array{array, array} $visitsLoader + * @param Closure(int $page, int $itemsPerPage): list{array, array} $visitsLoader * @param Closure(array): VisitType $itemMapper */ private function __construct(Closure $visitsLoader, Closure $itemMapper) @@ -25,7 +25,7 @@ private function __construct(Closure $visitsLoader, Closure $itemMapper) } /** - * @param Closure(int $page, int $itemsPerPage): array{array, array} $visitsLoader + * @param Closure(int $page, int $itemsPerPage): list{array, array} $visitsLoader * @return VisitsList */ public static function forTupleLoader(Closure $visitsLoader): self @@ -34,7 +34,7 @@ public static function forTupleLoader(Closure $visitsLoader): self } /** - * @param Closure(int $page, int $itemsPerPage): array{array, array} $visitsLoader + * @param Closure(int $page, int $itemsPerPage): list{array, array} $visitsLoader * @return VisitsList */ public static function forOrphanVisitsTupleLoader(Closure $visitsLoader): self diff --git a/test/ShortUrls/Model/ShortUrlCreationTest.php b/test/ShortUrls/Model/ShortUrlCreationTest.php index fae20c5..32ffbea 100644 --- a/test/ShortUrls/Model/ShortUrlCreationTest.php +++ b/test/ShortUrls/Model/ShortUrlCreationTest.php @@ -34,7 +34,7 @@ public static function provideConfigs(): iterable yield [ static fn () => ShortUrlCreation::forLongUrl('https://foo.com') ->withTags('foo', 'bar') - ->validSince($date) // @phpstan-ignore-line + ->validSince($date) ->withCustomSlug('some-slug') ->withPathPrefix('my-prefix-'), [ @@ -42,7 +42,7 @@ public static function provideConfigs(): iterable 'tags' => ['foo', 'bar'], 'customSlug' => 'some-slug', 'pathPrefix' => 'my-prefix-', - 'validSince' => $date->format(DateTimeInterface::ATOM), // @phpstan-ignore-line + 'validSince' => $date->format(DateTimeInterface::ATOM), ], ]; yield [ diff --git a/test/ShortUrls/Model/ShortUrlEditionTest.php b/test/ShortUrls/Model/ShortUrlEditionTest.php index 981ffa3..75f33a3 100644 --- a/test/ShortUrls/Model/ShortUrlEditionTest.php +++ b/test/ShortUrls/Model/ShortUrlEditionTest.php @@ -24,19 +24,20 @@ public function payloadIsBuiltAsExpected(callable $createObject, array $expected public static function provideConfigs(): iterable { + /** @var DateTimeImmutable $date */ $date = DateTimeImmutable::createFromFormat('Y-m-d', '2021-01-01'); yield [ShortUrlEdition::create(...), []]; yield [ static fn () => ShortUrlEdition::create() ->withTags('foo', 'bar') - ->validUntil($date) // @phpstan-ignore-line + ->validUntil($date) ->withTitle('the title') ->withMaxVisits(50), [ 'tags' => ['foo', 'bar'], 'maxVisits' => 50, - 'validUntil' => $date->format(DateTimeInterface::ATOM), // @phpstan-ignore-line + 'validUntil' => $date->format(DateTimeInterface::ATOM), 'title' => 'the title', ], ]; diff --git a/test/Tags/TagsClientTest.php b/test/Tags/TagsClientTest.php index fb86363..4830171 100644 --- a/test/Tags/TagsClientTest.php +++ b/test/Tags/TagsClientTest.php @@ -19,7 +19,6 @@ use Shlinkio\Shlink\SDK\Tags\Model\TagRenaming; use Shlinkio\Shlink\SDK\Tags\Model\TagsFilter; use Shlinkio\Shlink\SDK\Tags\Model\TagsListOrderField; -use Shlinkio\Shlink\SDK\Tags\Model\TagWithStats; use Shlinkio\Shlink\SDK\Tags\TagsClient; use Throwable; @@ -89,7 +88,6 @@ public function listTagsWithStatsReturnsExpectedVisitsCount(): void $list = $this->tagsClient->listTagsWithStats(); - /** @var TagWithStats $item */ foreach ($list as $item) { self::assertEquals(3, $item->visitsSummary->total); self::assertEquals(2, $item->visitsSummary->nonBots); From d7d0b20245c7c8b101846f89b7061e682e1f23f8 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 30 Jul 2026 13:33:07 +0200 Subject: [PATCH 2/2] Fixed some more baseline analysis errors --- mago-analyze-baseline.toml | 492 ++++++------------ .../Exception/DomainNotFoundException.php | 3 + src/Exception/InvalidDataException.php | 3 + src/Http/Exception/HttpException.php | 10 + src/RedirectRules/Model/RedirectRule.php | 7 + src/RedirectRules/Model/RedirectRulesList.php | 3 + .../DeleteShortUrlThresholdException.php | 3 + .../Exception/InvalidLongUrlException.php | 3 + .../Exception/NonUniqueSlugException.php | 3 + .../Exception/ShortUrlNotFoundException.php | 3 + src/ShortUrls/Model/ShortUrl.php | 18 +- src/ShortUrls/Model/ShortUrlMeta.php | 3 + src/Tags/Exception/TagConflictException.php | 3 + src/Tags/Exception/TagNotFoundException.php | 3 + src/Tags/Model/TagWithStats.php | 10 + src/Visits/Model/OrphanVisit.php | 12 + src/Visits/Model/Visit.php | 13 +- src/Visits/Model/VisitLocation.php | 11 + src/Visits/Model/VisitsDeletion.php | 3 + src/Visits/Model/VisitsOverview.php | 6 + src/Visits/Model/VisitsSummary.php | 3 + 21 files changed, 274 insertions(+), 341 deletions(-) diff --git a/mago-analyze-baseline.toml b/mago-analyze-baseline.toml index 63f8ffc..aee773e 100644 --- a/mago-analyze-baseline.toml +++ b/mago-analyze-baseline.toml @@ -79,51 +79,21 @@ message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink count = 1 [[issues]] -file = "src/Domains/Exception/DomainNotFoundException.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `Shlinkio\Shlink\SDK\Domains\Exception\DomainNotFoundException::__construct`: expected `string`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/Domains/Exception/DomainNotFoundException.php" -code = "mixed-assignment" -message = "Assigning `nonnull` type to a variable may lead to unexpected behavior." -count = 1 - -[[issues]] -file = "src/Exception/InvalidDataException.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `Shlinkio\Shlink\SDK\Exception\InvalidDataException::__construct`: expected `array`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/Exception/InvalidDataException.php" -code = "mixed-assignment" -message = "Assigning `nonnull` type to a variable may lead to unexpected behavior." -count = 1 - -[[issues]] -file = "src/Http/Exception/HttpException.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Http\ErrorType::tryFrom`: expected `string`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/Http/Exception/HttpException.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `Shlinkio\Shlink\SDK\Http\Exception\HttpException::__construct`: expected `string`, but found `nonnull`.' +file = "src/Domains/DomainsClient.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Exception\InvalidDataException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException}>`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException`.''' count = 1 [[issues]] file = "src/Http/Exception/HttpException.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #3 of `Shlinkio\Shlink\SDK\Http\Exception\HttpException::__construct`: expected `string`, but found `nonnull`.' +code = "less-specific-nested-argument-type" +message = '''Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Http\Exception\HttpException::fromPayload`: expected `array{'detail'?: string, 'status'?: int, 'title'?: string, 'type'?: string, ...}`, but provided type `array` is less specific.''' count = 1 [[issues]] file = "src/Http/Exception/HttpException.php" code = "mixed-argument" -message = 'Invalid argument type for argument #4 of `Shlinkio\Shlink\SDK\Http\Exception\HttpException::__construct`: expected `int`, but found `nonnull`.' +message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Http\ErrorType::tryFrom`: expected `string`, but found `nonnull`.' count = 1 [[issues]] @@ -181,457 +151,301 @@ message = 'Could not infer a precise return type for function `Shlinkio\Shlink\S count = 1 [[issues]] -file = "src/RedirectRules/Model/RedirectRule.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\RedirectRules\Model\RedirectRule::__construct`: expected `string`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/RedirectRules/Model/RedirectRule.php" -code = "mixed-argument" -message = "Invalid argument type for argument #2 of `array_map`: expected `array<('K.array_map() extends array-key), array{'matchKey'?: string, 'matchValue'?: string, 'type'?: string}>`, but found `nonnull`." -count = 1 - -[[issues]] -file = "src/RedirectRules/Model/RedirectRulesList.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\RedirectRules\Model\RedirectRulesList::__construct`: expected `string`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/RedirectRules/Model/RedirectRulesList.php" -code = "mixed-argument" -message = "Invalid argument type for argument #2 of `array_map`: expected `array<('K.array_map() extends array-key), array>`, but found `nonnull`." -count = 1 - -[[issues]] -file = "src/ShortUrls/Exception/DeleteShortUrlThresholdException.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrlIdentifier::fromShortCodeAndDomain`: expected `string`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/ShortUrls/Exception/DeleteShortUrlThresholdException.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrlIdentifier::fromShortCode`: expected `string`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/ShortUrls/Exception/DeleteShortUrlThresholdException.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrlIdentifier::fromShortCodeAndDomain`: expected `string`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/ShortUrls/Exception/DeleteShortUrlThresholdException.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #3 of `Shlinkio\Shlink\SDK\ShortUrls\Exception\DeleteShortUrlThresholdException::__construct`: expected `int`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/ShortUrls/Exception/DeleteShortUrlThresholdException.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." +file = "src/RedirectRules/RedirectRulesClient.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Exception\InvalidDataException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException}>`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException`.''' count = 1 [[issues]] -file = "src/ShortUrls/Exception/DeleteShortUrlThresholdException.php" -code = "mixed-assignment" -message = "Assigning `nonnull` type to a variable may lead to unexpected behavior." +file = "src/RedirectRules/RedirectRulesClient.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\RedirectRules\Model\RedirectRulesList::fromArray`: expected `array{'defaultLongUrl'?: string, 'redirectRules'?: list}`, but possibly received `array`.''' count = 2 [[issues]] -file = "src/ShortUrls/Exception/InvalidLongUrlException.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `Shlinkio\Shlink\SDK\ShortUrls\Exception\InvalidLongUrlException::__construct`: expected `string`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/ShortUrls/Exception/InvalidLongUrlException.php" -code = "mixed-assignment" -message = "Assigning `nonnull` type to a variable may lead to unexpected behavior." -count = 1 - -[[issues]] -file = "src/ShortUrls/Exception/NonUniqueSlugException.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `Shlinkio\Shlink\SDK\ShortUrls\Exception\NonUniqueSlugException::__construct`: expected `string`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/ShortUrls/Exception/NonUniqueSlugException.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #3 of `Shlinkio\Shlink\SDK\ShortUrls\Exception\NonUniqueSlugException::__construct`: expected `null|string`, but found `mixed`.' -count = 1 - -[[issues]] -file = "src/ShortUrls/Exception/NonUniqueSlugException.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 1 - -[[issues]] -file = "src/ShortUrls/Exception/NonUniqueSlugException.php" -code = "mixed-assignment" -message = "Assigning `nonnull` type to a variable may lead to unexpected behavior." -count = 1 - -[[issues]] -file = "src/ShortUrls/Exception/ShortUrlNotFoundException.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrlIdentifier::fromShortCodeAndDomain`: expected `string`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/ShortUrls/Exception/ShortUrlNotFoundException.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrlIdentifier::fromShortCode`: expected `string`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/ShortUrls/Exception/ShortUrlNotFoundException.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrlIdentifier::fromShortCodeAndDomain`: expected `string`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/ShortUrls/Exception/ShortUrlNotFoundException.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 1 - -[[issues]] -file = "src/ShortUrls/Exception/ShortUrlNotFoundException.php" -code = "mixed-assignment" -message = "Assigning `nonnull` type to a variable may lead to unexpected behavior." -count = 1 - -[[issues]] -file = "src/ShortUrls/Model/ShortUrl.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrl::__construct`: expected `string`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/ShortUrls/Model/ShortUrl.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrlMeta::fromArray`: expected `array`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/ShortUrls/Model/ShortUrl.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsSummary::fromArray`: expected `array`, but found `nonnull`.' -count = 1 - -[[issues]] -file = "src/ShortUrls/Model/ShortUrl.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #10 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrl::__construct`: expected `array`, but found `nonnull`.' -count = 1 +file = "src/RedirectRules/RedirectRulesClient.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Exception\ShortUrlNotFoundException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException`.''' +count = 2 [[issues]] file = "src/ShortUrls/Model/ShortUrl.php" -code = "mixed-argument" -message = "Invalid argument type for argument #2 of `DateTimeImmutable::createFromFormat`: expected `string`, but found `mixed`." +code = "possibly-false-argument" +message = 'Argument #4 of method `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrl::__construct` is possibly `false`, but parameter type `DateTimeInterface` does not accept it.' count = 1 [[issues]] file = "src/ShortUrls/Model/ShortUrl.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrl::__construct`: expected `string`, but found `nonnull`.' +code = "possibly-null-argument" +message = "Argument #2 of method `DateTimeImmutable::createFromFormat` is possibly `null`, but parameter type `string` does not accept it." count = 1 [[issues]] file = "src/ShortUrls/Model/ShortUrl.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #3 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrl::__construct`: expected `string`, but found `nonnull`.' +code = "possibly-undefined-string-array-index" +message = "Possibly undefined array key 'dateCreated' accessed on `array{'crawlable'?: bool, 'dateCreated'?: string, 'domain'?: string, 'forwardQuery'?: bool, 'hasRedirectRules'?: bool, 'longUrl'?: string, 'meta'?: array{'maxVisits'?: positive-int, 'validSince'?: string, 'validUntil'?: string}, 'shortCode'?: string, 'shortUrl'?: string, 'tags'?: array, 'title'?: string, 'visitsSummary'?: array{'bots'?: non-negative-int, 'nonBots'?: non-negative-int, 'total'?: non-negative-int}}`." count = 1 [[issues]] -file = "src/ShortUrls/Model/ShortUrl.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #5 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrl::__construct`: expected `null|string`, but found `mixed`.' +file = "src/ShortUrls/Model/ShortUrlsList.php" +code = "less-specific-nested-argument-type" +message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Model\ListEndpointIterator::__construct`: expected `(closure(int, int): list{array, array})`, but provided type `(closure(...mixed=): mixed)` is less specific.' count = 1 [[issues]] -file = "src/ShortUrls/Model/ShortUrl.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #6 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrl::__construct`: expected `null|string`, but found `mixed`.' +file = "src/ShortUrls/ShortUrlsClient.php" +code = "less-specific-nested-argument-type" +message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrlsList::forTupleLoader`: expected `(closure(int, int): list{list, list})`, but provided type `(closure(int, int): list{nonnull, nonnull})` is less specific.' count = 1 [[issues]] -file = "src/ShortUrls/Model/ShortUrl.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #7 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrl::__construct`: expected `bool`, but found `nonnull`.' -count = 1 +file = "src/ShortUrls/ShortUrlsClient.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Exception\InvalidDataException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException}>`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException`.''' +count = 2 [[issues]] -file = "src/ShortUrls/Model/ShortUrl.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #8 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrl::__construct`: expected `bool`, but found `nonnull`.' +file = "src/ShortUrls/ShortUrlsClient.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Exception\DeleteShortUrlThresholdException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException`.''' count = 1 [[issues]] -file = "src/ShortUrls/Model/ShortUrl.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #9 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrl::__construct`: expected `bool|null`, but found `mixed`.' +file = "src/ShortUrls/ShortUrlsClient.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Exception\NonUniqueSlugException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException`.''' count = 1 [[issues]] -file = "src/ShortUrls/Model/ShortUrl.php" -code = "possibly-false-argument" -message = 'Argument #4 of method `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrl::__construct` is possibly `false`, but parameter type `DateTimeInterface` does not accept it.' -count = 1 +file = "src/ShortUrls/ShortUrlsClient.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Exception\ShortUrlNotFoundException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException`.''' +count = 3 [[issues]] -file = "src/ShortUrls/Model/ShortUrlMeta.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrlMeta::toNullableDate`: expected `null|string`, but found `mixed`.' -count = 2 +file = "src/ShortUrls/ShortUrlsClient.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrl::fromArray`: expected `array{'crawlable'?: bool, 'dateCreated'?: string, 'domain'?: string, 'forwardQuery'?: bool, 'hasRedirectRules'?: bool, 'longUrl'?: string, 'meta'?: array{'maxVisits'?: positive-int, 'validSince'?: string, 'validUntil'?: string}, 'shortCode'?: string, 'shortUrl'?: string, 'tags'?: array, 'title'?: string, 'visitsSummary'?: array{'bots'?: non-negative-int, 'nonBots'?: non-negative-int, 'total'?: non-negative-int}}`, but possibly received `array`.''' +count = 3 [[issues]] -file = "src/ShortUrls/Model/ShortUrlMeta.php" +file = "src/Tags/Model/TagsFilter.php" code = "mixed-argument" -message = 'Invalid argument type for argument #3 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrlMeta::__construct`: expected `int|null`, but found `mixed`.' +message = "Invalid argument type for argument #2 of `explode`: expected `string`, but found `nonnull`." count = 1 [[issues]] -file = "src/ShortUrls/Model/ShortUrlsList.php" +file = "src/Tags/Model/TagsWithStatsList.php" code = "less-specific-nested-argument-type" message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Model\ListEndpointIterator::__construct`: expected `(closure(int, int): list{array, array})`, but provided type `(closure(...mixed=): mixed)` is less specific.' count = 1 [[issues]] -file = "src/ShortUrls/ShortUrlsClient.php" -code = "less-specific-nested-argument-type" -message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrlsList::forTupleLoader`: expected `(closure(int, int): list{list, list})`, but provided type `(closure(int, int): list{nonnull, nonnull})` is less specific.' +file = "src/Tags/TagsClient.php" +code = "mixed-return-statement" +message = 'Could not infer a precise return type for function `Shlinkio\Shlink\SDK\Tags\TagsClient::listTagsWithFilter`. Saw type `nonnull`.' count = 1 [[issues]] -file = "src/Tags/Exception/TagConflictException.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `Shlinkio\Shlink\SDK\Tags\Exception\TagConflictException::__construct`: expected `string`, but found `nonnull`.' +file = "src/Tags/TagsClient.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Exception\InvalidDataException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException}>`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException`.''' count = 1 [[issues]] -file = "src/Tags/Exception/TagConflictException.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #3 of `Shlinkio\Shlink\SDK\Tags\Exception\TagConflictException::__construct`: expected `string`, but found `nonnull`.' +file = "src/Tags/TagsClient.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Tags\Exception\TagConflictException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException`.''' count = 1 [[issues]] -file = "src/Tags/Exception/TagConflictException.php" -code = "mixed-assignment" -message = "Assigning `nonnull` type to a variable may lead to unexpected behavior." -count = 2 - -[[issues]] -file = "src/Tags/Exception/TagNotFoundException.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `Shlinkio\Shlink\SDK\Tags\Exception\TagNotFoundException::__construct`: expected `string`, but found `nonnull`.' +file = "src/Tags/TagsClient.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Tags\Exception\TagNotFoundException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException`.''' count = 1 [[issues]] -file = "src/Tags/Exception/TagNotFoundException.php" -code = "mixed-assignment" -message = "Assigning `nonnull` type to a variable may lead to unexpected behavior." +file = "src/Visits/Model/OrphanVisit.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\Visit::fromArray`: expected `array{'date': string, 'potentialBot'?: bool, 'redirectUrl'?: string, 'referer'?: string, 'userAgent'?: string, 'visitLocation'?: array, 'visitedUrl'?: string}`, but possibly received `array{'date': string, 'potentialBot'?: bool, 'redirectUrl'?: string, 'referer'?: string, 'type'?: string, 'userAgent'?: string, 'visitLocation'?: array, 'visitedUrl'?: string}`.''' count = 1 [[issues]] -file = "src/Tags/Model/TagWithStats.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Tags\Model\TagWithStats::__construct`: expected `string`, but found `nonnull`.' +file = "src/Visits/Model/Visit.php" +code = "possibly-false-argument" +message = 'Argument #2 of method `Shlinkio\Shlink\SDK\Visits\Model\Visit::__construct` is possibly `false`, but parameter type `DateTimeInterface` does not accept it.' count = 1 [[issues]] -file = "src/Tags/Model/TagWithStats.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsSummary::fromArray`: expected `array`, but found `nonnull`.' +file = "src/Visits/Model/Visit.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitLocation::fromArray`: expected `array{'cityName'?: string, 'countryCode'?: string, 'countryName'?: string, 'latitude'?: float, 'longitude'?: float, 'regionName'?: string, 'timezone'?: string}`, but possibly received `array`.''' count = 1 [[issues]] -file = "src/Tags/Model/TagWithStats.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `Shlinkio\Shlink\SDK\Tags\Model\TagWithStats::__construct`: expected `int`, but found `nonnull`.' +file = "src/Visits/Model/VisitsList.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #2 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsList::__construct`: expected `(closure(array): Shlinkio\Shlink\SDK\Visits\Model\OrphanVisit)`, but possibly received `(closure(array{'date': string, 'potentialBot'?: bool, 'redirectUrl'?: string, 'referer'?: string, 'type'?: string, 'userAgent'?: string, 'visitLocation'?: array, 'visitedUrl'?: string}): Shlinkio\Shlink\SDK\Visits\Model\OrphanVisit)`.''' count = 1 [[issues]] -file = "src/Tags/Model/TagsFilter.php" -code = "mixed-argument" -message = "Invalid argument type for argument #2 of `explode`: expected `string`, but found `nonnull`." +file = "src/Visits/Model/VisitsList.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #2 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsList::__construct`: expected `(closure(array): Shlinkio\Shlink\SDK\Visits\Model\Visit)`, but possibly received `(closure(array{'date': string, 'potentialBot'?: bool, 'redirectUrl'?: string, 'referer'?: string, 'userAgent'?: string, 'visitLocation'?: array, 'visitedUrl'?: string}): Shlinkio\Shlink\SDK\Visits\Model\Visit)`.''' count = 1 [[issues]] -file = "src/Tags/Model/TagsWithStatsList.php" +file = "src/Visits/VisitsClient.php" code = "less-specific-nested-argument-type" -message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Model\ListEndpointIterator::__construct`: expected `(closure(int, int): list{array, array})`, but provided type `(closure(...mixed=): mixed)` is less specific.' -count = 1 - -[[issues]] -file = "src/Tags/TagsClient.php" -code = "mixed-return-statement" -message = 'Could not infer a precise return type for function `Shlinkio\Shlink\SDK\Tags\TagsClient::listTagsWithFilter`. Saw type `nonnull`.' +message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsList::forOrphanVisitsTupleLoader`: expected `(closure(int, int): list{array, array})`, but provided type `(closure(...mixed=): mixed)` is less specific.' count = 1 [[issues]] -file = "src/Visits/Model/OrphanVisit.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\OrphanVisitType::tryFrom`: expected `string`, but found `nonnull`.' -count = 1 +file = "src/Visits/VisitsClient.php" +code = "less-specific-nested-argument-type" +message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsList::forTupleLoader`: expected `(closure(int, int): list{array, array})`, but provided type `(closure(...mixed=): mixed)` is less specific.' +count = 4 [[issues]] -file = "src/Visits/Model/Visit.php" +file = "src/Visits/VisitsClient.php" code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\Visit::__construct`: expected `string`, but found `nonnull`.' +message = '''Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsOverview::fromArray`: expected `array{'nonOrphanVisits'?: array{'bots'?: non-negative-int, 'nonBots'?: non-negative-int, 'total'?: non-negative-int}, 'orphanVisits'?: array{'bots'?: non-negative-int, 'nonBots'?: non-negative-int, 'total'?: non-negative-int}}`, but found `nonnull`.''' count = 1 [[issues]] -file = "src/Visits/Model/Visit.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitLocation::fromArray`: expected `array`, but found `nonnull`.' +file = "src/Visits/VisitsClient.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Domains\Exception\DomainNotFoundException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException`.''' count = 1 [[issues]] -file = "src/Visits/Model/Visit.php" -code = "mixed-argument" -message = "Invalid argument type for argument #2 of `DateTimeImmutable::createFromFormat`: expected `string`, but found `mixed`." +file = "src/Visits/VisitsClient.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Exception\ShortUrlNotFoundException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException`.''' count = 1 [[issues]] -file = "src/Visits/Model/Visit.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #3 of `Shlinkio\Shlink\SDK\Visits\Model\Visit::__construct`: expected `string`, but found `nonnull`.' +file = "src/Visits/VisitsClient.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Tags\Exception\TagNotFoundException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException`.''' count = 1 [[issues]] -file = "src/Visits/Model/Visit.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #4 of `Shlinkio\Shlink\SDK\Visits\Model\Visit::__construct`: expected `bool`, but found `nonnull`.' -count = 1 +file = "src/Visits/VisitsClient.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsDeletion::fromArray`: expected `array{'deletedVisits'?: non-negative-int}`, but possibly received `array`.''' +count = 2 [[issues]] -file = "src/Visits/Model/Visit.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #5 of `Shlinkio\Shlink\SDK\Visits\Model\Visit::__construct`: expected `string`, but found `nonnull`.' +file = "test-integration/TestCase/AbstractTestCase.php" +code = "mixed-return-statement" +message = 'Could not infer a precise return type for function `ShlinkioIntegrationTest\Shlink\SDK\TestCase\AbstractTestCase::httpClient`. Saw type `nonnull`.' count = 1 [[issues]] -file = "src/Visits/Model/Visit.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #6 of `Shlinkio\Shlink\SDK\Visits\Model\Visit::__construct`: expected `null|string`, but found `mixed`.' +file = "test-integration/TestCase/AbstractTestCase.php" +code = "mixed-return-statement" +message = 'Could not infer a precise return type for function `ShlinkioIntegrationTest\Shlink\SDK\TestCase\AbstractTestCase::shlinkClient`. Saw type `nonnull`.' count = 1 [[issues]] -file = "src/Visits/Model/Visit.php" -code = "possibly-false-argument" -message = 'Argument #2 of method `Shlinkio\Shlink\SDK\Visits\Model\Visit::__construct` is possibly `false`, but parameter type `DateTimeInterface` does not accept it.' +file = "test/Config/ArrayShlinkConfigTest.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Config\ArrayShlinkConfig::fromArray`: expected `array{'apiKey'?: string, 'baseUrl'?: string, 'version'?: string}`, but possibly received `array`.''' count = 1 [[issues]] -file = "src/Visits/Model/VisitLocation.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitLocation::__construct`: expected `string`, but found `nonnull`.' +file = "test/Domains/Exception/DomainNotFoundExceptionTest.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Domains\Exception\DomainNotFoundException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException>`.''' count = 1 [[issues]] -file = "src/Visits/Model/VisitLocation.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `Shlinkio\Shlink\SDK\Visits\Model\VisitLocation::__construct`: expected `string`, but found `nonnull`.' +file = "test/Exception/InvalidDataExceptionTest.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Exception\InvalidDataException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException}>`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException>`.''' count = 1 [[issues]] -file = "src/Visits/Model/VisitLocation.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #3 of `Shlinkio\Shlink\SDK\Visits\Model\VisitLocation::__construct`: expected `string`, but found `nonnull`.' +file = "test/Http/Exception/HttpExceptionTest.php" +code = "less-specific-nested-argument-type" +message = '''Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Http\Exception\HttpException::fromPayload`: expected `array{'detail'?: string, 'status'?: int, 'title'?: string, 'type'?: string, ...}`, but provided type `array` is less specific.''' count = 1 [[issues]] -file = "src/Visits/Model/VisitLocation.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #4 of `Shlinkio\Shlink\SDK\Visits\Model\VisitLocation::__construct`: expected `string`, but found `nonnull`.' +file = "test/RedirectRules/Model/RedirectRuleTest.php" +code = "less-specific-nested-argument-type" +message = '''Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\RedirectRules\Model\RedirectRule::fromArray`: expected `array{'conditions'?: list, 'longUrl'?: string, 'priority'?: positive-int}`, but provided type `array{'priority': mixed}` is less specific.''' count = 1 [[issues]] -file = "src/Visits/Model/VisitLocation.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #5 of `Shlinkio\Shlink\SDK\Visits\Model\VisitLocation::__construct`: expected `float`, but found `nonnull`.' +file = "test/ShortUrls/Exception/DeleteShortUrlThresholdExceptionTest.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Exception\DeleteShortUrlThresholdException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException>`.''' count = 1 [[issues]] -file = "src/Visits/Model/VisitLocation.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #6 of `Shlinkio\Shlink\SDK\Visits\Model\VisitLocation::__construct`: expected `float`, but found `nonnull`.' +file = "test/ShortUrls/Exception/InvalidLongUrlExceptionTest.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Exception\InvalidLongUrlException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException>`.''' count = 1 [[issues]] -file = "src/Visits/Model/VisitLocation.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #7 of `Shlinkio\Shlink\SDK\Visits\Model\VisitLocation::__construct`: expected `string`, but found `nonnull`.' +file = "test/ShortUrls/Exception/NonUniqueSlugExceptionTest.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Exception\NonUniqueSlugException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException>`.''' count = 1 [[issues]] -file = "src/Visits/Model/VisitsDeletion.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsDeletion::__construct`: expected `non-negative-int`, but found `nonnull`.' +file = "test/ShortUrls/Exception/ShortUrlNotFoundExceptionTest.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Exception\ShortUrlNotFoundException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException>`.''' count = 1 [[issues]] -file = "src/Visits/Model/VisitsOverview.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsSummary::fromArray`: expected `array`, but found `nonnull`.' -count = 2 - -[[issues]] -file = "src/Visits/Model/VisitsSummary.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsSummary::__construct`: expected `non-negative-int`, but found `nonnull`.' +file = "test/ShortUrls/Model/ShortUrlMetaTest.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrlMeta::fromArray`: expected `array{'maxVisits'?: positive-int, 'validSince'?: string, 'validUntil'?: string}`, but possibly received `array`.''' count = 1 [[issues]] -file = "src/Visits/Model/VisitsSummary.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsSummary::__construct`: expected `non-negative-int`, but found `nonnull`.' +file = "test/ShortUrls/Model/ShortUrlTest.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\ShortUrls\Model\ShortUrl::fromArray`: expected `array{'crawlable'?: bool, 'dateCreated'?: string, 'domain'?: string, 'forwardQuery'?: bool, 'hasRedirectRules'?: bool, 'longUrl'?: string, 'meta'?: array{'maxVisits'?: positive-int, 'validSince'?: string, 'validUntil'?: string}, 'shortCode'?: string, 'shortUrl'?: string, 'tags'?: array, 'title'?: string, 'visitsSummary'?: array{'bots'?: non-negative-int, 'nonBots'?: non-negative-int, 'total'?: non-negative-int}}`, but possibly received `array`.''' count = 1 [[issues]] -file = "src/Visits/Model/VisitsSummary.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #3 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsSummary::__construct`: expected `non-negative-int`, but found `nonnull`.' +file = "test/Tags/Exception/TagConflictExceptionTest.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Tags\Exception\TagConflictException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException>`.''' count = 1 [[issues]] -file = "src/Visits/VisitsClient.php" -code = "less-specific-nested-argument-type" -message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsList::forOrphanVisitsTupleLoader`: expected `(closure(int, int): list{array, array})`, but provided type `(closure(...mixed=): mixed)` is less specific.' +file = "test/Tags/Exception/TagNotFoundExceptionTest.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Tags\Exception\TagNotFoundException::fromHttpException`: expected `Shlinkio\Shlink\SDK\Http\Exception\HttpException`, but possibly received `Shlinkio\Shlink\SDK\Http\Exception\HttpException>`.''' count = 1 [[issues]] -file = "src/Visits/VisitsClient.php" -code = "less-specific-nested-argument-type" -message = 'Argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsList::forTupleLoader`: expected `(closure(int, int): list{array, array})`, but provided type `(closure(...mixed=): mixed)` is less specific.' -count = 4 - -[[issues]] -file = "src/Visits/VisitsClient.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitsOverview::fromArray`: expected `array`, but found `nonnull`.' +file = "test/Tags/Model/TagWithStatsTest.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Tags\Model\TagWithStats::fromArray`: expected `array{'shortUrlsCount'?: non-negative-int, 'tag'?: string, 'visitsSummary'?: array{'bots'?: non-negative-int, 'nonBots'?: non-negative-int, 'total'?: non-negative-int}}`, but possibly received `array`.''' count = 1 [[issues]] -file = "test-integration/TestCase/AbstractTestCase.php" -code = "mixed-return-statement" -message = 'Could not infer a precise return type for function `ShlinkioIntegrationTest\Shlink\SDK\TestCase\AbstractTestCase::httpClient`. Saw type `nonnull`.' +file = "test/Visits/Model/OrphanVisitTest.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\OrphanVisit::fromArray`: expected `array{'date': string, 'potentialBot'?: bool, 'redirectUrl'?: string, 'referer'?: string, 'type'?: string, 'userAgent'?: string, 'visitLocation'?: array, 'visitedUrl'?: string}`, but possibly received `array`.''' count = 1 [[issues]] -file = "test-integration/TestCase/AbstractTestCase.php" -code = "mixed-return-statement" -message = 'Could not infer a precise return type for function `ShlinkioIntegrationTest\Shlink\SDK\TestCase\AbstractTestCase::shlinkClient`. Saw type `nonnull`.' +file = "test/Visits/Model/VisitLocationTest.php" +code = "possibly-invalid-argument" +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\VisitLocation::fromArray`: expected `array{'cityName'?: string, 'countryCode'?: string, 'countryName'?: string, 'latitude'?: float, 'longitude'?: float, 'regionName'?: string, 'timezone'?: string}`, but possibly received `array`.''' count = 1 [[issues]] -file = "test/Config/ArrayShlinkConfigTest.php" +file = "test/Visits/Model/VisitTest.php" code = "possibly-invalid-argument" -message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Config\ArrayShlinkConfig::fromArray`: expected `array{'apiKey'?: string, 'baseUrl'?: string, 'version'?: string}`, but possibly received `array`.''' +message = '''Possible argument type mismatch for argument #1 of `Shlinkio\Shlink\SDK\Visits\Model\Visit::fromArray`: expected `array{'date': string, 'potentialBot'?: bool, 'redirectUrl'?: string, 'referer'?: string, 'userAgent'?: string, 'visitLocation'?: array, 'visitedUrl'?: string}`, but possibly received `array`.''' count = 1 diff --git a/src/Domains/Exception/DomainNotFoundException.php b/src/Domains/Exception/DomainNotFoundException.php index d68e064..38f372e 100644 --- a/src/Domains/Exception/DomainNotFoundException.php +++ b/src/Domains/Exception/DomainNotFoundException.php @@ -15,6 +15,9 @@ private function __construct(HttpException $previous, public readonly string $au parent::__construct($previous->detail, $previous->status, $previous); } + /** + * @param HttpException $prev + */ public static function fromHttpException(HttpException $prev): self { $authority = $prev->additional['authority'] ?? ''; diff --git a/src/Exception/InvalidDataException.php b/src/Exception/InvalidDataException.php index 410cdfb..fbab5c8 100644 --- a/src/Exception/InvalidDataException.php +++ b/src/Exception/InvalidDataException.php @@ -17,6 +17,9 @@ private function __construct(HttpException $previous, public readonly array $inv parent::__construct($previous->detail, $previous->status, $previous); } + /** + * @param HttpException $prev + */ public static function fromHttpException(HttpException $prev): self { $invalidElements = $prev->additional['invalidElements'] ?? []; diff --git a/src/Http/Exception/HttpException.php b/src/Http/Exception/HttpException.php index 488f795..ac7d9be 100644 --- a/src/Http/Exception/HttpException.php +++ b/src/Http/Exception/HttpException.php @@ -16,10 +16,16 @@ use const ARRAY_FILTER_USE_KEY; +/** + * @template AdditionalT of array + */ class HttpException extends RuntimeException implements ExceptionInterface { private const array STANDARD_PROBLEM_DETAILS_PROPS = ['type', 'title', 'detail', 'status']; + /** + * @param AdditionalT $additional + */ private function __construct( public readonly ErrorType $type, public readonly string $title, @@ -35,8 +41,12 @@ public static function fromNonSuccessfulResponse(ResponseInterface $resp): self return self::fromPayload(JsonDecoder::decode($resp->getBody()->__toString())); } + /** + * @param array{type?: string, title?: string, detail?: string, status?: int, ...} $payload + */ public static function fromPayload(array $payload): self { + /** @var AdditionalT $additional */ $additional = array_filter( $payload, static fn (string $key) => !in_array($key, self::STANDARD_PROBLEM_DETAILS_PROPS, strict: true), diff --git a/src/RedirectRules/Model/RedirectRule.php b/src/RedirectRules/Model/RedirectRule.php index 9f3a2bc..ba0d4c9 100644 --- a/src/RedirectRules/Model/RedirectRule.php +++ b/src/RedirectRules/Model/RedirectRule.php @@ -18,6 +18,13 @@ */ private function __construct(public string $longUrl, public int $priority, public array $conditions) {} + /** + * @param array{ + * longUrl?: string, + * priority?: positive-int, + * conditions?: list + * } $payload + */ public static function fromArray(array $payload): self { return new self( diff --git a/src/RedirectRules/Model/RedirectRulesList.php b/src/RedirectRules/Model/RedirectRulesList.php index 76c33cc..0ea25e9 100644 --- a/src/RedirectRules/Model/RedirectRulesList.php +++ b/src/RedirectRules/Model/RedirectRulesList.php @@ -16,6 +16,9 @@ */ private function __construct(public string $defaultLongUrl, public array $redirectRules) {} + /** + * @param array{defaultLongUrl?: string, redirectRules?: list} $payload + */ public static function fromArray(array $payload): self { return new self( diff --git a/src/ShortUrls/Exception/DeleteShortUrlThresholdException.php b/src/ShortUrls/Exception/DeleteShortUrlThresholdException.php index 461ae15..a257a5b 100644 --- a/src/ShortUrls/Exception/DeleteShortUrlThresholdException.php +++ b/src/ShortUrls/Exception/DeleteShortUrlThresholdException.php @@ -19,6 +19,9 @@ private function __construct( parent::__construct($previous->detail, $previous->status, $previous); } + /** + * @param HttpException $prev + */ public static function fromHttpException(HttpException $prev): self { $shortCode = $prev->additional['shortCode'] ?? ''; diff --git a/src/ShortUrls/Exception/InvalidLongUrlException.php b/src/ShortUrls/Exception/InvalidLongUrlException.php index f4af8cc..279a6ef 100644 --- a/src/ShortUrls/Exception/InvalidLongUrlException.php +++ b/src/ShortUrls/Exception/InvalidLongUrlException.php @@ -15,6 +15,9 @@ private function __construct(HttpException $previous, public readonly string $lo parent::__construct($previous->detail, $previous->status, $previous); } + /** + * @param HttpException $prev + */ public static function fromHttpException(HttpException $prev): self { $longUrl = $prev->additional['url'] ?? ''; diff --git a/src/ShortUrls/Exception/NonUniqueSlugException.php b/src/ShortUrls/Exception/NonUniqueSlugException.php index 696ec42..c591a86 100644 --- a/src/ShortUrls/Exception/NonUniqueSlugException.php +++ b/src/ShortUrls/Exception/NonUniqueSlugException.php @@ -18,6 +18,9 @@ private function __construct( parent::__construct($previous->detail, $previous->status, $previous); } + /** + * @param HttpException $prev + */ public static function fromHttpException(HttpException $prev): self { $customSlug = $prev->additional['customSlug'] ?? ''; diff --git a/src/ShortUrls/Exception/ShortUrlNotFoundException.php b/src/ShortUrls/Exception/ShortUrlNotFoundException.php index 32e2e7e..b778b69 100644 --- a/src/ShortUrls/Exception/ShortUrlNotFoundException.php +++ b/src/ShortUrls/Exception/ShortUrlNotFoundException.php @@ -16,6 +16,9 @@ private function __construct(HttpException $previous, public readonly ShortUrlId parent::__construct($previous->detail, $previous->status, $previous); } + /** + * @param HttpException $prev + */ public static function fromHttpException(HttpException $prev): self { $shortCode = $prev->additional['shortCode'] ?? ''; diff --git a/src/ShortUrls/Model/ShortUrl.php b/src/ShortUrls/Model/ShortUrl.php index c3ca036..6b7bf7d 100644 --- a/src/ShortUrls/Model/ShortUrl.php +++ b/src/ShortUrls/Model/ShortUrl.php @@ -28,13 +28,29 @@ private function __construct( public VisitsSummary $visitsSummary, ) {} + /** + * @param array{ + * shortCode?: string, + * shortUrl?: string, + * longUrl?: string, + * dateCreated?: string, + * domain?: string, + * title?: string, + * crawlable?: bool, + * forwardQuery?: bool, + * hasRedirectRules?: bool, + * tags?: string[], + * meta?: array{validSince?: string, validUntil?: string, maxVisits?: positive-int}, + * visitsSummary?: array{total?: int<0, max>, nonBots?: int<0, max>, bots?: int<0, max>}, + * } $payload + */ public static function fromArray(array $payload): self { return new self( shortCode: $payload['shortCode'] ?? '', shortUrl: $payload['shortUrl'] ?? '', longUrl: $payload['longUrl'] ?? '', - // @phpstan-ignore-next-line + // @mago-ignore dateCreated: DateTimeImmutable::createFromFormat(DateTimeInterface::ATOM, $payload['dateCreated']), domain: $payload['domain'] ?? null, title: $payload['title'] ?? null, diff --git a/src/ShortUrls/Model/ShortUrlMeta.php b/src/ShortUrls/Model/ShortUrlMeta.php index 6728eb4..0c08b04 100644 --- a/src/ShortUrls/Model/ShortUrlMeta.php +++ b/src/ShortUrls/Model/ShortUrlMeta.php @@ -15,6 +15,9 @@ private function __construct( public int|null $maxVisits, ) {} + /** + * @param array{validSince?: string, validUntil?: string, maxVisits?: positive-int} $payload + */ public static function fromArray(array $payload): self { return new self( diff --git a/src/Tags/Exception/TagConflictException.php b/src/Tags/Exception/TagConflictException.php index 1f26ce4..728d2c3 100644 --- a/src/Tags/Exception/TagConflictException.php +++ b/src/Tags/Exception/TagConflictException.php @@ -18,6 +18,9 @@ private function __construct( parent::__construct($previous->detail, $previous->status, $previous); } + /** + * @param HttpException $prev + */ public static function fromHttpException(HttpException $prev): self { $oldName = $prev->additional['oldName'] ?? ''; diff --git a/src/Tags/Exception/TagNotFoundException.php b/src/Tags/Exception/TagNotFoundException.php index 9d9d870..bd4e323 100644 --- a/src/Tags/Exception/TagNotFoundException.php +++ b/src/Tags/Exception/TagNotFoundException.php @@ -15,6 +15,9 @@ private function __construct(HttpException $previous, public readonly string $ta parent::__construct($previous->detail, $previous->status, $previous); } + /** + * @param HttpException $prev + */ public static function fromHttpException(HttpException $prev): self { $tag = $prev->additional['tag'] ?? ''; diff --git a/src/Tags/Model/TagWithStats.php b/src/Tags/Model/TagWithStats.php index 8f2c5a0..d06f446 100644 --- a/src/Tags/Model/TagWithStats.php +++ b/src/Tags/Model/TagWithStats.php @@ -8,8 +8,18 @@ final readonly class TagWithStats { + /** + * @param int<0, max> $shortUrlsCount + */ private function __construct(public string $tag, public int $shortUrlsCount, public VisitsSummary $visitsSummary) {} + /** + * @param array{ + * tag?: string, + * shortUrlsCount?: int<0, max>, + * visitsSummary?: array{total?: int<0, max>, nonBots?: int<0, max>, bots?: int<0, max>} + * } $payload + */ public static function fromArray(array $payload): self { return new self( diff --git a/src/Visits/Model/OrphanVisit.php b/src/Visits/Model/OrphanVisit.php index 388e337..4999c41 100644 --- a/src/Visits/Model/OrphanVisit.php +++ b/src/Visits/Model/OrphanVisit.php @@ -10,6 +10,18 @@ { private function __construct(private Visit $visit, private OrphanVisitType $type) {} + /** + * @param array{ + * type?: string, + * referer?: string, + * date: string, + * userAgent?: string, + * potentialBot?: bool, + * visitedUrl?: string, + * redirectUrl?: string, + * visitLocation?: array, + * } $payload + */ public static function fromArray(array $payload): self { return new self( diff --git a/src/Visits/Model/Visit.php b/src/Visits/Model/Visit.php index c2671d7..9ec484e 100644 --- a/src/Visits/Model/Visit.php +++ b/src/Visits/Model/Visit.php @@ -19,11 +19,22 @@ private function __construct( private VisitLocation|null $location, ) {} + /** + * @param array{ + * referer?: string, + * date: string, + * userAgent?: string, + * potentialBot?: bool, + * visitedUrl?: string, + * redirectUrl?: string, + * visitLocation?: array, + * } $payload + */ public static function fromArray(array $payload): self { return new self( referer: $payload['referer'] ?? '', - // @phpstan-ignore-next-line + // @mago-ignore date: DateTimeImmutable::createFromFormat(DateTimeInterface::ATOM, $payload['date']), userAgent: $payload['userAgent'] ?? '', potentialBot: $payload['potentialBot'] ?? false, diff --git a/src/Visits/Model/VisitLocation.php b/src/Visits/Model/VisitLocation.php index dbb3671..c5e0cab 100644 --- a/src/Visits/Model/VisitLocation.php +++ b/src/Visits/Model/VisitLocation.php @@ -16,6 +16,17 @@ private function __construct( public string $timezone, ) {} + /** + * @param array{ + * countryCode?: string, + * countryName?: string, + * regionName?: string, + * cityName?: string, + * latitude?: float, + * longitude?: float, + * timezone?: string, + * } $payload + */ public static function fromArray(array $payload): self { return new self( diff --git a/src/Visits/Model/VisitsDeletion.php b/src/Visits/Model/VisitsDeletion.php index 41bfcee..894be5c 100644 --- a/src/Visits/Model/VisitsDeletion.php +++ b/src/Visits/Model/VisitsDeletion.php @@ -13,6 +13,9 @@ */ private function __construct(public int $deletedVisits) {} + /** + * @param array{deletedVisits?: int<0, max>} $payload + */ public static function fromArray(array $payload): self { return new self($payload['deletedVisits'] ?? 0); diff --git a/src/Visits/Model/VisitsOverview.php b/src/Visits/Model/VisitsOverview.php index 56c865d..308e4f1 100644 --- a/src/Visits/Model/VisitsOverview.php +++ b/src/Visits/Model/VisitsOverview.php @@ -10,6 +10,12 @@ { private function __construct(public VisitsSummary $nonOrphanVisits, public VisitsSummary $orphanVisits) {} + /** + * @param array{ + * nonOrphanVisits?: array{total?: int<0, max>, nonBots?: int<0, max>, bots?: int<0, max>}, + * orphanVisits?: array{total?: int<0, max>, nonBots?: int<0, max>, bots?: int<0, max>}, + * } $payload + */ public static function fromArray(array $payload): self { return new self( diff --git a/src/Visits/Model/VisitsSummary.php b/src/Visits/Model/VisitsSummary.php index 68a0ae9..72f69e2 100644 --- a/src/Visits/Model/VisitsSummary.php +++ b/src/Visits/Model/VisitsSummary.php @@ -13,6 +13,9 @@ */ private function __construct(public int $total, public int $nonBots, public int $bots) {} + /** + * @param array{total?: int<0, max>, nonBots?: int<0, max>, bots?: int<0, max>} $payload + */ public static function fromArray(array $payload): self { return new self(