Add missing generic tyoes annotations

This commit is contained in:
Alejandro Celaya
2024-07-29 20:43:52 +02:00
parent 1d24750f43
commit 037cd8a389
53 changed files with 144 additions and 51 deletions

View File

@@ -33,6 +33,9 @@ class GetDomainVisitsCommand extends AbstractVisitsListCommand
->addArgument('domain', InputArgument::REQUIRED, 'The domain which visits we want to get.'); ->addArgument('domain', InputArgument::REQUIRED, 'The domain which visits we want to get.');
} }
/**
* @return Paginator<Visit>
*/
protected function getVisitsPaginator(InputInterface $input, DateRange $dateRange): Paginator protected function getVisitsPaginator(InputInterface $input, DateRange $dateRange): Paginator
{ {
$domain = $input->getArgument('domain'); $domain = $input->getArgument('domain');

View File

@@ -46,6 +46,9 @@ class GetShortUrlVisitsCommand extends AbstractVisitsListCommand
} }
} }
/**
* @return Paginator<Visit>
*/
protected function getVisitsPaginator(InputInterface $input, DateRange $dateRange): Paginator protected function getVisitsPaginator(InputInterface $input, DateRange $dateRange): Paginator
{ {
$identifier = $this->shortUrlIdentifierInput->toShortUrlIdentifier($input); $identifier = $this->shortUrlIdentifierInput->toShortUrlIdentifier($input);

View File

@@ -177,6 +177,7 @@ class ListShortUrlsCommand extends Command
/** /**
* @param array<string, callable(array $serializedShortUrl, ShortUrl $shortUrl): ?string> $columnsMap * @param array<string, callable(array $serializedShortUrl, ShortUrl $shortUrl): ?string> $columnsMap
* @return Paginator<ShortUrlWithVisitsSummary>
*/ */
private function renderPage( private function renderPage(
OutputInterface $output, OutputInterface $output,

View File

@@ -33,6 +33,9 @@ class GetTagVisitsCommand extends AbstractVisitsListCommand
->addArgument('tag', InputArgument::REQUIRED, 'The tag which visits we want to get.'); ->addArgument('tag', InputArgument::REQUIRED, 'The tag which visits we want to get.');
} }
/**
* @return Paginator<Visit>
*/
protected function getVisitsPaginator(InputInterface $input, DateRange $dateRange): Paginator protected function getVisitsPaginator(InputInterface $input, DateRange $dateRange): Paginator
{ {
$tag = $input->getArgument('tag'); $tag = $input->getArgument('tag');

View File

@@ -46,6 +46,9 @@ abstract class AbstractVisitsListCommand extends Command
return ExitCode::EXIT_SUCCESS; return ExitCode::EXIT_SUCCESS;
} }
/**
* @param Paginator<Visit> $paginator
*/
private function resolveRowsAndHeaders(Paginator $paginator): array private function resolveRowsAndHeaders(Paginator $paginator): array
{ {
$extraKeys = []; $extraKeys = [];
@@ -74,6 +77,9 @@ abstract class AbstractVisitsListCommand extends Command
]; ];
} }
/**
* @return Paginator<Visit>
*/
abstract protected function getVisitsPaginator(InputInterface $input, DateRange $dateRange): Paginator; abstract protected function getVisitsPaginator(InputInterface $input, DateRange $dateRange): Paginator;
/** /**

View File

@@ -30,6 +30,9 @@ class GetNonOrphanVisitsCommand extends AbstractVisitsListCommand
->setDescription('Returns the list of non-orphan visits.'); ->setDescription('Returns the list of non-orphan visits.');
} }
/**
* @return Paginator<Visit>
*/
protected function getVisitsPaginator(InputInterface $input, DateRange $dateRange): Paginator protected function getVisitsPaginator(InputInterface $input, DateRange $dateRange): Paginator
{ {
return $this->visitsHelper->nonOrphanVisits(new VisitsParams($dateRange)); return $this->visitsHelper->nonOrphanVisits(new VisitsParams($dateRange));

View File

@@ -30,6 +30,9 @@ class GetOrphanVisitsCommand extends AbstractVisitsListCommand
)); ));
} }
/**
* @return Paginator<Visit>
*/
protected function getVisitsPaginator(InputInterface $input, DateRange $dateRange): Paginator protected function getVisitsPaginator(InputInterface $input, DateRange $dateRange): Paginator
{ {
$rawType = $input->getOption('type'); $rawType = $input->getOption('type');

View File

@@ -7,6 +7,7 @@ namespace ShlinkioTest\Shlink\CLI\Command\Db;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\SQLitePlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\ClassMetadata;
@@ -31,6 +32,7 @@ class CreateDatabaseCommandTest extends TestCase
private MockObject & ProcessRunnerInterface $processHelper; private MockObject & ProcessRunnerInterface $processHelper;
private MockObject & Connection $regularConn; private MockObject & Connection $regularConn;
private MockObject & ClassMetadataFactory $metadataFactory; private MockObject & ClassMetadataFactory $metadataFactory;
/** @var MockObject&AbstractSchemaManager<SQLitePlatform> */
private MockObject & AbstractSchemaManager $schemaManager; private MockObject & AbstractSchemaManager $schemaManager;
private MockObject & Driver $driver; private MockObject & Driver $driver;

View File

@@ -14,6 +14,7 @@ use Shlinkio\Shlink\Core\ShortUrl\Spec\BelongsToApiKey;
use Shlinkio\Shlink\Rest\ApiKey\Role; use Shlinkio\Shlink\Rest\ApiKey\Role;
use Shlinkio\Shlink\Rest\Entity\ApiKey; use Shlinkio\Shlink\Rest\Entity\ApiKey;
/** @extends EntitySpecificationRepository<Domain> */
class DomainRepository extends EntitySpecificationRepository implements DomainRepositoryInterface class DomainRepository extends EntitySpecificationRepository implements DomainRepositoryInterface
{ {
/** /**

View File

@@ -9,6 +9,7 @@ use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepositoryInterfa
use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Core\Domain\Entity\Domain;
use Shlinkio\Shlink\Rest\Entity\ApiKey; use Shlinkio\Shlink\Rest\Entity\ApiKey;
/** @extends ObjectRepository<Domain> */
interface DomainRepositoryInterface extends ObjectRepository, EntitySpecificationRepositoryInterface interface DomainRepositoryInterface extends ObjectRepository, EntitySpecificationRepositoryInterface
{ {
/** /**

View File

@@ -8,6 +8,7 @@ use Laminas\InputFilter\InputFilter;
use Shlinkio\Shlink\Common\Validation\HostAndPortValidator; use Shlinkio\Shlink\Common\Validation\HostAndPortValidator;
use Shlinkio\Shlink\Common\Validation\InputFactory; use Shlinkio\Shlink\Common\Validation\InputFactory;
/** @extends InputFilter<mixed> */
class DomainRedirectsInputFilter extends InputFilter class DomainRedirectsInputFilter extends InputFilter
{ {
public const DOMAIN = 'domain'; public const DOMAIN = 'domain';

View File

@@ -26,6 +26,9 @@ class ValidationException extends InvalidArgumentException implements ProblemDet
private array $invalidElements; private array $invalidElements;
/**
* @param InputFilterInterface<mixed> $inputFilter
*/
public static function fromInputFilter(InputFilterInterface $inputFilter, ?Throwable $prev = null): self public static function fromInputFilter(InputFilterInterface $inputFilter, ?Throwable $prev = null): self
{ {
return static::fromArray($inputFilter->getMessages(), $prev); return static::fromArray($inputFilter->getMessages(), $prev);

View File

@@ -6,6 +6,10 @@ namespace Shlinkio\Shlink\Core\Paginator\Adapter;
use Pagerfanta\Adapter\AdapterInterface; use Pagerfanta\Adapter\AdapterInterface;
/**
* @template T
* @implements AdapterInterface<T>
*/
abstract class AbstractCacheableCountPaginatorAdapter implements AdapterInterface abstract class AbstractCacheableCountPaginatorAdapter implements AdapterInterface
{ {
private ?int $count = null; private ?int $count = null;

View File

@@ -17,6 +17,7 @@ use Shlinkio\Shlink\Core\Util\IpAddressUtils;
use function Shlinkio\Shlink\Core\ArrayUtils\contains; use function Shlinkio\Shlink\Core\ArrayUtils\contains;
use function Shlinkio\Shlink\Core\enumValues; use function Shlinkio\Shlink\Core\enumValues;
/** @extends InputFilter<mixed> */
class RedirectRulesInputFilter extends InputFilter class RedirectRulesInputFilter extends InputFilter
{ {
public const REDIRECT_RULES = 'redirectRules'; public const REDIRECT_RULES = 'redirectRules';
@@ -44,6 +45,9 @@ class RedirectRulesInputFilter extends InputFilter
return $instance; return $instance;
} }
/**
* @return InputFilter<mixed>
*/
private static function createRedirectRuleInputFilter(): InputFilter private static function createRedirectRuleInputFilter(): InputFilter
{ {
$redirectRuleInputFilter = new InputFilter(); $redirectRuleInputFilter = new InputFilter();
@@ -60,6 +64,9 @@ class RedirectRulesInputFilter extends InputFilter
return $redirectRuleInputFilter; return $redirectRuleInputFilter;
} }
/**
* @return InputFilter<mixed>
*/
private static function createRedirectConditionInputFilter(): InputFilter private static function createRedirectConditionInputFilter(): InputFilter
{ {
$redirectConditionInputFilter = new InputFilter(); $redirectConditionInputFilter = new InputFilter();

View File

@@ -37,8 +37,8 @@ class ShortUrl extends AbstractEntity
{ {
/** /**
* @param Collection<int, Tag> $tags * @param Collection<int, Tag> $tags
* @param Collection<int, Visit> & Selectable $visits * @param Collection<int, Visit> & Selectable<int, Visit> $visits
* @param Collection<int, ShortUrlVisitsCount> & Selectable $visitsCounts * @param Collection<int, ShortUrlVisitsCount> & Selectable<int, ShortUrlVisitsCount> $visitsCounts
*/ */
private function __construct( private function __construct(
private string $longUrl, private string $longUrl,
@@ -213,7 +213,7 @@ class ShortUrl extends AbstractEntity
} }
/** /**
* @param Collection<int, Visit> & Selectable $visits * @param Collection<int, Visit> & Selectable<int, Visit> $visits
* @internal * @internal
*/ */
public function setVisits(Collection & Selectable $visits): self public function setVisits(Collection & Selectable $visits): self

View File

@@ -20,6 +20,7 @@ use function substr;
use const Shlinkio\Shlink\LOOSE_URI_MATCHER; use const Shlinkio\Shlink\LOOSE_URI_MATCHER;
use const Shlinkio\Shlink\MIN_SHORT_CODES_LENGTH; use const Shlinkio\Shlink\MIN_SHORT_CODES_LENGTH;
/** @extends InputFilter<mixed> */
class ShortUrlInputFilter extends InputFilter class ShortUrlInputFilter extends InputFilter
{ {
// Fields for creation only // Fields for creation only

View File

@@ -13,6 +13,7 @@ use Shlinkio\Shlink\Core\ShortUrl\Model\TagsMode;
use function Shlinkio\Shlink\Core\enumValues; use function Shlinkio\Shlink\Core\enumValues;
/** @extends InputFilter<mixed> */
class ShortUrlsParamsInputFilter extends InputFilter class ShortUrlsParamsInputFilter extends InputFilter
{ {
public const PAGE = 'page'; public const PAGE = 'page';

View File

@@ -6,11 +6,13 @@ namespace Shlinkio\Shlink\Core\ShortUrl\Paginator\Adapter;
use Pagerfanta\Adapter\AdapterInterface; use Pagerfanta\Adapter\AdapterInterface;
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlsParams; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlsParams;
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlWithVisitsSummary;
use Shlinkio\Shlink\Core\ShortUrl\Persistence\ShortUrlsCountFiltering; use Shlinkio\Shlink\Core\ShortUrl\Persistence\ShortUrlsCountFiltering;
use Shlinkio\Shlink\Core\ShortUrl\Persistence\ShortUrlsListFiltering; use Shlinkio\Shlink\Core\ShortUrl\Persistence\ShortUrlsListFiltering;
use Shlinkio\Shlink\Core\ShortUrl\Repository\ShortUrlListRepositoryInterface; use Shlinkio\Shlink\Core\ShortUrl\Repository\ShortUrlListRepositoryInterface;
use Shlinkio\Shlink\Rest\Entity\ApiKey; use Shlinkio\Shlink\Rest\Entity\ApiKey;
/** @implements AdapterInterface<ShortUrlWithVisitsSummary> */
readonly class ShortUrlRepositoryAdapter implements AdapterInterface readonly class ShortUrlRepositoryAdapter implements AdapterInterface
{ {
public function __construct( public function __construct(

View File

@@ -7,6 +7,7 @@ namespace Shlinkio\Shlink\Core\ShortUrl\Repository;
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository; use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
/** @extends EntitySpecificationRepository<ShortUrl> */
class CrawlableShortCodesQuery extends EntitySpecificationRepository implements CrawlableShortCodesQueryInterface class CrawlableShortCodesQuery extends EntitySpecificationRepository implements CrawlableShortCodesQueryInterface
{ {
/** /**

View File

@@ -13,6 +13,7 @@ use Shlinkio\Shlink\Core\Visit\Entity\ShortUrlVisitsCount;
use function sprintf; use function sprintf;
/** @extends EntitySpecificationRepository<ShortUrl> */
class ExpiredShortUrlsRepository extends EntitySpecificationRepository implements ExpiredShortUrlsRepositoryInterface class ExpiredShortUrlsRepository extends EntitySpecificationRepository implements ExpiredShortUrlsRepositoryInterface
{ {
/** /**

View File

@@ -20,6 +20,7 @@ use Shlinkio\Shlink\Core\Visit\Entity\ShortUrlVisitsCount;
use function Shlinkio\Shlink\Core\ArrayUtils\map; use function Shlinkio\Shlink\Core\ArrayUtils\map;
use function sprintf; use function sprintf;
/** @extends EntitySpecificationRepository<ShortUrl> */
class ShortUrlListRepository extends EntitySpecificationRepository implements ShortUrlListRepositoryInterface class ShortUrlListRepository extends EntitySpecificationRepository implements ShortUrlListRepositoryInterface
{ {
/** /**

View File

@@ -20,6 +20,7 @@ use Shlinkio\Shlink\Importer\Model\ImportedShlinkUrl;
use function count; use function count;
use function strtolower; use function strtolower;
/** @extends EntitySpecificationRepository<ShortUrl> */
class ShortUrlRepository extends EntitySpecificationRepository implements ShortUrlRepositoryInterface class ShortUrlRepository extends EntitySpecificationRepository implements ShortUrlRepositoryInterface
{ {
public function findOneWithDomainFallback(ShortUrlIdentifier $identifier, ShortUrlMode $shortUrlMode): ?ShortUrl public function findOneWithDomainFallback(ShortUrlIdentifier $identifier, ShortUrlMode $shortUrlMode): ?ShortUrl

View File

@@ -13,6 +13,7 @@ use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlMode; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlMode;
use Shlinkio\Shlink\Importer\Model\ImportedShlinkUrl; use Shlinkio\Shlink\Importer\Model\ImportedShlinkUrl;
/** @extends ObjectRepository<ShortUrl> */
interface ShortUrlRepositoryInterface extends ObjectRepository, EntitySpecificationRepositoryInterface interface ShortUrlRepositoryInterface extends ObjectRepository, EntitySpecificationRepositoryInterface
{ {
public function findOneWithDomainFallback(ShortUrlIdentifier $identifier, ShortUrlMode $shortUrlMode): ?ShortUrl; public function findOneWithDomainFallback(ShortUrlIdentifier $identifier, ShortUrlMode $shortUrlMode): ?ShortUrl;

View File

@@ -7,7 +7,6 @@ namespace Shlinkio\Shlink\Core\ShortUrl;
use Shlinkio\Shlink\Common\Paginator\Paginator; use Shlinkio\Shlink\Common\Paginator\Paginator;
use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Options\UrlShortenerOptions;
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlsParams; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlsParams;
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlWithVisitsSummary;
use Shlinkio\Shlink\Core\ShortUrl\Paginator\Adapter\ShortUrlRepositoryAdapter; use Shlinkio\Shlink\Core\ShortUrl\Paginator\Adapter\ShortUrlRepositoryAdapter;
use Shlinkio\Shlink\Core\ShortUrl\Repository\ShortUrlListRepositoryInterface; use Shlinkio\Shlink\Core\ShortUrl\Repository\ShortUrlListRepositoryInterface;
use Shlinkio\Shlink\Rest\Entity\ApiKey; use Shlinkio\Shlink\Rest\Entity\ApiKey;
@@ -21,7 +20,7 @@ readonly class ShortUrlListService implements ShortUrlListServiceInterface
} }
/** /**
* @return ShortUrlWithVisitsSummary[]|Paginator * @inheritDoc
*/ */
public function listShortUrls(ShortUrlsParams $params, ?ApiKey $apiKey = null): Paginator public function listShortUrls(ShortUrlsParams $params, ?ApiKey $apiKey = null): Paginator
{ {

View File

@@ -12,7 +12,7 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
interface ShortUrlListServiceInterface interface ShortUrlListServiceInterface
{ {
/** /**
* @return ShortUrlWithVisitsSummary[]|Paginator * @return Paginator<ShortUrlWithVisitsSummary>
*/ */
public function listShortUrls(ShortUrlsParams $params, ?ApiKey $apiKey = null): Paginator; public function listShortUrls(ShortUrlsParams $params, ?ApiKey $apiKey = null): Paginator;
} }

View File

@@ -7,9 +7,11 @@ namespace Shlinkio\Shlink\Core\Tag\Entity;
use Doctrine\Common\Collections; use Doctrine\Common\Collections;
use JsonSerializable; use JsonSerializable;
use Shlinkio\Shlink\Common\Entity\AbstractEntity; use Shlinkio\Shlink\Common\Entity\AbstractEntity;
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
class Tag extends AbstractEntity implements JsonSerializable class Tag extends AbstractEntity implements JsonSerializable
{ {
/** @var Collections\Collection<int, ShortUrl> */
private Collections\Collection $shortUrls; private Collections\Collection $shortUrls;
public function __construct(private string $name) public function __construct(private string $name)

View File

@@ -12,6 +12,10 @@ use Shlinkio\Shlink\Core\Tag\Repository\TagRepositoryInterface;
use Shlinkio\Shlink\Rest\ApiKey\Spec\WithApiKeySpecsEnsuringJoin; use Shlinkio\Shlink\Rest\ApiKey\Spec\WithApiKeySpecsEnsuringJoin;
use Shlinkio\Shlink\Rest\Entity\ApiKey; use Shlinkio\Shlink\Rest\Entity\ApiKey;
/**
* @template T
* @implements AdapterInterface<T>
*/
abstract class AbstractTagsPaginatorAdapter implements AdapterInterface abstract class AbstractTagsPaginatorAdapter implements AdapterInterface
{ {
public function __construct( public function __construct(

View File

@@ -4,8 +4,10 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Tag\Paginator\Adapter; namespace Shlinkio\Shlink\Core\Tag\Paginator\Adapter;
use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
use Shlinkio\Shlink\Core\Tag\Model\TagsListFiltering; use Shlinkio\Shlink\Core\Tag\Model\TagsListFiltering;
/** @extends AbstractTagsPaginatorAdapter<TagInfo> */
class TagsInfoPaginatorAdapter extends AbstractTagsPaginatorAdapter class TagsInfoPaginatorAdapter extends AbstractTagsPaginatorAdapter
{ {
public function getSlice(int $offset, int $length): iterable public function getSlice(int $offset, int $length): iterable

View File

@@ -5,8 +5,10 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Tag\Paginator\Adapter; namespace Shlinkio\Shlink\Core\Tag\Paginator\Adapter;
use Happyr\DoctrineSpecification\Spec; use Happyr\DoctrineSpecification\Spec;
use Shlinkio\Shlink\Core\Tag\Entity\Tag;
use Shlinkio\Shlink\Rest\ApiKey\Spec\WithApiKeySpecsEnsuringJoin; use Shlinkio\Shlink\Rest\ApiKey\Spec\WithApiKeySpecsEnsuringJoin;
/** @extends AbstractTagsPaginatorAdapter<Tag> */
class TagsPaginatorAdapter extends AbstractTagsPaginatorAdapter class TagsPaginatorAdapter extends AbstractTagsPaginatorAdapter
{ {
public function getSlice(int $offset, int $length): iterable public function getSlice(int $offset, int $length): iterable

View File

@@ -23,6 +23,7 @@ use function Shlinkio\Shlink\Core\camelCaseToSnakeCase;
use const PHP_INT_MAX; use const PHP_INT_MAX;
/** @extends EntitySpecificationRepository<Tag> */
class TagRepository extends EntitySpecificationRepository implements TagRepositoryInterface class TagRepository extends EntitySpecificationRepository implements TagRepositoryInterface
{ {
public function deleteByName(array $names): int public function deleteByName(array $names): int

View File

@@ -6,10 +6,12 @@ namespace Shlinkio\Shlink\Core\Tag\Repository;
use Doctrine\Persistence\ObjectRepository; use Doctrine\Persistence\ObjectRepository;
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepositoryInterface; use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepositoryInterface;
use Shlinkio\Shlink\Core\Tag\Entity\Tag;
use Shlinkio\Shlink\Core\Tag\Model\TagInfo; use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
use Shlinkio\Shlink\Core\Tag\Model\TagsListFiltering; use Shlinkio\Shlink\Core\Tag\Model\TagsListFiltering;
use Shlinkio\Shlink\Rest\Entity\ApiKey; use Shlinkio\Shlink\Rest\Entity\ApiKey;
/** @extends ObjectRepository<Tag> */
interface TagRepositoryInterface extends ObjectRepository, EntitySpecificationRepositoryInterface interface TagRepositoryInterface extends ObjectRepository, EntitySpecificationRepositoryInterface
{ {
public function deleteByName(array $names): int; public function deleteByName(array $names): int;

View File

@@ -11,7 +11,6 @@ use Shlinkio\Shlink\Core\Exception\ForbiddenTagOperationException;
use Shlinkio\Shlink\Core\Exception\TagConflictException; use Shlinkio\Shlink\Core\Exception\TagConflictException;
use Shlinkio\Shlink\Core\Exception\TagNotFoundException; use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
use Shlinkio\Shlink\Core\Tag\Entity\Tag; use Shlinkio\Shlink\Core\Tag\Entity\Tag;
use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
use Shlinkio\Shlink\Core\Tag\Model\TagRenaming; use Shlinkio\Shlink\Core\Tag\Model\TagRenaming;
use Shlinkio\Shlink\Core\Tag\Model\TagsParams; use Shlinkio\Shlink\Core\Tag\Model\TagsParams;
use Shlinkio\Shlink\Core\Tag\Paginator\Adapter\TagsInfoPaginatorAdapter; use Shlinkio\Shlink\Core\Tag\Paginator\Adapter\TagsInfoPaginatorAdapter;
@@ -20,14 +19,14 @@ use Shlinkio\Shlink\Core\Tag\Repository\TagRepository;
use Shlinkio\Shlink\Core\Tag\Repository\TagRepositoryInterface; use Shlinkio\Shlink\Core\Tag\Repository\TagRepositoryInterface;
use Shlinkio\Shlink\Rest\Entity\ApiKey; use Shlinkio\Shlink\Rest\Entity\ApiKey;
class TagService implements TagServiceInterface readonly class TagService implements TagServiceInterface
{ {
public function __construct(private readonly ORM\EntityManagerInterface $em) public function __construct(private ORM\EntityManagerInterface $em)
{ {
} }
/** /**
* @return Tag[]|Paginator * @inheritDoc
*/ */
public function listTags(TagsParams $params, ?ApiKey $apiKey = null): Paginator public function listTags(TagsParams $params, ?ApiKey $apiKey = null): Paginator
{ {
@@ -37,7 +36,7 @@ class TagService implements TagServiceInterface
} }
/** /**
* @return TagInfo[]|Paginator * @inheritDoc
*/ */
public function tagsInfo(TagsParams $params, ?ApiKey $apiKey = null): Paginator public function tagsInfo(TagsParams $params, ?ApiKey $apiKey = null): Paginator
{ {
@@ -46,6 +45,11 @@ class TagService implements TagServiceInterface
return $this->createPaginator(new TagsInfoPaginatorAdapter($repo, $params, $apiKey), $params); return $this->createPaginator(new TagsInfoPaginatorAdapter($repo, $params, $apiKey), $params);
} }
/**
* @template T
* @param AdapterInterface<T> $adapter
* @return Paginator<T>
*/
private function createPaginator(AdapterInterface $adapter, TagsParams $params): Paginator private function createPaginator(AdapterInterface $adapter, TagsParams $params): Paginator
{ {
return (new Paginator($adapter)) return (new Paginator($adapter))
@@ -54,8 +58,7 @@ class TagService implements TagServiceInterface
} }
/** /**
* @param string[] $tagNames * @inheritDoc
* @throws ForbiddenTagOperationException
*/ */
public function deleteTags(array $tagNames, ?ApiKey $apiKey = null): void public function deleteTags(array $tagNames, ?ApiKey $apiKey = null): void
{ {
@@ -69,9 +72,7 @@ class TagService implements TagServiceInterface
} }
/** /**
* @throws TagNotFoundException * @inheritDoc
* @throws TagConflictException
* @throws ForbiddenTagOperationException
*/ */
public function renameTag(TagRenaming $renaming, ?ApiKey $apiKey = null): Tag public function renameTag(TagRenaming $renaming, ?ApiKey $apiKey = null): Tag
{ {

View File

@@ -17,12 +17,12 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
interface TagServiceInterface interface TagServiceInterface
{ {
/** /**
* @return Tag[]|Paginator * @return Paginator<Tag>
*/ */
public function listTags(TagsParams $params, ?ApiKey $apiKey = null): Paginator; public function listTags(TagsParams $params, ?ApiKey $apiKey = null): Paginator;
/** /**
* @return TagInfo[]|Paginator * @return Paginator<TagInfo>
*/ */
public function tagsInfo(TagsParams $params, ?ApiKey $apiKey = null): Paginator; public function tagsInfo(TagsParams $params, ?ApiKey $apiKey = null): Paginator;

View File

@@ -5,19 +5,23 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Visit\Paginator\Adapter; namespace Shlinkio\Shlink\Core\Visit\Paginator\Adapter;
use Shlinkio\Shlink\Core\Paginator\Adapter\AbstractCacheableCountPaginatorAdapter; use Shlinkio\Shlink\Core\Paginator\Adapter\AbstractCacheableCountPaginatorAdapter;
use Shlinkio\Shlink\Core\Visit\Entity\Visit;
use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; use Shlinkio\Shlink\Core\Visit\Model\VisitsParams;
use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering;
use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering;
use Shlinkio\Shlink\Core\Visit\Repository\VisitRepositoryInterface; use Shlinkio\Shlink\Core\Visit\Repository\VisitRepositoryInterface;
use Shlinkio\Shlink\Rest\Entity\ApiKey; use Shlinkio\Shlink\Rest\Entity\ApiKey;
/**
* @extends AbstractCacheableCountPaginatorAdapter<Visit>
*/
class DomainVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapter class DomainVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapter
{ {
public function __construct( public function __construct(
private VisitRepositoryInterface $visitRepository, private readonly VisitRepositoryInterface $visitRepository,
private string $domain, private readonly string $domain,
private VisitsParams $params, private readonly VisitsParams $params,
private ?ApiKey $apiKey, private readonly ?ApiKey $apiKey,
) { ) {
} }

View File

@@ -5,18 +5,20 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Visit\Paginator\Adapter; namespace Shlinkio\Shlink\Core\Visit\Paginator\Adapter;
use Shlinkio\Shlink\Core\Paginator\Adapter\AbstractCacheableCountPaginatorAdapter; use Shlinkio\Shlink\Core\Paginator\Adapter\AbstractCacheableCountPaginatorAdapter;
use Shlinkio\Shlink\Core\Visit\Entity\Visit;
use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; use Shlinkio\Shlink\Core\Visit\Model\VisitsParams;
use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering;
use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering;
use Shlinkio\Shlink\Core\Visit\Repository\VisitRepositoryInterface; use Shlinkio\Shlink\Core\Visit\Repository\VisitRepositoryInterface;
use Shlinkio\Shlink\Rest\Entity\ApiKey; use Shlinkio\Shlink\Rest\Entity\ApiKey;
/** @extends AbstractCacheableCountPaginatorAdapter<Visit> */
class NonOrphanVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapter class NonOrphanVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapter
{ {
public function __construct( public function __construct(
private VisitRepositoryInterface $repo, private readonly VisitRepositoryInterface $repo,
private VisitsParams $params, private readonly VisitsParams $params,
private ?ApiKey $apiKey, private readonly ?ApiKey $apiKey,
) { ) {
} }

View File

@@ -5,12 +5,14 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Visit\Paginator\Adapter; namespace Shlinkio\Shlink\Core\Visit\Paginator\Adapter;
use Shlinkio\Shlink\Core\Paginator\Adapter\AbstractCacheableCountPaginatorAdapter; use Shlinkio\Shlink\Core\Paginator\Adapter\AbstractCacheableCountPaginatorAdapter;
use Shlinkio\Shlink\Core\Visit\Entity\Visit;
use Shlinkio\Shlink\Core\Visit\Model\OrphanVisitsParams; use Shlinkio\Shlink\Core\Visit\Model\OrphanVisitsParams;
use Shlinkio\Shlink\Core\Visit\Persistence\OrphanVisitsCountFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\OrphanVisitsCountFiltering;
use Shlinkio\Shlink\Core\Visit\Persistence\OrphanVisitsListFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\OrphanVisitsListFiltering;
use Shlinkio\Shlink\Core\Visit\Repository\VisitRepositoryInterface; use Shlinkio\Shlink\Core\Visit\Repository\VisitRepositoryInterface;
use Shlinkio\Shlink\Rest\Entity\ApiKey; use Shlinkio\Shlink\Rest\Entity\ApiKey;
/** @extends AbstractCacheableCountPaginatorAdapter<Visit> */
class OrphanVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapter class OrphanVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapter
{ {
public function __construct( public function __construct(

View File

@@ -6,19 +6,21 @@ namespace Shlinkio\Shlink\Core\Visit\Paginator\Adapter;
use Shlinkio\Shlink\Core\Paginator\Adapter\AbstractCacheableCountPaginatorAdapter; use Shlinkio\Shlink\Core\Paginator\Adapter\AbstractCacheableCountPaginatorAdapter;
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Visit\Entity\Visit;
use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; use Shlinkio\Shlink\Core\Visit\Model\VisitsParams;
use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering;
use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering;
use Shlinkio\Shlink\Core\Visit\Repository\VisitRepositoryInterface; use Shlinkio\Shlink\Core\Visit\Repository\VisitRepositoryInterface;
use Shlinkio\Shlink\Rest\Entity\ApiKey; use Shlinkio\Shlink\Rest\Entity\ApiKey;
/** @extends AbstractCacheableCountPaginatorAdapter<Visit> */
class ShortUrlVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapter class ShortUrlVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapter
{ {
public function __construct( public function __construct(
private VisitRepositoryInterface $visitRepository, private readonly VisitRepositoryInterface $visitRepository,
private ShortUrlIdentifier $identifier, private readonly ShortUrlIdentifier $identifier,
private VisitsParams $params, private readonly VisitsParams $params,
private ?ApiKey $apiKey, private readonly ?ApiKey $apiKey,
) { ) {
} }

View File

@@ -5,19 +5,21 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Visit\Paginator\Adapter; namespace Shlinkio\Shlink\Core\Visit\Paginator\Adapter;
use Shlinkio\Shlink\Core\Paginator\Adapter\AbstractCacheableCountPaginatorAdapter; use Shlinkio\Shlink\Core\Paginator\Adapter\AbstractCacheableCountPaginatorAdapter;
use Shlinkio\Shlink\Core\Visit\Entity\Visit;
use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; use Shlinkio\Shlink\Core\Visit\Model\VisitsParams;
use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering;
use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering;
use Shlinkio\Shlink\Core\Visit\Repository\VisitRepositoryInterface; use Shlinkio\Shlink\Core\Visit\Repository\VisitRepositoryInterface;
use Shlinkio\Shlink\Rest\Entity\ApiKey; use Shlinkio\Shlink\Rest\Entity\ApiKey;
/** @extends AbstractCacheableCountPaginatorAdapter<Visit> */
class TagVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapter class TagVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapter
{ {
public function __construct( public function __construct(
private VisitRepositoryInterface $visitRepository, private readonly VisitRepositoryInterface $visitRepository,
private string $tag, private readonly string $tag,
private VisitsParams $params, private readonly VisitsParams $params,
private ?ApiKey $apiKey, private readonly ?ApiKey $apiKey,
) { ) {
} }

View File

@@ -9,6 +9,7 @@ use Shlinkio\Shlink\Core\Visit\Entity\OrphanVisitsCount;
use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering;
use Shlinkio\Shlink\Rest\ApiKey\Role; use Shlinkio\Shlink\Rest\ApiKey\Role;
/** @extends EntitySpecificationRepository<OrphanVisitsCount> */
class OrphanVisitsCountRepository extends EntitySpecificationRepository implements OrphanVisitsCountRepositoryInterface class OrphanVisitsCountRepository extends EntitySpecificationRepository implements OrphanVisitsCountRepositoryInterface
{ {
public function countOrphanVisits(VisitsCountFiltering $filtering): int public function countOrphanVisits(VisitsCountFiltering $filtering): int

View File

@@ -5,9 +5,11 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Visit\Repository; namespace Shlinkio\Shlink\Core\Visit\Repository;
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository; use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Visit\Entity\ShortUrlVisitsCount; use Shlinkio\Shlink\Core\Visit\Entity\ShortUrlVisitsCount;
use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering;
/** @extends EntitySpecificationRepository<ShortUrl> */
class ShortUrlVisitsCountRepository extends EntitySpecificationRepository implements class ShortUrlVisitsCountRepository extends EntitySpecificationRepository implements
ShortUrlVisitsCountRepositoryInterface ShortUrlVisitsCountRepositoryInterface
{ {

View File

@@ -8,6 +8,7 @@ use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Visit\Entity\Visit; use Shlinkio\Shlink\Core\Visit\Entity\Visit;
/** @extends EntitySpecificationRepository<Visit> */
class VisitDeleterRepository extends EntitySpecificationRepository implements VisitDeleterRepositoryInterface class VisitDeleterRepository extends EntitySpecificationRepository implements VisitDeleterRepositoryInterface
{ {
public function deleteShortUrlVisits(ShortUrl $shortUrl): int public function deleteShortUrlVisits(ShortUrl $shortUrl): int

View File

@@ -12,6 +12,7 @@ use Shlinkio\Shlink\Core\Visit\Entity\Visit;
/** /**
* Allows iterating large amounts of visits in a memory-efficient way, to use in batch processes * Allows iterating large amounts of visits in a memory-efficient way, to use in batch processes
* @extends EntitySpecificationRepository<Visit>
*/ */
class VisitIterationRepository extends EntitySpecificationRepository implements VisitIterationRepositoryInterface class VisitIterationRepository extends EntitySpecificationRepository implements VisitIterationRepositoryInterface
{ {

View File

@@ -23,6 +23,7 @@ use Shlinkio\Shlink\Rest\ApiKey\Role;
use const PHP_INT_MAX; use const PHP_INT_MAX;
/** @extends EntitySpecificationRepository<Visit> */
class VisitRepository extends EntitySpecificationRepository implements VisitRepositoryInterface class VisitRepository extends EntitySpecificationRepository implements VisitRepositoryInterface
{ {
/** /**

View File

@@ -13,6 +13,9 @@ use Shlinkio\Shlink\Core\Visit\Persistence\OrphanVisitsListFiltering;
use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\VisitsCountFiltering;
use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering; use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering;
/**
* @extends ObjectRepository<Visit>
*/
interface VisitRepositoryInterface extends ObjectRepository, EntitySpecificationRepositoryInterface interface VisitRepositoryInterface extends ObjectRepository, EntitySpecificationRepositoryInterface
{ {
/** /**

View File

@@ -63,8 +63,7 @@ readonly class VisitsStatsHelper implements VisitsStatsHelperInterface
} }
/** /**
* @return Visit[]|Paginator * @inheritDoc
* @throws ShortUrlNotFoundException
*/ */
public function visitsForShortUrl( public function visitsForShortUrl(
ShortUrlIdentifier $identifier, ShortUrlIdentifier $identifier,
@@ -87,8 +86,7 @@ readonly class VisitsStatsHelper implements VisitsStatsHelperInterface
} }
/** /**
* @return Visit[]|Paginator * @inheritDoc
* @throws TagNotFoundException
*/ */
public function visitsForTag(string $tag, VisitsParams $params, ?ApiKey $apiKey = null): Paginator public function visitsForTag(string $tag, VisitsParams $params, ?ApiKey $apiKey = null): Paginator
{ {
@@ -105,8 +103,7 @@ readonly class VisitsStatsHelper implements VisitsStatsHelperInterface
} }
/** /**
* @return Visit[]|Paginator * @inheritDoc
* @throws DomainNotFoundException
*/ */
public function visitsForDomain(string $domain, VisitsParams $params, ?ApiKey $apiKey = null): Paginator public function visitsForDomain(string $domain, VisitsParams $params, ?ApiKey $apiKey = null): Paginator
{ {
@@ -123,7 +120,7 @@ readonly class VisitsStatsHelper implements VisitsStatsHelperInterface
} }
/** /**
* @return Visit[]|Paginator * @inheritDoc
*/ */
public function orphanVisits(OrphanVisitsParams $params, ?ApiKey $apiKey = null): Paginator public function orphanVisits(OrphanVisitsParams $params, ?ApiKey $apiKey = null): Paginator
{ {
@@ -141,6 +138,10 @@ readonly class VisitsStatsHelper implements VisitsStatsHelperInterface
return $this->createPaginator(new NonOrphanVisitsPaginatorAdapter($repo, $params, $apiKey), $params); return $this->createPaginator(new NonOrphanVisitsPaginatorAdapter($repo, $params, $apiKey), $params);
} }
/**
* @param AdapterInterface<Visit> $adapter
* @return Paginator<Visit>
*/
private function createPaginator(AdapterInterface $adapter, VisitsParams $params): Paginator private function createPaginator(AdapterInterface $adapter, VisitsParams $params): Paginator
{ {
$paginator = new Paginator($adapter); $paginator = new Paginator($adapter);

View File

@@ -20,7 +20,7 @@ interface VisitsStatsHelperInterface
public function getVisitsStats(?ApiKey $apiKey = null): VisitsStats; public function getVisitsStats(?ApiKey $apiKey = null): VisitsStats;
/** /**
* @return Visit[]|Paginator * @return Paginator<Visit>
* @throws ShortUrlNotFoundException * @throws ShortUrlNotFoundException
*/ */
public function visitsForShortUrl( public function visitsForShortUrl(
@@ -30,24 +30,24 @@ interface VisitsStatsHelperInterface
): Paginator; ): Paginator;
/** /**
* @return Visit[]|Paginator * @return Paginator<Visit>
* @throws TagNotFoundException * @throws TagNotFoundException
*/ */
public function visitsForTag(string $tag, VisitsParams $params, ?ApiKey $apiKey = null): Paginator; public function visitsForTag(string $tag, VisitsParams $params, ?ApiKey $apiKey = null): Paginator;
/** /**
* @return Visit[]|Paginator * @return Paginator<Visit>
* @throws DomainNotFoundException * @throws DomainNotFoundException
*/ */
public function visitsForDomain(string $domain, VisitsParams $params, ?ApiKey $apiKey = null): Paginator; public function visitsForDomain(string $domain, VisitsParams $params, ?ApiKey $apiKey = null): Paginator;
/** /**
* @return Visit[]|Paginator * @return Paginator<Visit>
*/ */
public function orphanVisits(OrphanVisitsParams $params, ?ApiKey $apiKey = null): Paginator; public function orphanVisits(OrphanVisitsParams $params, ?ApiKey $apiKey = null): Paginator;
/** /**
* @return Visit[]|Paginator * @return Paginator<Visit>
*/ */
public function nonOrphanVisits(VisitsParams $params, ?ApiKey $apiKey = null): Paginator; public function nonOrphanVisits(VisitsParams $params, ?ApiKey $apiKey = null): Paginator;
} }

View File

@@ -4,11 +4,11 @@ declare(strict_types=1);
namespace ShlinkioDbTest\Shlink\Core\Visit\Listener; namespace ShlinkioDbTest\Shlink\Core\Visit\Listener;
use Doctrine\ORM\EntityRepository;
use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\Test;
use Shlinkio\Shlink\Core\Visit\Entity\OrphanVisitsCount; use Shlinkio\Shlink\Core\Visit\Entity\OrphanVisitsCount;
use Shlinkio\Shlink\Core\Visit\Entity\Visit; use Shlinkio\Shlink\Core\Visit\Entity\Visit;
use Shlinkio\Shlink\Core\Visit\Model\Visitor; use Shlinkio\Shlink\Core\Visit\Model\Visitor;
use Shlinkio\Shlink\Core\Visit\Repository\OrphanVisitsCountRepository;
use Shlinkio\Shlink\TestUtils\DbTest\DatabaseTestCase; use Shlinkio\Shlink\TestUtils\DbTest\DatabaseTestCase;
use function array_filter; use function array_filter;
@@ -16,7 +16,7 @@ use function array_values;
class OrphanVisitsCountTrackerTest extends DatabaseTestCase class OrphanVisitsCountTrackerTest extends DatabaseTestCase
{ {
private EntityRepository $repo; private OrphanVisitsCountRepository $repo;
protected function setUp(): void protected function setUp(): void
{ {

View File

@@ -4,12 +4,12 @@ declare(strict_types=1);
namespace ShlinkioDbTest\Shlink\Core\Visit\Listener; namespace ShlinkioDbTest\Shlink\Core\Visit\Listener;
use Doctrine\ORM\EntityRepository;
use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\Test;
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Visit\Entity\ShortUrlVisitsCount; use Shlinkio\Shlink\Core\Visit\Entity\ShortUrlVisitsCount;
use Shlinkio\Shlink\Core\Visit\Entity\Visit; use Shlinkio\Shlink\Core\Visit\Entity\Visit;
use Shlinkio\Shlink\Core\Visit\Model\Visitor; use Shlinkio\Shlink\Core\Visit\Model\Visitor;
use Shlinkio\Shlink\Core\Visit\Repository\ShortUrlVisitsCountRepository;
use Shlinkio\Shlink\TestUtils\DbTest\DatabaseTestCase; use Shlinkio\Shlink\TestUtils\DbTest\DatabaseTestCase;
use function array_filter; use function array_filter;
@@ -17,7 +17,7 @@ use function array_values;
class ShortUrlVisitsCountTrackerTest extends DatabaseTestCase class ShortUrlVisitsCountTrackerTest extends DatabaseTestCase
{ {
private EntityRepository $repo; private ShortUrlVisitsCountRepository $repo;
protected function setUp(): void protected function setUp(): void
{ {

View File

@@ -104,7 +104,7 @@ class ShortUrlRedirectRuleTest extends TestCase
} }
/** /**
* @param ArrayCollection<RedirectCondition> $conditions * @param ArrayCollection<int, RedirectCondition> $conditions
*/ */
private function createRule(ArrayCollection $conditions): ShortUrlRedirectRule private function createRule(ArrayCollection $conditions): ShortUrlRedirectRule
{ {

View File

@@ -103,6 +103,9 @@ class ShortUrlTitleResolutionHelperTest extends TestCase
self::assertEquals('Resolved "title"', $result->title); self::assertEquals('Resolved "title"', $result->title);
} }
/**
* @return InvocationMocker<ClientInterface>
*/
private function expectRequestToBeCalled(): InvocationMocker private function expectRequestToBeCalled(): InvocationMocker
{ {
return $this->httpClient->expects($this->once())->method('request')->with( return $this->httpClient->expects($this->once())->method('request')->with(

View File

@@ -9,6 +9,9 @@ use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta; use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta;
use Shlinkio\Shlink\Rest\Entity\ApiKey; use Shlinkio\Shlink\Rest\Entity\ApiKey;
/**
* @extends EntitySpecificationRepository<ApiKey>
*/
class ApiKeyRepository extends EntitySpecificationRepository implements ApiKeyRepositoryInterface class ApiKeyRepository extends EntitySpecificationRepository implements ApiKeyRepositoryInterface
{ {
/** /**

View File

@@ -8,6 +8,9 @@ use Doctrine\Persistence\ObjectRepository;
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepositoryInterface; use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepositoryInterface;
use Shlinkio\Shlink\Rest\Entity\ApiKey; use Shlinkio\Shlink\Rest\Entity\ApiKey;
/**
* @extends ObjectRepository<ApiKey>
*/
interface ApiKeyRepositoryInterface extends ObjectRepository, EntitySpecificationRepositoryInterface interface ApiKeyRepositoryInterface extends ObjectRepository, EntitySpecificationRepositoryInterface
{ {
/** /**

View File

@@ -12,5 +12,4 @@ parameters:
ignoreErrors: ignoreErrors:
- '#should return int<0, max> but returns int#' - '#should return int<0, max> but returns int#'
- '#expects -1\|int<1, max>, int given#' - '#expects -1\|int<1, max>, int given#'
- identifier: missingType.generics
- identifier: missingType.iterableValue - identifier: missingType.iterableValue