More type fixes!

This commit is contained in:
Buster Neece 2024-01-14 13:20:01 -06:00
parent c2f352c115
commit bbf8d56b46
No known key found for this signature in database
26 changed files with 149 additions and 84 deletions

View File

@ -12,7 +12,7 @@ $environment = App\AppFactory::buildEnvironment();
$console = new Symfony\Component\Console\Application( $console = new Symfony\Component\Console\Application(
'AzuraCast installer', 'AzuraCast installer',
App\Version::FALLBACK_VERSION App\Version::STABLE_VERSION
); );
$installCommand = new App\Installer\Command\InstallCommand(); $installCommand = new App\Installer\Command\InstallCommand();

View File

@ -330,6 +330,10 @@ return [
// Register plugin-provided message queue receivers // Register plugin-provided message queue receivers
$receivers = $plugins->registerMessageQueueReceivers($receivers); $receivers = $plugins->registerMessageQueueReceivers($receivers);
/**
* @var class-string $messageClass
* @var class-string $handlerClass
*/
foreach ($receivers as $messageClass => $handlerClass) { foreach ($receivers as $messageClass => $handlerClass) {
$handlers[$messageClass][] = static function ($message) use ($handlerClass, $di) { $handlers[$messageClass][] = static function ($message) use ($handlerClass, $di) {
$obj = $di->get($handlerClass); $obj = $di->get($handlerClass);

View File

@ -19,12 +19,30 @@ use Psr\EventDispatcher\EventDispatcherInterface;
use function in_array; use function in_array;
use function is_array; use function is_array;
/**
* @phpstan-type PermissionsArray array{
* global: array<string, string>,
* station: array<string, string>
* }
*/
final class Acl final class Acl
{ {
use RequestAwareTrait; use RequestAwareTrait;
/**
* @var PermissionsArray
*/
private array $permissions; private array $permissions;
/**
* @var null|array<
* int,
* array{
* stations?: array<int, array<string>>,
* global?: array<string>
* }
* >
*/
private ?array $actions; private ?array $actions;
public function __construct( public function __construct(
@ -41,12 +59,12 @@ final class Acl
{ {
$sql = $this->em->createQuery( $sql = $this->em->createQuery(
<<<'DQL' <<<'DQL'
SELECT rp FROM App\Entity\RolePermission rp SELECT rp.station_id, rp.role_id, rp.action_name FROM App\Entity\RolePermission rp
DQL DQL
); );
$this->actions = []; $this->actions = [];
foreach ($sql->getArrayResult() as $row) { foreach ($sql->toIterable() as $row) {
if ($row['station_id']) { if ($row['station_id']) {
$this->actions[$row['role_id']]['stations'][$row['station_id']][] = $row['action_name']; $this->actions[$row['role_id']]['stations'][$row['station_id']][] = $row['action_name'];
} else { } else {
@ -69,12 +87,11 @@ final class Acl
} }
/** /**
* @return mixed[] * @return array
*/ */
public function listPermissions(): array public function listPermissions(): array
{ {
if (!isset($this->permissions)) { if (!isset($this->permissions)) {
/** @var array<string,array<string, string>> $permissions */
$permissions = [ $permissions = [
'global' => [], 'global' => [],
'station' => [], 'station' => [],

View File

@ -8,6 +8,7 @@ use App\Container\EnvironmentAwareTrait;
use App\Entity\Repository\UserRepository; use App\Entity\Repository\UserRepository;
use App\Entity\User; use App\Entity\User;
use App\Exception\NotLoggedInException; use App\Exception\NotLoggedInException;
use App\Utilities\Types;
use Mezzio\Session\SessionInterface; use Mezzio\Session\SessionInterface;
final class Auth final class Auth
@ -83,7 +84,7 @@ final class Auth
if (!$this->session->has(self::SESSION_MASQUERADE_USER_ID_KEY)) { if (!$this->session->has(self::SESSION_MASQUERADE_USER_ID_KEY)) {
$this->masqueraded_user = false; $this->masqueraded_user = false;
} else { } else {
$maskUserId = (int)$this->session->get(self::SESSION_MASQUERADE_USER_ID_KEY); $maskUserId = Types::int($this->session->get(self::SESSION_MASQUERADE_USER_ID_KEY));
if (0 !== $maskUserId) { if (0 !== $maskUserId) {
$user = $this->userRepo->getRepository()->find($maskUserId); $user = $this->userRepo->getRepository()->find($maskUserId);
} else { } else {
@ -125,7 +126,7 @@ final class Auth
*/ */
public function isLoginComplete(): bool public function isLoginComplete(): bool
{ {
return $this->session->get(self::SESSION_IS_LOGIN_COMPLETE_KEY, false) ?? false; return Types::bool($this->session->get(self::SESSION_IS_LOGIN_COMPLETE_KEY, false));
} }
/** /**
@ -136,7 +137,7 @@ final class Auth
public function getUser(): ?User public function getUser(): ?User
{ {
if (null === $this->user) { if (null === $this->user) {
$userId = (int)$this->session->get(self::SESSION_USER_ID_KEY); $userId = Types::int($this->session->get(self::SESSION_USER_ID_KEY));
if (0 === $userId) { if (0 === $userId) {
$this->user = false; $this->user = false;

View File

@ -6,6 +6,7 @@ namespace App\Cache;
use App\Entity\Api\NowPlaying\NowPlaying; use App\Entity\Api\NowPlaying\NowPlaying;
use App\Entity\Station; use App\Entity\Station;
use App\Utilities\Types;
use Psr\Cache\CacheItemInterface; use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface; use Psr\Cache\CacheItemPoolInterface;
@ -41,9 +42,13 @@ final class NowPlayingCache
$stationCacheItem = $this->getStationCache($station); $stationCacheItem = $this->getStationCache($station);
return ($stationCacheItem->isHit()) if (!$stationCacheItem->isHit()) {
? $stationCacheItem->get() return null;
: null; }
$np = $stationCacheItem->get();
assert($np instanceof NowPlaying);
return $np;
} }
/** /**
@ -78,11 +83,18 @@ final class NowPlayingCache
return $np; return $np;
} }
/**
* @return array<int, array{
* short_name: string,
* is_public: bool,
* updated_at: int
* }>
*/
public function getLookup(): array public function getLookup(): array
{ {
$lookupCacheItem = $this->getLookupCache(); $lookupCacheItem = $this->getLookupCache();
return $lookupCacheItem->isHit() return $lookupCacheItem->isHit()
? (array)$lookupCacheItem->get() ? Types::array($lookupCacheItem->get())
: []; : [];
} }
@ -114,7 +126,7 @@ final class NowPlayingCache
$lookupCacheItem = $this->getLookupCache(); $lookupCacheItem = $this->getLookupCache();
$lookupCache = $lookupCacheItem->isHit() $lookupCache = $lookupCacheItem->isHit()
? (array)$lookupCacheItem->get() ? Types::array($lookupCacheItem->get())
: []; : [];
$lookupCache[$station->getIdRequired()] = [ $lookupCache[$station->getIdRequired()] = [
@ -131,12 +143,9 @@ final class NowPlayingCache
private function getStationCache(string $identifier): CacheItemInterface private function getStationCache(string $identifier): CacheItemInterface
{ {
if (is_numeric($identifier)) { if (is_numeric($identifier)) {
$lookupCacheItem = $this->getLookupCache(); $lookupCache = $this->getLookup();
$lookupCache = $lookupCacheItem->isHit()
? (array)$lookupCacheItem->get()
: [];
$identifier = (int)$identifier; $identifier = Types::int($identifier);
if (isset($lookupCache[$identifier])) { if (isset($lookupCache[$identifier])) {
$identifier = $lookupCache[$identifier]['short_name']; $identifier = $lookupCache[$identifier]['short_name'];
} }

View File

@ -51,7 +51,7 @@ final class GenerateApiDocsCommand extends CommandAbstract
define('AZURACAST_API_NAME', 'AzuraCast Public Demo Server'); define('AZURACAST_API_NAME', 'AzuraCast Public Demo Server');
define( define(
'AZURACAST_VERSION', 'AZURACAST_VERSION',
$useCurrentVersion ? $this->version->getVersion() : Version::FALLBACK_VERSION $useCurrentVersion ? $this->version->getVersion() : Version::STABLE_VERSION
); );
$finder = Util::finder( $finder = Util::finder(

View File

@ -206,7 +206,8 @@ final class CloneAction extends StationsController implements SingleActionInterf
} }
/** /**
* @param Collection<int, mixed> $collection * @template T of mixed
* @param Collection<int, T> $collection
*/ */
private function cloneCollection( private function cloneCollection(
Collection $collection, Collection $collection,

View File

@ -86,6 +86,7 @@ final class DownloadAction implements SingleActionInterface
$csv->insertOne($headerRow); $csv->insertOne($headerRow);
/** @var array $row */
foreach ($query->getArrayResult() as $row) { foreach ($query->getArrayResult() as $row) {
$customFieldsById = []; $customFieldsById = [];
foreach ($row['custom_fields'] ?? [] as $rowCustomField) { foreach ($row['custom_fields'] ?? [] as $rowCustomField) {

View File

@ -28,6 +28,7 @@ use App\Radio\Backend\Liquidsoap;
use App\Radio\Enums\BackendAdapters; use App\Radio\Enums\BackendAdapters;
use App\Radio\Enums\LiquidsoapQueues; use App\Radio\Enums\LiquidsoapQueues;
use App\Utilities\File; use App\Utilities\File;
use App\Utilities\Types;
use Exception; use Exception;
use InvalidArgumentException; use InvalidArgumentException;
use League\Flysystem\StorageAttributes; use League\Flysystem\StorageAttributes;
@ -411,8 +412,12 @@ final class BatchAction implements SingleActionInterface
ExtendedFilesystemInterface $fs, ExtendedFilesystemInterface $fs,
bool $recursive = false bool $recursive = false
): BatchResult { ): BatchResult {
$files = array_values((array)$request->getParam('files', [])); $files = array_values(
$directories = array_values((array)$request->getParam('dirs', [])); Types::array($request->getParam('files', []))
);
$directories = array_values(
Types::array($request->getParam('dirs', []))
);
if ($recursive) { if ($recursive) {
foreach ($directories as $dir) { foreach ($directories as $dir) {

View File

@ -9,6 +9,7 @@ use App\Entity\Api\Error;
use App\Flysystem\StationFilesystems; use App\Flysystem\StationFilesystems;
use App\Http\Response; use App\Http\Response;
use App\Http\ServerRequest; use App\Http\ServerRequest;
use App\Utilities\Types;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
final class DownloadAction implements SingleActionInterface final class DownloadAction implements SingleActionInterface
@ -29,7 +30,7 @@ final class DownloadAction implements SingleActionInterface
$fsMedia = $this->stationFilesystems->getMediaFilesystem($station); $fsMedia = $this->stationFilesystems->getMediaFilesystem($station);
$path = $request->getParam('file'); $path = Types::string($request->getParam('file'));
if (!$fsMedia->fileExists($path)) { if (!$fsMedia->fileExists($path)) {
return $response->withStatus(404) return $response->withStatus(404)

View File

@ -20,6 +20,7 @@ use App\Http\Response;
use App\Http\ServerRequest; use App\Http\ServerRequest;
use App\Media\MediaProcessor; use App\Media\MediaProcessor;
use App\Service\Flow; use App\Service\Flow;
use App\Utilities\Types;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
final class FlowUploadAction implements SingleActionInterface final class FlowUploadAction implements SingleActionInterface
@ -51,7 +52,7 @@ final class FlowUploadAction implements SingleActionInterface
return $flowResponse; return $flowResponse;
} }
$currentDir = $request->getParam('currentDirectory', ''); $currentDir = Types::string($request->getParam('currentDirectory'));
$destPath = $flowResponse->getClientFilename(); $destPath = $flowResponse->getClientFilename();
if (!empty($currentDir)) { if (!empty($currentDir)) {

View File

@ -52,10 +52,10 @@ final class ListAction implements SingleActionInterface
$fs = $this->stationFilesystems->getMediaFilesystem($station); $fs = $this->stationFilesystems->getMediaFilesystem($station);
$currentDir = $request->getParam('currentDirectory', ''); $currentDir = Types::string($request->getParam('currentDirectory'));
$searchPhraseFull = trim($request->getParam('searchPhrase', '')); $searchPhraseFull = Types::stringOrNull($request->getParam('searchPhrase'), true);
$isSearch = !empty($searchPhraseFull); $isSearch = null !== $searchPhraseFull;
[$searchPhrase, $playlist, $special] = $this->parseSearchQuery( [$searchPhrase, $playlist, $special] = $this->parseSearchQuery(
$station, $station,
@ -74,7 +74,7 @@ final class ListAction implements SingleActionInterface
$cacheKey = implode('.', $cacheKeyParts); $cacheKey = implode('.', $cacheKeyParts);
$flushCache = (bool)$request->getParam('flushCache', false); $flushCache = Types::bool($request->getParam('flushCache'));
if (!$flushCache && $this->cache->has($cacheKey)) { if (!$flushCache && $this->cache->has($cacheKey)) {
/** @var array<int, FileList> $result */ /** @var array<int, FileList> $result */

View File

@ -61,7 +61,7 @@ trait CanSortResults
} }
/** /**
* @return array{string, string} * @return array{string|null, Criteria::ASC|Criteria::DESC}
*/ */
protected function getSortFromRequest( protected function getSortFromRequest(
ServerRequest $request, ServerRequest $request,
@ -69,7 +69,7 @@ trait CanSortResults
): array { ): array {
$sortOrder = Types::stringOrNull($request->getParam('sortOrder'), true) ?? $defaultSortOrder; $sortOrder = Types::stringOrNull($request->getParam('sortOrder'), true) ?? $defaultSortOrder;
return [ return [
$request->getParam('sort'), Types::stringOrNull($request->getParam('sort'), true),
(Criteria::DESC === strtoupper($sortOrder)) (Criteria::DESC === strtoupper($sortOrder))
? Criteria::DESC ? Criteria::DESC
: Criteria::ASC, : Criteria::ASC,

View File

@ -41,10 +41,9 @@ trait HasMediaSearch
FROM App\Entity\StationPlaylist sp FROM App\Entity\StationPlaylist sp
WHERE sp.station = :station WHERE sp.station = :station
DQL DQL
)->setParameter('station', $station) )->setParameter('station', $station);
->getArrayResult();
foreach ($playlistNameLookupRaw as $playlistRow) { foreach ($playlistNameLookupRaw->toIterable() as $playlistRow) {
$shortName = StationPlaylist::generateShortName($playlistRow['name']); $shortName = StationPlaylist::generateShortName($playlistRow['name']);
if ($shortName === $playlistId) { if ($shortName === $playlistId) {
$playlistId = $playlistRow['id']; $playlistId = $playlistRow['id'];

View File

@ -8,8 +8,10 @@ use JsonSerializable;
final class BatchResult implements JsonSerializable final class BatchResult implements JsonSerializable
{ {
/** @var string[] */
public array $files = []; public array $files = [];
/** @var string[] */
public array $directories = []; public array $directories = [];
public array $errors = []; public array $errors = [];

View File

@ -4,23 +4,33 @@ declare(strict_types=1);
namespace App\Event; namespace App\Event;
use App\Acl;
use Symfony\Contracts\EventDispatcher\Event; use Symfony\Contracts\EventDispatcher\Event;
/**
* @phpstan-import-type PermissionsArray from Acl
*/
final class BuildPermissions extends Event final class BuildPermissions extends Event
{ {
/**
* @param PermissionsArray $permissions
*/
public function __construct( public function __construct(
private array $permissions private array $permissions
) { ) {
} }
/** /**
* @return mixed[] * @return PermissionsArray
*/ */
public function getPermissions(): array public function getPermissions(): array
{ {
return $this->permissions; return $this->permissions;
} }
/**
* @param PermissionsArray $permissions
*/
public function setPermissions(array $permissions): void public function setPermissions(array $permissions): void
{ {
$this->permissions = $permissions; $this->permissions = $permissions;

View File

@ -56,7 +56,7 @@ final class RemoteAlbumArt
[ [
RequestOptions::TIMEOUT => 10, RequestOptions::TIMEOUT => 10,
RequestOptions::HEADERS => [ RequestOptions::HEADERS => [
'User-Agent' => 'AzuraCast ' . Version::FALLBACK_VERSION, 'User-Agent' => 'AzuraCast ' . Version::STABLE_VERSION,
], ],
] ]
); );

View File

@ -43,7 +43,7 @@ final class AzuraCastCentral
if ($commitHash) { if ($commitHash) {
$requestBody['version'] = $commitHash; $requestBody['version'] = $commitHash;
} else { } else {
$requestBody['release'] = Version::FALLBACK_VERSION; $requestBody['release'] = Version::STABLE_VERSION;
} }
$this->logger->debug( $this->logger->debug(

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace App\Service\Dropbox; namespace App\Service\Dropbox;
use App\Entity\StorageLocation; use App\Entity\StorageLocation;
use App\Utilities\Types;
use GuzzleHttp\Exception\ClientException; use GuzzleHttp\Exception\ClientException;
use Psr\Cache\CacheItemPoolInterface; use Psr\Cache\CacheItemPoolInterface;
use Spatie\Dropbox\RefreshableTokenProvider; use Spatie\Dropbox\RefreshableTokenProvider;
@ -80,7 +81,7 @@ final class OAuthAdapter implements RefreshableTokenProvider
$this->psr6Cache->save($cacheItem); $this->psr6Cache->save($cacheItem);
} }
return $cacheItem->get(); return Types::string($cacheItem->get());
} }
private function getOauthProvider(): OAuthProvider private function getOauthProvider(): OAuthProvider

View File

@ -81,7 +81,7 @@ final class LastFm
[ [
RequestOptions::HTTP_ERRORS => true, RequestOptions::HTTP_ERRORS => true,
RequestOptions::HEADERS => [ RequestOptions::HEADERS => [
'User-Agent' => 'AzuraCast ' . Version::FALLBACK_VERSION, 'User-Agent' => 'AzuraCast ' . Version::STABLE_VERSION,
'Accept' => 'application/json', 'Accept' => 'application/json',
], ],
RequestOptions::QUERY => $query, RequestOptions::QUERY => $query,

View File

@ -65,7 +65,7 @@ final class MusicBrainz
RequestOptions::TIMEOUT => 7, RequestOptions::TIMEOUT => 7,
RequestOptions::HTTP_ERRORS => true, RequestOptions::HTTP_ERRORS => true,
RequestOptions::HEADERS => [ RequestOptions::HEADERS => [
'User-Agent' => 'AzuraCast ' . Version::FALLBACK_VERSION, 'User-Agent' => 'AzuraCast ' . Version::STABLE_VERSION,
'Accept' => 'application/json', 'Accept' => 'application/json',
], ],
RequestOptions::QUERY => $query, RequestOptions::QUERY => $query,
@ -157,7 +157,7 @@ final class MusicBrainz
[ [
RequestOptions::ALLOW_REDIRECTS => true, RequestOptions::ALLOW_REDIRECTS => true,
RequestOptions::HEADERS => [ RequestOptions::HEADERS => [
'User-Agent' => 'AzuraCast ' . Version::FALLBACK_VERSION, 'User-Agent' => 'AzuraCast ' . Version::STABLE_VERSION,
'Accept' => 'application/json', 'Accept' => 'application/json',
], ],
] ]

View File

@ -6,6 +6,7 @@ namespace App\Session;
use App\Environment; use App\Environment;
use App\Exception; use App\Exception;
use App\Utilities\Types;
use Mezzio\Session\SessionInterface; use Mezzio\Session\SessionInterface;
final class Csrf final class Csrf
@ -32,9 +33,9 @@ final class Csrf
{ {
$sessionKey = $this->getSessionIdentifier($namespace); $sessionKey = $this->getSessionIdentifier($namespace);
if ($this->session->has($sessionKey)) { if ($this->session->has($sessionKey)) {
$csrf = $this->session->get($sessionKey); $csrf = Types::stringOrNull($this->session->get($sessionKey), true);
if (!empty($csrf)) { if (null !== $csrf) {
return (string)$csrf; return $csrf;
} }
} }
@ -71,9 +72,8 @@ final class Csrf
throw new Exception\CsrfValidationException('No CSRF token supplied for this namespace.'); throw new Exception\CsrfValidationException('No CSRF token supplied for this namespace.');
} }
$sessionKey = $this->session->get($sessionIdentifier); $sessionKey = Types::string($this->session->get($sessionIdentifier));
if (0 !== strcmp($key, $sessionKey)) {
if (0 !== strcmp($key, (string)$sessionKey)) {
throw new Exception\CsrfValidationException('Invalid CSRF token supplied.'); throw new Exception\CsrfValidationException('Invalid CSRF token supplied.');
} }
} }

View File

@ -14,11 +14,19 @@ use Throwable;
/** /**
* App Core Framework Version * App Core Framework Version
*
* @phpstan-type VersionDetails array{
* commit: string|null,
* commit_short: string,
* commit_timestamp: int,
* commit_date: string,
* branch: string|null,
* }
*/ */
final class Version final class Version
{ {
/** @var string Version that is displayed if no Git repository information is present. */ /** @var string The current latest stable version. */
public const FALLBACK_VERSION = '0.19.4'; public const STABLE_VERSION = '0.19.4';
private string $repoDir; private string $repoDir;
@ -41,19 +49,10 @@ final class Version
: ReleaseChannel::RollingRelease; : ReleaseChannel::RollingRelease;
} }
/**
* @return string The current tagged version.
*/
public function getVersion(): string
{
$details = $this->getDetails();
return $details['tag'] ?? self::FALLBACK_VERSION;
}
/** /**
* Load cache or generate new repository details from the underlying Git repository. * Load cache or generate new repository details from the underlying Git repository.
* *
* @return string[] * @return VersionDetails
*/ */
public function getDetails(): array public function getDetails(): array
{ {
@ -63,12 +62,16 @@ final class Version
$details = $this->cache->get('app_version_details'); $details = $this->cache->get('app_version_details');
if (empty($details)) { if (empty($details)) {
$details = $this->getRawDetails(); $rawDetails = $this->getRawDetails();
$details['commit_short'] = substr($details['commit'] ?? '', 0, 7); $details = [
'commit' => $rawDetails['commit'],
'commit_short' => substr($rawDetails['commit'] ?? '', 0, 7),
'branch' => $rawDetails['branch'],
];
if (!empty($details['commit_date_raw'])) { if (!empty($rawDetails['commit_date_raw'])) {
$commitDate = new DateTime($details['commit_date_raw']); $commitDate = new DateTime($rawDetails['commit_date_raw']);
$commitDate->setTimezone(new DateTimeZone('UTC')); $commitDate->setTimezone(new DateTimeZone('UTC'));
$details['commit_timestamp'] = $commitDate->getTimestamp(); $details['commit_timestamp'] = $commitDate->getTimestamp();
@ -78,8 +81,6 @@ final class Version
$details['commit_date'] = 'N/A'; $details['commit_date'] = 'N/A';
} }
$details['tag'] = self::FALLBACK_VERSION;
$ttl = $this->environment->isProduction() ? 86400 : 600; $ttl = $this->environment->isProduction() ? 86400 : 600;
$this->cache->set('app_version_details', $details, $ttl); $this->cache->set('app_version_details', $details, $ttl);
@ -92,7 +93,11 @@ final class Version
/** /**
* Generate new repository details from the underlying Git repository. * Generate new repository details from the underlying Git repository.
* *
* @return mixed[] * @return array{
* commit: string|null,
* commit_date_raw: string|null,
* branch: string|null
* }
*/ */
private function getRawDetails(): array private function getRawDetails(): array
{ {
@ -120,7 +125,11 @@ final class Version
]; ];
} }
return []; return [
'commit' => null,
'commit_date_raw' => null,
'branch' => null,
];
} }
/** /**
@ -145,8 +154,9 @@ final class Version
public function getVersionText(): string public function getVersionText(): string
{ {
$details = $this->getDetails(); $details = $this->getDetails();
$releaseChannel = $this->getReleaseChannelEnum();
if (isset($details['tag'])) { if (ReleaseChannel::RollingRelease === $releaseChannel) {
$commitLink = 'https://github.com/AzuraCast/AzuraCast/commit/' . $details['commit']; $commitLink = 'https://github.com/AzuraCast/AzuraCast/commit/' . $details['commit'];
$commitText = sprintf( $commitText = sprintf(
'#<a href="%s" target="_blank">%s</a> (%s)', '#<a href="%s" target="_blank">%s</a> (%s)',
@ -155,14 +165,10 @@ final class Version
$details['commit_date'] $details['commit_date']
); );
$releaseChannel = $this->getReleaseChannelEnum(); return 'Rolling Release ' . $commitText;
if (ReleaseChannel::RollingRelease === $releaseChannel) {
return 'Rolling Release ' . $commitText;
}
return 'v' . $details['tag'] . ' Stable';
} }
return 'v' . self::FALLBACK_VERSION . ' Release Build'; return 'v' . self::STABLE_VERSION . ' Stable';
} }
/** /**
@ -171,15 +177,20 @@ final class Version
public function getCommitHash(): ?string public function getCommitHash(): ?string
{ {
$details = $this->getDetails(); $details = $this->getDetails();
return $details['commit'] ?? null; return $details['commit'];
} }
/** /**
* @return string|null The shortened Git hash corresponding to the current commit. * @return string The shortened Git hash corresponding to the current commit.
*/ */
public function getCommitShort(): ?string public function getCommitShort(): string
{ {
$details = $this->getDetails(); $details = $this->getDetails();
return $details['commit_short'] ?? null; return $details['commit_short'];
}
public function getVersion(): string
{
return self::STABLE_VERSION;
} }
} }

View File

@ -7,7 +7,9 @@ namespace App\Webhook\Connector;
use App\Container\LoggerAwareTrait; use App\Container\LoggerAwareTrait;
use App\Entity\Api\NowPlaying\NowPlaying; use App\Entity\Api\NowPlaying\NowPlaying;
use App\Entity\StationWebhook; use App\Entity\StationWebhook;
use App\Utilities; use App\Utilities\Arrays;
use App\Utilities\Types;
use App\Utilities\Urls;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use InvalidArgumentException; use InvalidArgumentException;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
@ -87,7 +89,7 @@ abstract class AbstractConnector implements ConnectorInterface
*/ */
public function replaceVariables(array $rawVars, NowPlaying $np): array public function replaceVariables(array $rawVars, NowPlaying $np): array
{ {
$values = Utilities\Arrays::flattenArray($np); $values = Arrays::flattenArray($np);
$vars = []; $vars = [];
foreach ($rawVars as $varKey => $varValue) { foreach ($rawVars as $varKey => $varValue) {
@ -96,7 +98,7 @@ abstract class AbstractConnector implements ConnectorInterface
"/\{\{(\s*)([a-zA-Z\d\-_.]+)(\s*)}}/", "/\{\{(\s*)([a-zA-Z\d\-_.]+)(\s*)}}/",
static function (array $matches) use ($values): string { static function (array $matches) use ($values): string {
$innerValue = strtolower(trim($matches[2])); $innerValue = strtolower(trim($matches[2]));
return $values[$innerValue] ?? ''; return Types::string($values[$innerValue] ?? '');
}, },
$varValue $varValue
); );
@ -110,9 +112,9 @@ abstract class AbstractConnector implements ConnectorInterface
*/ */
protected function getValidUrl(mixed $urlString = null): ?string protected function getValidUrl(mixed $urlString = null): ?string
{ {
$urlString = Utilities\Types::stringOrNull($urlString, true); $urlString = Types::stringOrNull($urlString, true);
$uri = Utilities\Urls::tryParseUserUrl( $uri = Urls::tryParseUserUrl(
$urlString, $urlString,
'Webhook' 'Webhook'
); );

View File

@ -18,7 +18,7 @@ final class Mastodon extends AbstractSocialConnector
protected function getRateLimitTime(StationWebhook $webhook): ?int protected function getRateLimitTime(StationWebhook $webhook): ?int
{ {
$config = $webhook->getConfig(); $config = $webhook->getConfig();
$rateLimitSeconds = (int)($config['rate_limit'] ?? 0); $rateLimitSeconds = Types::int($config['rate_limit'] ?? null);
return max(10, $rateLimitSeconds); return max(10, $rateLimitSeconds);
} }

View File

@ -8,7 +8,7 @@ ini_set('display_errors', 1);
require dirname(__DIR__) . '/vendor/autoload.php'; require dirname(__DIR__) . '/vendor/autoload.php';
const AZURACAST_VERSION = App\Version::FALLBACK_VERSION; const AZURACAST_VERSION = App\Version::STABLE_VERSION;
const AZURACAST_API_URL = 'https://localhost/api'; const AZURACAST_API_URL = 'https://localhost/api';
const AZURACAST_API_NAME = 'Testing API'; const AZURACAST_API_NAME = 'Testing API';