Improve accuracy of generics.

This commit is contained in:
Buster "Silver Eagle" Neece 2022-05-31 06:41:35 -05:00
parent b6767e1bc3
commit f46314d8c4
No known key found for this signature in database
GPG Key ID: F1D2E64A0005E80E
20 changed files with 120 additions and 32 deletions

View File

@ -5,7 +5,6 @@ includes:
parameters: parameters:
level: 8 level: 8
checkGenericClassInNonGenericObjectType: false
checkMissingIterableValueType: false checkMissingIterableValueType: false
paths: paths:

View File

@ -135,8 +135,10 @@ class Acl
$numRoles = $user->getRoles()->count(); $numRoles = $user->getRoles()->count();
if ($numRoles > 0) { if ($numRoles > 0) {
if ($numRoles === 1) { if ($numRoles === 1) {
/** @var Entity\Role $role */
$role = $user->getRoles()->first(); $role = $user->getRoles()->first();
return $this->roleAllowed($role->getId(), $action, $stationId);
return $this->roleAllowed($role->getIdRequired(), $action, $stationId);
} }
$roles = []; $roles = [];

View File

@ -222,6 +222,9 @@ final class CloneAction extends StationsController
return $response->withJson(Entity\Api\Status::created()); return $response->withJson(Entity\Api\Status::created());
} }
/**
* @param Collection<int, mixed> $collection
*/
private function cloneCollection( private function cloneCollection(
Collection $collection, Collection $collection,
Entity\Station $newStation, Entity\Station $newStation,

View File

@ -331,6 +331,7 @@ final class ListAction
} }
); );
/** @var array<int, Entity\Api\FileList> $result */
$paginator = Paginator::fromArray($result, $request); $paginator = Paginator::fromArray($result, $request);
// Add processor-intensive data for just this page. // Add processor-intensive data for just this page.

View File

