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:
level: 8
checkGenericClassInNonGenericObjectType: false
checkMissingIterableValueType: false
paths:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -21,7 +21,11 @@ class User extends AbstractFixture implements DependentFixtureInterface
$demo_user->setEmail('demo@azuracast.com');
$demo_user->setNewPassword('demo');
$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);
$this->addReference('demo_user', $demo_user);
@ -32,7 +36,9 @@ class User extends AbstractFixture implements DependentFixtureInterface
$admin_user->setNewPassword($admin_password);
$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');
if (!empty($admin_2fa_secret)) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -99,6 +99,7 @@ class StationStreamer implements
]
protected int $art_updated_at = 0;
/** @var Collection<int, StationSchedule> */
#[
OA\Property(type: "array", items: new OA\Items()),
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
{

View File

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

View File

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

View File

@ -20,6 +20,11 @@ use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Traversable;
/**
* @template TKey of array-key
* @template T of mixed
* @implements IteratorAggregate<TKey, T>
*/
class Paginator implements IteratorAggregate, Countable
{
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. */
protected $postprocessor;
/**
* @param Pagerfanta<T> $paginator
*/
public function __construct(
protected Pagerfanta $paginator,
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
{
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
{
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
{
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
{
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
* @return Entity\StationSchedule|null
*/