@ -136,8 +136,8 @@ class AuditLog implements EventSubscriber
$associated = []; $associated = [];
$disassociated = []; $disassociated = [];
/** @var PersistentCollection<int, object> $collection */
foreach ($uow->getScheduledCollectionUpdates() as $collection) { foreach ($uow->getScheduledCollectionUpdates() as $collection) {
/** @var PersistentCollection $collection */
$owner = $collection->getOwner(); $owner = $collection->getOwner();
if (null === $owner) { if (null === $owner) {
@ -180,8 +180,8 @@ class AuditLog implements EventSubscriber
} }
} }
/** @var PersistentCollection<int, object> $collection */
foreach ($uow->getScheduledCollectionDeletions() as $collection) { foreach ($uow->getScheduledCollectionDeletions() as $collection) {
/** @var PersistentCollection $collection */
$owner = $collection->getOwner(); $owner = $collection->getOwner();
if (null === $owner) { if (null === $owner) {
@ -259,6 +259,11 @@ class AuditLog implements EventSubscriber
return !$em->getMetadataFactory()->isTransient($class); return !$em->getMetadataFactory()->isTransient($class);
} }
/**
* @template TObject of object
* @param ReflectionClass<TObject> $refl
* @return bool
*/
protected function isAuditable(ReflectionClass $refl): bool protected function isAuditable(ReflectionClass $refl): bool
{ {
$auditable = $refl->getAttributes(Auditable::class); $auditable = $refl->getAttributes(Auditable::class);

View File

@ -33,6 +33,9 @@ class Repository
} }
} }
/**
* @return ObjectRepository<TEntity>
*/
public function getRepository(): ObjectRepository public function getRepository(): ObjectRepository
{ {
return $this->repository; return $this->repository;

View File

@ -21,7 +21,11 @@ class User extends AbstractFixture implements DependentFixtureInterface
$demo_user->setEmail('demo@azuracast.com'); $demo_user->setEmail('demo@azuracast.com');
$demo_user->setNewPassword('demo'); $demo_user->setNewPassword('demo');
$demo_user->setName('AzuraCast Demo User'); $demo_user->setName('AzuraCast Demo User');
$demo_user->getRoles()->add($this->getReference('demo_role'));
/** @var Entity\Role $demoRole */
$demoRole = $this->getReference('demo_role');
$demo_user->getRoles()->add($demoRole);
$manager->persist($demo_user); $manager->persist($demo_user);
$this->addReference('demo_user', $demo_user); $this->addReference('demo_user', $demo_user);
@ -32,7 +36,9 @@ class User extends AbstractFixture implements DependentFixtureInterface
$admin_user->setNewPassword($admin_password); $admin_user->setNewPassword($admin_password);
$admin_user->setTheme('dark'); $admin_user->setTheme('dark');
$admin_user->getRoles()->add($this->getReference('admin_role')); /** @var Entity\Role $adminRole */
$adminRole = $this->getReference('admin_role');
$admin_user->getRoles()->add($adminRole);
$admin_2fa_secret = getenv('INIT_ADMIN_2FA_SECRET'); $admin_2fa_secret = getenv('INIT_ADMIN_2FA_SECRET');
if (!empty($admin_2fa_secret)) { if (!empty($admin_2fa_secret)) {

View File

@ -51,9 +51,11 @@ class Podcast implements Interfaces\IdentifiableEntityInterface
#[Attributes\AuditIgnore] #[Attributes\AuditIgnore]
protected int $art_updated_at = 0; protected int $art_updated_at = 0;
/** @var Collection<int, PodcastCategory> */
#[ORM\OneToMany(mappedBy: 'podcast', targetEntity: PodcastCategory::class)] #[ORM\OneToMany(mappedBy: 'podcast', targetEntity: PodcastCategory::class)]
protected Collection $categories; protected Collection $categories;
/** @var Collection<int, PodcastEpisode> */
#[ORM\OneToMany(mappedBy: 'podcast', targetEntity: PodcastEpisode::class)] #[ORM\OneToMany(mappedBy: 'podcast', targetEntity: PodcastEpisode::class)]
protected Collection $episodes; protected Collection $episodes;
@ -155,7 +157,7 @@ class Podcast implements Interfaces\IdentifiableEntityInterface
} }
/** /**
* @return Collection|PodcastCategory[] * @return Collection<int, PodcastCategory>
*/ */
public function getCategories(): Collection public function getCategories(): Collection
{ {
@ -163,7 +165,7 @@ class Podcast implements Interfaces\IdentifiableEntityInterface
} }
/** /**
* @return Collection|PodcastEpisode[] * @return Collection<int, PodcastEpisode>
*/ */
public function getEpisodes(): Collection public function getEpisodes(): Collection
{ {

View File

@ -55,6 +55,7 @@ class Relay implements IdentifiableEntityInterface
] ]
protected int $updated_at; protected int $updated_at;
/** @var Collection<int, StationRemote> */
#[ORM\OneToMany(mappedBy: 'relay', targetEntity: StationRemote::class)] #[ORM\OneToMany(mappedBy: 'relay', targetEntity: StationRemote::class)]
protected Collection $remotes; protected Collection $remotes;
@ -129,6 +130,9 @@ class Relay implements IdentifiableEntityInterface
$this->updated_at = $updated_at; $this->updated_at = $updated_at;
} }
/**
* @return Collection<int, StationRemote>
*/
public function getRemotes(): Collection public function getRemotes(): Collection
{ {
return $this->remotes; return $this->remotes;

View File

@ -31,9 +31,11 @@ class Role implements JsonSerializable, Stringable, IdentifiableEntityInterface
] ]
protected string $name; protected string $name;
/** @var Collection<int, User> */
#[ORM\ManyToMany(targetEntity: User::class, mappedBy: 'roles')] #[ORM\ManyToMany(targetEntity: User::class, mappedBy: 'roles')]
protected Collection $users; protected Collection $users;
/** @var Collection<int, RolePermission> */
#[ #[
OA\Property(type: "array", items: new OA\Items()), OA\Property(type: "array", items: new OA\Items()),
ORM\OneToMany(mappedBy: 'role', targetEntity: RolePermission::class) ORM\OneToMany(mappedBy: 'role', targetEntity: RolePermission::class)
@ -56,11 +58,17 @@ class Role implements JsonSerializable, Stringable, IdentifiableEntityInterface
$this->name = $this->truncateString($name, 100); $this->name = $this->truncateString($name, 100);
} }
/**
* @return Collection<int, User>
*/
public function getUsers(): Collection public function getUsers(): Collection
{ {
return $this->users; return $this->users;
} }
/**
* @return Collection<int, RolePermission>
*/
public function getPermissions(): Collection public function getPermissions(): Collection
{ {
return $this->permissions; return $this->permissions;

View File

@ -303,6 +303,7 @@ class Station implements Stringable, IdentifiableEntityInterface
] ]
protected ?string $default_album_art_url = null; protected ?string $default_album_art_url = null;
/** @var Collection<int, SongHistory> */
#[ #[
ORM\OneToMany(mappedBy: 'station', targetEntity: SongHistory::class), ORM\OneToMany(mappedBy: 'station', targetEntity: SongHistory::class),
ORM\OrderBy(['timestamp_start' => 'desc']) ORM\OrderBy(['timestamp_start' => 'desc'])
@ -351,6 +352,7 @@ class Station implements Stringable, IdentifiableEntityInterface
] ]
protected ?StorageLocation $podcasts_storage_location = null; protected ?StorageLocation $podcasts_storage_location = null;
/** @var Collection<int, StationStreamer> */
#[ORM\OneToMany(mappedBy: 'station', targetEntity: StationStreamer::class)] #[ORM\OneToMany(mappedBy: 'station', targetEntity: StationStreamer::class)]
protected Collection $streamers; protected Collection $streamers;
@ -366,21 +368,26 @@ class Station implements Stringable, IdentifiableEntityInterface
#[ORM\Column(length: 255, nullable: true)] #[ORM\Column(length: 255, nullable: true)]
protected ?string $fallback_path = null; protected ?string $fallback_path = null;
/** @var Collection<int, RolePermission> */
#[ORM\OneToMany(mappedBy: 'station', targetEntity: RolePermission::class)] #[ORM\OneToMany(mappedBy: 'station', targetEntity: RolePermission::class)]
protected Collection $permissions; protected Collection $permissions;
/** @var Collection<int, StationPlaylist> */
#[ #[
ORM\OneToMany(mappedBy: 'station', targetEntity: StationPlaylist::class), ORM\OneToMany(mappedBy: 'station', targetEntity: StationPlaylist::class),
ORM\OrderBy(['type' => 'ASC', 'weight' => 'DESC']) ORM\OrderBy(['type' => 'ASC', 'weight' => 'DESC'])
] ]
protected Collection $playlists; protected Collection $playlists;
/** @var Collection<int, StationMount> */
#[ORM\OneToMany(mappedBy: 'station', targetEntity: StationMount::class)] #[ORM\OneToMany(mappedBy: 'station', targetEntity: StationMount::class)]
protected Collection $mounts; protected Collection $mounts;
/** @var Collection<int, StationRemote> */
#[ORM\OneToMany(mappedBy: 'station', targetEntity: StationRemote::class)] #[ORM\OneToMany(mappedBy: 'station', targetEntity: StationRemote::class)]
protected Collection $remotes; protected Collection $remotes;
/** @var Collection<int, StationWebhook> */
#[ORM\OneToMany( #[ORM\OneToMany(
mappedBy: 'station', mappedBy: 'station',
targetEntity: StationWebhook::class, targetEntity: StationWebhook::class,
@ -389,6 +396,7 @@ class Station implements Stringable, IdentifiableEntityInterface
)] )]
protected Collection $webhooks; protected Collection $webhooks;
/** @var Collection<int, SftpUser> */
#[ORM\OneToMany(mappedBy: 'station', targetEntity: SftpUser::class)] #[ORM\OneToMany(mappedBy: 'station', targetEntity: SftpUser::class)]
protected Collection $sftp_users; protected Collection $sftp_users;
@ -398,6 +406,7 @@ class Station implements Stringable, IdentifiableEntityInterface
$this->backend_type = BackendAdapters::Liquidsoap->value; $this->backend_type = BackendAdapters::Liquidsoap->value;
$this->history = new ArrayCollection(); $this->history = new ArrayCollection();
$this->permissions = new ArrayCollection();
$this->playlists = new ArrayCollection(); $this->playlists = new ArrayCollection();
$this->mounts = new ArrayCollection(); $this->mounts = new ArrayCollection();
$this->remotes = new ArrayCollection(); $this->remotes = new ArrayCollection();
@ -941,7 +950,7 @@ class Station implements Stringable, IdentifiableEntityInterface
} }
/** /**
* @return Collection<SongHistory> * @return Collection<int, SongHistory>
*/ */
public function getHistory(): Collection public function getHistory(): Collection
{ {
@ -949,7 +958,7 @@ class Station implements Stringable, IdentifiableEntityInterface
} }
/** /**
* @return Collection<StationStreamer> * @return Collection<int, StationStreamer>
*/ */
public function getStreamers(): Collection public function getStreamers(): Collection
{ {
@ -1065,7 +1074,7 @@ class Station implements Stringable, IdentifiableEntityInterface
} }
/** /**
* @return Collection<RolePermission> * @return Collection<int, RolePermission>
*/ */
public function getPermissions(): Collection public function getPermissions(): Collection
{ {
@ -1073,7 +1082,7 @@ class Station implements Stringable, IdentifiableEntityInterface
} }
/** /**
* @return Collection<StationMedia> * @return Collection<int, StationMedia>
*/ */
public function getMedia(): Collection public function getMedia(): Collection
{ {
@ -1081,7 +1090,7 @@ class Station implements Stringable, IdentifiableEntityInterface
} }
/** /**
* @return Collection<StationPlaylist> * @return Collection<int, StationPlaylist>
*/ */
public function getPlaylists(): Collection public function getPlaylists(): Collection
{ {
@ -1089,7 +1098,7 @@ class Station implements Stringable, IdentifiableEntityInterface
} }
/** /**
* @return Collection<StationMount> * @return Collection<int, StationMount>
*/ */
public function getMounts(): Collection public function getMounts(): Collection
{ {
@ -1097,7 +1106,7 @@ class Station implements Stringable, IdentifiableEntityInterface
} }
/** /**
* @return Collection<StationRemote> * @return Collection<int, StationRemote>
*/ */
public function getRemotes(): Collection public function getRemotes(): Collection
{ {
@ -1105,7 +1114,7 @@ class Station implements Stringable, IdentifiableEntityInterface
} }
/** /**
* @return Collection<StationWebhook> * @return Collection<int, StationWebhook>
*/ */
public function getWebhooks(): Collection public function getWebhooks(): Collection
{ {
@ -1113,7 +1122,7 @@ class Station implements Stringable, IdentifiableEntityInterface
} }
/** /**
* @return Collection<SftpUser> * @return Collection<int, SftpUser>
*/ */
public function getSftpUsers(): Collection public function getSftpUsers(): Collection
{ {

View File

@ -10,6 +10,9 @@ use App\Radio\Enums\StreamFormats;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use InvalidArgumentException; use InvalidArgumentException;
/**
* @extends ArrayCollection<string, mixed>
*/
class StationBackendConfiguration extends ArrayCollection class StationBackendConfiguration extends ArrayCollection
{ {
public const CHARSET = 'charset'; public const CHARSET = 'charset';

View File

@ -7,6 +7,9 @@ namespace App\Entity;
use App\Utilities\Strings; use App\Utilities\Strings;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
/**
* @extends ArrayCollection<string, mixed>
*/
class StationFrontendConfiguration extends ArrayCollection class StationFrontendConfiguration extends ArrayCollection
{ {
public function __construct(array $elements = []) public function __construct(array $elements = [])

View File

@ -191,6 +191,7 @@ class StationMedia implements
] ]
protected int $art_updated_at = 0; protected int $art_updated_at = 0;
/** @var Collection<int, StationPlaylistMedia> */
#[ #[
OA\Property(type: "array", items: new OA\Items()), OA\Property(type: "array", items: new OA\Items()),
ORM\OneToMany(mappedBy: 'media', targetEntity: StationPlaylistMedia::class), ORM\OneToMany(mappedBy: 'media', targetEntity: StationPlaylistMedia::class),
@ -199,6 +200,7 @@ class StationMedia implements
] ]
protected Collection $playlists; protected Collection $playlists;
/** @var Collection<int, StationMediaCustomField> */
#[ORM\OneToMany(mappedBy: 'media', targetEntity: StationMediaCustomField::class)] #[ORM\OneToMany(mappedBy: 'media', targetEntity: StationMediaCustomField::class)]
protected Collection $custom_fields; protected Collection $custom_fields;
@ -428,13 +430,16 @@ class StationMedia implements
} }
/** /**
* @return Collection<CustomField> * @return Collection<int, StationMediaCustomField>
*/ */
public function getCustomFields(): Collection public function getCustomFields(): Collection
{ {
return $this->custom_fields; return $this->custom_fields;
} }
/**
* @param Collection<int, StationMediaCustomField> $custom_fields
*/
public function setCustomFields(Collection $custom_fields): void public function setCustomFields(Collection $custom_fields): void
{ {
$this->custom_fields = $custom_fields; $this->custom_fields = $custom_fields;
@ -462,7 +467,7 @@ class StationMedia implements
} }
/** /**
* @return Collection<StationPlaylistMedia> * @return Collection<int, StationPlaylistMedia>
*/ */
public function getPlaylists(): Collection public function getPlaylists(): Collection
{ {

View File

@ -181,17 +181,20 @@ class StationPlaylist implements
] ]
protected int $queue_reset_at = 0; protected int $queue_reset_at = 0;
/** @var Collection<int, StationPlaylistMedia> */
#[ #[
ORM\OneToMany(mappedBy: 'playlist', targetEntity: StationPlaylistMedia::class, fetch: 'EXTRA_LAZY'), ORM\OneToMany(mappedBy: 'playlist', targetEntity: StationPlaylistMedia::class, fetch: 'EXTRA_LAZY'),
ORM\OrderBy(['weight' => 'ASC']) ORM\OrderBy(['weight' => 'ASC'])
] ]
protected Collection $media_items; protected Collection $media_items;
/** @var Collection<int, StationPlaylistFolder> */
#[ #[
ORM\OneToMany(mappedBy: 'playlist', targetEntity: StationPlaylistFolder::class, fetch: 'EXTRA_LAZY') ORM\OneToMany(mappedBy: 'playlist', targetEntity: StationPlaylistFolder::class, fetch: 'EXTRA_LAZY')
] ]
protected Collection $folders; protected Collection $folders;
/** @var Collection<int, StationSchedule> */
#[ #[
OA\Property(type: "array", items: new OA\Items()), OA\Property(type: "array", items: new OA\Items()),
ORM\OneToMany(mappedBy: 'playlist', targetEntity: StationSchedule::class, fetch: 'EXTRA_LAZY'), ORM\OneToMany(mappedBy: 'playlist', targetEntity: StationSchedule::class, fetch: 'EXTRA_LAZY'),
@ -438,7 +441,7 @@ class StationPlaylist implements
} }
/** /**
* @return Collection<StationPlaylistMedia> * @return Collection<int, StationPlaylistMedia>
*/ */
public function getMediaItems(): Collection public function getMediaItems(): Collection
{ {
@ -446,7 +449,7 @@ class StationPlaylist implements
} }
/** /**
* @return Collection<StationPlaylistFolder> * @return Collection<int, StationPlaylistFolder>
*/ */
public function getFolders(): Collection public function getFolders(): Collection
{ {
@ -454,7 +457,7 @@ class StationPlaylist implements
} }
/** /**
* @return Collection<StationSchedule> * @return Collection<int, StationSchedule>
*/ */
public function getScheduleItems(): Collection public function getScheduleItems(): Collection
{ {

View File

@ -99,6 +99,7 @@ class StationStreamer implements
] ]
protected int $art_updated_at = 0; protected int $art_updated_at = 0;
/** @var Collection<int, StationSchedule> */
#[ #[
OA\Property(type: "array", items: new OA\Items()), OA\Property(type: "array", items: new OA\Items()),
ORM\OneToMany(mappedBy: 'streamer', targetEntity: StationSchedule::class), ORM\OneToMany(mappedBy: 'streamer', targetEntity: StationSchedule::class),
@ -228,7 +229,7 @@ class StationStreamer implements
} }
/** /**
* @return Collection<StationSchedule> * @return Collection<int, StationSchedule>
*/ */
public function getScheduleItems(): Collection public function getScheduleItems(): Collection
{ {

View File

@ -100,6 +100,7 @@ class StorageLocation implements Stringable, IdentifiableEntityInterface
#[Attributes\AuditIgnore] #[Attributes\AuditIgnore]
protected ?string $storageUsed = null; protected ?string $storageUsed = null;
/** @var Collection<int, StationMedia> */
#[ORM\OneToMany(mappedBy: 'storage_location', targetEntity: StationMedia::class)] #[ORM\OneToMany(mappedBy: 'storage_location', targetEntity: StationMedia::class)]
protected Collection $media; protected Collection $media;
@ -462,7 +463,7 @@ class StorageLocation implements Stringable, IdentifiableEntityInterface
} }
/** /**
* @return Collection<StationMedia> * @return Collection<int, StationMedia>
*/ */
public function getMedia(): Collection public function getMedia(): Collection
{ {
@ -508,7 +509,7 @@ class StorageLocation implements Stringable, IdentifiableEntityInterface
$client = $this->getS3Client(); $client = $this->getS3Client();
$client->listObjectsV2( $client->listObjectsV2(
[ [
'Bucket' => $this->s3Bucket, 'Bucket' => $this->s3Bucket,
'max-keys' => 1, 'max-keys' => 1,
] ]
); );
@ -550,12 +551,12 @@ class StorageLocation implements Stringable, IdentifiableEntityInterface
$s3Options = array_filter( $s3Options = array_filter(
[ [
'credentials' => [ 'credentials' => [
'key' => $this->s3CredentialKey, 'key' => $this->s3CredentialKey,
'secret' => $this->s3CredentialSecret, 'secret' => $this->s3CredentialSecret,
], ],
'region' => $this->s3Region, 'region' => $this->s3Region,
'version' => $this->s3Version, 'version' => $this->s3Version,
'endpoint' => $this->s3Endpoint, 'endpoint' => $this->s3Endpoint,
] ]
); );
return new S3Client($s3Options); return new S3Client($s3Options);

View File

@ -105,6 +105,7 @@ class User implements Stringable, IdentifiableEntityInterface
] ]
protected int $updated_at; protected int $updated_at;
/** @var Collection<int, Role> */
#[ #[
OA\Property(type: "array", items: new OA\Items()), OA\Property(type: "array", items: new OA\Items()),
ORM\ManyToMany(targetEntity: Role::class, inversedBy: 'users', fetch: 'EAGER'), ORM\ManyToMany(targetEntity: Role::class, inversedBy: 'users', fetch: 'EAGER'),
@ -117,6 +118,7 @@ class User implements Stringable, IdentifiableEntityInterface
] ]
protected Collection $roles; protected Collection $roles;
/** @var Collection<int, ApiKey> */
#[ #[
ORM\OneToMany(mappedBy: 'user', targetEntity: ApiKey::class), ORM\OneToMany(mappedBy: 'user', targetEntity: ApiKey::class),
Groups([EntityGroupsInterface::GROUP_ADMIN, EntityGroupsInterface::GROUP_ALL]), Groups([EntityGroupsInterface::GROUP_ADMIN, EntityGroupsInterface::GROUP_ALL]),
@ -262,7 +264,7 @@ class User implements Stringable, IdentifiableEntityInterface
} }
/** /**
* @return Collection<Role> * @return Collection<int, Role>
*/ */
public function getRoles(): Collection public function getRoles(): Collection
{ {
@ -270,7 +272,7 @@ class User implements Stringable, IdentifiableEntityInterface
} }
/** /**
* @return Collection<ApiKey> * @return Collection<int, ApiKey>
*/ */
public function getApiKeys(): Collection public function getApiKeys(): Collection
{ {

View File

@ -20,6 +20,11 @@ use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Traversable; use Traversable;
/**
* @template TKey of array-key
* @template T of mixed
* @implements IteratorAggregate<TKey, T>
*/
class Paginator implements IteratorAggregate, Countable class Paginator implements IteratorAggregate, Countable
{ {
protected RouterInterface $router; protected RouterInterface $router;
@ -39,6 +44,9 @@ class Paginator implements IteratorAggregate, Countable
/** @var callable|null A callable postprocessor that can be run on each result. */ /** @var callable|null A callable postprocessor that can be run on each result. */
protected $postprocessor; protected $postprocessor;
/**
* @param Pagerfanta<T> $paginator
*/
public function __construct( public function __construct(
protected Pagerfanta $paginator, protected Pagerfanta $paginator,
ServerRequestInterface $request ServerRequestInterface $request
@ -206,6 +214,13 @@ class Paginator implements IteratorAggregate, Countable
); );
} }
/**
* @template XKey of array-key
* @template X of mixed
*
* @param array<XKey, X> $input
* @return static<XKey, X>
*/
public static function fromArray(array $input, ServerRequestInterface $request): self public static function fromArray(array $input, ServerRequestInterface $request): self
{ {
return new self( return new self(
@ -214,6 +229,13 @@ class Paginator implements IteratorAggregate, Countable
); );
} }
/**
* @template XKey of array-key
* @template X of mixed
*
* @param Collection<XKey, X> $collection
* @return static<XKey, X>
*/
public static function fromCollection(Collection $collection, ServerRequestInterface $request): self public static function fromCollection(Collection $collection, ServerRequestInterface $request): self
{ {
return new self( return new self(
@ -222,6 +244,9 @@ class Paginator implements IteratorAggregate, Countable
); );
} }
/**
* @return static<int, mixed>
*/
public static function fromQueryBuilder(QueryBuilder $qb, ServerRequestInterface $request): self public static function fromQueryBuilder(QueryBuilder $qb, ServerRequestInterface $request): self
{ {
return new self( return new self(
@ -230,6 +255,9 @@ class Paginator implements IteratorAggregate, Countable
); );
} }
/**
* @return static<int, mixed>
*/
public static function fromQuery(Query $query, ServerRequestInterface $request): self public static function fromQuery(Query $query, ServerRequestInterface $request): self
{ {
return new self( return new self(

View File

@ -227,7 +227,7 @@ class Scheduler
} }
/** /**
* @param Collection<Entity\StationSchedule> $scheduleItems * @param Collection<int, Entity\StationSchedule> $scheduleItems
* @param CarbonInterface $now * @param CarbonInterface $now
* @return Entity\StationSchedule|null * @return Entity\StationSchedule|null
*/ */