diff --git a/bin/console b/bin/console
index 2ab156a60..1440605bf 100644
--- a/bin/console
+++ b/bin/console
@@ -1,7 +1,10 @@
#!/usr/bin/env php
=7.4"
+ "league/mime-type-detection": "^1.7",
+ "php": ">=7.4",
+ "psr/http-message": ">1.0"
},
"require-dev": {
"php-parallel-lint/php-console-highlighter": "^0.5.0",
@@ -165,7 +167,7 @@
"type": "patreon"
}
],
- "time": "2021-04-26T10:41:32+00:00"
+ "time": "2021-07-18T17:02:47+00:00"
},
{
"name": "azuracast/flysystem-v2-extensions",
diff --git a/config/assets.php b/config/assets.php
index 2ad9c3b7e..c0c0b402a 100644
--- a/config/assets.php
+++ b/config/assets.php
@@ -148,12 +148,12 @@ return [
return '$(function () { ' . implode('', $notifies) . ' });';
},
function (Request $request) {
- /** @var Locale|null $locale */
+ /** @var App\Locale|null $locale */
$localeObj = $request->getAttribute(ServerRequest::ATTR_LOCALE);
$locale = ($localeObj instanceof App\Locale)
? (string)$localeObj
- : Locale::DEFAULT_LOCALE;
+ : App\Locale::DEFAULT_LOCALE;
$locale = explode('.', $locale, 2)[0];
$localeShort = substr($locale, 0, 2);
diff --git a/config/menus/admin.php b/config/menus/admin.php
index 432cd68eb..af62f1cb2 100644
--- a/config/menus/admin.php
+++ b/config/menus/admin.php
@@ -17,32 +17,32 @@ return function (App\Event\BuildAdminMenu $e) {
'items' => [
'settings' => [
'label' => __('System Settings'),
- 'url' => $router->named('admin:settings:index'),
+ 'url' => (string)$router->named('admin:settings:index'),
'permission' => Acl::GLOBAL_SETTINGS,
],
'branding' => [
'label' => __('Custom Branding'),
- 'url' => $router->named('admin:branding:index'),
+ 'url' => (string)$router->named('admin:branding:index'),
'permission' => Acl::GLOBAL_SETTINGS,
],
'logs' => [
'label' => __('System Logs'),
- 'url' => $router->named('admin:logs:index'),
+ 'url' => (string)$router->named('admin:logs:index'),
'permission' => Acl::GLOBAL_LOGS,
],
'storage_locations' => [
'label' => __('Storage Locations'),
- 'url' => $router->named('admin:storage_locations:index'),
+ 'url' => (string)$router->named('admin:storage_locations:index'),
'permission' => Acl::GLOBAL_STORAGE_LOCATIONS,
],
'backups' => [
'label' => __('Backups'),
- 'url' => $router->named('admin:backups:index'),
+ 'url' => (string)$router->named('admin:backups:index'),
'permission' => Acl::GLOBAL_BACKUPS,
],
'debug' => [
'label' => __('System Debugger'),
- 'url' => $router->named('admin:debug:index'),
+ 'url' => (string)$router->named('admin:debug:index'),
'permission' => Acl::GLOBAL_ALL,
],
],
@@ -53,22 +53,22 @@ return function (App\Event\BuildAdminMenu $e) {
'items' => [
'manage_users' => [
'label' => __('User Accounts'),
- 'url' => $router->named('admin:users:index'),
+ 'url' => (string)$router->named('admin:users:index'),
'permission' => Acl::GLOBAL_ALL,
],
'permissions' => [
'label' => __('Permissions'),
- 'url' => $router->named('admin:permissions:index'),
+ 'url' => (string)$router->named('admin:permissions:index'),
'permission' => Acl::GLOBAL_ALL,
],
'auditlog' => [
'label' => __('Audit Log'),
- 'url' => $router->named('admin:auditlog:index'),
+ 'url' => (string)$router->named('admin:auditlog:index'),
'permission' => Acl::GLOBAL_LOGS,
],
'api_keys' => [
'label' => __('API Keys'),
- 'url' => $router->named('admin:api:index'),
+ 'url' => (string)$router->named('admin:api:index'),
'permission' => Acl::GLOBAL_API_KEYS,
],
],
@@ -79,27 +79,27 @@ return function (App\Event\BuildAdminMenu $e) {
'items' => [
'manage_stations' => [
'label' => __('Stations'),
- 'url' => $router->named('admin:stations:index'),
+ 'url' => (string)$router->named('admin:stations:index'),
'permission' => Acl::GLOBAL_STATIONS,
],
'custom_fields' => [
'label' => __('Custom Fields'),
- 'url' => $router->named('admin:custom_fields:index'),
+ 'url' => (string)$router->named('admin:custom_fields:index'),
'permission' => Acl::GLOBAL_CUSTOM_FIELDS,
],
'relays' => [
'label' => __('Connected AzuraRelays'),
- 'url' => $router->named('admin:relays:index'),
+ 'url' => (string)$router->named('admin:relays:index'),
'permission' => Acl::GLOBAL_STATIONS,
],
'shoutcast' => [
'label' => __('Install SHOUTcast'),
- 'url' => $router->named('admin:install_shoutcast:index'),
+ 'url' => (string)$router->named('admin:install_shoutcast:index'),
'permission' => Acl::GLOBAL_ALL,
],
'geolite' => [
'label' => __('Install GeoLite IP Database'),
- 'url' => $router->named('admin:install_geolite:index'),
+ 'url' => (string)$router->named('admin:install_geolite:index'),
'permission' => Acl::GLOBAL_ALL,
],
],
diff --git a/config/menus/station.php b/config/menus/station.php
index 91f212727..8315eb98d 100644
--- a/config/menus/station.php
+++ b/config/menus/station.php
@@ -21,7 +21,7 @@ return function (App\Event\BuildStationMenu $e) {
'label' => __('Start Station'),
'title' => __('Ready to start broadcasting? Click to start your station.'),
'icon' => 'refresh',
- 'url' => $router->fromHere('api:stations:restart'),
+ 'url' => (string)$router->fromHere('api:stations:restart'),
'class' => 'api-call text-success',
'confirm' => __('Restart broadcasting? This will disconnect any current listeners.'),
'visible' => !$station->getHasStarted(),
@@ -31,7 +31,7 @@ return function (App\Event\BuildStationMenu $e) {
'label' => __('Restart to Apply Changes'),
'title' => __('Click to restart your station and apply configuration changes.'),
'icon' => 'refresh',
- 'url' => $router->fromHere('api:stations:restart'),
+ 'url' => (string)$router->fromHere('api:stations:restart'),
'class' => 'api-call text-warning',
'confirm' => __('Restart broadcasting? This will disconnect any current listeners.'),
'visible' => $station->getHasStarted() && $station->getNeedsRestart(),
@@ -40,53 +40,53 @@ return function (App\Event\BuildStationMenu $e) {
'profile' => [
'label' => __('Profile'),
'icon' => 'image',
- 'url' => $router->fromHere('stations:profile:index'),
+ 'url' => (string)$router->fromHere('stations:profile:index'),
],
'public' => [
'label' => __('Public Page'),
'icon' => 'public',
- 'url' => $router->named('public:index', ['station_id' => $station->getShortName()]),
+ 'url' => (string)$router->named('public:index', ['station_id' => $station->getShortName()]),
'external' => true,
'visible' => $station->getEnablePublicPage(),
],
'ondemand' => [
'label' => __('On-Demand Media'),
'icon' => 'cloud_download',
- 'url' => $router->named('public:ondemand', ['station_id' => $station->getShortName()]),
+ 'url' => (string)$router->named('public:ondemand', ['station_id' => $station->getShortName()]),
'external' => true,
'visible' => $station->getEnableOnDemand(),
],
'files' => [
'label' => __('Music Files'),
'icon' => 'library_music',
- 'url' => $router->fromHere('stations:files:index'),
+ 'url' => (string)$router->fromHere('stations:files:index'),
'visible' => $backend->supportsMedia(),
'permission' => Acl::STATION_MEDIA,
],
'playlists' => [
'label' => __('Playlists'),
'icon' => 'queue_music',
- 'url' => $router->fromHere('stations:playlists:index'),
+ 'url' => (string)$router->fromHere('stations:playlists:index'),
'visible' => $backend->supportsMedia(),
'permission' => Acl::STATION_MEDIA,
],
'podcasts' => [
'label' => __('Podcasts (Beta)'),
'icon' => 'cast',
- 'url' => $router->fromHere('stations:podcasts:index'),
+ 'url' => (string)$router->fromHere('stations:podcasts:index'),
'permission' => Acl::STATION_PODCASTS,
],
'streamers' => [
'label' => __('Streamer/DJ Accounts'),
'icon' => 'mic',
- 'url' => $router->fromHere('stations:streamers:index'),
+ 'url' => (string)$router->fromHere('stations:streamers:index'),
'visible' => $backend->supportsStreamers(),
'permission' => Acl::STATION_STREAMERS,
],
'web_dj' => [
'label' => __('Web DJ'),
'icon' => 'surround_sound',
- 'url' => $router->named('public:dj', ['station_id' => $station->getShortName()], [], true)
+ 'url' => (string)$router->named('public:dj', ['station_id' => $station->getShortName()], [], true)
->withScheme('https'),
'visible' => $station->getEnablePublicPage() && $station->getEnableStreamers(),
'external' => true,
@@ -94,20 +94,20 @@ return function (App\Event\BuildStationMenu $e) {
'mounts' => [
'label' => __('Mount Points'),
'icon' => 'wifi_tethering',
- 'url' => $router->fromHere('stations:mounts:index'),
+ 'url' => (string)$router->fromHere('stations:mounts:index'),
'visible' => $frontend->supportsMounts(),
'permission' => Acl::STATION_MOUNTS,
],
'remotes' => [
'label' => __('Remote Relays'),
'icon' => 'router',
- 'url' => $router->fromHere('stations:remotes:index'),
+ 'url' => (string)$router->fromHere('stations:remotes:index'),
'permission' => Acl::STATION_REMOTES,
],
'webhooks' => [
'label' => __('Web Hooks'),
'icon' => 'code',
- 'url' => $router->fromHere('stations:webhooks:index'),
+ 'url' => (string)$router->fromHere('stations:webhooks:index'),
'permission' => Acl::STATION_WEB_HOOKS,
],
'reports' => [
@@ -117,40 +117,40 @@ return function (App\Event\BuildStationMenu $e) {
'items' => [
'reports_overview' => [
'label' => __('Statistics Overview'),
- 'url' => $router->fromHere('stations:reports:overview'),
+ 'url' => (string)$router->fromHere('stations:reports:overview'),
],
'reports_listeners' => [
'label' => __('Listeners'),
- 'url' => $router->fromHere('stations:reports:listeners'),
+ 'url' => (string)$router->fromHere('stations:reports:listeners'),
'visible' => $frontend->supportsListenerDetail(),
],
'reports_requests' => [
'label' => __('Song Requests'),
- 'url' => $router->fromHere('stations:reports:requests'),
+ 'url' => (string)$router->fromHere('stations:reports:requests'),
'visible' => $station->getEnableRequests(),
],
'reports_timeline' => [
'label' => __('Song Playback Timeline'),
- 'url' => $router->fromHere('stations:reports:timeline'),
+ 'url' => (string)$router->fromHere('stations:reports:timeline'),
],
'reports_performance' => [
'label' => __('Song Listener Impact'),
- 'url' => $router->fromHere('stations:reports:performance'),
+ 'url' => (string)$router->fromHere('stations:reports:performance'),
'visible' => $backend->supportsMedia(),
],
'reports_duplicates' => [
'label' => __('Duplicate Songs'),
- 'url' => $router->fromHere('stations:files:index') . '#special:duplicates',
+ 'url' => (string)$router->fromHere('stations:files:index') . '#special:duplicates',
'visible' => $backend->supportsMedia(),
],
'reports_unprocessable' => [
'label' => __('Unprocessable Files'),
- 'url' => $router->fromHere('stations:files:index') . '#special:unprocessable',
+ 'url' => (string)$router->fromHere('stations:files:index') . '#special:unprocessable',
'visible' => $backend->supportsMedia(),
],
'reports_soundexchange' => [
'label' => __('SoundExchange Royalties'),
- 'url' => $router->fromHere('stations:reports:soundexchange'),
+ 'url' => (string)$router->fromHere('stations:reports:soundexchange'),
'visible' => $frontend->supportsListenerDetail(),
],
],
@@ -161,36 +161,36 @@ return function (App\Event\BuildStationMenu $e) {
'items' => [
'sftp_users' => [
'label' => __('SFTP Users'),
- 'url' => $router->fromHere('stations:sftp_users:index'),
+ 'url' => (string)$router->fromHere('stations:sftp_users:index'),
'visible' => App\Service\SftpGo::isSupportedForStation($station),
'permission' => Acl::STATION_MEDIA,
],
'automation' => [
'label' => __('Automated Assignment'),
- 'url' => $router->fromHere('stations:automation:index'),
+ 'url' => (string)$router->fromHere('stations:automation:index'),
'visible' => $backend->supportsMedia(),
'permission' => Acl::STATION_AUTOMATION,
],
'ls_config' => [
'label' => __('Edit Liquidsoap Configuration'),
- 'url' => $router->fromHere('stations:util:ls_config'),
+ 'url' => (string)$router->fromHere('stations:util:ls_config'),
'visible' => $settings->getEnableAdvancedFeatures()
&& $backend instanceof App\Radio\Backend\Liquidsoap,
'permission' => Acl::STATION_BROADCASTING,
],
'logs' => [
'label' => __('Log Viewer'),
- 'url' => $router->fromHere('stations:logs:index'),
+ 'url' => (string)$router->fromHere('stations:logs:index'),
'permission' => Acl::STATION_LOGS,
],
'queue' => [
'label' => __('Upcoming Song Queue'),
- 'url' => $router->fromHere('stations:queue:index'),
+ 'url' => (string)$router->fromHere('stations:queue:index'),
'permission' => Acl::STATION_BROADCASTING,
],
'restart' => [
'label' => __('Restart Broadcasting'),
- 'url' => $router->fromHere('api:stations:restart'),
+ 'url' => (string)$router->fromHere('api:stations:restart'),
'class' => 'api-call',
'confirm' => __('Restart broadcasting? This will disconnect any current listeners.'),
'permission' => Acl::STATION_BROADCASTING,
diff --git a/phpcs.xml b/phpcs.xml
index b86ec0128..f2b1c9a0e 100755
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -30,6 +30,10 @@
src/Installer/EnvFiles/*.php
+
+
+
+
diff --git a/phpstan.neon b/phpstan.neon
index 283b543d8..1a956d530 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -1,5 +1,8 @@
parameters:
- level: 3
+ level: 8
+
+ checkGenericClassInNonGenericObjectType: false
+ checkMissingIterableValueType: false
paths:
- src
@@ -15,6 +18,13 @@ parameters:
# Caused by Symfony Validator (perhaps wrongly) returning the interface.
- '#Cannot cast Symfony\\Component\\Validator\\ConstraintViolationListInterface to string.#'
+ # Some doctrine migrations fail because of these
+ - '#Parameter \#3 \$criteria of method Doctrine\\DBAL\\Connection::update\(\) expects array, array given.#'
+ - '#Parameter \#2 \$criteria of method Doctrine\\DBAL\\Connection::delete\(\) expects array, array given.#'
+
+ # Known upstream issue with Plates template engine
+ - '#Parameter \#2 \$callback of method League\\Plates\\Engine::registerFunction\(\) expects League\\Plates\\callback, Closure given.#'
+
parallel:
maximumNumberOfProcesses: 1
diff --git a/src/Acl.php b/src/Acl.php
index 54a9e66a4..9d1e02967 100644
--- a/src/Acl.php
+++ b/src/Acl.php
@@ -1,5 +1,7 @@
permissions)) {
+ /** @var array $permissions */
$permissions = [
'global' => [
self::GLOBAL_ALL => __('All Permissions'),
@@ -118,7 +121,7 @@ class Acl
* @param array|string $action
* @param int|Entity\Station|null $stationId
*/
- public function isAllowed(array|string $action, $stationId = null): bool
+ public function isAllowed(array|string $action, Entity\Station|int $stationId = null): bool
{
if ($this->request instanceof ServerRequestInterface) {
$user = $this->request->getAttribute(ServerRequest::ATTR_USER);
@@ -135,8 +138,11 @@ class Acl
* @param array|string $action
* @param int|Entity\Station|null $stationId
*/
- public function userAllowed(?Entity\User $user = null, array|string $action, $stationId = null): bool
- {
+ public function userAllowed(
+ ?Entity\User $user = null,
+ array|string $action,
+ Entity\Station|int $stationId = null
+ ): bool {
if (null === $user) {
return false;
}
@@ -172,7 +178,7 @@ class Acl
* @param array|string $action
* @param int|Entity\Station|null $station_id
*/
- public function roleAllowed(array|int $role_id, array|string $action, $station_id = null): bool
+ public function roleAllowed(array|int $role_id, array|string $action, Entity\Station|int $station_id = null): bool
{
if ($station_id instanceof Entity\Station) {
$station_id = $station_id->getId();
diff --git a/src/AppFactory.php b/src/AppFactory.php
index 1daf853a0..a50cb05b7 100644
--- a/src/AppFactory.php
+++ b/src/AppFactory.php
@@ -1,10 +1,13 @@
$appEnvironment
+ * @param array $diDefinitions
+ *
+ */
+ public static function createApp(
+ ?ClassLoader $autoloader = null,
+ array $appEnvironment = [],
+ array $diDefinitions = []
+ ): App {
$di = self::buildContainer($autoloader, $appEnvironment, $diDefinitions);
return self::buildAppFromContainer($di);
}
- public static function createCli($autoloader = null, $appEnvironment = [], $diDefinitions = []): Application
- {
+ /**
+ * @param ClassLoader|null $autoloader
+ * @param array $appEnvironment
+ * @param array $diDefinitions
+ *
+ */
+ public static function createCli(
+ ?ClassLoader $autoloader = null,
+ array $appEnvironment = [],
+ array $diDefinitions = []
+ ): Application {
$di = self::buildContainer($autoloader, $appEnvironment, $diDefinitions);
self::buildAppFromContainer($di);
@@ -83,11 +104,18 @@ class AppFactory
return $app;
}
- /** @noinspection SummerTimeUnsafeTimeManipulationInspection */
+ /**
+ * @param ClassLoader|null $autoloader
+ * @param array $appEnvironment
+ * @param array $diDefinitions
+ *
+ * @noinspection SummerTimeUnsafeTimeManipulationInspection
+ *
+ */
public static function buildContainer(
- $autoloader = null,
- $appEnvironment = [],
- $diDefinitions = []
+ ?ClassLoader $autoloader = null,
+ array $appEnvironment = [],
+ array $diDefinitions = []
): DI\Container {
// Register Annotation autoloader
if (null !== $autoloader) {
@@ -165,7 +193,10 @@ class AppFactory
return $di;
}
- public static function buildEnvironment(array $environment): Environment
+ /**
+ * @param array $environment
+ */
+ public static function buildEnvironment(array $environment = []): Environment
{
if (!isset($environment[Environment::BASE_DIR])) {
throw new Exception\BootstrapException('No base directory specified!');
@@ -180,7 +211,10 @@ class AppFactory
$environment[Environment::VIEWS_DIR] ??= $environment[Environment::BASE_DIR] . '/templates';
if (file_exists($environment[Environment::BASE_DIR] . '/env.ini')) {
- $_ENV = array_merge($_ENV, parse_ini_file($environment[Environment::BASE_DIR] . '/env.ini'));
+ $envIni = parse_ini_file($environment[Environment::BASE_DIR] . '/env.ini');
+ if (false !== $envIni) {
+ $_ENV = array_merge($_ENV, $envIni);
+ }
} else {
$_ENV = getenv();
}
diff --git a/src/Assets.php b/src/Assets.php
index 663dcb848..3be392ea3 100644
--- a/src/Assets.php
+++ b/src/Assets.php
@@ -1,11 +1,14 @@
Known libraries loaded in initialization. */
protected array $libraries = [];
- /** @var array An optional array lookup for versioned files. */
+ /** @var array An optional array lookup for versioned files. */
protected array $versioned_files = [];
- /** @var array Loaded libraries. */
+ /** @var array Loaded libraries. */
protected array $loaded = [];
/** @var bool Whether the current loaded libraries have been sorted by order. */
@@ -40,9 +43,6 @@ class Assets
/** @var array The loaded domains that should be included in the CSP header. */
protected array $csp_domains;
- /** @var ServerRequestInterface|null The current request (if it's available) */
- protected ?ServerRequestInterface $request = null;
-
public function __construct(
protected Environment $environment,
Config $config
@@ -51,18 +51,10 @@ class Assets
$this->addLibrary($library, $library_name);
}
- $versioned_files = [];
- $assets_file = $environment->getBaseDirectory() . '/web/static/assets.json';
- if (is_file($assets_file)) {
- $versioned_files = json_decode(file_get_contents($assets_file), true, 512, JSON_THROW_ON_ERROR);
- }
+ $versioned_files = Json::loadFromFile($environment->getBaseDirectory() . '/web/static/assets.json');
$this->versioned_files = $versioned_files;
- $vueComponents = [];
- $assets_file = $environment->getBaseDirectory() . '/web/static/webpack.json';
- if (is_file($assets_file)) {
- $vueComponents = json_decode(file_get_contents($assets_file), true, 512, JSON_THROW_ON_ERROR);
- }
+ $vueComponents = Json::loadFromFile($environment->getBaseDirectory() . '/web/static/webpack.json');
$this->addVueComponents($vueComponents);
$this->csp_nonce = (string)preg_replace('/[^A-Za-z0-9\+\/=]/', '', base64_encode(random_bytes(18)));
@@ -538,10 +530,9 @@ class Assets
*/
protected function addDomainToCsp(string $src): void
{
- $src_parts = parse_url($src);
-
- $domain = $src_parts['scheme'] . '://' . $src_parts['host'];
+ $uri = new Uri($src);
+ $domain = $uri->getScheme() . '://' . $uri->getHost();
if (!isset($this->csp_domains[$domain])) {
$this->csp_domains[$domain] = $domain;
}
@@ -550,7 +541,7 @@ class Assets
public function writeCsp(ResponseInterface $response): ResponseInterface
{
$csp = [];
- if ('https' === $this->request->getUri()->getScheme()) {
+ if (null !== $this->request && 'https' === $this->request->getUri()->getScheme()) {
$csp[] = 'upgrade-insecure-requests';
}
diff --git a/src/Auth.php b/src/Auth.php
index a14506fc3..b1334e3c5 100644
--- a/src/Auth.php
+++ b/src/Auth.php
@@ -1,5 +1,7 @@
isMasqueraded()) {
return $this->getMasquerade();
@@ -193,13 +195,16 @@ class Auth
*/
public function getMasquerade(): ?User
{
- return $this->masqueraded_user;
+ if ($this->masqueraded_user instanceof User) {
+ return $this->masqueraded_user;
+ }
+ return null;
}
/**
* Become a different user across the application.
*
- * @param array|User $user_info
+ * @param array|User $user_info
*/
public function masqueradeAsUser(User|array $user_info): void
{
diff --git a/src/Config.php b/src/Config.php
index 4d2fd6018..153042dc0 100644
--- a/src/Config.php
+++ b/src/Config.php
@@ -1,5 +1,7 @@
* @noinspection PhpIncludeInspection
* @noinspection UselessUnsetInspection
*/
diff --git a/src/Console/Application.php b/src/Console/Application.php
index f9ed57973..563b9417e 100644
--- a/src/Console/Application.php
+++ b/src/Console/Application.php
@@ -1,5 +1,7 @@
setInteractive(false);
$temp_stream = fopen($outputFile, 'wb+');
+ if (false === $temp_stream) {
+ throw new \RuntimeException(sprintf('Could not open output file: "%s"', $outputFile));
+ }
+
$output = new StreamOutput($temp_stream);
$command = $this->find($command);
@@ -31,7 +37,7 @@ class Application extends \Silly\Edition\PhpDi\Application
$result_output = stream_get_contents($temp_stream);
fclose($temp_stream);
- $result_output = trim($result_output);
+ $result_output = trim((string)$result_output);
return [
$result_code,
diff --git a/src/Console/Command/Backup/BackupCommand.php b/src/Console/Command/Backup/BackupCommand.php
index e420b1c54..5e92a9c00 100644
--- a/src/Console/Command/Backup/BackupCommand.php
+++ b/src/Console/Command/Backup/BackupCommand.php
@@ -1,5 +1,7 @@
application;
}
- protected function runCommand(OutputInterface $output, $command_name, $command_args = []): void
+ protected function runCommand(OutputInterface $output, string $command_name, array $command_args = []): void
{
$command = $this->getApplication()->find($command_name);
diff --git a/src/Console/Command/Debug/OptimizeTablesCommand.php b/src/Console/Command/Debug/OptimizeTablesCommand.php
index 7564f5d27..3963ad099 100644
--- a/src/Console/Command/Debug/OptimizeTablesCommand.php
+++ b/src/Console/Command/Debug/OptimizeTablesCommand.php
@@ -1,5 +1,7 @@
write($acCentral->getIp());
+ $io->write($acCentral->getIp() ?? 'Unknown');
return 0;
}
}
diff --git a/src/Console/Command/Internal/NextSongCommand.php b/src/Console/Command/Internal/NextSongCommand.php
index 34890fb4f..d6835d861 100644
--- a/src/Console/Command/Internal/NextSongCommand.php
+++ b/src/Console/Command/Internal/NextSongCommand.php
@@ -1,5 +1,7 @@
getRepository(SftpUser::class)->findOneBy(['username' => $username]);
diff --git a/src/Console/Command/Internal/SftpEventCommand.php b/src/Console/Command/Internal/SftpEventCommand.php
index 615c0721e..844ea08eb 100644
--- a/src/Console/Command/Internal/SftpEventCommand.php
+++ b/src/Console/Command/Internal/SftpEventCommand.php
@@ -1,5 +1,7 @@
logger->error('No path specified for action.');
+ return 1;
+ }
+
return match ($action) {
'upload' => $this->handleNewUpload($storageLocation, $path),
'pre-delete' => $this->handleDelete($storageLocation, $path),
@@ -99,7 +106,7 @@ class SftpEventCommand extends CommandAbstract
);
$message = new Message\AddNewMediaMessage();
- $message->storage_location_id = $storageLocation->getId();
+ $message->storage_location_id = $storageLocation->getIdRequired();
$message->path = $relativePath;
$this->messageBus->dispatch($message);
@@ -148,8 +155,13 @@ class SftpEventCommand extends CommandAbstract
protected function handleRename(
Entity\StorageLocation $storageLocation,
string $path,
- string $newPath
+ ?string $newPath
): int {
+ if (null === $newPath) {
+ $this->logger->error('No new path specified for rename.');
+ return 1;
+ }
+
$pathPrefixer = new PathPrefixer($storageLocation->getPath(), DIRECTORY_SEPARATOR);
$from = $pathPrefixer->stripPrefix($path);
diff --git a/src/Console/Command/Locale/GenerateCommand.php b/src/Console/Command/Locale/GenerateCommand.php
index 9252d2841..649b62e78 100644
--- a/src/Console/Command/Locale/GenerateCommand.php
+++ b/src/Console/Command/Locale/GenerateCommand.php
@@ -1,5 +1,7 @@
getRepository(Entity\Role::class)
->find(Entity\Role::SUPER_ADMINISTRATOR_ROLE_ID);
- $perms_repo->setActionsForRole($admin_role, [
- 'actions_global' => [
- Acl::GLOBAL_ALL,
- ],
- ]);
+ if (null === $admin_role) {
+ $io->error('Administrator role not found.');
+ return 1;
+ }
+
+ $perms_repo->setActionsForRole(
+ $admin_role,
+ [
+ 'actions_global' => [
+ Acl::GLOBAL_ALL,
+ ],
+ ]
+ );
$user_roles = $user->getRoles();
diff --git a/src/Console/ErrorHandler.php b/src/Console/ErrorHandler.php
index a32d31469..3e4afe4f0 100644
--- a/src/Console/ErrorHandler.php
+++ b/src/Console/ErrorHandler.php
@@ -1,5 +1,7 @@
processLog($request, $log);
return $response->withJson([
@@ -55,8 +57,12 @@ abstract class AbstractLogViewerController
if ($log_visible_size > 0) {
$fp = fopen($log_path, 'rb');
+ if (false === $fp) {
+ throw new \RuntimeException(sprintf('Could not open file at path "%s".', $log_path));
+ }
+
fseek($fp, -$log_visible_size, SEEK_END);
- $log_contents_raw = fread($fp, $log_visible_size);
+ $log_contents_raw = fread($fp, $log_visible_size) ?: '';
fclose($fp);
$log_contents = $this->processLog($request, $log_contents_raw, $cut_first_line, true);
@@ -91,7 +97,7 @@ abstract class AbstractLogViewerController
}
/**
- * @return mixed[]
+ * @return array
*/
protected function getStationLogs(Entity\Station $station): array
{
diff --git a/src/Controller/Admin/AbstractAdminCrudController.php b/src/Controller/Admin/AbstractAdminCrudController.php
index 2a47892f0..283ee792a 100644
--- a/src/Controller/Admin/AbstractAdminCrudController.php
+++ b/src/Controller/Admin/AbstractAdminCrudController.php
@@ -1,5 +1,7 @@
getRecord($id);
return $this->form->process($request, $record);
}
/**
- * @param string|int|null $id
+ * @param int|string|null $id
*/
- protected function getRecord($id = null): ?object
+ protected function getRecord(int|string $id = null): ?object
{
if (null === $id) {
return null;
diff --git a/src/Controller/Admin/ApiController.php b/src/Controller/Admin/ApiController.php
index 531e5a445..df685c9b0 100644
--- a/src/Controller/Admin/ApiController.php
+++ b/src/Controller/Admin/ApiController.php
@@ -1,5 +1,7 @@
doEdit($request, $id)) {
$request->getFlash()->addMessage(__('API Key updated.'), Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->named('admin:api:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('admin:api:index'));
}
- return $request->getView()->renderToResponse($response, 'system/form_page', [
- 'form' => $this->form,
- 'render_mode' => 'edit',
- 'title' => __('Edit API Key'),
- ]);
+ return $request->getView()->renderToResponse(
+ $response,
+ 'system/form_page',
+ [
+ 'form' => $this->form,
+ 'render_mode' => 'edit',
+ 'title' => __('Edit API Key'),
+ ]
+ );
}
- public function deleteAction(ServerRequest $request, Response $response, $id, $csrf): ResponseInterface
- {
+ public function deleteAction(
+ ServerRequest $request,
+ Response $response,
+ string $id,
+ string $csrf
+ ): ResponseInterface {
$this->doDelete($request, $id, $csrf);
$request->getFlash()->addMessage(__('API Key deleted.'), Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->named('admin:api:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('admin:api:index'));
}
}
diff --git a/src/Controller/Admin/AuditLogController.php b/src/Controller/Admin/AuditLogController.php
index a78ca1d67..02f3abe0a 100644
--- a/src/Controller/Admin/AuditLogController.php
+++ b/src/Controller/Admin/AuditLogController.php
@@ -1,5 +1,7 @@
process($request)) {
$request->getFlash()->addMessage(__('Changes saved.'), Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->fromHere('admin:backups:index'));
+ return $response->withRedirect((string)$request->getRouter()->fromHere('admin:backups:index'));
}
return $request->getView()->renderToResponse(
@@ -123,7 +125,7 @@ class BackupsController extends AbstractLogViewerController
);
// Handle submission.
- if ($request->isPost() && $runForm->isValid($request->getParsedBody())) {
+ if ($runForm->isValid($request)) {
$data = $runForm->getValues();
$tempFile = File::generateTempPath('backup.log');
@@ -176,7 +178,7 @@ class BackupsController extends AbstractLogViewerController
public function downloadAction(
ServerRequest $request,
Response $response,
- $path
+ string $path
): ResponseInterface {
[$path, $fs] = $this->getFile($path);
@@ -186,8 +188,12 @@ class BackupsController extends AbstractLogViewerController
->streamFilesystemFile($fs, $path);
}
- public function deleteAction(ServerRequest $request, Response $response, $path, $csrf): ResponseInterface
- {
+ public function deleteAction(
+ ServerRequest $request,
+ Response $response,
+ string $path,
+ string $csrf
+ ): ResponseInterface {
$request->getCsrf()->verify($csrf, $this->csrfNamespace);
[$path, $fs] = $this->getFile($path);
@@ -196,7 +202,7 @@ class BackupsController extends AbstractLogViewerController
$fs->delete($path);
$request->getFlash()->addMessage('' . __('Backup deleted.') . ' ', Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->named('admin:backups:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('admin:backups:index'));
}
/**
diff --git a/src/Controller/Admin/BrandingController.php b/src/Controller/Admin/BrandingController.php
index 03e252aa3..d643ba5ef 100644
--- a/src/Controller/Admin/BrandingController.php
+++ b/src/Controller/Admin/BrandingController.php
@@ -1,5 +1,7 @@
doEdit($request, $id)) {
$request->getFlash()->addMessage(
($id ? __('Custom Field updated.') : __('Custom Field added.')),
Flash::SUCCESS
);
- return $response->withRedirect($request->getRouter()->named('admin:custom_fields:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('admin:custom_fields:index'));
}
- return $request->getView()->renderToResponse($response, 'system/form_page', [
- 'form' => $this->form,
- 'render_mode' => 'edit',
- 'title' => $id ? __('Edit Custom Field') : __('Add Custom Field'),
- ]);
+ return $request->getView()->renderToResponse(
+ $response,
+ 'system/form_page',
+ [
+ 'form' => $this->form,
+ 'render_mode' => 'edit',
+ 'title' => $id ? __('Edit Custom Field') : __('Add Custom Field'),
+ ]
+ );
}
- public function deleteAction(ServerRequest $request, Response $response, $id, $csrf): ResponseInterface
- {
+ public function deleteAction(
+ ServerRequest $request,
+ Response $response,
+ int $id,
+ string $csrf
+ ): ResponseInterface {
$this->doDelete($request, $id, $csrf);
$request->getFlash()->addMessage('' . __('Custom Field deleted.') . ' ', Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->named('admin:custom_fields:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('admin:custom_fields:index'));
}
}
diff --git a/src/Controller/Admin/DebugController.php b/src/Controller/Admin/DebugController.php
index b0901dbda..1ede312e2 100644
--- a/src/Controller/Admin/DebugController.php
+++ b/src/Controller/Admin/DebugController.php
@@ -1,5 +1,7 @@
getFlash()->addMessage($resultOutput, Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->fromHere('admin:debug:index'));
+ return $response->withRedirect((string)$request->getRouter()->fromHere('admin:debug:index'));
}
public function clearQueueAction(
@@ -187,6 +189,6 @@ class DebugController extends AbstractLogViewerController
// Flash an update to ensure the session is recreated.
$request->getFlash()->addMessage($resultOutput, Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->fromHere('admin:debug:index'));
+ return $response->withRedirect((string)$request->getRouter()->fromHere('admin:debug:index'));
}
}
diff --git a/src/Controller/Admin/IndexController.php b/src/Controller/Admin/IndexController.php
index 81b1fe347..6505b36e3 100644
--- a/src/Controller/Admin/IndexController.php
+++ b/src/Controller/Admin/IndexController.php
@@ -1,5 +1,7 @@
minus($spaceFree);
// Get memory info.
- $meminfoRaw = file("/proc/meminfo", FILE_IGNORE_NEW_LINES);
+ $meminfoRaw = file("/proc/meminfo", FILE_IGNORE_NEW_LINES) ?: [];
$meminfo = [];
foreach ($meminfoRaw as $line) {
if (str_contains($line, ':')) {
diff --git a/src/Controller/Admin/InstallGeoLiteController.php b/src/Controller/Admin/InstallGeoLiteController.php
index bcf91c2b9..51cbdd748 100644
--- a/src/Controller/Admin/InstallGeoLiteController.php
+++ b/src/Controller/Admin/InstallGeoLiteController.php
@@ -1,5 +1,7 @@
getCsrf()->verify($csrf, $this->csrf_namespace);
@@ -74,6 +76,6 @@ class InstallGeoLiteController
@unlink(GeoLite::getDatabasePath());
$request->getFlash()->addMessage(__('GeoLite database uninstalled.'), Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->fromHere('admin:install_geolite:index'));
+ return $response->withRedirect((string)$request->getRouter()->fromHere('admin:install_geolite:index'));
}
}
diff --git a/src/Controller/Admin/InstallShoutcastController.php b/src/Controller/Admin/InstallShoutcastController.php
index 4b0b90170..6e516bcf9 100644
--- a/src/Controller/Admin/InstallShoutcastController.php
+++ b/src/Controller/Admin/InstallShoutcastController.php
@@ -1,5 +1,7 @@
isPost() && $form->isValid($request->getParsedBody())) {
+ if ($form->isValid($request)) {
try {
$sc_base_dir = $environment->getParentDirectory() . '/servers/shoutcast2';
- $files = $request->getUploadedFiles();
- /** @var UploadedFileInterface $import_file */
- $import_file = $files['binary'];
+ $values = $form->getValues();
- if (UPLOAD_ERR_OK === $import_file->getError()) {
+ $import_file = $values['binary'] ?? null;
+ if ($import_file instanceof UploadedFileInterface) {
$sc_tgz_path = $sc_base_dir . '/sc_serv.tar.gz';
if (is_file($sc_tgz_path)) {
unlink($sc_tgz_path);
diff --git a/src/Controller/Admin/LogsController.php b/src/Controller/Admin/LogsController.php
index 89a4dea9c..59bfb17be 100644
--- a/src/Controller/Admin/LogsController.php
+++ b/src/Controller/Admin/LogsController.php
@@ -1,5 +1,7 @@
*/
protected function getGlobalLogs(): array
{
@@ -82,8 +84,12 @@ class LogsController extends AbstractLogViewerController
return $logPaths;
}
- public function viewAction(ServerRequest $request, Response $response, $station_id, $log): ResponseInterface
- {
+ public function viewAction(
+ ServerRequest $request,
+ Response $response,
+ string|int $station_id,
+ string $log
+ ): ResponseInterface {
if ('global' === $station_id) {
$log_areas = $this->getGlobalLogs();
} else {
@@ -94,7 +100,7 @@ class LogsController extends AbstractLogViewerController
throw new Exception('Invalid log file specified.');
}
- $log = $log_areas[$log];
- return $this->view($request, $response, $log['path'], $log['tail'] ?? true);
+ $logArea = $log_areas[$log];
+ return $this->view($request, $response, $logArea['path'], $logArea['tail'] ?? true);
}
}
diff --git a/src/Controller/Admin/PermissionsController.php b/src/Controller/Admin/PermissionsController.php
index b5e62b8ef..d6e9f1ff1 100644
--- a/src/Controller/Admin/PermissionsController.php
+++ b/src/Controller/Admin/PermissionsController.php
@@ -1,5 +1,7 @@
doEdit($request, $id)) {
$request->getFlash()->addMessage(
'' . ($id ? __('Permission updated.') : __('Permission added.')) . ' ',
Flash::SUCCESS
);
- return $response->withRedirect($request->getRouter()->named('admin:permissions:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('admin:permissions:index'));
}
- return $request->getView()->renderToResponse($response, 'system/form_page', [
- 'form' => $this->form,
- 'render_mode' => 'edit',
- 'title' => $id ? __('Edit Permission') : __('Add Permission'),
- ]);
+ return $request->getView()->renderToResponse(
+ $response,
+ 'system/form_page',
+ [
+ 'form' => $this->form,
+ 'render_mode' => 'edit',
+ 'title' => $id ? __('Edit Permission') : __('Add Permission'),
+ ]
+ );
}
- public function deleteAction(ServerRequest $request, Response $response, $id, $csrf): ResponseInterface
- {
+ public function deleteAction(
+ ServerRequest $request,
+ Response $response,
+ int $id,
+ string $csrf
+ ): ResponseInterface {
$this->doDelete($request, $id, $csrf);
$request->getFlash()->addMessage('' . __('Permission deleted.') . ' ', Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->named('admin:permissions:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('admin:permissions:index'));
}
}
diff --git a/src/Controller/Admin/RelaysController.php b/src/Controller/Admin/RelaysController.php
index 409eb4e72..24ea43879 100644
--- a/src/Controller/Admin/RelaysController.php
+++ b/src/Controller/Admin/RelaysController.php
@@ -1,5 +1,7 @@
doEdit($request, $id)) {
$request->getFlash()->addMessage(($id ? __('Station updated.') : __('Station added.')), Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->named('admin:stations:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('admin:stations:index'));
}
- return $request->getView()->renderToResponse($response, 'admin/stations/edit', [
- 'form' => $this->form,
- 'title' => $id ? __('Edit Station') : 'Add Station',
- ]);
+ return $request->getView()->renderToResponse(
+ $response,
+ 'admin/stations/edit',
+ [
+ 'form' => $this->form,
+ 'title' => $id ? __('Edit Station') : 'Add Station',
+ ]
+ );
}
- public function deleteAction(ServerRequest $request, Response $response, $id, $csrf): ResponseInterface
- {
+ public function deleteAction(
+ ServerRequest $request,
+ Response $response,
+ int $id,
+ string $csrf
+ ): ResponseInterface {
$request->getCsrf()->verify($csrf, $this->csrf_namespace);
- $record = $this->record_repo->find((int)$id);
+ $record = $this->record_repo->find($id);
if ($record instanceof Entity\Station) {
$this->stationRepo->destroy($record);
}
$request->getFlash()->addMessage(__('Station deleted.'), Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->named('admin:stations:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('admin:stations:index'));
}
- public function cloneAction(ServerRequest $request, Response $response, $id): ResponseInterface
+ public function cloneAction(ServerRequest $request, Response $response, int $id): ResponseInterface
{
$cloneForm = $this->factory->make(Form\StationCloneForm::class);
- $record = $this->record_repo->find((int)$id);
+ $record = $this->record_repo->find($id);
if (!($record instanceof Entity\Station)) {
throw new NotFoundException(__('Station not found.'));
}
if (false !== $cloneForm->process($request, $record)) {
$request->getFlash()->addMessage(__('Changes saved.'), Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->named('admin:stations:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('admin:stations:index'));
}
return $request->getView()->renderToResponse(
$response,
'system/form_page',
[
- 'form' => $cloneForm,
- 'render_mode' => 'edit',
- 'title' => __('Clone Station: %s', $record->getName()),
+ 'form' => $cloneForm,
+ 'render_mode' => 'edit',
+ 'title' => __('Clone Station: %s', $record->getName()),
]
);
}
diff --git a/src/Controller/Admin/StorageLocationsController.php b/src/Controller/Admin/StorageLocationsController.php
index 55db38969..874d46ec5 100644
--- a/src/Controller/Admin/StorageLocationsController.php
+++ b/src/Controller/Admin/StorageLocationsController.php
@@ -1,5 +1,7 @@
doEdit($request, $id)) {
$request->getFlash()->addMessage(($id ? __('User updated.') : __('User added.')), Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->named('admin:users:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('admin:users:index'));
}
} catch (UniqueConstraintViolationException) {
$request->getFlash()->addMessage(
@@ -54,18 +56,26 @@ class UsersController extends AbstractAdminCrudController
);
}
- return $request->getView()->renderToResponse($response, 'system/form_page', [
- 'form' => $this->form,
- 'render_mode' => 'edit',
- 'title' => $id ? __('Edit User') : __('Add User'),
- ]);
+ return $request->getView()->renderToResponse(
+ $response,
+ 'system/form_page',
+ [
+ 'form' => $this->form,
+ 'render_mode' => 'edit',
+ 'title' => $id ? __('Edit User') : __('Add User'),
+ ]
+ );
}
- public function deleteAction(ServerRequest $request, Response $response, $id, $csrf): ResponseInterface
- {
+ public function deleteAction(
+ ServerRequest $request,
+ Response $response,
+ int $id,
+ string $csrf
+ ): ResponseInterface {
$request->getCsrf()->verify($csrf, $this->csrf_namespace);
- $user = $this->record_repo->find((int)$id);
+ $user = $this->record_repo->find($id);
$current_user = $request->getUser();
@@ -78,18 +88,18 @@ class UsersController extends AbstractAdminCrudController
$request->getFlash()->addMessage('' . __('User deleted.') . ' ', Flash::SUCCESS);
}
- return $response->withRedirect($request->getRouter()->named('admin:users:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('admin:users:index'));
}
public function impersonateAction(
ServerRequest $request,
Response $response,
- $id,
- $csrf
+ int $id,
+ string $csrf
): ResponseInterface {
$request->getCsrf()->verify($csrf, $this->csrf_namespace);
- $user = $this->record_repo->find((int)$id);
+ $user = $this->record_repo->find($id);
if (!($user instanceof Entity\User)) {
throw new NotFoundException(__('User not found.'));
@@ -103,6 +113,6 @@ class UsersController extends AbstractAdminCrudController
Flash::SUCCESS
);
- return $response->withRedirect($request->getRouter()->named('dashboard'));
+ return $response->withRedirect((string)$request->getRouter()->named('dashboard'));
}
}
diff --git a/src/Controller/Api/AbstractApiCrudController.php b/src/Controller/Api/AbstractApiCrudController.php
index 962d04f04..b4e7b69d7 100644
--- a/src/Controller/Api/AbstractApiCrudController.php
+++ b/src/Controller/Api/AbstractApiCrudController.php
@@ -1,15 +1,16 @@
The fully-qualified (::class) class name of the entity being managed. */
protected string $entityClass;
/** @var string The route name used to generate the "self" links for each record. */
@@ -59,7 +63,7 @@ abstract class AbstractApiCrudController
}
/**
- * @param object $record
+ * @param TEntity $record
* @param ServerRequest $request
*
*/
@@ -74,21 +78,28 @@ abstract class AbstractApiCrudController
$isInternal = ('true' === $request->getParam('internal', 'false'));
$router = $request->getRouter();
- $return['links'] = [
- 'self' => $router->fromHere($this->resourceRouteName, ['id' => $record->getId()], [], !$isInternal),
- ];
+ if ($record instanceof IdentifiableEntityInterface) {
+ $return['links'] = [
+ 'self' => (string)$router->fromHere(
+ $this->resourceRouteName,
+ ['id' => $record->getIdRequired()],
+ [],
+ !$isInternal
+ ),
+ ];
+ }
return $return;
}
/**
- * @param object $record
- * @param array $context
+ * @param TEntity $record
+ * @param array $context
*
- * @return mixed[]
+ * @return array
*/
protected function toArray(object $record, array $context = []): array
{
- return $this->serializer->normalize(
+ return (array)$this->serializer->normalize(
$record,
null,
array_merge(
@@ -126,15 +137,25 @@ abstract class AbstractApiCrudController
return $object->getName();
}
- return $object->getId();
+ if ($object instanceof IdentifiableEntityInterface) {
+ return $object->getIdRequired();
+ }
+
+ if ($object instanceof \Stringable) {
+ return (string)$object;
+ }
+
+ return get_class($object) . ': ' . spl_object_hash($object);
}
/**
- * @param array|null $data
- * @param object|null $record
- * @param array $context
+ * @param array|null $data
+ * @param TEntity|null $record
+ * @param array $context
+ *
+ * @return TEntity
*/
- protected function editRecord(?array $data, $record = null, array $context = []): object
+ protected function editRecord(?array $data, ?object $record = null, array $context = []): object
{
if (null === $data) {
throw new InvalidArgumentException('Could not parse input data.');
@@ -156,11 +177,13 @@ abstract class AbstractApiCrudController
}
/**
- * @param array $data
- * @param object|null $record
- * @param array $context
+ * @param array $data
+ * @param TEntity|null $record
+ * @param array $context
+ *
+ * @return TEntity
*/
- protected function fromArray(array $data, $record = null, array $context = []): object
+ protected function fromArray(array $data, ?object $record = null, array $context = []): object
{
if (null !== $record) {
$context[ObjectNormalizer::OBJECT_TO_POPULATE] = $record;
@@ -170,10 +193,7 @@ abstract class AbstractApiCrudController
}
/**
- * @param object $record
- *
- * @throws ORMException
- * @throws OptimisticLockException
+ * @param TEntity $record
*/
protected function deleteRecord(object $record): void
{
diff --git a/src/Controller/Api/Admin/AbstractAdminApiCrudController.php b/src/Controller/Api/Admin/AbstractAdminApiCrudController.php
index 4db76ec32..680e7ea4f 100644
--- a/src/Controller/Api/Admin/AbstractAdminApiCrudController.php
+++ b/src/Controller/Api/Admin/AbstractAdminApiCrudController.php
@@ -1,5 +1,7 @@
+ */
abstract class AbstractAdminApiCrudController extends AbstractApiCrudController
{
public function listAction(ServerRequest $request, Response $response): ResponseInterface
@@ -29,7 +32,7 @@ abstract class AbstractAdminApiCrudController extends AbstractApiCrudController
*/
public function createAction(ServerRequest $request, Response $response): ResponseInterface
{
- $row = $this->createRecord($request->getParsedBody());
+ $row = $this->createRecord((array)$request->getParsedBody());
$return = $this->viewRecord($row, $request);
return $response->withJson($return);
@@ -37,6 +40,8 @@ abstract class AbstractAdminApiCrudController extends AbstractApiCrudController
/**
* @param array $data
+ *
+ * @return TEntity
*/
protected function createRecord(array $data): object
{
@@ -54,7 +59,7 @@ abstract class AbstractAdminApiCrudController extends AbstractApiCrudController
if (null === $record) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
$return = $this->viewRecord($record, $request);
@@ -64,9 +69,7 @@ abstract class AbstractAdminApiCrudController extends AbstractApiCrudController
/**
* @param mixed $id
*
- * @throws ORMException
- * @throws OptimisticLockException
- * @throws TransactionRequiredException
+ * @return TEntity|null
*/
protected function getRecord(mixed $id): ?object
{
@@ -84,10 +87,10 @@ abstract class AbstractAdminApiCrudController extends AbstractApiCrudController
if (null === $record) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
- $this->editRecord($request->getParsedBody(), $record);
+ $this->editRecord((array)$request->getParsedBody(), $record);
return $response->withJson(new Entity\Api\Status(true, __('Changes saved successfully.')));
}
@@ -103,7 +106,7 @@ abstract class AbstractAdminApiCrudController extends AbstractApiCrudController
if (null === $record) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
$this->deleteRecord($record);
diff --git a/src/Controller/Api/Admin/AuditLogController.php b/src/Controller/Api/Admin/AuditLogController.php
index 83bcc03b7..823cd549b 100644
--- a/src/Controller/Api/Admin/AuditLogController.php
+++ b/src/Controller/Api/Admin/AuditLogController.php
@@ -1,5 +1,7 @@
+ */
class CustomFieldsController extends AbstractAdminApiCrudController
{
protected string $entityClass = Entity\CustomField::class;
diff --git a/src/Controller/Api/Admin/PermissionsController.php b/src/Controller/Api/Admin/PermissionsController.php
index 2a676ba3b..918e59721 100644
--- a/src/Controller/Api/Admin/PermissionsController.php
+++ b/src/Controller/Api/Admin/PermissionsController.php
@@ -1,5 +1,7 @@
adapters->getFrontendAdapter($station);
$row = new Entity\Api\Admin\Relay();
- $row->id = $station->getId();
+ $row->id = $station->getIdRequired();
$row->name = $station->getName();
$row->shortcode = $station->getShortName();
$row->description = $station->getDescription();
@@ -108,14 +110,13 @@ class RelaysController
{
$relay_repo = $this->em->getRepository(Entity\Relay::class);
- $body = $request->getParsedBody();
+ $body = (array)$request->getParsedBody();
if (!empty($body['base_url'])) {
$base_url = $body['base_url'];
} else {
- $serverParams = $request->getServerParams();
/** @noinspection HttpUrlsUsage */
- $base_url = 'http://' . $serverParams('REMOTE_ADDR');
+ $base_url = 'http://' . $request->getIp();
}
$relay = $relay_repo->findOneBy(['base_url' => $base_url]);
diff --git a/src/Controller/Api/Admin/RolesController.php b/src/Controller/Api/Admin/RolesController.php
index 51e6b4203..0c422cdfa 100644
--- a/src/Controller/Api/Admin/RolesController.php
+++ b/src/Controller/Api/Admin/RolesController.php
@@ -1,5 +1,7 @@
+ */
class RolesController extends AbstractAdminApiCrudController
{
protected string $entityClass = Entity\Role::class;
diff --git a/src/Controller/Api/Admin/SettingsController.php b/src/Controller/Api/Admin/SettingsController.php
index 775f84787..449a0ff79 100644
--- a/src/Controller/Api/Admin/SettingsController.php
+++ b/src/Controller/Api/Admin/SettingsController.php
@@ -1,5 +1,7 @@
+ */
class SettingsController extends AbstractApiCrudController
{
public function __construct(
@@ -66,7 +71,7 @@ class SettingsController extends AbstractApiCrudController
public function updateAction(ServerRequest $request, Response $response): ResponseInterface
{
$settings = $this->settingsRepo->readSettings();
- $this->editRecord($request->getParsedBody(), $settings);
+ $this->editRecord((array)$request->getParsedBody(), $settings);
return $response->withJson(new Entity\Api\Status());
}
diff --git a/src/Controller/Api/Admin/StationsController.php b/src/Controller/Api/Admin/StationsController.php
index 410c0163d..8fc91fa1e 100644
--- a/src/Controller/Api/Admin/StationsController.php
+++ b/src/Controller/Api/Admin/StationsController.php
@@ -1,5 +1,7 @@
+ */
class StationsController extends AbstractAdminApiCrudController
{
protected string $entityClass = Entity\Station::class;
@@ -104,7 +109,12 @@ class StationsController extends AbstractAdminApiCrudController
* )
*/
- /** @inheritDoc */
+ /**
+ * @param Entity\Station $record
+ * @param array $context
+ *
+ * @return array
+ */
protected function toArray(object $record, array $context = []): array
{
return parent::toArray(
@@ -122,8 +132,14 @@ class StationsController extends AbstractAdminApiCrudController
);
}
- /** @inheritDoc */
- protected function editRecord(?array $data, $record = null, array $context = []): object
+ /**
+ * @param array|null $data
+ * @param Entity\Station|null $record
+ * @param array $context
+ *
+ * @return Entity\Station
+ */
+ protected function editRecord(?array $data, object $record = null, array $context = []): object
{
$create_mode = (null === $record);
@@ -150,7 +166,9 @@ class StationsController extends AbstractAdminApiCrudController
return $this->station_repo->edit($record);
}
- /** @inheritDoc */
+ /**
+ * @param Entity\Station $record
+ */
protected function deleteRecord(object $record): void
{
$this->station_repo->destroy($record);
diff --git a/src/Controller/Api/Admin/StorageLocationsController.php b/src/Controller/Api/Admin/StorageLocationsController.php
index 6aa08fed5..acd26bac1 100644
--- a/src/Controller/Api/Admin/StorageLocationsController.php
+++ b/src/Controller/Api/Admin/StorageLocationsController.php
@@ -1,5 +1,7 @@
+ */
class StorageLocationsController extends AbstractAdminApiCrudController
{
protected string $entityClass = Entity\StorageLocation::class;
@@ -125,9 +130,8 @@ class StorageLocationsController extends AbstractAdminApiCrudController
}
/** @inheritDoc */
- protected function viewRecord(object $record, ServerRequest $request): Entity\Api\Admin\StorageLocation
+ protected function viewRecord(object $record, ServerRequest $request): object
{
- /** @var Entity\StorageLocation $record */
$original = parent::viewRecord($record, $request);
$return = new Entity\Api\Admin\StorageLocation();
diff --git a/src/Controller/Api/Admin/UsersController.php b/src/Controller/Api/Admin/UsersController.php
index 3a2ac7225..41edfbf8b 100644
--- a/src/Controller/Api/Admin/UsersController.php
+++ b/src/Controller/Api/Admin/UsersController.php
@@ -1,5 +1,7 @@
+ */
class UsersController extends AbstractAdminApiCrudController
{
protected string $entityClass = Entity\User::class;
@@ -97,12 +102,11 @@ class UsersController extends AbstractAdminApiCrudController
*/
public function deleteAction(ServerRequest $request, Response $response, mixed $id): ResponseInterface
{
- /** @var Entity\User|null $record */
$record = $this->getRecord($id);
if (null === $record) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
$current_user = $request->getUser();
diff --git a/src/Controller/Api/Frontend/Account/GetMeAction.php b/src/Controller/Api/Frontend/Account/GetMeAction.php
index 228acd473..255ba51cb 100644
--- a/src/Controller/Api/Frontend/Account/GetMeAction.php
+++ b/src/Controller/Api/Frontend/Account/GetMeAction.php
@@ -1,5 +1,7 @@
getUser();
- $this->editRecord($request->getParsedBody(), $user);
+ $this->editRecord((array)$request->getParsedBody(), $user);
return $response->withJson(new Entity\Api\Status(true, __('Changes saved successfully.')));
}
diff --git a/src/Controller/Api/Frontend/Dashboard/ChartsAction.php b/src/Controller/Api/Frontend/Dashboard/ChartsAction.php
index 55f7aa293..76e9c15d3 100644
--- a/src/Controller/Api/Frontend/Dashboard/ChartsAction.php
+++ b/src/Controller/Api/Frontend/Dashboard/ChartsAction.php
@@ -1,5 +1,7 @@
format('Y-m-d');
$jsTimestamp = $moment->getTimestamp() * 1000;
- $average = round($row['number_avg'], 2);
+ $average = round((float)$row['number_avg'], 2);
$unique = $row['number_unique'];
$rawStats['average'][$stationId][$sortableKey] = [
diff --git a/src/Controller/Api/Frontend/Dashboard/NotificationsAction.php b/src/Controller/Api/Frontend/Dashboard/NotificationsAction.php
index df3b5f7be..cee991923 100644
--- a/src/Controller/Api/Frontend/Dashboard/NotificationsAction.php
+++ b/src/Controller/Api/Frontend/Dashboard/NotificationsAction.php
@@ -1,5 +1,7 @@
getRouter();
// Pull NP data from the fastest/first available source using the EventDispatcher.
@@ -101,7 +106,7 @@ class NowplayingController implements EventSubscriberInterface
}
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, 'Station not found.'));
+ ->withJson(Entity\Api\Error::notFound());
}
// If unauthenticated, hide non-public stations from full view.
diff --git a/src/Controller/Api/OpenApiController.php b/src/Controller/Api/OpenApiController.php
index 2ccc58d92..066b65baa 100644
--- a/src/Controller/Api/OpenApiController.php
+++ b/src/Controller/Api/OpenApiController.php
@@ -1,5 +1,7 @@
getRouter()->fromHere(null, [], [], true);
+ $api_base_url = (string)$request->getRouter()->fromHere(absolute: true);
$api_base_url = str_replace('/openapi.yml', '', $api_base_url);
define('AZURACAST_API_URL', $api_base_url);
diff --git a/src/Controller/Api/Stations/AbstractScheduledEntityController.php b/src/Controller/Api/Stations/AbstractScheduledEntityController.php
index 75b0048a6..5d4ac25cb 100644
--- a/src/Controller/Api/Stations/AbstractScheduledEntityController.php
+++ b/src/Controller/Api/Stations/AbstractScheduledEntityController.php
@@ -1,5 +1,7 @@
+ */
abstract class AbstractScheduledEntityController extends AbstractStationApiCrudController
{
public function __construct(
@@ -37,11 +43,21 @@ abstract class AbstractScheduledEntityController extends AbstractStationApiCrudC
$params = $request->getQueryParams();
$startDateStr = substr($params['start'], 0, 10);
- $startDate = CarbonImmutable::createFromFormat('Y-m-d', $startDateStr, $tz)->subDay();
+ $startDate = CarbonImmutable::createFromFormat('Y-m-d', $startDateStr, $tz);
+
+ if (false === $startDate) {
+ throw new \InvalidArgumentException(sprintf('Could not parse start date: "%s"', $startDateStr));
+ }
+
+ $startDate = $startDate->subDay();
$endDateStr = substr($params['end'], 0, 10);
$endDate = CarbonImmutable::createFromFormat('Y-m-d', $endDateStr, $tz);
+ if (false === $endDate) {
+ throw new \InvalidArgumentException(sprintf('Could not parse end date: "%s"', $endDateStr));
+ }
+
$events = [];
foreach ($scheduleItems as $scheduleItem) {
@@ -73,13 +89,13 @@ abstract class AbstractScheduledEntityController extends AbstractStationApiCrudC
return $response->withJson($events);
}
- protected function editRecord(?array $data, $record = null, array $context = []): object
+ protected function editRecord(?array $data, object $record = null, array $context = []): object
{
if (null === $data) {
throw new InvalidArgumentException('Could not parse input data.');
}
- $scheduleItems = $data['schedule_items'] ?? null;
+ $scheduleItems = $data['schedule_items'] ?? [];
unset($data['schedule_items']);
$record = $this->fromArray($data, $record, $context);
diff --git a/src/Controller/Api/Stations/AbstractStationApiCrudController.php b/src/Controller/Api/Stations/AbstractStationApiCrudController.php
index a3f1579b5..79e15d5f3 100644
--- a/src/Controller/Api/Stations/AbstractStationApiCrudController.php
+++ b/src/Controller/Api/Stations/AbstractStationApiCrudController.php
@@ -1,5 +1,7 @@
+ */
abstract class AbstractStationApiCrudController extends AbstractApiCrudController
{
/**
@@ -46,7 +52,7 @@ abstract class AbstractStationApiCrudController extends AbstractApiCrudControlle
public function createAction(ServerRequest $request, Response $response): ResponseInterface
{
$station = $this->getStation($request);
- $row = $this->createRecord($request->getParsedBody(), $station);
+ $row = $this->createRecord((array)$request->getParsedBody(), $station);
$return = $this->viewRecord($row, $request);
@@ -56,6 +62,8 @@ abstract class AbstractStationApiCrudController extends AbstractApiCrudControlle
/**
* @param array $data
* @param Entity\Station $station
+ *
+ * @return TEntity
*/
protected function createRecord(array $data, Entity\Station $station): object
{
@@ -75,23 +83,23 @@ abstract class AbstractStationApiCrudController extends AbstractApiCrudControlle
/**
* @param ServerRequest $request
* @param Response $response
- * @param int|string $station_id
- * @param int|string $id
+ * @param int $station_id
+ * @param int $id
*
* @throws Exception
*/
public function getAction(
ServerRequest $request,
Response $response,
- int|string $station_id,
- int|string $id
+ int $station_id,
+ int $id
): ResponseInterface {
$station = $this->getStation($request);
$record = $this->getRecord($station, $id);
if (null === $record) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
$return = $this->viewRecord($record, $request);
@@ -101,6 +109,8 @@ abstract class AbstractStationApiCrudController extends AbstractApiCrudControlle
/**
* @param Entity\Station $station
* @param int|string $id
+ *
+ * @return TEntity
*/
protected function getRecord(Entity\Station $station, int|string $id): ?object
{
@@ -115,23 +125,23 @@ abstract class AbstractStationApiCrudController extends AbstractApiCrudControlle
/**
* @param ServerRequest $request
* @param Response $response
- * @param int|string $station_id
- * @param int|string $id
+ * @param int $station_id
+ * @param int $id
*/
public function editAction(
ServerRequest $request,
Response $response,
- int|string $station_id,
- int|string $id
+ int $station_id,
+ int $id
): ResponseInterface {
$record = $this->getRecord($this->getStation($request), $id);
if (null === $record) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
- $this->editRecord($request->getParsedBody(), $record);
+ $this->editRecord((array)$request->getParsedBody(), $record);
return $response->withJson(new Entity\Api\Status(true, __('Changes saved successfully.')));
}
@@ -139,20 +149,20 @@ abstract class AbstractStationApiCrudController extends AbstractApiCrudControlle
/**
* @param ServerRequest $request
* @param Response $response
- * @param int|string $station_id
- * @param int|string $id
+ * @param int $station_id
+ * @param int $id
*/
public function deleteAction(
ServerRequest $request,
Response $response,
- int|string $station_id,
- int|string $id
+ int $station_id,
+ int $id
): ResponseInterface {
$record = $this->getRecord($this->getStation($request), $id);
if (null === $record) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
$this->deleteRecord($record);
diff --git a/src/Controller/Api/Stations/Art/DeleteArtAction.php b/src/Controller/Api/Stations/Art/DeleteArtAction.php
index e5b4f0024..2df1533c3 100644
--- a/src/Controller/Api/Stations/Art/DeleteArtAction.php
+++ b/src/Controller/Api/Stations/Art/DeleteArtAction.php
@@ -1,5 +1,7 @@
find($media_id, $station);
if (!($media instanceof Entity\StationMedia)) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found.')));
+ ->withJson(Entity\Api\Error::notFound());
}
$mediaRepo->removeAlbumArt($media);
diff --git a/src/Controller/Api/Stations/Art/GetArtAction.php b/src/Controller/Api/Stations/Art/GetArtAction.php
index b765c975e..7d18fd45f 100644
--- a/src/Controller/Api/Stations/Art/GetArtAction.php
+++ b/src/Controller/Api/Stations/Art/GetArtAction.php
@@ -1,5 +1,7 @@
getMediaFilesystem();
- $defaultArtRedirect = $response->withRedirect($stationRepo->getDefaultAlbumArtUrl($station), 302);
+ $defaultArtRedirect = $response->withRedirect((string)$stationRepo->getDefaultAlbumArtUrl($station), 302);
// If a timestamp delimiter is added, strip it automatically.
$media_id = explode('-', $media_id, 2)[0];
diff --git a/src/Controller/Api/Stations/Art/PostArtAction.php b/src/Controller/Api/Stations/Art/PostArtAction.php
index 0235adf8b..9a8edc542 100644
--- a/src/Controller/Api/Stations/Art/PostArtAction.php
+++ b/src/Controller/Api/Stations/Art/PostArtAction.php
@@ -1,5 +1,7 @@
find($media_id, $station);
if (!($media instanceof Entity\StationMedia)) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found.')));
+ ->withJson(Entity\Api\Error::notFound());
}
$flowResponse = Flow::process($request, $response, $station->getRadioTempDir());
diff --git a/src/Controller/Api/Stations/Files/BatchAction.php b/src/Controller/Api/Stations/Files/BatchAction.php
index 2e7cb7f3e..7c68996e1 100644
--- a/src/Controller/Api/Stations/Files/BatchAction.php
+++ b/src/Controller/Api/Stations/Files/BatchAction.php
@@ -1,5 +1,7 @@
em->flush();
foreach ($playlists as $playlistRecord) {
+ /** @var Entity\StationPlaylist $playlist */
$playlist = $this->em->refetchAsReference($playlistRecord);
$playlistWeights[$playlist->getId()]++;
@@ -170,6 +173,7 @@ class BatchAction
}
}
+ /** @var Entity\Station $station */
$station = $this->em->refetch($station);
foreach ($result->directories as $dir) {
@@ -226,7 +230,7 @@ class BatchAction
$toMove = [
$this->batchUtilities->iterateMediaInDirectory($storageLocation, $dirPath),
$this->batchUtilities->iterateUnprocessableMediaInDirectory($storageLocation, $dirPath),
- $this->batchUtilities->iteratePlaylistFoldersInDirectory($station, $dirPath),
+ $this->batchUtilities->iteratePlaylistFoldersInDirectory($storageLocation, $dirPath),
];
foreach ($toMove as $iterator) {
diff --git a/src/Controller/Api/Stations/Files/DownloadAction.php b/src/Controller/Api/Stations/Files/DownloadAction.php
index 36b99570d..9811ced15 100644
--- a/src/Controller/Api/Stations/Files/DownloadAction.php
+++ b/src/Controller/Api/Stations/Files/DownloadAction.php
@@ -1,8 +1,10 @@
fileExists($path)) {
return $response->withStatus(404)
- ->withJson(new Error(404, 'File not found.'));
+ ->withJson(Entity\Api\Error::notFound());
}
return $response->streamFilesystemFile($fsMedia, $path);
diff --git a/src/Controller/Api/Stations/Files/FlowUploadAction.php b/src/Controller/Api/Stations/Files/FlowUploadAction.php
index 389ca37c7..cfa4e173d 100644
--- a/src/Controller/Api/Stations/Files/FlowUploadAction.php
+++ b/src/Controller/Api/Stations/Files/FlowUploadAction.php
@@ -1,5 +1,7 @@
withStatus(400)
- ->withJson(new Entity\Api\Error('Playlist not found.'));
+ ->withJson(new Entity\Api\Error(400, 'Playlist not found.'));
}
$mediaQueryBuilder->andWhere(
@@ -177,7 +179,7 @@ class ListAction
$media->genre = (string)$row['genre'];
$media->is_playable = ($row['length'] !== 0);
- $media->length = $row['length'];
+ $media->length = (int)$row['length'];
$media->length_text = $row['length_text'];
$media->media_id = $row['id'];
@@ -315,7 +317,7 @@ class ListAction
$paginator = Paginator::fromArray($result, $request);
// Add processor-intensive data for just this page.
- $stationId = $station->getId();
+ $stationId = $station->getIdRequired();
$isInternal = (bool)$request->getParam('internal', false);
$defaultAlbumArtUrl = (string)$stationRepo->getDefaultAlbumArtUrl($station);
diff --git a/src/Controller/Api/Stations/Files/ListDirectoriesAction.php b/src/Controller/Api/Stations/Files/ListDirectoriesAction.php
index d621fa8de..a73006744 100644
--- a/src/Controller/Api/Stations/Files/ListDirectoriesAction.php
+++ b/src/Controller/Api/Stations/Files/ListDirectoriesAction.php
@@ -1,5 +1,7 @@
withStatus(404)
- ->withJson(new Entity\Api\Error(404, 'Not Found'));
+ ->withJson(Entity\Api\Error::notFound());
}
$fsMedia = (new StationFilesystems($station))->getMediaFilesystem();
diff --git a/src/Controller/Api/Stations/Files/RenameAction.php b/src/Controller/Api/Stations/Files/RenameAction.php
index 69ab2b5a2..6b581bad3 100644
--- a/src/Controller/Api/Stations/Files/RenameAction.php
+++ b/src/Controller/Api/Stations/Files/RenameAction.php
@@ -1,5 +1,7 @@
+ */
class FilesController extends AbstractStationApiCrudController
{
protected string $entityClass = Entity\StationMedia::class;
@@ -179,19 +184,19 @@ class FilesController extends AbstractStationApiCrudController
public function editAction(
ServerRequest $request,
Response $response,
- int|string $station_id,
- int|string $id
+ int $station_id,
+ int $id
): ResponseInterface {
$station = $this->getStation($request);
$record = $this->getRecord($station, $id);
if (null === $record) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
$data = $request->getParsedBody();
- if (null === $data) {
+ if (!is_array($data)) {
throw new InvalidArgumentException('Could not parse input data.');
}
diff --git a/src/Controller/Api/Stations/HistoryController.php b/src/Controller/Api/Stations/HistoryController.php
index 25ea8cc26..58f93171a 100644
--- a/src/Controller/Api/Stations/HistoryController.php
+++ b/src/Controller/Api/Stations/HistoryController.php
@@ -1,5 +1,7 @@
parse($userAgent);
if ($dd->isBot()) {
- $clientBot = $dd->getBot();
+ $clientBot = (array)$dd->getBot();
$clientBotName = $clientBot['name'] ?? 'Unknown Crawler';
$clientBotType = $clientBot['category'] ?? 'Generic Crawler';
$client = $clientBotName . ' (' . $clientBotType . ')';
} else {
- $clientInfo = $dd->getClient();
+ $clientInfo = (array)$dd->getClient();
$clientBrowser = $clientInfo['name'] ?? 'Unknown Browser';
$clientVersion = $clientInfo['version'] ?? '0.00';
- $clientOsInfo = $dd->getOs();
+ $clientOsInfo = (array)$dd->getOs();
$clientOs = $clientOsInfo['name'] ?? 'Unknown OS';
$client = $clientBrowser . ' ' . $clientVersion . ', ' . $clientOs;
@@ -237,7 +239,7 @@ class ListenersAction
array $listeners,
string $filename
): ResponseInterface {
- $tempFile = tmpfile();
+ $tempFile = tmpfile() ?: throw new \RuntimeException('Could not create temp file.');
$csv = Writer::createFromStream($tempFile);
$tz = $station->getTimezoneObject();
diff --git a/src/Controller/Api/Stations/MountsController.php b/src/Controller/Api/Stations/MountsController.php
index c28d2b2c3..2511870a1 100644
--- a/src/Controller/Api/Stations/MountsController.php
+++ b/src/Controller/Api/Stations/MountsController.php
@@ -1,5 +1,7 @@
+ */
class MountsController extends AbstractStationApiCrudController
{
protected string $entityClass = Entity\StationMount::class;
diff --git a/src/Controller/Api/Stations/OnDemand/DownloadAction.php b/src/Controller/Api/Stations/OnDemand/DownloadAction.php
index c7bce7455..4ae975111 100644
--- a/src/Controller/Api/Stations/OnDemand/DownloadAction.php
+++ b/src/Controller/Api/Stations/OnDemand/DownloadAction.php
@@ -1,5 +1,7 @@
withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('File not found.')));
+ ->withJson(Entity\Api\Error::notFound());
}
$fsMedia = (new StationFilesystems($station))->getMediaFilesystem();
diff --git a/src/Controller/Api/Stations/OnDemand/ListAction.php b/src/Controller/Api/Stations/OnDemand/ListAction.php
index 2f0639590..18fa9055d 100644
--- a/src/Controller/Api/Stations/OnDemand/ListAction.php
+++ b/src/Controller/Api/Stations/OnDemand/ListAction.php
@@ -1,5 +1,7 @@
requireRecord($request->getStation(), $id);
- $data = $request->getParsedBody();
+ $data = (array)$request->getParsedBody();
$copier = new DeepCopy\DeepCopy();
$copier->addFilter(
diff --git a/src/Controller/Api/Stations/Playlists/DeleteQueueAction.php b/src/Controller/Api/Stations/Playlists/DeleteQueueAction.php
index 84c10ee7d..40e6a00b7 100644
--- a/src/Controller/Api/Stations/Playlists/DeleteQueueAction.php
+++ b/src/Controller/Api/Stations/Playlists/DeleteQueueAction.php
@@ -1,5 +1,7 @@
requireRecord($request->getStation(), $id);
diff --git a/src/Controller/Api/Stations/Playlists/ExportAction.php b/src/Controller/Api/Stations/Playlists/ExportAction.php
index f9237b433..ef4131f1a 100644
--- a/src/Controller/Api/Stations/Playlists/ExportAction.php
+++ b/src/Controller/Api/Stations/Playlists/ExportAction.php
@@ -1,5 +1,7 @@
requireRecord($request->getStation(), $id);
diff --git a/src/Controller/Api/Stations/Playlists/GetOrderAction.php b/src/Controller/Api/Stations/Playlists/GetOrderAction.php
index 464efa5bb..ea266eeb8 100644
--- a/src/Controller/Api/Stations/Playlists/GetOrderAction.php
+++ b/src/Controller/Api/Stations/Playlists/GetOrderAction.php
@@ -1,5 +1,7 @@
requireRecord($request->getStation(), $id);
diff --git a/src/Controller/Api/Stations/Playlists/GetQueueAction.php b/src/Controller/Api/Stations/Playlists/GetQueueAction.php
index 105a08083..681b23920 100644
--- a/src/Controller/Api/Stations/Playlists/GetQueueAction.php
+++ b/src/Controller/Api/Stations/Playlists/GetQueueAction.php
@@ -1,5 +1,7 @@
requireRecord($request->getStation(), $id);
diff --git a/src/Controller/Api/Stations/Playlists/ImportAction.php b/src/Controller/Api/Stations/Playlists/ImportAction.php
index b22fa43ed..19f319f52 100644
--- a/src/Controller/Api/Stations/Playlists/ImportAction.php
+++ b/src/Controller/Api/Stations/Playlists/ImportAction.php
@@ -1,5 +1,7 @@
requireRecord($request->getStation(), $id);
@@ -31,7 +33,7 @@ class ImportAction extends AbstractPlaylistsAction
if (UPLOAD_ERR_OK !== $file->getError()) {
return $response->withStatus(500)
- ->withJson(new Entity\Api\Error(500, $file->getError()));
+ ->withJson(Entity\Api\Error::fromFileError($file->getError()));
}
$playlistFile = $file->getStream()->getContents();
diff --git a/src/Controller/Api/Stations/Playlists/PutOrderAction.php b/src/Controller/Api/Stations/Playlists/PutOrderAction.php
index 7c8e37554..bd8928b17 100644
--- a/src/Controller/Api/Stations/Playlists/PutOrderAction.php
+++ b/src/Controller/Api/Stations/Playlists/PutOrderAction.php
@@ -1,5 +1,7 @@
requireRecord($request->getStation(), $id);
diff --git a/src/Controller/Api/Stations/Playlists/ReshuffleAction.php b/src/Controller/Api/Stations/Playlists/ReshuffleAction.php
index 1db87e88a..b38e74fab 100644
--- a/src/Controller/Api/Stations/Playlists/ReshuffleAction.php
+++ b/src/Controller/Api/Stations/Playlists/ReshuffleAction.php
@@ -1,5 +1,7 @@
requireRecord($request->getStation(), $id);
diff --git a/src/Controller/Api/Stations/Playlists/ToggleAction.php b/src/Controller/Api/Stations/Playlists/ToggleAction.php
index 727894d9b..68fb9a7bb 100644
--- a/src/Controller/Api/Stations/Playlists/ToggleAction.php
+++ b/src/Controller/Api/Stations/Playlists/ToggleAction.php
@@ -1,5 +1,7 @@
requireRecord($request->getStation(), $id);
diff --git a/src/Controller/Api/Stations/PlaylistsController.php b/src/Controller/Api/Stations/PlaylistsController.php
index faa626cab..19a115bfc 100644
--- a/src/Controller/Api/Stations/PlaylistsController.php
+++ b/src/Controller/Api/Stations/PlaylistsController.php
@@ -1,5 +1,7 @@
+ */
class PlaylistsController extends AbstractScheduledEntityController
{
protected string $entityClass = Entity\StationPlaylist::class;
@@ -201,33 +206,43 @@ class PlaylistsController extends AbstractScheduledEntityController
$router = $request->getRouter();
$return['links'] = [
- 'toggle' => $router->fromHere('api:stations:playlist:toggle', ['id' => $record->getId()], [], !$isInternal),
- 'order' => $router->fromHere('api:stations:playlist:order', ['id' => $record->getId()], [], !$isInternal),
- 'reshuffle' => $router->fromHere(
+ 'toggle' => (string)$router->fromHere(
+ 'api:stations:playlist:toggle',
+ ['id' => $record->getId()],
+ [],
+ !$isInternal
+ ),
+ 'order' => (string)$router->fromHere(
+ 'api:stations:playlist:order',
+ ['id' => $record->getId()],
+ [],
+ !$isInternal
+ ),
+ 'reshuffle' => (string)$router->fromHere(
route_name: 'api:stations:playlist:reshuffle',
route_params: ['id' => $record->getId()],
absolute: !$isInternal
),
- 'queue' => $router->fromHere(
+ 'queue' => (string)$router->fromHere(
route_name: 'api:stations:playlist:queue',
route_params: ['id' => $record->getId()],
absolute: !$isInternal
),
- 'import' => $router->fromHere(
+ 'import' => (string)$router->fromHere(
route_name: 'api:stations:playlist:import',
route_params: ['id' => $record->getId()],
absolute: !$isInternal
),
- 'clone' => $router->fromHere(
+ 'clone' => (string)$router->fromHere(
route_name: 'api:stations:playlist:clone',
route_params: ['id' => $record->getId()],
absolute: !$isInternal
),
- 'self' => $router->fromHere($this->resourceRouteName, ['id' => $record->getId()], [], !$isInternal),
+ 'self' => (string)$router->fromHere($this->resourceRouteName, ['id' => $record->getId()], [], !$isInternal),
];
foreach (['pls', 'm3u'] as $format) {
- $return['links']['export'][$format] = $router->fromHere(
+ $return['links']['export'][$format] = (string)$router->fromHere(
route_name: 'api:stations:playlist:export',
route_params: ['id' => $record->getId(), 'format' => $format],
absolute: !$isInternal
diff --git a/src/Controller/Api/Stations/PodcastEpisodesController.php b/src/Controller/Api/Stations/PodcastEpisodesController.php
index 9e42cb36d..92cd4464a 100644
--- a/src/Controller/Api/Stations/PodcastEpisodesController.php
+++ b/src/Controller/Api/Stations/PodcastEpisodesController.php
@@ -17,6 +17,9 @@ use Psr\Http\Message\ResponseInterface;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Validator\Validator\ValidatorInterface;
+/**
+ * @extends AbstractApiCrudController
+ */
class PodcastEpisodesController extends AbstractApiCrudController
{
protected string $entityClass = Entity\PodcastEpisode::class;
@@ -153,9 +156,6 @@ class PodcastEpisodesController extends AbstractApiCrudController
* )
*/
- /**
- * @inheritDoc
- */
public function listAction(
ServerRequest $request,
Response $response,
@@ -193,7 +193,7 @@ class PodcastEpisodesController extends AbstractApiCrudController
if (null === $record) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
$return = $this->viewRecord($record, $request);
@@ -208,11 +208,14 @@ class PodcastEpisodesController extends AbstractApiCrudController
$station = $request->getStation();
$podcast = $this->podcastRepository->fetchPodcastForStation($station, $podcast_id);
- $parsedBody = $request->getParsedBody();
+ if (null === $podcast) {
+ throw new \RuntimeException('Podcast not found.');
+ }
+
+ $parsedBody = (array)$request->getParsedBody();
- /** @var Entity\PodcastEpisode $record */
$record = $this->editRecord(
- $request->getParsedBody(),
+ $parsedBody,
new Entity\PodcastEpisode($podcast)
);
@@ -249,10 +252,10 @@ class PodcastEpisodesController extends AbstractApiCrudController
if ($podcast === null) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
- $this->editRecord($request->getParsedBody(), $podcast);
+ $this->editRecord((array)$request->getParsedBody(), $podcast);
return $response->withJson(new Entity\Api\Status(true, __('Changes saved successfully.')));
}
@@ -267,7 +270,7 @@ class PodcastEpisodesController extends AbstractApiCrudController
if (null === $record) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
$fsStation = new StationFilesystems($station);
@@ -279,12 +282,17 @@ class PodcastEpisodesController extends AbstractApiCrudController
/**
* @param Entity\Station $station
* @param string $id
+ *
+ * @return Entity\PodcastEpisode|null
*/
protected function getRecord(Entity\Station $station, string $id): ?object
{
return $this->episodeRepository->fetchEpisodeForStation($station, $id);
}
+ /**
+ * @inheritDoc
+ */
protected function viewRecord(object $record, ServerRequest $request): mixed
{
if (!($record instanceof Entity\PodcastEpisode)) {
@@ -321,24 +329,24 @@ class PodcastEpisodesController extends AbstractApiCrudController
$return->art_updated_at = $record->getArtUpdatedAt();
$return->has_custom_art = (0 !== $return->art_updated_at);
- $return->art = $router->fromHere(
+ $return->art = (string)$router->fromHere(
route_name: 'api:stations:podcast:episode:art',
route_params: ['episode_id' => $record->getId() . '|' . $record->getArtUpdatedAt()],
absolute: true
);
$return->links = [
- 'self' => $router->fromHere(
+ 'self' => (string)$router->fromHere(
route_name: $this->resourceRouteName,
route_params: ['episode_id' => $record->getId()],
absolute: !$isInternal
),
- 'public' => $router->fromHere(
+ 'public' => (string)$router->fromHere(
route_name: 'public:podcast:episode',
route_params: ['episode_id' => $record->getId()],
absolute: !$isInternal
),
- 'download' => $router->fromHere(
+ 'download' => (string)$router->fromHere(
route_name: 'api:stations:podcast:episode:download',
route_params: ['episode_id' => $record->getId()],
absolute: !$isInternal
@@ -349,12 +357,12 @@ class PodcastEpisodesController extends AbstractApiCrudController
$station = $request->getStation();
if ($acl->isAllowed(Acl::STATION_PODCASTS, $station)) {
- $return->links['art'] = $router->fromHere(
+ $return->links['art'] = (string)$router->fromHere(
route_name: 'api:stations:podcast:episode:art-internal',
route_params: ['episode_id' => $record->getId()],
absolute: !$isInternal
);
- $return->links['media'] = $router->fromHere(
+ $return->links['media'] = (string)$router->fromHere(
route_name: 'api:stations:podcast:episode:media-internal',
route_params: ['episode_id' => $record->getId()],
absolute: !$isInternal
diff --git a/src/Controller/Api/Stations/Podcasts/Art/PostArtAction.php b/src/Controller/Api/Stations/Podcasts/Art/PostArtAction.php
index 70dcb2983..2d31cda2c 100644
--- a/src/Controller/Api/Stations/Podcasts/Art/PostArtAction.php
+++ b/src/Controller/Api/Stations/Podcasts/Art/PostArtAction.php
@@ -30,7 +30,7 @@ class PostArtAction
if (null === $podcast) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Podcast not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
$podcastRepo->writePodcastArt(
diff --git a/src/Controller/Api/Stations/Podcasts/Episodes/Art/DeleteArtAction.php b/src/Controller/Api/Stations/Podcasts/Episodes/Art/DeleteArtAction.php
index 8263f0f1c..59ccf0a4b 100644
--- a/src/Controller/Api/Stations/Podcasts/Episodes/Art/DeleteArtAction.php
+++ b/src/Controller/Api/Stations/Podcasts/Episodes/Art/DeleteArtAction.php
@@ -24,7 +24,7 @@ class DeleteArtAction
$episode = $episodeRepo->fetchEpisodeForStation($station, $episode_id);
if ($episode === null) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Episode not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
$episodeRepo->removeEpisodeArt($episode);
diff --git a/src/Controller/Api/Stations/Podcasts/Episodes/Art/PostArtAction.php b/src/Controller/Api/Stations/Podcasts/Episodes/Art/PostArtAction.php
index bd9776877..5eeca05f1 100644
--- a/src/Controller/Api/Stations/Podcasts/Episodes/Art/PostArtAction.php
+++ b/src/Controller/Api/Stations/Podcasts/Episodes/Art/PostArtAction.php
@@ -30,7 +30,7 @@ class PostArtAction
if (null === $episode) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Episode not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
$episodeRepo->writeEpisodeArt(
diff --git a/src/Controller/Api/Stations/Podcasts/Episodes/Media/DeleteMediaAction.php b/src/Controller/Api/Stations/Podcasts/Episodes/Media/DeleteMediaAction.php
index 3d143fc67..1365f06e9 100644
--- a/src/Controller/Api/Stations/Podcasts/Episodes/Media/DeleteMediaAction.php
+++ b/src/Controller/Api/Stations/Podcasts/Episodes/Media/DeleteMediaAction.php
@@ -23,7 +23,7 @@ class DeleteMediaAction
if (!($episode instanceof Entity\PodcastEpisode)) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, 'Media file not found.'));
+ ->withJson(Entity\Api\Error::notFound());
}
$podcastMedia = $episode->getMedia();
diff --git a/src/Controller/Api/Stations/Podcasts/Episodes/Media/GetMediaAction.php b/src/Controller/Api/Stations/Podcasts/Episodes/Media/GetMediaAction.php
index 4df891cc7..669c660b7 100644
--- a/src/Controller/Api/Stations/Podcasts/Episodes/Media/GetMediaAction.php
+++ b/src/Controller/Api/Stations/Podcasts/Episodes/Media/GetMediaAction.php
@@ -42,6 +42,6 @@ class GetMediaAction
}
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, 'Media file not found.'));
+ ->withJson(Entity\Api\Error::notFound());
}
}
diff --git a/src/Controller/Api/Stations/Podcasts/Episodes/Media/PostMediaAction.php b/src/Controller/Api/Stations/Podcasts/Episodes/Media/PostMediaAction.php
index aade4969a..879267eee 100644
--- a/src/Controller/Api/Stations/Podcasts/Episodes/Media/PostMediaAction.php
+++ b/src/Controller/Api/Stations/Podcasts/Episodes/Media/PostMediaAction.php
@@ -33,7 +33,7 @@ class PostMediaAction
if (null === $episode) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Episode not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
$fsStation = new StationFilesystems($station);
diff --git a/src/Controller/Api/Stations/PodcastsController.php b/src/Controller/Api/Stations/PodcastsController.php
index 6ba95b5b5..e586fb9b7 100644
--- a/src/Controller/Api/Stations/PodcastsController.php
+++ b/src/Controller/Api/Stations/PodcastsController.php
@@ -19,6 +19,9 @@ use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Validator\Validator\ValidatorInterface;
+/**
+ * @extends AbstractApiCrudController
+ */
class PodcastsController extends AbstractApiCrudController
{
protected string $entityClass = Entity\Podcast::class;
@@ -152,7 +155,7 @@ class PodcastsController extends AbstractApiCrudController
if (null === $record) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
$return = $this->viewRecord($record, $request);
@@ -163,11 +166,11 @@ class PodcastsController extends AbstractApiCrudController
{
$station = $request->getStation();
- $parsedBody = $request->getParsedBody();
+ $parsedBody = (array)$request->getParsedBody();
/** @var Entity\Podcast $record */
$record = $this->editRecord(
- $request->getParsedBody(),
+ $parsedBody,
new Entity\Podcast($station->getPodcastsStorageLocation())
);
@@ -194,10 +197,10 @@ class PodcastsController extends AbstractApiCrudController
if ($podcast === null) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
- $this->editRecord($request->getParsedBody(), $podcast);
+ $this->editRecord((array)$request->getParsedBody(), $podcast);
return $response->withJson(new Entity\Api\Status(true, __('Changes saved successfully.')));
}
@@ -212,7 +215,7 @@ class PodcastsController extends AbstractApiCrudController
if (null === $record) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
$fsStation = new StationFilesystems($station);
@@ -224,12 +227,20 @@ class PodcastsController extends AbstractApiCrudController
/**
* @param Entity\Station $station
* @param string $id
+ *
+ * @return Entity\Podcast|null
*/
protected function getRecord(Entity\Station $station, string $id): ?object
{
- return $this->podcastRepository->fetchPodcastForStation($station, $id);
+ $record = $this->podcastRepository->fetchPodcastForStation($station, $id);
+ return $record;
}
+ /**
+ * @param Entity\Podcast $record
+ * @param ServerRequest $request
+ *
+ */
protected function viewRecord(object $record, ServerRequest $request): mixed
{
if (!($record instanceof Entity\Podcast)) {
@@ -242,7 +253,7 @@ class PodcastsController extends AbstractApiCrudController
$return = new Entity\Api\Podcast();
$return->id = $record->getId();
- $return->storage_location_id = $record->getStorageLocation()?->getId();
+ $return->storage_location_id = $record->getStorageLocation()->getId();
$return->title = $record->getTitle();
$return->link = $record->getLink();
$return->description = $record->getDescription();
@@ -261,29 +272,29 @@ class PodcastsController extends AbstractApiCrudController
$return->episodes = $episodes;
$return->has_custom_art = (0 !== $record->getArtUpdatedAt());
- $return->art = $router->fromHere(
+ $return->art = (string)$router->fromHere(
route_name: 'api:stations:podcast:art',
route_params: ['podcast_id' => $record->getId() . '|' . $record->getArtUpdatedAt()],
absolute: true
);
$return->links = [
- 'self' => $router->fromHere(
+ 'self' => (string)$router->fromHere(
route_name: $this->resourceRouteName,
route_params: ['podcast_id' => $record->getId()],
absolute: !$isInternal
),
- 'episodes' => $router->fromHere(
+ 'episodes' => (string)$router->fromHere(
route_name: 'api:stations:podcast:episodes',
route_params: ['podcast_id' => $record->getId()],
absolute: !$isInternal
),
- 'public_episodes' => $router->fromHere(
+ 'public_episodes' => (string)$router->fromHere(
route_name: 'public:podcast:episodes',
route_params: ['podcast_id' => $record->getId()],
absolute: !$isInternal
),
- 'public_feed' => $router->fromHere(
+ 'public_feed' => (string)$router->fromHere(
route_name: 'public:podcast:feed',
route_params: ['podcast_id' => $record->getId()],
absolute: !$isInternal
@@ -293,27 +304,34 @@ class PodcastsController extends AbstractApiCrudController
$acl = $request->getAcl();
if ($acl->isAllowed(Acl::STATION_PODCASTS, $station)) {
- $return->links['art'] = $router->fromHere(
+ $return->links['art'] = (string)$router->fromHere(
route_name: 'api:stations:podcast:art-internal',
route_params: ['podcast_id' => $record->getId()],
absolute: !$isInternal
);
- $return->links['episode_new_art'] = $router->fromHere(
+ $return->links['episode_new_art'] = (string)$router->fromHere(
route_name: 'api:stations:podcast:episodes:new-art',
route_params: ['podcast_id' => $record->getId()],
absolute: !$isInternal
);
- $return->links['episode_new_media'] = $router->fromHere(
- route_name: 'api:stations:podcast:episodes:new-media',
+ $return->links['episode_new_media'] = (string)$router->fromHere(
+ route_name: 'api:stations:podcast:episodes:new-media',
route_params: ['podcast_id' => $record->getId()],
- absolute: !$isInternal
+ absolute: !$isInternal
);
}
return $return;
}
+ /**
+ * @param mixed[] $data
+ * @param Entity\Podcast|null $record
+ * @param array $context
+ *
+ * @return Entity\Podcast
+ */
protected function fromArray($data, $record = null, array $context = []): object
{
return parent::fromArray(
diff --git a/src/Controller/Api/Stations/ProfileController.php b/src/Controller/Api/Stations/ProfileController.php
index 49633eea5..db14983c4 100644
--- a/src/Controller/Api/Stations/ProfileController.php
+++ b/src/Controller/Api/Stations/ProfileController.php
@@ -1,5 +1,7 @@
+ */
class QueueController extends AbstractStationApiCrudController
{
protected string $entityClass = Entity\StationQueue::class;
@@ -124,7 +129,7 @@ class QueueController extends AbstractStationApiCrudController
$apiResponse->log = $record->getLog();
$apiResponse->links = [
- 'self' => $router->fromHere($this->resourceRouteName, ['id' => $record->getId()], [], !$isInternal),
+ 'self' => (string)$router->fromHere($this->resourceRouteName, ['id' => $record->getId()], [], !$isInternal),
];
return $apiResponse;
diff --git a/src/Controller/Api/Stations/RemotesController.php b/src/Controller/Api/Stations/RemotesController.php
index fa4bebb67..3c018bd5e 100644
--- a/src/Controller/Api/Stations/RemotesController.php
+++ b/src/Controller/Api/Stations/RemotesController.php
@@ -1,12 +1,16 @@
+ */
class RemotesController extends AbstractStationApiCrudController
{
protected string $entityClass = Entity\StationRemote::class;
@@ -103,7 +107,7 @@ class RemotesController extends AbstractStationApiCrudController
{
$record = parent::getRecord($station, $id);
- if ($record instanceof StationRemote && !$record->isEditable()) {
+ if ($record instanceof Entity\StationRemote && !$record->isEditable()) {
throw new PermissionDeniedException('This record cannot be edited.');
}
diff --git a/src/Controller/Api/Stations/Reports/Overview/BestAndWorstAction.php b/src/Controller/Api/Stations/Reports/Overview/BestAndWorstAction.php
index c520be08d..7f1964ae6 100644
--- a/src/Controller/Api/Stations/Reports/Overview/BestAndWorstAction.php
+++ b/src/Controller/Api/Stations/Reports/Overview/BestAndWorstAction.php
@@ -1,5 +1,7 @@
t = $statTime->getTimestamp() * 1000;
- $avg_row->y = round($stat['number_avg'], 2);
+ $avg_row->y = round((float)$stat['number_avg'], 2);
$daily_averages[] = $avg_row;
$row_date = $statTime->format('Y-m-d');
diff --git a/src/Controller/Api/Stations/Reports/Overview/MostPlayedAction.php b/src/Controller/Api/Stations/Reports/Overview/MostPlayedAction.php
index 9f94846c7..fca189744 100644
--- a/src/Controller/Api/Stations/Reports/Overview/MostPlayedAction.php
+++ b/src/Controller/Api/Stations/Reports/Overview/MostPlayedAction.php
@@ -1,5 +1,7 @@
+ */
class BroadcastsController extends AbstractApiCrudController
{
protected string $entityClass = Entity\StationStreamerBroadcast::class;
@@ -19,13 +24,11 @@ class BroadcastsController extends AbstractApiCrudController
/**
* @param ServerRequest $request
* @param Response $response
- * @param int|string $station_id
* @param int|null $id
*/
public function listAction(
ServerRequest $request,
Response $response,
- int|string $station_id,
?int $id = null
): ResponseInterface {
$station = $request->getStation();
@@ -35,7 +38,7 @@ class BroadcastsController extends AbstractApiCrudController
if (null === $streamer) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
$query = $this->em->createQuery(
@@ -68,7 +71,6 @@ class BroadcastsController extends AbstractApiCrudController
$paginator->setPostprocessor(
function ($row) use ($id, $is_bootgrid, $router, $fsRecordings) {
- /** @var Entity\StationStreamerBroadcast $row */
$return = $this->toArray($row);
unset($return['recordingPath']);
@@ -95,13 +97,13 @@ class BroadcastsController extends AbstractApiCrudController
'path' => $recordingPath,
'size' => $fsRecordings->fileSize($recordingPath),
'links' => [
- 'download' => $router->fromHere(
+ 'download' => (string)$router->fromHere(
'api:stations:streamer:broadcast:download',
$routeParams,
[],
true
),
- 'delete' => $router->fromHere(
+ 'delete' => (string)$router->fromHere(
'api:stations:streamer:broadcast:delete',
$routeParams,
[],
@@ -127,15 +129,11 @@ class BroadcastsController extends AbstractApiCrudController
/**
* @param ServerRequest $request
* @param Response $response
- * @param int|string $station_id
- * @param int $id
* @param int $broadcast_id
*/
public function downloadAction(
ServerRequest $request,
Response $response,
- int|string $station_id,
- int $id,
int $broadcast_id
): ResponseInterface {
$station = $request->getStation();
@@ -143,7 +141,7 @@ class BroadcastsController extends AbstractApiCrudController
if (null === $broadcast) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
$recordingPath = $broadcast->getRecordingPath();
@@ -167,16 +165,14 @@ class BroadcastsController extends AbstractApiCrudController
public function deleteAction(
ServerRequest $request,
Response $response,
- $station_id,
- $id,
- $broadcast_id
+ int $broadcast_id
): ResponseInterface {
$station = $request->getStation();
$broadcast = $this->getRecord($station, $broadcast_id);
if (null === $broadcast) {
return $response->withStatus(404)
- ->withJson(new Entity\Api\Error(404, __('Record not found!')));
+ ->withJson(Entity\Api\Error::notFound());
}
$recordingPath = $broadcast->getRecordingPath();
diff --git a/src/Controller/Api/Stations/StreamersController.php b/src/Controller/Api/Stations/StreamersController.php
index 88d5aaab2..8e2675930 100644
--- a/src/Controller/Api/Stations/StreamersController.php
+++ b/src/Controller/Api/Stations/StreamersController.php
@@ -1,5 +1,7 @@
+ */
class StreamersController extends AbstractScheduledEntityController
{
protected string $entityClass = Entity\StationStreamer::class;
@@ -149,7 +154,7 @@ class StreamersController extends AbstractScheduledEntityController
}
/**
- * @param object $record
+ * @param Entity\StationStreamer $record
* @param ServerRequest $request
*
* @return mixed[]
@@ -159,11 +164,10 @@ class StreamersController extends AbstractScheduledEntityController
$return = parent::viewRecord($record, $request);
$isInternal = ('true' === $request->getParam('internal', 'false'));
- $return['links']['broadcasts'] = $request->getRouter()->fromHere(
- 'api:stations:streamer:broadcasts',
- ['id' => $record->getId()],
- [],
- !$isInternal
+ $return['links']['broadcasts'] = (string)$request->getRouter()->fromHere(
+ route_name: 'api:stations:streamer:broadcasts',
+ route_params: ['id' => $record->getId()],
+ absolute: !$isInternal
);
return $return;
diff --git a/src/Controller/Api/Stations/UpdateMetadataController.php b/src/Controller/Api/Stations/UpdateMetadataController.php
index 99c91d53a..08e116be2 100644
--- a/src/Controller/Api/Stations/UpdateMetadataController.php
+++ b/src/Controller/Api/Stations/UpdateMetadataController.php
@@ -1,5 +1,7 @@
withCacheLifetime(Response::CACHE_ONE_YEAR);
diff --git a/src/Controller/Api/Stations/WebhooksController.php b/src/Controller/Api/Stations/WebhooksController.php
index d8f169282..4953ac11a 100644
--- a/src/Controller/Api/Stations/WebhooksController.php
+++ b/src/Controller/Api/Stations/WebhooksController.php
@@ -1,10 +1,15 @@
+ */
class WebhooksController extends AbstractStationApiCrudController
{
protected string $entityClass = Entity\StationWebhook::class;
diff --git a/src/Controller/Frontend/Account/EndMasqueradeAction.php b/src/Controller/Frontend/Account/EndMasqueradeAction.php
index c31d6d4a6..163710998 100644
--- a/src/Controller/Frontend/Account/EndMasqueradeAction.php
+++ b/src/Controller/Frontend/Account/EndMasqueradeAction.php
@@ -1,5 +1,7 @@
getAuth();
$auth->endMasquerade();
- return $response->withRedirect($request->getRouter()->named('admin:users:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('admin:users:index'));
}
}
diff --git a/src/Controller/Frontend/Account/ForgotPasswordAction.php b/src/Controller/Frontend/Account/ForgotPasswordAction.php
index 3940d343f..fdf947a8d 100644
--- a/src/Controller/Frontend/Account/ForgotPasswordAction.php
+++ b/src/Controller/Frontend/Account/ForgotPasswordAction.php
@@ -1,5 +1,7 @@
withRedirect($request->getRouter()->named('account:login'));
+ return $response->withRedirect((string)$request->getRouter()->named('account:login'));
}
return $view->renderToResponse($response, 'frontend/account/forgot');
diff --git a/src/Controller/Frontend/Account/LoginAction.php b/src/Controller/Frontend/Account/LoginAction.php
index 6a2f96312..3b470aebc 100644
--- a/src/Controller/Frontend/Account/LoginAction.php
+++ b/src/Controller/Frontend/Account/LoginAction.php
@@ -1,5 +1,7 @@
getSingleScalarResult();
if (0 === $num_users) {
- return $response->withRedirect($request->getRouter()->named('setup:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('setup:index'));
}
}
if ($auth->isLoggedIn()) {
- return $response->withRedirect($request->getRouter()->named('dashboard'));
+ return $response->withRedirect((string)$request->getRouter()->named('dashboard'));
}
$flash = $request->getFlash();
@@ -82,7 +84,7 @@ class LoginAction
// Redirect for 2FA.
if (!$auth->isLoginComplete()) {
- return $response->withRedirect($request->getRouter()->named('account:login:2fa'));
+ return $response->withRedirect((string)$request->getRouter()->named('account:login:2fa'));
}
// Redirect to complete setup if it's not completed yet.
@@ -95,7 +97,7 @@ class LoginAction
),
Flash::SUCCESS
);
- return $response->withRedirect($request->getRouter()->named('setup:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('setup:index'));
}
$flash->addMessage(
@@ -108,7 +110,7 @@ class LoginAction
return $response->withRedirect($referrer);
}
- return $response->withRedirect($request->getRouter()->named('dashboard'));
+ return $response->withRedirect((string)$request->getRouter()->named('dashboard'));
}
$flash->addMessage(
@@ -116,7 +118,7 @@ class LoginAction
Flash::ERROR
);
- return $response->withRedirect($request->getUri());
+ return $response->withRedirect((string)$request->getUri());
}
return $request->getView()->renderToResponse($response, 'frontend/account/login');
diff --git a/src/Controller/Frontend/Account/LogoutAction.php b/src/Controller/Frontend/Account/LogoutAction.php
index de6be5f38..4311487f2 100644
--- a/src/Controller/Frontend/Account/LogoutAction.php
+++ b/src/Controller/Frontend/Account/LogoutAction.php
@@ -1,5 +1,7 @@
getAuth();
$auth->logout();
- return $response->withRedirect($request->getRouter()->named('account:login'));
+ return $response->withRedirect((string)$request->getRouter()->named('account:login'));
}
}
diff --git a/src/Controller/Frontend/Account/RecoverAction.php b/src/Controller/Frontend/Account/RecoverAction.php
index 823f93812..529787000 100644
--- a/src/Controller/Frontend/Account/RecoverAction.php
+++ b/src/Controller/Frontend/Account/RecoverAction.php
@@ -1,5 +1,7 @@
withRedirect($request->getRouter()->named('account:login'));
+ return $response->withRedirect((string)$request->getRouter()->named('account:login'));
}
if ($request->isPost()) {
@@ -54,7 +56,7 @@ class RecoverAction
Flash::SUCCESS
);
- return $response->withRedirect($request->getRouter()->named('dashboard'));
+ return $response->withRedirect((string)$request->getRouter()->named('dashboard'));
}
return $request->getView()->renderToResponse($response, 'frontend/account/recover');
diff --git a/src/Controller/Frontend/Account/TwoFactorAction.php b/src/Controller/Frontend/Account/TwoFactorAction.php
index 4e1bb01a9..d47c820c1 100644
--- a/src/Controller/Frontend/Account/TwoFactorAction.php
+++ b/src/Controller/Frontend/Account/TwoFactorAction.php
@@ -1,7 +1,10 @@
getParam('otp');
if ($auth->verifyTwoFactor($otp)) {
+ /** @var User $user */
$user = $auth->getUser();
$flash->addMessage(
@@ -32,7 +36,7 @@ class TwoFactorAction
return $response->withRedirect($referrer);
}
- return $response->withRedirect($request->getRouter()->named('dashboard'));
+ return $response->withRedirect((string)$request->getRouter()->named('dashboard'));
}
$flash->addMessage(
@@ -40,7 +44,7 @@ class TwoFactorAction
Flash::ERROR
);
- return $response->withRedirect($request->getUri());
+ return $response->withRedirect((string)$request->getUri());
}
return $request->getView()->renderToResponse($response, 'frontend/account/two_factor');
diff --git a/src/Controller/Frontend/ApiKeysController.php b/src/Controller/Frontend/ApiKeysController.php
index 2f5dffcd7..988e1617d 100644
--- a/src/Controller/Frontend/ApiKeysController.php
+++ b/src/Controller/Frontend/ApiKeysController.php
@@ -1,5 +1,7 @@
getUser();
$view = $request->getView();
@@ -57,7 +59,7 @@ class ApiKeysController
$record = null;
}
- if ($_POST && $form->isValid($_POST)) {
+ if ($form->isValid($request)) {
$data = $form->getValues();
$key = null;
@@ -80,18 +82,26 @@ class ApiKeysController
}
$request->getFlash()->addMessage(__('API Key updated.'), 'green');
- return $response->withRedirect($request->getRouter()->named('api_keys:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('api_keys:index'));
}
- return $view->renderToResponse($response, 'system/form_page', [
- 'form' => $form,
- 'render_mode' => 'edit',
- 'title' => $id ? __('Edit API Key') : __('Add API Key'),
- ]);
+ return $view->renderToResponse(
+ $response,
+ 'system/form_page',
+ [
+ 'form' => $form,
+ 'render_mode' => 'edit',
+ 'title' => $id ? __('Edit API Key') : __('Add API Key'),
+ ]
+ );
}
- public function deleteAction(ServerRequest $request, Response $response, $id, $csrf): ResponseInterface
- {
+ public function deleteAction(
+ ServerRequest $request,
+ Response $response,
+ string $id,
+ string $csrf
+ ): ResponseInterface {
$request->getCsrf()->verify($csrf, $this->csrf_namespace);
/** @var Entity\User $user */
@@ -108,6 +118,6 @@ class ApiKeysController
$request->getFlash()->addMessage(__('API Key deleted.'), 'green');
- return $response->withRedirect($request->getRouter()->named('api_keys:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('api_keys:index'));
}
}
diff --git a/src/Controller/Frontend/DashboardAction.php b/src/Controller/Frontend/DashboardAction.php
index 75de667bd..d02f3a4ff 100644
--- a/src/Controller/Frontend/DashboardAction.php
+++ b/src/Controller/Frontend/DashboardAction.php
@@ -1,5 +1,7 @@
readSettings();
if (!$settings->isSetupComplete()) {
- return $response->withRedirect($request->getRouter()->named('setup:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('setup:index'));
}
// Redirect to login screen if the user isn't logged in.
@@ -25,16 +27,16 @@ class IndexAction
if (!($user instanceof Entity\User)) {
// Redirect to a custom homepage URL if specified in settings.
- $homepage_redirect = trim($settings->getHomepageRedirectUrl());
+ $homepage_redirect = trim($settings->getHomepageRedirectUrl() ?? '');
if (!empty($homepage_redirect)) {
return $response->withRedirect($homepage_redirect, 302);
}
- return $response->withRedirect($request->getRouter()->named('account:login'));
+ return $response->withRedirect((string)$request->getRouter()->named('account:login'));
}
// Redirect to dashboard if no other custom redirection rules exist.
- return $response->withRedirect($request->getRouter()->named('dashboard'));
+ return $response->withRedirect((string)$request->getRouter()->named('dashboard'));
}
}
diff --git a/src/Controller/Frontend/PWA/AppManifestAction.php b/src/Controller/Frontend/PWA/AppManifestAction.php
index 5351c8ad1..657faba43 100644
--- a/src/Controller/Frontend/PWA/AppManifestAction.php
+++ b/src/Controller/Frontend/PWA/AppManifestAction.php
@@ -1,5 +1,7 @@
getFlash()->addMessage(__('Two-factor authentication disabled.'), Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->named('profile:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('profile:index'));
}
}
diff --git a/src/Controller/Frontend/Profile/EditAction.php b/src/Controller/Frontend/Profile/EditAction.php
index 087547db9..f48e8ec8d 100644
--- a/src/Controller/Frontend/Profile/EditAction.php
+++ b/src/Controller/Frontend/Profile/EditAction.php
@@ -1,5 +1,7 @@
process($request)) {
$request->getFlash()->addMessage(__('Profile saved!'), Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->named('profile:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('profile:index'));
}
return $request->getView()->renderToResponse(
diff --git a/src/Controller/Frontend/Profile/EnableTwoFactorAction.php b/src/Controller/Frontend/Profile/EnableTwoFactorAction.php
index 08d4a5e8a..d67bd28fe 100644
--- a/src/Controller/Frontend/Profile/EnableTwoFactorAction.php
+++ b/src/Controller/Frontend/Profile/EnableTwoFactorAction.php
@@ -1,5 +1,7 @@
isPost() && $form->isValid($request->getParsedBody())) {
+ if ($form->isValid($request)) {
$user->setTwoFactorSecret($totp->getProvisioningUri());
$em->persist($user);
@@ -56,7 +58,7 @@ class EnableTwoFactorAction
$request->getFlash()->addMessage(__('Two-factor authentication enabled.'), Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->named('profile:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('profile:index'));
}
// Further customize TOTP code (with metadata that won't be stored in the DB)
diff --git a/src/Controller/Frontend/Profile/IndexAction.php b/src/Controller/Frontend/Profile/IndexAction.php
index 445de37c2..12f46ded0 100644
--- a/src/Controller/Frontend/Profile/IndexAction.php
+++ b/src/Controller/Frontend/Profile/IndexAction.php
@@ -1,5 +1,7 @@
getStation();
diff --git a/src/Controller/Frontend/PublicPages/PodcastEpisodeController.php b/src/Controller/Frontend/PublicPages/PodcastEpisodeController.php
index 896a384c9..73f1e80fd 100644
--- a/src/Controller/Frontend/PublicPages/PodcastEpisodeController.php
+++ b/src/Controller/Frontend/PublicPages/PodcastEpisodeController.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Controller\Frontend\PublicPages;
+use App\Entity\PodcastEpisode;
use App\Entity\Repository\PodcastEpisodeRepository;
use App\Entity\Repository\PodcastRepository;
use App\Exception\PodcastNotFoundException;
@@ -50,12 +51,12 @@ class PodcastEpisodeController
]
);
- if (!$episode->isPublished()) {
+ if (!($episode instanceof PodcastEpisode) || !$episode->isPublished()) {
$request->getFlash()->addMessage(__('Episode not found.'), Flash::ERROR);
return $response->withRedirect($podcastEpisodesLink);
}
- $feedLink = $router->named(
+ $feedLink = (string)$router->named(
'public:podcast:feed',
[
'station_id' => $station->getId(),
diff --git a/src/Controller/Frontend/PublicPages/PodcastEpisodesController.php b/src/Controller/Frontend/PublicPages/PodcastEpisodesController.php
index 88ef44adc..4fb52093b 100644
--- a/src/Controller/Frontend/PublicPages/PodcastEpisodesController.php
+++ b/src/Controller/Frontend/PublicPages/PodcastEpisodesController.php
@@ -52,7 +52,7 @@ class PodcastEpisodesController
}
);
- $podcastsLink = $router->fromHere(
+ $podcastsLink = (string)$router->fromHere(
'public:podcasts',
[
'station_id' => $station->getId(),
@@ -64,7 +64,7 @@ class PodcastEpisodesController
return $response->withRedirect($podcastsLink);
}
- $feedLink = $router->named(
+ $feedLink = (string)$router->named(
'public:podcast:feed',
[
'station_id' => $station->getId(),
diff --git a/src/Controller/Frontend/PublicPages/PodcastFeedController.php b/src/Controller/Frontend/PublicPages/PodcastFeedController.php
index de8cf1b12..91c832999 100644
--- a/src/Controller/Frontend/PublicPages/PodcastFeedController.php
+++ b/src/Controller/Frontend/PublicPages/PodcastFeedController.php
@@ -132,7 +132,7 @@ class PodcastFeedController
$channelLink = $podcast->getLink();
if (empty($channelLink)) {
- $channelLink = $serverRequest->getRouter()->fromHere(
+ $channelLink = (string)$serverRequest->getRouter()->fromHere(
route_name: 'public:podcast:episodes',
absolute: true
);
@@ -217,11 +217,11 @@ class PodcastFeedController
$this->stationRepository->getDefaultAlbumArtUrl($station)
);
- if ($podcastsFilesystem->fileExists(Podcast::getArtPath($podcast->getId()))) {
- $podcastArtworkSrc = $this->router->fromHere(
- route_name: 'api:stations:podcast:art',
- route_params: ['podcast_id' => $podcast->getId() . '|' . $podcast->getArtUpdatedAt()],
- absolute: true
+ if ($podcastsFilesystem->fileExists(Podcast::getArtPath($podcast->getIdRequired()))) {
+ $podcastArtworkSrc = (string)$this->router->fromHere(
+ route_name: 'api:stations:podcast:art',
+ route_params: ['podcast_id' => $podcast->getIdRequired() . '|' . $podcast->getArtUpdatedAt()],
+ absolute: true
);
}
@@ -256,7 +256,7 @@ class PodcastFeedController
$episodeLink = $episode->getLink();
if (empty($episodeLink)) {
- $episodeLink = $this->router->fromHere(
+ $episodeLink = (string)$this->router->fromHere(
route_name: 'public:podcast:episode',
route_params: ['episode_id' => $episode->getId()],
absolute: true
@@ -299,17 +299,19 @@ class PodcastFeedController
): RssEnclosure {
$rssEnclosure = new RssEnclosure();
- $podcastMediaPlayUrl = $this->router->fromHere(
- route_name: 'api:stations:podcast:episode:download',
+ $podcastMediaPlayUrl = (string)$this->router->fromHere(
+ route_name: 'api:stations:podcast:episode:download',
route_params: ['episode_id' => $episode->getId()],
- absolute: true
+ absolute: true
);
$rssEnclosure->setUrl($podcastMediaPlayUrl);
$podcastMedia = $episode->getMedia();
- $rssEnclosure->setType($podcastMedia->getMimeType());
- $rssEnclosure->setLength($podcastMedia->getLength());
+ if (null !== $podcastMedia) {
+ $rssEnclosure->setType($podcastMedia->getMimeType());
+ $rssEnclosure->setLength($podcastMedia->getLength());
+ }
return $rssEnclosure;
}
@@ -323,11 +325,11 @@ class PodcastFeedController
$this->stationRepository->getDefaultAlbumArtUrl($station)
);
- if ($podcastsFilesystem->fileExists(PodcastEpisode::getArtPath($episode->getId()))) {
- $episodeArtworkSrc = $this->router->fromHere(
- route_name: 'api:stations:podcast:episode:art',
+ if ($podcastsFilesystem->fileExists(PodcastEpisode::getArtPath($episode->getIdRequired()))) {
+ $episodeArtworkSrc = (string)$this->router->fromHere(
+ route_name: 'api:stations:podcast:episode:art',
route_params: ['episode_id' => $episode->getId() . '|' . $episode->getArtUpdatedAt()],
- absolute: true
+ absolute: true
);
}
diff --git a/src/Controller/Frontend/PublicPages/RequestsAction.php b/src/Controller/Frontend/PublicPages/RequestsAction.php
index 085f036eb..c271c7dff 100644
--- a/src/Controller/Frontend/PublicPages/RequestsAction.php
+++ b/src/Controller/Frontend/PublicPages/RequestsAction.php
@@ -1,5 +1,7 @@
withHeader('X-Frame-Options', '*');
diff --git a/src/Controller/Frontend/SetupController.php b/src/Controller/Frontend/SetupController.php
index 512e80094..a9edb923f 100644
--- a/src/Controller/Frontend/SetupController.php
+++ b/src/Controller/Frontend/SetupController.php
@@ -1,5 +1,7 @@
getSetupStep($request);
- return $response->withRedirect($request->getRouter()->named('setup:' . $current_step));
+ return $response->withRedirect((string)$request->getRouter()->named('setup:' . $current_step));
}
/**
@@ -89,7 +91,7 @@ class SetupController
{
$request->getFlash()->addMessage('' . __('Setup has already been completed!') . ' ', Flash::ERROR);
- return $response->withRedirect($request->getRouter()->named('dashboard'));
+ return $response->withRedirect((string)$request->getRouter()->named('dashboard'));
}
/**
@@ -101,7 +103,7 @@ class SetupController
// Verify current step.
$current_step = $this->getSetupStep($request);
if ($current_step !== 'register' && $this->environment->isProduction()) {
- return $response->withRedirect($request->getRouter()->named('setup:' . $current_step));
+ return $response->withRedirect((string)$request->getRouter()->named('setup:' . $current_step));
}
// Create first account form.
@@ -137,7 +139,7 @@ class SetupController
$acl = $request->getAcl();
$acl->reload();
- return $response->withRedirect($request->getRouter()->named('setup:index'));
+ return $response->withRedirect((string)$request->getRouter()->named('setup:index'));
}
return $request->getView()
@@ -158,11 +160,11 @@ class SetupController
// Verify current step.
$current_step = $this->getSetupStep($request);
if ($current_step !== 'station' && $this->environment->isProduction()) {
- return $response->withRedirect($request->getRouter()->named('setup:' . $current_step));
+ return $response->withRedirect((string)$request->getRouter()->named('setup:' . $current_step));
}
if (false !== $stationForm->process($request)) {
- return $response->withRedirect($request->getRouter()->named('setup:settings'));
+ return $response->withRedirect((string)$request->getRouter()->named('setup:settings'));
}
return $request->getView()->renderToResponse(
@@ -188,7 +190,7 @@ class SetupController
// Verify current step.
$current_step = $this->getSetupStep($request);
if ($current_step !== 'settings' && $this->environment->isProduction()) {
- return $response->withRedirect($request->getRouter()->named('setup:' . $current_step));
+ return $response->withRedirect((string)$request->getRouter()->named('setup:' . $current_step));
}
if ($settingsForm->process($request)) {
@@ -206,7 +208,7 @@ class SetupController
Flash::SUCCESS
);
- return $response->withRedirect($request->getRouter()->named('dashboard'));
+ return $response->withRedirect((string)$request->getRouter()->named('dashboard'));
}
return $request->getView()->renderToResponse(
diff --git a/src/Controller/Stations/AbstractStationCrudController.php b/src/Controller/Stations/AbstractStationCrudController.php
index d865f9efd..2fec87b14 100644
--- a/src/Controller/Stations/AbstractStationCrudController.php
+++ b/src/Controller/Stations/AbstractStationCrudController.php
@@ -1,5 +1,7 @@
getStation();
$this->form->setStation($station);
@@ -49,7 +51,7 @@ abstract class AbstractStationCrudController
* @param Station $station
* @param string|int|null $id
*/
- protected function getRecord(Station $station, $id = null): ?object
+ protected function getRecord(Station $station, string|int $id = null): ?object
{
if (null === $id) {
return null;
diff --git a/src/Controller/Stations/AutomationController.php b/src/Controller/Stations/AutomationController.php
index be53bdc83..a5fd4913b 100644
--- a/src/Controller/Stations/AutomationController.php
+++ b/src/Controller/Stations/AutomationController.php
@@ -1,5 +1,7 @@
form_config);
$form->populate($automation_settings);
- if (!empty($_POST) && $form->isValid($_POST)) {
+ if ($form->isValid($request)) {
$data = $form->getValues();
$station->setAutomationSettings($data);
@@ -43,7 +45,7 @@ class AutomationController
$request->getFlash()->addMessage(__('Changes saved.'), Flash::SUCCESS);
- return $response->withRedirect($request->getUri());
+ return $response->withRedirect((string)$request->getUri());
}
return $request->getView()->renderToResponse($response, 'stations/automation/index', [
@@ -66,6 +68,6 @@ class AutomationController
);
}
- return $response->withRedirect($request->getRouter()->fromHere('stations:automation:index'));
+ return $response->withRedirect((string)$request->getRouter()->fromHere('stations:automation:index'));
}
}
diff --git a/src/Controller/Stations/EditLiquidsoapConfigAction.php b/src/Controller/Stations/EditLiquidsoapConfigAction.php
index 024c175e0..32d48cc05 100644
--- a/src/Controller/Stations/EditLiquidsoapConfigAction.php
+++ b/src/Controller/Stations/EditLiquidsoapConfigAction.php
@@ -1,5 +1,7 @@
getBackendConfig();
$form = new Form($formConfig, ['backend_config' => $backendConfig->toArray()]);
- if ($request->isPost() && $form->isValid($request->getParsedBody())) {
+ if ($form->isValid($request)) {
$data = $form->getValues();
foreach ($data['backend_config'] as $configKey => $configValue) {
diff --git a/src/Controller/Stations/FilesAction.php b/src/Controller/Stations/FilesAction.php
index 0917ab409..993bd7659 100644
--- a/src/Controller/Stations/FilesAction.php
+++ b/src/Controller/Stations/FilesAction.php
@@ -1,5 +1,7 @@
getStation();
$log_areas = $this->getStationLogs($station);
@@ -28,8 +30,8 @@ class LogsController extends AbstractLogViewerController
throw new Exception('Invalid log file specified.');
}
- $log = $log_areas[$log];
- return $this->view($request, $response, $log['path'], $log['tail'] ?? true);
+ $logArea = $log_areas[$log];
+ return $this->view($request, $response, $logArea['path'], $logArea['tail'] ?? true);
}
protected function processLog(
diff --git a/src/Controller/Stations/PlaylistsAction.php b/src/Controller/Stations/PlaylistsAction.php
index 174cfa411..e256fb6a9 100644
--- a/src/Controller/Stations/PlaylistsAction.php
+++ b/src/Controller/Stations/PlaylistsAction.php
@@ -1,5 +1,7 @@
factory->make(StationForm::class);
if (false !== $stationForm->process($request, $station)) {
- return $response->withRedirect($request->getRouter()->fromHere('stations:profile:index'));
+ return $response->withRedirect((string)$request->getRouter()->fromHere('stations:profile:index'));
}
return $request->getView()->renderToResponse(
@@ -87,8 +89,8 @@ class ProfileController
public function toggleAction(
ServerRequest $request,
Response $response,
- $feature,
- $csrf
+ string $feature,
+ string $csrf
): ResponseInterface {
$request->getCsrf()->verify($csrf, $this->csrf_namespace);
@@ -111,6 +113,6 @@ class ProfileController
$this->em->persist($station);
$this->em->flush();
- return $response->withRedirect($request->getRouter()->fromHere('stations:profile:index'));
+ return $response->withRedirect((string)$request->getRouter()->fromHere('stations:profile:index'));
}
}
diff --git a/src/Controller/Stations/QueueAction.php b/src/Controller/Stations/QueueAction.php
index 2bf8a7979..82eeef637 100644
--- a/src/Controller/Stations/QueueAction.php
+++ b/src/Controller/Stations/QueueAction.php
@@ -1,5 +1,7 @@
doEdit($request, $id)) {
$request->getFlash()->addMessage(
'' . ($id ? __('Remote Relay updated.') : __('Remote Relay added.')) . ' ',
Flash::SUCCESS
);
- return $response->withRedirect($request->getRouter()->fromHere('stations:remotes:index'));
+ return $response->withRedirect((string)$request->getRouter()->fromHere('stations:remotes:index'));
}
- return $request->getView()->renderToResponse($response, 'stations/remotes/edit', [
- 'form' => $this->form,
- 'render_mode' => 'edit',
- 'title' => $id ? __('Edit Remote Relay') : __('Add Remote Relay'),
- ]);
+ return $request->getView()->renderToResponse(
+ $response,
+ 'stations/remotes/edit',
+ [
+ 'form' => $this->form,
+ 'render_mode' => 'edit',
+ 'title' => $id ? __('Edit Remote Relay') : __('Add Remote Relay'),
+ ]
+ );
}
public function deleteAction(
ServerRequest $request,
Response $response,
- $id,
- $csrf
+ int $id,
+ string $csrf
): ResponseInterface {
$this->doDelete($request, $id, $csrf);
$request->getFlash()->addMessage('' . __('Remote Relay deleted.') . ' ', Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->fromHere('stations:remotes:index'));
+ return $response->withRedirect((string)$request->getRouter()->fromHere('stations:remotes:index'));
}
- protected function getRecord(Station $station, $id = null): ?object
+ protected function getRecord(Station $station, int|string|null $id = null): ?object
{
$record = parent::getRecord($station, $id);
diff --git a/src/Controller/Stations/Reports/ListenersController.php b/src/Controller/Stations/Reports/ListenersController.php
index 3f2244fe2..7b1e585f5 100644
--- a/src/Controller/Stations/Reports/ListenersController.php
+++ b/src/Controller/Stations/Reports/ListenersController.php
@@ -1,5 +1,7 @@
getStation();
diff --git a/src/Controller/Stations/Reports/RequestsController.php b/src/Controller/Stations/Reports/RequestsController.php
index 21bec9017..d8f1edd9f 100644
--- a/src/Controller/Stations/Reports/RequestsController.php
+++ b/src/Controller/Stations/Reports/RequestsController.php
@@ -1,5 +1,7 @@
getCsrf()->verify($csrf, $this->csrf_namespace);
@@ -62,13 +64,13 @@ class RequestsController
$request->getFlash()->addMessage('Request deleted! ', Flash::SUCCESS);
}
- return $response->withRedirect($request->getRouter()->fromHere('stations:reports:requests'));
+ return $response->withRedirect((string)$request->getRouter()->fromHere('stations:reports:requests'));
}
public function clearAction(
ServerRequest $request,
Response $response,
- $csrf
+ string $csrf
): ResponseInterface {
$request->getCsrf()->verify($csrf, $this->csrf_namespace);
@@ -85,6 +87,6 @@ class RequestsController
$request->getFlash()->addMessage('All pending requests cleared. ', Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->fromHere('stations:reports:requests'));
+ return $response->withRedirect((string)$request->getRouter()->fromHere('stations:reports:requests'));
}
}
diff --git a/src/Controller/Stations/Reports/SoundExchangeController.php b/src/Controller/Stations/Reports/SoundExchangeController.php
index 2fc6f2e94..be2904818 100644
--- a/src/Controller/Stations/Reports/SoundExchangeController.php
+++ b/src/Controller/Stations/Reports/SoundExchangeController.php
@@ -1,5 +1,7 @@
getStation();
+ $tzObject = $station->getTimezoneObject();
+
+ $startDate = CarbonImmutable::parse('first day of last month', $tzObject);
+ $endDate = CarbonImmutable::parse('last day of last month', $tzObject);
+
$form = new Form($this->form_config);
$form->populate(
[
- 'start_date' => date('Y-m-d', strtotime('first day of last month')),
- 'end_date' => date('Y-m-d', strtotime('last day of last month')),
+ 'start_date' => $startDate->format('Y-m-d'),
+ 'end_date' => $endDate->format('Y-m-d'),
]
);
- if ($request->isPost() && $form->isValid($request->getParsedBody())) {
+ if ($form->isValid($request)) {
$data = $form->getValues();
- $start_date = strtotime($data['start_date'] . ' 00:00:00');
- $end_date = strtotime($data['end_date'] . ' 23:59:59');
+ $startDate = CarbonImmutable::parse($data['start_date'] . ' 00:00:00', $tzObject);
+ $endDate = CarbonImmutable::parse($data['end_date'] . ' 23:59:59', $tzObject);
$fetchIsrc = $data['fetch_isrc'];
@@ -87,8 +95,8 @@ class SoundExchangeController
GROUP BY sh.song_id
DQL
)->setParameter('station', $station)
- ->setParameter('time_start', $start_date)
- ->setParameter('time_end', $end_date)
+ ->setParameter('time_start', $startDate->getTimestamp())
+ ->setParameter('time_end', $endDate->getTimestamp())
->getArrayResult();
$history_rows_by_id = array_column($history_rows, null, 'media_id');
@@ -149,8 +157,8 @@ class SoundExchangeController
// Example: WABC01012009-31012009_A.txt
$export_filename = strtoupper($station->getShortName())
- . date('dmY', $start_date) . '-'
- . date('dmY', $end_date) . '_A.txt';
+ . $startDate->format('dmY') . '-'
+ . $endDate->format('dmY') . '_A.txt';
return $response->renderStringAsFile($export_txt, 'text/plain', $export_filename);
}
@@ -162,7 +170,7 @@ class SoundExchangeController
]);
}
- protected function findISRC($song_row): ?string
+ protected function findISRC(array $song_row): ?string
{
$song = Entity\Song::createFromArray($song_row);
diff --git a/src/Controller/Stations/Reports/TimelineController.php b/src/Controller/Stations/Reports/TimelineController.php
index d0b56bef0..8d1d8dc91 100644
--- a/src/Controller/Stations/Reports/TimelineController.php
+++ b/src/Controller/Stations/Reports/TimelineController.php
@@ -1,5 +1,7 @@
doEdit($request, $id)) {
$request->getFlash()->addMessage('' . __('Changes saved.') . ' ', Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->fromHere('stations:sftp_users:index'));
+ return $response->withRedirect((string)$request->getRouter()->fromHere('stations:sftp_users:index'));
}
- return $request->getView()->renderToResponse($response, 'system/form_page', [
- 'form' => $this->form,
- 'render_mode' => 'edit',
- 'title' => $id ? __('Edit SFTP User') : __('Add SFTP User'),
- ]);
+ return $request->getView()->renderToResponse(
+ $response,
+ 'system/form_page',
+ [
+ 'form' => $this->form,
+ 'render_mode' => 'edit',
+ 'title' => $id ? __('Edit SFTP User') : __('Add SFTP User'),
+ ]
+ );
}
public function deleteAction(
ServerRequest $request,
Response $response,
- $id,
- $csrf
+ int $id,
+ string $csrf
): ResponseInterface {
$this->doDelete($request, $id, $csrf);
$request->getFlash()->addMessage('' . __('SFTP User deleted.') . ' ', Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->fromHere('stations:sftp_users:index'));
+ return $response->withRedirect((string)$request->getRouter()->fromHere('stations:sftp_users:index'));
}
}
diff --git a/src/Controller/Stations/StreamersAction.php b/src/Controller/Stations/StreamersAction.php
index 627652b2c..352223238 100644
--- a/src/Controller/Stations/StreamersAction.php
+++ b/src/Controller/Stations/StreamersAction.php
@@ -1,5 +1,7 @@
withRedirect($request->getRouter()->fromHere('stations:streamers:index'));
+ return $response->withRedirect((string)$request->getRouter()->fromHere('stations:streamers:index'));
}
return $view->renderToResponse($response, 'stations/streamers/disabled');
diff --git a/src/Controller/Stations/WebhooksController.php b/src/Controller/Stations/WebhooksController.php
index f2dfc344a..c6b5d76aa 100644
--- a/src/Controller/Stations/WebhooksController.php
+++ b/src/Controller/Stations/WebhooksController.php
@@ -1,5 +1,7 @@
getView();
if ($type === null) {
- return $view->renderToResponse($response, 'stations/webhooks/add', [
- 'connectors' => array_filter(
- $this->webhook_config['webhooks'],
- static function ($webhook) {
- return !empty($webhook['name']);
- }
- ),
- ]);
+ return $view->renderToResponse(
+ $response,
+ 'stations/webhooks/add',
+ [
+ 'connectors' => array_filter(
+ $this->webhook_config['webhooks'],
+ static function ($webhook) {
+ return !empty($webhook['name']);
+ }
+ ),
+ ]
+ );
}
$record = new Entity\StationWebhook($request->getStation(), $type);
if (false !== $this->form->process($request, $record)) {
$request->getFlash()->addMessage('' . __('Web Hook added.') . ' ', Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->fromHere('stations:webhooks:index'));
+ return $response->withRedirect((string)$request->getRouter()->fromHere('stations:webhooks:index'));
}
return $view->renderToResponse($response, 'system/form_page', [
@@ -66,25 +72,29 @@ class WebhooksController extends AbstractStationCrudController
]);
}
- public function editAction(ServerRequest $request, Response $response, $id): ResponseInterface
+ public function editAction(ServerRequest $request, Response $response, int $id): ResponseInterface
{
if (false !== $this->doEdit($request, $id)) {
$request->getFlash()->addMessage('' . __('Web Hook updated.') . ' ', Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->fromHere('stations:webhooks:index'));
+ return $response->withRedirect((string)$request->getRouter()->fromHere('stations:webhooks:index'));
}
- return $request->getView()->renderToResponse($response, 'system/form_page', [
- 'form' => $this->form,
- 'render_mode' => 'edit',
- 'title' => __('Edit Web Hook'),
- ]);
+ return $request->getView()->renderToResponse(
+ $response,
+ 'system/form_page',
+ [
+ 'form' => $this->form,
+ 'render_mode' => 'edit',
+ 'title' => __('Edit Web Hook'),
+ ]
+ );
}
public function toggleAction(
ServerRequest $request,
Response $response,
- $id,
- $csrf
+ int $id,
+ string $csrf
): ResponseInterface {
$request->getCsrf()->verify($csrf, $this->csrf_namespace);
@@ -100,14 +110,14 @@ class WebhooksController extends AbstractStationCrudController
'' . ($new_status ? __('Web hook enabled.') : __('Web Hook disabled.')) . ' ',
Flash::SUCCESS
);
- return $response->withRedirect($request->getRouter()->fromHere('stations:webhooks:index'));
+ return $response->withRedirect((string)$request->getRouter()->fromHere('stations:webhooks:index'));
}
public function testAction(
ServerRequest $request,
Response $response,
- $id,
- $csrf
+ int $id,
+ string $csrf
): ResponseInterface {
$request->getCsrf()->verify($csrf, $this->csrf_namespace);
@@ -127,13 +137,13 @@ class WebhooksController extends AbstractStationCrudController
public function deleteAction(
ServerRequest $request,
Response $response,
- $id,
- $csrf
+ int $id,
+ string $csrf
): ResponseInterface {
$this->doDelete($request, $id, $csrf);
$request->getFlash()->addMessage('' . __('Web Hook deleted.') . ' ', Flash::SUCCESS);
- return $response->withRedirect($request->getRouter()->fromHere('stations:webhooks:index'));
+ return $response->withRedirect((string)$request->getRouter()->fromHere('stations:webhooks:index'));
}
}
diff --git a/src/Customization.php b/src/Customization.php
index 6533ab64e..1af19bfa8 100644
--- a/src/Customization.php
+++ b/src/Customization.php
@@ -1,5 +1,7 @@
hideProductName()) {
if ($title) {
@@ -137,7 +139,7 @@ class Customization
$title = '(' . ucfirst($this->environment->getAppEnvironment()) . ') ' . $title;
}
- return $title;
+ return $title ?? '';
}
/**
diff --git a/src/DeferredCallable.php b/src/DeferredCallable.php
index 8d726c681..80c4bb66b 100644
--- a/src/DeferredCallable.php
+++ b/src/DeferredCallable.php
@@ -33,6 +33,7 @@ class DeferredCallable
}
/**
+ * @param mixed ...$args
*/
public function __invoke(...$args): mixed
{
diff --git a/src/Doctrine/DecoratedEntityManager.php b/src/Doctrine/DecoratedEntityManager.php
index b7961b411..8d22d631d 100644
--- a/src/Doctrine/DecoratedEntityManager.php
+++ b/src/Doctrine/DecoratedEntityManager.php
@@ -1,7 +1,10 @@
getId();
$this->wrapped->persist($object);
@@ -48,12 +51,19 @@ class DecoratedEntityManager extends EntityManagerDecorator implements Reloadabl
/**
* @inheritDoc
+ *
+ * @template TEntity as object
+ *
+ * @param TEntity $entity
+ *
+ * @return TEntity
*/
- public function refetch(mixed $entity)
+ public function refetch(object $entity): object
{
// phpcs:enable
$metadata = $this->wrapped->getClassMetadata(get_class($entity));
+ /** @var TEntity|null $freshValue */
$freshValue = $this->wrapped->find($metadata->getName(), $metadata->getIdentifierValues($entity));
if (!$freshValue) {
throw ORMInvalidArgumentException::entityHasNoIdentity($entity, 'refetch');
@@ -64,12 +74,19 @@ class DecoratedEntityManager extends EntityManagerDecorator implements Reloadabl
/**
* @inheritDoc
+ *
+ * @template TEntity as object
+ *
+ * @param TEntity $entity
+ *
+ * @return TEntity
*/
- public function refetchAsReference(mixed $entity)
+ public function refetchAsReference(object $entity): object
{
// phpcs:enable
$metadata = $this->wrapped->getClassMetadata(get_class($entity));
+ /** @var TEntity|null $freshValue */
$freshValue = $this->wrapped->getReference($metadata->getName(), $metadata->getIdentifierValues($entity));
if (!$freshValue) {
throw ORMInvalidArgumentException::entityHasNoIdentity($entity, 'refetch');
diff --git a/src/Doctrine/Event/AuditLog.php b/src/Doctrine/Event/AuditLog.php
index e5af48744..db0a8b7ff 100644
--- a/src/Doctrine/Event/AuditLog.php
+++ b/src/Doctrine/Event/AuditLog.php
@@ -1,5 +1,7 @@
getIdentifier($entity);
- if (null === $identifier) {
- continue;
- }
$newRecords[] = new Entity\AuditLog(
$changeType,
@@ -142,6 +140,10 @@ class AuditLog implements EventSubscriber
/** @var PersistentCollection $collection */
$owner = $collection->getOwner();
+ if (null === $owner) {
+ continue;
+ }
+
$reflectionClass = new ReflectionObject($owner);
if (!$this->isAuditable($reflectionClass)) {
continue;
@@ -149,6 +151,9 @@ class AuditLog implements EventSubscriber
// Ignore inverse side or one to many relations
$mapping = $collection->getMapping();
+ if (null === $mapping) {
+ continue;
+ }
if (!$mapping['isOwningSide'] || $mapping['type'] !== ClassMetadataInfo::MANY_TO_MANY) {
continue;
}
@@ -190,6 +195,9 @@ class AuditLog implements EventSubscriber
// Ignore inverse side or one to many relations
$mapping = $collection->getMapping();
+ if (null === $mapping) {
+ continue;
+ }
if (!$mapping['isOwningSide'] || $mapping['type'] !== ClassMetadataInfo::MANY_TO_MANY) {
continue;
}
@@ -238,7 +246,9 @@ class AuditLog implements EventSubscriber
$class = ($class instanceof Proxy || $class instanceof GhostObjectInterface)
? get_parent_class($class)
: get_class($class);
- } elseif (!is_string($class)) {
+ }
+
+ if (!is_string($class)) {
return false;
}
@@ -249,7 +259,7 @@ class AuditLog implements EventSubscriber
return !$em->getMetadataFactory()->isTransient($class);
}
- #[Pure] protected function isAuditable(ReflectionClass $refl): bool
+ protected function isAuditable(ReflectionClass $refl): bool
{
$auditable = $refl->getAttributes(Auditable::class);
return !empty($auditable);
@@ -260,7 +270,7 @@ class AuditLog implements EventSubscriber
*
* @param object $entity
*/
- protected function getIdentifier(object $entity): ?string
+ protected function getIdentifier(object $entity): string
{
if ($entity instanceof Stringable) {
return (string)$entity;
@@ -270,6 +280,13 @@ class AuditLog implements EventSubscriber
return $entity->getName();
}
- return null;
+ if ($entity instanceof Entity\Interfaces\IdentifiableEntityInterface) {
+ $entityId = $entity->getId();
+ if (null !== $entityId) {
+ return (string)$entityId;
+ }
+ }
+
+ return spl_object_hash($entity);
}
}
diff --git a/src/Doctrine/Event/SetExplicitChangeTracking.php b/src/Doctrine/Event/SetExplicitChangeTracking.php
index 7148b8738..fb4297ea7 100644
--- a/src/Doctrine/Event/SetExplicitChangeTracking.php
+++ b/src/Doctrine/Event/SetExplicitChangeTracking.php
@@ -1,5 +1,7 @@
*/
protected string $entityClass;
+ /** @var ObjectRepository */
protected ObjectRepository $repository;
public function __construct(
@@ -22,18 +29,16 @@ class Repository
protected LoggerInterface $logger
) {
if (!isset($this->entityClass)) {
- $this->entityClass = $this->getEntityClass();
+ /** @var class-string $defaultClass */
+ $defaultClass = str_replace(['Repository', '\\\\'], ['', '\\'], static::class);
+ $this->entityClass = $defaultClass;
}
+
if (!isset($this->repository)) {
$this->repository = $em->getRepository($this->entityClass);
}
}
- protected function getEntityClass(): string
- {
- return str_replace(['Repository', '\\\\'], ['', '\\'], static::class);
- }
-
public function getRepository(): ObjectRepository
{
return $this->repository;
@@ -48,7 +53,7 @@ class Repository
*
* @return mixed[]
*/
- public function fetchArray($cached = true, $order_by = null, $order_dir = 'ASC'): array
+ public function fetchArray(bool $cached = true, ?string $order_by = null, string $order_dir = 'ASC'): array
{
$qb = $this->em->createQueryBuilder()
->select('e')
@@ -71,8 +76,12 @@ class Repository
*
* @return mixed[]
*/
- public function fetchSelect($add_blank = false, Closure $display = null, $pk = 'id', $order_by = 'name'): array
- {
+ public function fetchSelect(
+ bool|string $add_blank = false,
+ Closure $display = null,
+ string $pk = 'id',
+ string $order_by = 'name'
+ ): array {
$select = [];
// Specify custom text in the $add_blank parameter to override.
@@ -127,9 +136,9 @@ class Repository
*
* @return mixed[]
*/
- public function toArray(object $entity, $deep = false, $form_mode = false): array
+ public function toArray(object $entity, bool $deep = false, bool $form_mode = false): array
{
- return $this->serializer->normalize(
+ return (array)$this->serializer->normalize(
$entity,
null,
[
diff --git a/src/Entity/Analytics.php b/src/Entity/Analytics.php
index dfca7d936..34198e026 100644
--- a/src/Entity/Analytics.php
+++ b/src/Entity/Analytics.php
@@ -1,9 +1,10 @@
station) {
+ throw new \RuntimeException('Cannot get moment in station timezone; no station associated.');
+ }
+
$tz = $this->station->getTimezoneObject();
return CarbonImmutable::parse($this->moment, $tz)->shiftTimezone($tz);
}
diff --git a/src/Entity/Api/Admin/Relay.php b/src/Entity/Api/Admin/Relay.php
index f63510a43..cc35345d0 100644
--- a/src/Entity/Api/Admin/Relay.php
+++ b/src/Entity/Api/Admin/Relay.php
@@ -1,5 +1,7 @@
code = (int)$code;
- $this->message = (string)$message;
+ $this->code = $code;
+ $this->message = $message;
$this->formatted_message = ($formatted_message ?? $message);
- $this->extra_data = (array)$extra_data;
+ $this->extra_data = $extra_data;
$this->success = false;
}
+ public static function notFound(): self
+ {
+ return new self(404, __('Record not found'));
+ }
+
+ public static function fromFileError(int $fileError): self
+ {
+ $errorMessage = match ($fileError) {
+ UPLOAD_ERR_INI_SIZE => __('The uploaded file exceeds the upload_max_filesize directive in php.ini.'),
+ UPLOAD_ERR_FORM_SIZE => __('The uploaded file exceeds the MAX_FILE_SIZE directive from the HTML form.'),
+ UPLOAD_ERR_PARTIAL => __('The uploaded file was only partially uploaded.'),
+ UPLOAD_ERR_NO_FILE => __('No file was uploaded.'),
+ UPLOAD_ERR_NO_TMP_DIR => __('No temporary directory is available.'),
+ UPLOAD_ERR_CANT_WRITE => __('Could not write to filesystem.'),
+ UPLOAD_ERR_EXTENSION => __('Upload halted by a PHP extension.'),
+ default => __('Unspecified error.'),
+ };
+
+ return new self(500, $errorMessage);
+ }
+
public static function fromException(Throwable $e, bool $includeTrace = false): self
{
$code = $e->getCode();
diff --git a/src/Entity/Api/FileList.php b/src/Entity/Api/FileList.php
index 856e1b7b2..87cb76f69 100644
--- a/src/Entity/Api/FileList.php
+++ b/src/Entity/Api/FileList.php
@@ -1,5 +1,7 @@
now_playing->recalculate();
+ $this->now_playing?->recalculate();
}
/**
diff --git a/src/Entity/Api/NowPlayingCurrentSong.php b/src/Entity/Api/NowPlayingCurrentSong.php
index e284bae8f..2a9d68c82 100644
--- a/src/Entity/Api/NowPlayingCurrentSong.php
+++ b/src/Entity/Api/NowPlayingCurrentSong.php
@@ -1,5 +1,7 @@
is_live = (bool)$is_live;
- $this->streamer_name = (string)$streamer_name;
+ public function __construct(
+ bool $is_live = false,
+ string $streamer_name = '',
+ ?int $broadcast_start = null
+ ) {
+ $this->is_live = $is_live;
+ $this->streamer_name = $streamer_name;
$this->broadcast_start = $broadcast_start;
}
}
diff --git a/src/Entity/Api/Podcast.php b/src/Entity/Api/Podcast.php
index dca9f6781..dc57e9502 100644
--- a/src/Entity/Api/Podcast.php
+++ b/src/Entity/Api/Podcast.php
@@ -1,5 +1,7 @@
backend_running = (bool)$backend_running;
- $this->frontend_running = (bool)$frontend_running;
+ $this->backend_running = $backend_running;
+ $this->frontend_running = $frontend_running;
}
}
diff --git a/src/Entity/Api/Status.php b/src/Entity/Api/Status.php
index 4388f5ca3..8c5b1be7b 100644
--- a/src/Entity/Api/Status.php
+++ b/src/Entity/Api/Status.php
@@ -1,5 +1,7 @@
success = (bool)$success;
- $this->message = (string)$message;
-
- $this->formatted_message = ($formatted_message ?? $message);
+ $this->success = $success;
+ $this->message = $message;
+ $this->formatted_message = $formatted_message ?? $message;
}
}
diff --git a/src/Entity/Api/SystemStatus.php b/src/Entity/Api/SystemStatus.php
index e37153950..026e9dd51 100644
--- a/src/Entity/Api/SystemStatus.php
+++ b/src/Entity/Api/SystemStatus.php
@@ -1,5 +1,7 @@
tracksMatch($npResult, $npOld)) {
- $previousHistory = $this->historyRepo->getCurrent($station)
- ?? Entity\Song::createFromApiSong($npOld->now_playing->song);
+ $previousHistory = $this->historyRepo->getCurrent($station);
+
+ if (null === $previousHistory) {
+ $previousHistory = ($npOld->now_playing?->song)
+ ? Entity\Song::createFromApiSong($npOld->now_playing->song)
+ : Entity\Song::createOffline();
+ }
$sh_obj = $this->historyRepo->register($previousHistory, $station, $np);
@@ -175,6 +182,6 @@ class NowPlayingApiGenerator
Entity\Api\NowPlaying $npOld
): bool {
$current_song_hash = Entity\Song::getSongHash($npResult->currentSong);
- return (0 === strcmp($current_song_hash, $npOld->now_playing->song->id));
+ return (0 === strcmp($current_song_hash, $npOld->now_playing?->song?->id ?? ''));
}
}
diff --git a/src/Entity/ApiGenerator/SongApiGenerator.php b/src/Entity/ApiGenerator/SongApiGenerator.php
index 54396b3c8..01be33e63 100644
--- a/src/Entity/ApiGenerator/SongApiGenerator.php
+++ b/src/Entity/ApiGenerator/SongApiGenerator.php
@@ -1,5 +1,7 @@
sh_id = $record->getId();
+ $response->sh_id = $record->getIdRequired();
$response->played_at = (0 === $record->getTimestampStart())
? 0
: $record->getTimestampStart() + Entity\SongHistory::PLAYBACK_DELAY_SECONDS;
diff --git a/src/Entity/ApiGenerator/StationApiGenerator.php b/src/Entity/ApiGenerator/StationApiGenerator.php
index 1f394ce12..862772e54 100644
--- a/src/Entity/ApiGenerator/StationApiGenerator.php
+++ b/src/Entity/ApiGenerator/StationApiGenerator.php
@@ -1,5 +1,7 @@
is_public = $station->getEnablePublicPage();
$response->listen_url = $fa->getStreamUrl($station, $baseUri);
- $response->public_player_url = $this->router->named(
+ $response->public_player_url = (string)$this->router->named(
'public:index',
['station_id' => $station->getShortName()]
);
- $response->playlist_pls_url = $this->router->named(
+ $response->playlist_pls_url = (string)$this->router->named(
'public:playlist',
['station_id' => $station->getShortName(), 'format' => 'pls']
);
- $response->playlist_m3u_url = $this->router->named(
+ $response->playlist_m3u_url = (string)$this->router->named(
'public:playlist',
['station_id' => $station->getShortName(), 'format' => 'm3u']
);
diff --git a/src/Entity/ApiGenerator/StationQueueApiGenerator.php b/src/Entity/ApiGenerator/StationQueueApiGenerator.php
index 27329ab5e..43bd621a9 100644
--- a/src/Entity/ApiGenerator/StationQueueApiGenerator.php
+++ b/src/Entity/ApiGenerator/StationQueueApiGenerator.php
@@ -1,5 +1,7 @@
playlist = '';
}
- if ($record->getMedia()) {
+ $recordMedia = $record->getMedia();
+ if (null !== $recordMedia) {
$response->song = ($this->songApiGenerator)(
- $record->getMedia(),
+ $recordMedia,
$record->getStation(),
$baseUri,
$allowRemoteArt
diff --git a/src/Entity/ApiKey.php b/src/Entity/ApiKey.php
index 506f0a82f..1b1a3061d 100644
--- a/src/Entity/ApiKey.php
+++ b/src/Entity/ApiKey.php
@@ -1,6 +1,6 @@
user;
}
- public function getComment(): ?string
+ public function getComment(): string
{
return $this->comment;
}
- public function setComment(?string $comment): void
+ public function setComment(string $comment): void
{
- $this->comment = $this->truncateNullableString($comment);
+ $this->comment = $this->truncateString($comment);
}
/**
diff --git a/src/Entity/Attributes/AuditIgnore.php b/src/Entity/Attributes/AuditIgnore.php
index 2c85fceef..b86ca6dec 100644
--- a/src/Entity/Attributes/AuditIgnore.php
+++ b/src/Entity/Attributes/AuditIgnore.php
@@ -1,5 +1,7 @@
short_name))
? $this->short_name
: Station::getStationShortName($this->name);
}
- public function setShortName(?string $short_name): void
+ public function setShortName(string $short_name): void
{
$short_name = trim($short_name);
if (!empty($short_name)) {
@@ -79,6 +80,6 @@ class CustomField implements Stringable
public function __toString(): string
{
- return $this->name;
+ return $this->short_name;
}
}
diff --git a/src/Entity/Fixture/Analytics.php b/src/Entity/Fixture/Analytics.php
index 33817c332..13636ebb8 100644
--- a/src/Entity/Fixture/Analytics.php
+++ b/src/Entity/Fixture/Analytics.php
@@ -1,5 +1,7 @@
setTitle('Episode ' . $i . ': ' . sprintf($podcastName, $podcastFiller));
diff --git a/src/Entity/Fixture/Role.php b/src/Entity/Fixture/Role.php
index f925e0f79..f79ecb9c1 100644
--- a/src/Entity/Fixture/Role.php
+++ b/src/Entity/Fixture/Role.php
@@ -1,5 +1,7 @@
setBaseUrl(getenv('INIT_BASE_URL') ?? 'docker.local');
- $settings->setInstanceName(getenv('INIT_INSTANCE_NAME') ?? 'local test');
- $settings->setGeoliteLicenseKey(getenv('INIT_GEOLITE_LICENSE_KEY') ?? '');
+ $settings->setBaseUrl((string)(getenv('INIT_BASE_URL') ?? 'docker.local'));
+ $settings->setInstanceName((string)(getenv('INIT_INSTANCE_NAME') ?? 'local test'));
+ $settings->setGeoliteLicenseKey((string)(getenv('INIT_GEOLITE_LICENSE_KEY') ?? ''));
$settings->setSetupCompleteTime(time());
$settings->setPreferBrowserUrl(true);
diff --git a/src/Entity/Fixture/Station.php b/src/Entity/Fixture/Station.php
index 3ee4d9426..5b0d676fb 100644
--- a/src/Entity/Fixture/Station.php
+++ b/src/Entity/Fixture/Station.php
@@ -1,5 +1,7 @@
changeCharset('utf8mb4', 'utf8mb4_unicode_ci');
}
- private function changeCharset($charset, $collate): void
+ private function changeCharset(string $charset, string $collate): void
{
$db_name = $this->connection->getDatabase();
diff --git a/src/Entity/Migration/Version20170906080352.php b/src/Entity/Migration/Version20170906080352.php
index 4c3b2e92a..df1052aa6 100644
--- a/src/Entity/Migration/Version20170906080352.php
+++ b/src/Entity/Migration/Version20170906080352.php
@@ -1,5 +1,7 @@
changeCharset('utf8mb4', 'utf8mb4_bin');
}
- private function changeCharset($charset, $collate): void
+ private function changeCharset(string $charset, string $collate): void
{
$sqlLines = [
'ALTER TABLE `station_media` DROP FOREIGN KEY FK_32AADE3AA0BDB2F3',
diff --git a/src/Entity/Migration/Version20180826043500.php b/src/Entity/Migration/Version20180826043500.php
index 4cd957335..878cd6733 100644
--- a/src/Entity/Migration/Version20180826043500.php
+++ b/src/Entity/Migration/Version20180826043500.php
@@ -20,7 +20,7 @@ final class Version20180826043500 extends AbstractMigration
$this->changeCharset('utf8mb4', 'utf8mb4_general_ci');
}
- private function changeCharset($charset, $collate): void
+ private function changeCharset(string $charset, string $collate): void
{
$db_name = $this->connection->getDatabase();
diff --git a/src/Entity/Migration/Version20201204043539.php b/src/Entity/Migration/Version20201204043539.php
index 477db8f6b..faec8b0f7 100644
--- a/src/Entity/Migration/Version20201204043539.php
+++ b/src/Entity/Migration/Version20201204043539.php
@@ -84,12 +84,12 @@ final class Version20201204043539 extends AbstractMigration
}
}
- private function toInt($value): ?int
+ private function toInt(mixed $value): ?int
{
return (null === $value) ? null : (int)$value;
}
- private function toBool($value): ?bool
+ private function toBool(mixed $value): ?bool
{
return (null === $value) ? null : (bool)$value;
}
diff --git a/src/Entity/Migration/Version20210717164419.php b/src/Entity/Migration/Version20210717164419.php
new file mode 100644
index 000000000..1dea35d38
--- /dev/null
+++ b/src/Entity/Migration/Version20210717164419.php
@@ -0,0 +1,78 @@
+setEmptyWhereNull('api_keys', 'comment');
+ $this->setEmptyWhereNull('custom_field', 'short_name');
+ $this->setEmptyWhereNull('station', 'name');
+ $this->setEmptyWhereNull('station', 'short_name');
+ $this->setEmptyWhereNull('users', 'email');
+ $this->setEmptyWhereNull('users', 'auth_password');
+ $this->setEmptyWhereNull('storage_location', 'path');
+ $this->setEmptyWhereNull('station_remotes', 'url');
+ }
+
+ protected function setEmptyWhereNull(string $table, string $field): void
+ {
+ $this->connection->update(
+ $table,
+ [$field => ''],
+ [$field => null]
+ );
+ }
+
+ public function up(Schema $schema): void
+ {
+ // this up() migration is auto-generated, please modify it to your needs
+ $this->addSql('ALTER TABLE api_keys CHANGE comment comment VARCHAR(255) NOT NULL');
+ $this->addSql('ALTER TABLE custom_field CHANGE short_name short_name VARCHAR(100) NOT NULL');
+ $this->addSql(
+ 'ALTER TABLE station CHANGE name name VARCHAR(100) NOT NULL, CHANGE short_name short_name VARCHAR(100) NOT NULL'
+ );
+ $this->addSql(
+ 'ALTER TABLE users CHANGE email email VARCHAR(100) NOT NULL, CHANGE auth_password auth_password VARCHAR(255) NOT NULL'
+ );
+ $this->addSql('ALTER TABLE storage_location CHANGE path path VARCHAR(255) NOT NULL');
+ $this->addSql('ALTER TABLE station_remotes CHANGE url url VARCHAR(255) NOT NULL');
+ }
+
+ public function down(Schema $schema): void
+ {
+ // this down() migration is auto-generated, please modify it to your needs
+ $this->addSql(
+ 'ALTER TABLE api_keys CHANGE comment comment VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_general_ci`'
+ );
+ $this->addSql(
+ 'ALTER TABLE custom_field CHANGE short_name short_name VARCHAR(100) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_general_ci`'
+ );
+ $this->addSql(
+ 'ALTER TABLE station CHANGE name name VARCHAR(100) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_general_ci`, CHANGE short_name short_name VARCHAR(100) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_general_ci`'
+ );
+ $this->addSql(
+ 'ALTER TABLE users CHANGE email email VARCHAR(100) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_general_ci`, CHANGE auth_password auth_password VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_general_ci`'
+ );
+ $this->addSql(
+ 'ALTER TABLE storage_location CHANGE path path VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_general_ci`'
+ );
+ $this->addSql(
+ 'ALTER TABLE station_remotes CHANGE url url VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci`'
+ );
+ }
+}
diff --git a/src/Entity/Podcast.php b/src/Entity/Podcast.php
index bca84cf12..d0041c476 100644
--- a/src/Entity/Podcast.php
+++ b/src/Entity/Podcast.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Entity;
+use App\Entity\Interfaces\IdentifiableEntityInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
@@ -14,7 +15,7 @@ use Symfony\Component\Validator\Constraints as Assert;
ORM\Table(name: 'podcast'),
Attributes\Auditable
]
-class Podcast
+class Podcast implements IdentifiableEntityInterface
{
use Traits\HasUniqueId;
use Traits\TruncateStrings;
diff --git a/src/Entity/PodcastCategory.php b/src/Entity/PodcastCategory.php
index b2846aae9..ca595174d 100644
--- a/src/Entity/PodcastCategory.php
+++ b/src/Entity/PodcastCategory.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Entity;
+use App\Entity\Interfaces\IdentifiableEntityInterface;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
@@ -12,7 +13,7 @@ use Symfony\Component\Validator\Constraints as Assert;
ORM\Table(name: 'podcast_category'),
Attributes\Auditable
]
-class PodcastCategory
+class PodcastCategory implements IdentifiableEntityInterface
{
use Traits\HasAutoIncrementId;
use Traits\TruncateStrings;
diff --git a/src/Entity/PodcastEpisode.php b/src/Entity/PodcastEpisode.php
index 3af5b60e2..2ea1cfa9a 100644
--- a/src/Entity/PodcastEpisode.php
+++ b/src/Entity/PodcastEpisode.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Entity;
+use App\Entity\Interfaces\IdentifiableEntityInterface;
use App\Entity\Traits;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
@@ -13,7 +14,7 @@ use Symfony\Component\Validator\Constraints as Assert;
ORM\Table(name: 'podcast_episode'),
Attributes\Auditable
]
-class PodcastEpisode
+class PodcastEpisode implements IdentifiableEntityInterface
{
use Traits\HasUniqueId;
use Traits\TruncateStrings;
diff --git a/src/Entity/PodcastMedia.php b/src/Entity/PodcastMedia.php
index 817932732..4c172a638 100644
--- a/src/Entity/PodcastMedia.php
+++ b/src/Entity/PodcastMedia.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Entity;
+use App\Entity\Interfaces\IdentifiableEntityInterface;
use App\Entity\Traits;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
@@ -13,7 +14,7 @@ use Symfony\Component\Validator\Constraints as Assert;
ORM\Table(name: 'podcast_media'),
Attributes\Auditable
]
-class PodcastMedia
+class PodcastMedia implements IdentifiableEntityInterface
{
use Traits\HasUniqueId;
use Traits\TruncateStrings;
@@ -161,25 +162,4 @@ class PodcastMedia
return $this;
}
-
- /**
- * @param string|float|null $seconds
- */
- protected function parseSeconds($seconds = null): ?float
- {
- if ($seconds === '') {
- return null;
- }
-
- if (str_contains($seconds, ':')) {
- $sec = 0;
- foreach (array_reverse(explode(':', $seconds)) as $k => $v) {
- $sec += (60 ** (int)$k) * (int)$v;
- }
-
- return $sec;
- }
-
- return $seconds;
- }
}
diff --git a/src/Entity/Relay.php b/src/Entity/Relay.php
index c79641ade..6042d64a8 100644
--- a/src/Entity/Relay.php
+++ b/src/Entity/Relay.php
@@ -1,9 +1,10 @@
updated_at = time();
}
- public function getBaseUrl(): ?string
+ public function getBaseUrl(): string
{
return $this->base_url;
}
@@ -92,7 +93,7 @@ class Relay
return $this->nowplaying;
}
- public function setNowplaying($nowplaying): void
+ public function setNowplaying(mixed $nowplaying): void
{
$this->nowplaying = $nowplaying;
}
diff --git a/src/Entity/Repository/AbstractSplitTokenRepository.php b/src/Entity/Repository/AbstractSplitTokenRepository.php
index a97380b60..e9e7e5bb8 100644
--- a/src/Entity/Repository/AbstractSplitTokenRepository.php
+++ b/src/Entity/Repository/AbstractSplitTokenRepository.php
@@ -1,11 +1,17 @@
+ */
abstract class AbstractSplitTokenRepository extends Repository
{
/**
diff --git a/src/Entity/Repository/AnalyticsRepository.php b/src/Entity/Repository/AnalyticsRepository.php
index b03e74232..bd064c3d2 100644
--- a/src/Entity/Repository/AnalyticsRepository.php
+++ b/src/Entity/Repository/AnalyticsRepository.php
@@ -1,5 +1,7 @@
getId());
+ $episodeArtworkPath = PodcastEpisode::getArtPath($episode->getIdRequired());
$episodeArtworkStream = $episodeArtwork->stream('jpg');
$fsPodcasts = $episode->getPodcast()->getStorageLocation()->getFilesystem();
@@ -102,7 +102,7 @@ class PodcastEpisodeRepository extends Repository
PodcastEpisode $episode,
?ExtendedFilesystemInterface $fs = null
): void {
- $artworkPath = PodcastEpisode::getArtPath($episode->getId());
+ $artworkPath = PodcastEpisode::getArtPath($episode->getIdRequired());
$fs ??= $episode->getPodcast()->getStorageLocation()->getFilesystem();
diff --git a/src/Entity/Repository/PodcastRepository.php b/src/Entity/Repository/PodcastRepository.php
index de2774aa5..7ad9c216e 100644
--- a/src/Entity/Repository/PodcastRepository.php
+++ b/src/Entity/Repository/PodcastRepository.php
@@ -6,9 +6,7 @@ namespace App\Entity\Repository;
use App\Doctrine\ReloadableEntityManagerInterface;
use App\Doctrine\Repository;
-use App\Entity\Podcast;
-use App\Entity\Station;
-use App\Entity\StorageLocation;
+use App\Entity;
use App\Environment;
use Azura\Files\ExtendedFilesystemInterface;
use Intervention\Image\Constraint;
@@ -17,6 +15,9 @@ use League\Flysystem\UnableToDeleteFile;
use Psr\Log\LoggerInterface;
use Symfony\Component\Serializer\Serializer;
+/**
+ * @extends Repository
+ */
class PodcastRepository extends Repository
{
public function __construct(
@@ -30,15 +31,15 @@ class PodcastRepository extends Repository
parent::__construct($entityManager, $serializer, $environment, $logger);
}
- public function fetchPodcastForStation(Station $station, string $podcastId): ?Podcast
+ public function fetchPodcastForStation(Entity\Station $station, string $podcastId): ?Entity\Podcast
{
return $this->fetchPodcastForStorageLocation($station->getPodcastsStorageLocation(), $podcastId);
}
public function fetchPodcastForStorageLocation(
- StorageLocation $storageLocation,
+ Entity\StorageLocation $storageLocation,
string $podcastId
- ): ?Podcast {
+ ): ?Entity\Podcast {
return $this->repository->findOneBy(
[
'id' => $podcastId,
@@ -48,9 +49,9 @@ class PodcastRepository extends Repository
}
/**
- * @return Podcast[]
+ * @return Entity\Podcast[]
*/
- public function fetchPublishedPodcastsForStation(Station $station): array
+ public function fetchPublishedPodcastsForStation(Entity\Station $station): array
{
$podcasts = $this->em->createQuery(
<<<'DQL'
@@ -64,7 +65,7 @@ class PodcastRepository extends Repository
return array_filter(
$podcasts,
- static function (Podcast $podcast) {
+ static function (Entity\Podcast $podcast) {
foreach ($podcast->getEpisodes() as $episode) {
if ($episode->isPublished()) {
return true;
@@ -77,7 +78,7 @@ class PodcastRepository extends Repository
}
public function writePodcastArt(
- Podcast $podcast,
+ Entity\Podcast $podcast,
string $rawArtworkString,
?ExtendedFilesystemInterface $fs = null
): void {
@@ -92,7 +93,7 @@ class PodcastRepository extends Repository
}
);
- $podcastArtworkPath = Podcast::getArtPath($podcast->getId());
+ $podcastArtworkPath = Entity\Podcast::getArtPath($podcast->getIdRequired());
$podcastArtworkStream = $podcastArtwork->stream('jpg');
$fs->writeStream($podcastArtworkPath, $podcastArtworkStream->detach());
@@ -101,12 +102,12 @@ class PodcastRepository extends Repository
}
public function removePodcastArt(
- Podcast $podcast,
+ Entity\Podcast $podcast,
?ExtendedFilesystemInterface $fs = null
): void {
$fs ??= $podcast->getStorageLocation()->getFilesystem();
- $artworkPath = Podcast::getArtPath($podcast->getId());
+ $artworkPath = Entity\Podcast::getArtPath($podcast->getIdRequired());
try {
$fs->delete($artworkPath);
@@ -117,7 +118,7 @@ class PodcastRepository extends Repository
}
public function delete(
- Podcast $podcast,
+ Entity\Podcast $podcast,
?ExtendedFilesystemInterface $fs = null
): void {
$fs ??= $podcast->getStorageLocation()->getFilesystem();
diff --git a/src/Entity/Repository/RolePermissionRepository.php b/src/Entity/Repository/RolePermissionRepository.php
index d5035a58b..5687a0b9d 100644
--- a/src/Entity/Repository/RolePermissionRepository.php
+++ b/src/Entity/Repository/RolePermissionRepository.php
@@ -1,10 +1,15 @@
+ */
class RolePermissionRepository extends Repository
{
/**
diff --git a/src/Entity/Repository/RoleRepository.php b/src/Entity/Repository/RoleRepository.php
index bed368ace..8f5b7389e 100644
--- a/src/Entity/Repository/RoleRepository.php
+++ b/src/Entity/Repository/RoleRepository.php
@@ -1,9 +1,15 @@
+ */
class RoleRepository extends Repository
{
diff --git a/src/Entity/Repository/SettingsRepository.php b/src/Entity/Repository/SettingsRepository.php
index c876d3178..58c93023c 100644
--- a/src/Entity/Repository/SettingsRepository.php
+++ b/src/Entity/Repository/SettingsRepository.php
@@ -1,5 +1,7 @@
0) {
$delta_positive += $delta_delta;
} elseif ($delta_delta < 0) {
- $delta_negative += abs($delta_delta);
+ $delta_negative += (int)abs($delta_delta);
}
}
diff --git a/src/Entity/Repository/StationMediaRepository.php b/src/Entity/Repository/StationMediaRepository.php
index 1b96671b4..e3e9e0d1b 100644
--- a/src/Entity/Repository/StationMediaRepository.php
+++ b/src/Entity/Repository/StationMediaRepository.php
@@ -1,5 +1,7 @@
findByUniqueId($id, $source);
if ($media instanceof Entity\StationMedia) {
return $media;
@@ -106,7 +108,7 @@ class StationMediaRepository extends Repository
return $media;
}
- public function iteratePaths(array $paths, $source): Generator
+ public function iteratePaths(array $paths, Entity\Station|Entity\StorageLocation $source): Generator
{
$storageLocation = $this->getStorageLocation($source);
diff --git a/src/Entity/Repository/StationMountRepository.php b/src/Entity/Repository/StationMountRepository.php
index a92f579eb..5b07c619a 100644
--- a/src/Entity/Repository/StationMountRepository.php
+++ b/src/Entity/Repository/StationMountRepository.php
@@ -1,5 +1,7 @@
+ */
class StationRepository extends Repository
{
public function __construct(
@@ -243,7 +248,7 @@ class StationRepository extends Repository
public function getDefaultAlbumArtUrl(?Entity\Station $station = null): UriInterface
{
if (null !== $station) {
- $stationCustomUrl = trim($station->getDefaultAlbumArtUrl());
+ $stationCustomUrl = trim($station->getDefaultAlbumArtUrl() ?? '');
if (!empty($stationCustomUrl)) {
return new Uri($stationCustomUrl);
@@ -251,7 +256,7 @@ class StationRepository extends Repository
}
$settings = $this->settingsRepo->readSettings();
- $custom_url = trim($settings->getDefaultAlbumArtUrl());
+ $custom_url = trim($settings->getDefaultAlbumArtUrl() ?? '');
if (!empty($custom_url)) {
return new Uri($custom_url);
diff --git a/src/Entity/Repository/StationRequestRepository.php b/src/Entity/Repository/StationRequestRepository.php
index fd884549d..ea03e4407 100644
--- a/src/Entity/Repository/StationRequestRepository.php
+++ b/src/Entity/Repository/StationRequestRepository.php
@@ -1,5 +1,7 @@
em->persist($record);
$this->em->flush();
- return $record->getId();
+ return $record->getIdRequired();
}
/**
@@ -209,7 +211,7 @@ class StationRequestRepository extends Repository
->getArrayResult();
$eligibleTrack = new Entity\Api\StationPlaylistQueue();
- $eligibleTrack->media_id = $media->getId();
+ $eligibleTrack->media_id = $media->getIdRequired();
$eligibleTrack->song_id = $media->getSongId();
$eligibleTrack->title = $media->getTitle() ?? '';
$eligibleTrack->artist = $media->getArtist() ?? '';
diff --git a/src/Entity/Repository/StationScheduleRepository.php b/src/Entity/Repository/StationScheduleRepository.php
index 8d0faf20d..31f6ceadd 100644
--- a/src/Entity/Repository/StationScheduleRepository.php
+++ b/src/Entity/Repository/StationScheduleRepository.php
@@ -1,5 +1,7 @@
+ */
class StationScheduleRepository extends Repository
{
protected Scheduler $scheduler;
@@ -30,9 +35,9 @@ class StationScheduleRepository extends Repository
/**
* @param Entity\StationPlaylist|Entity\StationStreamer $relation
- * @param array|null $items
+ * @param array $items
*/
- public function setScheduleItems(Entity\StationPlaylist|Entity\StationStreamer $relation, ?array $items): void
+ public function setScheduleItems(Entity\StationPlaylist|Entity\StationStreamer $relation, array $items = []): void
{
$rawScheduleItems = $this->findByRelation($relation);
@@ -135,21 +140,20 @@ class StationScheduleRepository extends Repository
}
$row = new Entity\Api\StationSchedule();
- $row->id = $scheduleItem->getId();
+ $row->id = $scheduleItem->getIdRequired();
$row->start_timestamp = $start->getTimestamp();
$row->start = $start->toIso8601String();
$row->end_timestamp = $end->getTimestamp();
$row->end = $end->toIso8601String();
$row->is_now = $start->lessThanOrEqualTo($now);
- if ($scheduleItem->getPlaylist() instanceof Entity\StationPlaylist) {
- $playlist = $scheduleItem->getPlaylist();
+ $playlist = $scheduleItem->getPlaylist();
+ $streamer = $scheduleItem->getStreamer();
+ if ($playlist instanceof Entity\StationPlaylist) {
$row->type = Entity\Api\StationSchedule::TYPE_PLAYLIST;
$row->name = $playlist->getName();
- } elseif ($scheduleItem->getStreamer() instanceof Entity\StationStreamer) {
- $streamer = $scheduleItem->getStreamer();
-
+ } elseif ($streamer instanceof Entity\StationStreamer) {
$row->type = Entity\Api\StationSchedule::TYPE_STREAMER;
$row->name = $streamer->getDisplayName();
}
diff --git a/src/Entity/Repository/StationStreamerBroadcastRepository.php b/src/Entity/Repository/StationStreamerBroadcastRepository.php
index f0a60ba17..852ac93f5 100644
--- a/src/Entity/Repository/StationStreamerBroadcastRepository.php
+++ b/src/Entity/Repository/StationStreamerBroadcastRepository.php
@@ -1,10 +1,15 @@
+ */
class StationStreamerBroadcastRepository extends Repository
{
public function getLatestBroadcast(Entity\Station $station): ?Entity\StationStreamerBroadcast
diff --git a/src/Entity/Repository/StationStreamerRepository.php b/src/Entity/Repository/StationStreamerRepository.php
index affa3e53b..f3a78e6de 100644
--- a/src/Entity/Repository/StationStreamerRepository.php
+++ b/src/Entity/Repository/StationStreamerRepository.php
@@ -1,5 +1,7 @@
+ */
class StorageLocationRepository extends Repository
{
public function findByType(string $type, int $id): ?Entity\StorageLocation
diff --git a/src/Entity/Repository/UnprocessableMediaRepository.php b/src/Entity/Repository/UnprocessableMediaRepository.php
index 45a51f816..4a46ff026 100644
--- a/src/Entity/Repository/UnprocessableMediaRepository.php
+++ b/src/Entity/Repository/UnprocessableMediaRepository.php
@@ -1,5 +1,7 @@
+ */
class UserRepository extends Repository
{
public function find(int $id): ?Entity\User
diff --git a/src/Entity/Role.php b/src/Entity/Role.php
index cf4d0284f..0c478051f 100644
--- a/src/Entity/Role.php
+++ b/src/Entity/Role.php
@@ -1,9 +1,10 @@
permissions as $permission) {
/** @var RolePermission $permission */
- if ($permission->hasStation()) {
- $return['permissions']['station'][$permission->getStation()->getId()][] = $permission->getActionName();
+ $station = $permission->getStation();
+ if (null !== $station) {
+ $return['permissions']['station'][$station->getIdRequired()][] = $permission->getActionName();
} else {
$return['permissions']['global'][] = $permission->getActionName();
}
diff --git a/src/Entity/RolePermission.php b/src/Entity/RolePermission.php
index dc77fb938..0795e5580 100644
--- a/src/Entity/RolePermission.php
+++ b/src/Entity/RolePermission.php
@@ -1,6 +1,6 @@
role = $role;
$this->station = $station;
diff --git a/src/Entity/Settings.php b/src/Entity/Settings.php
index 4b32f5655..90111427f 100644
--- a/src/Entity/Settings.php
+++ b/src/Entity/Settings.php
@@ -1,5 +1,7 @@
app_unique_identifier ?? null;
+ if (!isset($this->app_unique_identifier)) {
+ throw new \RuntimeException('Application Unique ID not generated yet.');
+ }
+
+ return $this->app_unique_identifier;
}
/**
@@ -418,7 +424,7 @@ class Settings implements Stringable
public function setLastFmApiKey(?string $lastFmApiKey): void
{
- $lastFmApiKey = trim($lastFmApiKey);
+ $lastFmApiKey = trim($lastFmApiKey ?? '');
$lastFmApiKey = (!empty($lastFmApiKey)) ? $lastFmApiKey : null;
$this->last_fm_api_key = $this->truncateNullableString($lastFmApiKey);
diff --git a/src/Entity/SftpUser.php b/src/Entity/SftpUser.php
index ebed1bdc7..2a1240396 100644
--- a/src/Entity/SftpUser.php
+++ b/src/Entity/SftpUser.php
@@ -1,10 +1,11 @@
publicKeys) {
+ return [];
+ }
+
$pubKeysRaw = trim($this->publicKeys);
if (!empty($pubKeysRaw)) {
return array_filter(array_map('trim', explode("\n", $pubKeysRaw)));
diff --git a/src/Entity/Song.php b/src/Entity/Song.php
index 4a551751e..61aa0a556 100644
--- a/src/Entity/Song.php
+++ b/src/Entity/Song.php
@@ -1,5 +1,7 @@
getText());
+ return self::getSongHash($songText->getText() ?? '');
}
if ($songText instanceof CurrentSong) {
return self::getSongHash($songText->text);
diff --git a/src/Entity/SongHistory.php b/src/Entity/SongHistory.php
index e069f5a36..fa69c3c1f 100644
--- a/src/Entity/SongHistory.php
+++ b/src/Entity/SongHistory.php
@@ -1,16 +1,17 @@
request;
}
- public function setRequest($request): void
+ public function setRequest(?StationRequest $request): void
{
$this->request = $request;
}
@@ -159,7 +160,7 @@ class SongHistory implements SongInterface
return $this->duration;
}
- public function setDuration($duration): void
+ public function setDuration(?int $duration): void
{
$this->duration = $duration;
}
@@ -169,7 +170,7 @@ class SongHistory implements SongInterface
return $this->listeners_start;
}
- public function setListenersStart($listeners_start): void
+ public function setListenersStart(?int $listeners_start): void
{
$this->listeners_start = $listeners_start;
}
@@ -198,7 +199,7 @@ class SongHistory implements SongInterface
return $this->listeners_end;
}
- public function setListenersEnd($listeners_end): void
+ public function setListenersEnd(?int $listeners_end): void
{
$this->listeners_end = $listeners_end;
}
@@ -208,7 +209,7 @@ class SongHistory implements SongInterface
return $this->unique_listeners;
}
- public function setUniqueListeners($unique_listeners): void
+ public function setUniqueListeners(?int $unique_listeners): void
{
$this->unique_listeners = $unique_listeners;
}
@@ -248,14 +249,12 @@ class SongHistory implements SongInterface
$this->delta_negative = $this->truncateSmallInt($delta_negative);
}
- /**
- */
public function getDeltaPoints(): mixed
{
return $this->delta_points;
}
- public function addDeltaPoint($delta_point): void
+ public function addDeltaPoint(mixed $delta_point): void
{
$delta_points = (array)$this->delta_points;
$delta_points[] = $delta_point;
diff --git a/src/Entity/Station.php b/src/Entity/Station.php
index 288d762bb..b5b475238 100644
--- a/src/Entity/Station.php
+++ b/src/Entity/Station.php
@@ -1,10 +1,10 @@
name;
}
- public function setName(?string $name = null): void
+ public function setName(string $name): void
{
- $this->name = $this->truncateNullableString($name, 100);
+ $this->name = $this->truncateString($name, 100);
if (empty($this->short_name) && !empty($name)) {
- $this->setShortName(null);
+ $this->setShortName(self::getStationShortName($name));
}
}
- public function getShortName(): ?string
+ public function getShortName(): string
{
return (!empty($this->short_name))
? $this->short_name
: self::getStationShortName($this->name);
}
- public function setShortName(?string $shortName): void
+ public function setShortName(string $shortName): void
{
$shortName = trim($shortName);
if (empty($shortName)) {
@@ -489,7 +489,7 @@ class Station implements Stringable
*/
public function validateAdapterApiKey(string $api_key): bool
{
- return hash_equals($api_key, $this->adapter_api_key);
+ return hash_equals($api_key, $this->adapter_api_key ?? '');
}
public function getDescription(): ?string
@@ -509,7 +509,7 @@ class Station implements Stringable
public function setUrl(string $url = null): void
{
- $this->url = $this->truncateString($url);
+ $this->url = $this->truncateNullableString($url);
}
public function getGenre(): ?string
@@ -522,14 +522,18 @@ class Station implements Stringable
$this->genre = $this->truncateNullableString($genre, 150);
}
- public function getRadioBaseDir(): ?string
+ public function getRadioBaseDir(): string
{
- return $this->radio_base_dir;
+ if (null === $this->radio_base_dir) {
+ $this->setRadioBaseDir();
+ }
+
+ return (string)$this->radio_base_dir;
}
public function setRadioBaseDir(?string $newDir = null): void
{
- $newDir = $this->truncateNullableString(trim($newDir));
+ $newDir = $this->truncateNullableString(trim($newDir ?? ''));
if (empty($newDir)) {
$stationsBaseDir = Environment::getInstance()->getStationDirectory();
@@ -541,10 +545,6 @@ class Station implements Stringable
public function ensureDirectoriesExist(): void
{
- if (null === $this->radio_base_dir) {
- $this->setRadioBaseDir();
- }
-
// Flysystem adapters will automatically create the main directory.
$this->ensureDirectoryExists($this->getRadioBaseDir());
$this->ensureDirectoryExists($this->getRadioPlaylistsDir());
@@ -700,12 +700,12 @@ class Station implements Stringable
$this->request_threshold = $request_threshold;
}
- public function getDisconnectDeactivateStreamer(): int
+ public function getDisconnectDeactivateStreamer(): ?int
{
return $this->disconnect_deactivate_streamer;
}
- public function setDisconnectDeactivateStreamer(int $disconnect_deactivate_streamer): void
+ public function setDisconnectDeactivateStreamer(?int $disconnect_deactivate_streamer): void
{
$this->disconnect_deactivate_streamer = $disconnect_deactivate_streamer;
}
@@ -794,7 +794,7 @@ class Station implements Stringable
return $this->api_history_items ?? self::DEFAULT_API_HISTORY_ITEMS;
}
- public function setApiHistoryItems(?int $api_history_items): void
+ public function setApiHistoryItems(int $api_history_items): void
{
$this->api_history_items = $api_history_items;
}
@@ -831,11 +831,17 @@ class Station implements Stringable
$this->default_album_art_url = $default_album_art_url;
}
+ /**
+ * @return Collection
+ */
public function getHistory(): Collection
{
return $this->history;
}
+ /**
+ * @return Collection
+ */
public function getStreamers(): Collection
{
return $this->streamers;
@@ -855,6 +861,10 @@ class Station implements Stringable
public function getMediaStorageLocation(): StorageLocation
{
+ if (null === $this->media_storage_location) {
+ throw new \RuntimeException('Media storage location has not been configured yet.');
+ }
+
return $this->media_storage_location;
}
@@ -869,6 +879,10 @@ class Station implements Stringable
public function getRecordingsStorageLocation(): StorageLocation
{
+ if (null === $this->recordings_storage_location) {
+ throw new \RuntimeException('Recordings storage location has not been configured yet.');
+ }
+
return $this->recordings_storage_location;
}
@@ -883,6 +897,10 @@ class Station implements Stringable
public function getPodcastsStorageLocation(): StorageLocation
{
+ if (null === $this->podcasts_storage_location) {
+ throw new \RuntimeException('Podcasts storage location has not been configured yet.');
+ }
+
return $this->podcasts_storage_location;
}
@@ -905,21 +923,24 @@ class Station implements Stringable
];
}
+ /**
+ * @return Collection
+ */
public function getPermissions(): Collection
{
return $this->permissions;
}
/**
- * @return StationMedia[]|Collection
+ * @return Collection
*/
public function getMedia(): Collection
{
- return $this->media_storage_location->getMedia();
+ return $this->getMediaStorageLocation()->getMedia();
}
/**
- * @return StationPlaylist[]|Collection
+ * @return Collection
*/
public function getPlaylists(): Collection
{
@@ -927,7 +948,7 @@ class Station implements Stringable
}
/**
- * @return StationMount[]|Collection
+ * @return Collection
*/
public function getMounts(): Collection
{
@@ -935,18 +956,24 @@ class Station implements Stringable
}
/**
- * @return StationRemote[]|Collection
+ * @return Collection
*/
public function getRemotes(): Collection
{
return $this->remotes;
}
+ /**
+ * @return Collection
+ */
public function getWebhooks(): Collection
{
return $this->webhooks;
}
+ /**
+ * @return Collection
+ */
public function getSftpUsers(): Collection
{
return $this->sftp_users;
@@ -972,7 +999,7 @@ class Station implements Stringable
public function __clone()
{
$this->id = null;
- $this->short_name = null;
+ $this->short_name = '';
$this->radio_base_dir = null;
$this->adapter_api_key = null;
$this->nowplaying = null;
diff --git a/src/Entity/StationBackendConfiguration.php b/src/Entity/StationBackendConfiguration.php
index 71908e70f..30af5b1b4 100644
--- a/src/Entity/StationBackendConfiguration.php
+++ b/src/Entity/StationBackendConfiguration.php
@@ -1,5 +1,7 @@
isrc = $this->truncateNullableString($isrc, 15);
}
- public function getLength(): float
+ public function getLength(): ?float
{
return $this->length;
}
@@ -356,12 +358,9 @@ class StationMedia implements SongInterface, ProcessableMediaInterface, PathAwar
return $this->fade_in;
}
- /**
- * @param string|float|null $fade_in
- */
- public function setFadeIn($fade_in = null): void
+ public function setFadeIn(string|int|float $fade_in = null): void
{
- $this->fade_in = $this->parseSeconds($fade_in);
+ $this->fade_in = Time::displayTimeToSeconds($fade_in);
}
public function getFadeOut(): ?float
@@ -369,12 +368,9 @@ class StationMedia implements SongInterface, ProcessableMediaInterface, PathAwar
return $this->fade_out;
}
- /**
- * @param string|float|null $fade_out
- */
- public function setFadeOut($fade_out = null): void
+ public function setFadeOut(string|int|float $fade_out = null): void
{
- $this->fade_out = $this->parseSeconds($fade_out);
+ $this->fade_out = Time::displayTimeToSeconds($fade_out);
}
public function getCueIn(): ?float
@@ -382,12 +378,9 @@ class StationMedia implements SongInterface, ProcessableMediaInterface, PathAwar
return $this->cue_in;
}
- /**
- * @param string|float|null $cue_in
- */
- public function setCueIn($cue_in = null): void
+ public function setCueIn(string|int|float $cue_in = null): void
{
- $this->cue_in = $this->parseSeconds($cue_in);
+ $this->cue_in = Time::displayTimeToSeconds($cue_in);
}
public function getCueOut(): ?float
@@ -395,33 +388,9 @@ class StationMedia implements SongInterface, ProcessableMediaInterface, PathAwar
return $this->cue_out;
}
- /**
- * @param string|float|null $cue_out
- */
- public function setCueOut($cue_out = null): void
+ public function setCueOut(string|int|float $cue_out = null): void
{
- $this->cue_out = $this->parseSeconds($cue_out);
- }
-
- /**
- * @param string|float|null $seconds
- */
- protected function parseSeconds($seconds = null): ?float
- {
- if ($seconds === '') {
- return null;
- }
-
- if (str_contains($seconds, ':')) {
- $sec = 0;
- foreach (array_reverse(explode(':', $seconds)) as $k => $v) {
- $sec += (60 ** (int)$k) * (int)$v;
- }
-
- return $sec;
- }
-
- return $seconds;
+ $this->cue_out = Time::displayTimeToSeconds($cue_out);
}
/**
@@ -439,7 +408,7 @@ class StationMedia implements SongInterface, ProcessableMediaInterface, PathAwar
$length -= $this->cue_in;
}
- return $length;
+ return (int)$length;
}
public function getArtUpdatedAt(): int
@@ -493,7 +462,7 @@ class StationMedia implements SongInterface, ProcessableMediaInterface, PathAwar
public function fromMetadata(Metadata $metadata): void
{
- $this->setLength($metadata->getDuration());
+ $this->setLength((int)$metadata->getDuration());
$tags = $metadata->getTags();
@@ -522,7 +491,7 @@ class StationMedia implements SongInterface, ProcessableMediaInterface, PathAwar
public function toMetadata(): Metadata
{
$metadata = new Metadata();
- $metadata->setDuration($this->getLength());
+ $metadata->setDuration($this->getLength() ?? 0.0);
$tagsToSet = array_filter(
[
diff --git a/src/Entity/StationMediaCustomField.php b/src/Entity/StationMediaCustomField.php
index 752e1c2c4..f3151d5e3 100644
--- a/src/Entity/StationMediaCustomField.php
+++ b/src/Entity/StationMediaCustomField.php
@@ -1,16 +1,17 @@
enable_autodj) {
- return $this->autodj_bitrate . 'kbps ' . strtoupper($this->autodj_format);
+ return $this->autodj_bitrate . 'kbps ' . strtoupper($this->autodj_format ?? '');
}
return $this->name;
@@ -348,7 +351,7 @@ class StationMount implements Stringable, Interfaces\StationMountInterface, Inte
): Api\StationMount {
$response = new Api\StationMount();
- $response->id = $this->id;
+ $response->id = $this->getIdRequired();
$response->name = $this->getDisplayName();
$response->path = $this->getName();
$response->is_default = $this->is_default;
diff --git a/src/Entity/StationPlaylist.php b/src/Entity/StationPlaylist.php
index acda9ae60..9cdc69ce6 100644
--- a/src/Entity/StationPlaylist.php
+++ b/src/Entity/StationPlaylist.php
@@ -1,6 +1,6 @@
backend_options);
+ return explode(',', $this->backend_options ?? '');
}
/**
diff --git a/src/Entity/StationPlaylistFolder.php b/src/Entity/StationPlaylistFolder.php
index 2da2bf789..69c58f35b 100644
--- a/src/Entity/StationPlaylistFolder.php
+++ b/src/Entity/StationPlaylistFolder.php
@@ -1,6 +1,6 @@
request;
}
- public function setRequest($request): void
+ public function setRequest(?StationRequest $request): void
{
$this->request = $request;
}
diff --git a/src/Entity/StationRemote.php b/src/Entity/StationRemote.php
index b2975f21c..86f34fa20 100644
--- a/src/Entity/StationRemote.php
+++ b/src/Entity/StationRemote.php
@@ -1,6 +1,6 @@
custom_listen_url;
}
- public function setCustomListenUrl(string $custom_listen_url = null): void
+ public function setCustomListenUrl(?string $custom_listen_url = null): void
{
- $this->custom_listen_url = $this->truncateString($custom_listen_url);
+ $this->custom_listen_url = $this->truncateNullableString($custom_listen_url);
}
- /** @inheritdoc */
public function getAutodjUsername(): ?string
{
return $this->getSourceUsername();
@@ -212,7 +214,6 @@ class StationRemote implements Stringable, Interfaces\StationMountInterface, Int
$this->source_username = $this->truncateNullableString($source_username, 100);
}
- /** @inheritdoc */
public function getAutodjPassword(): ?string
{
$password = $this->getSourcePassword();
@@ -296,25 +297,29 @@ class StationRemote implements Stringable, Interfaces\StationMountInterface, Int
return $this->getMount();
}
- /** @inheritdoc */
public function getAutodjHost(): ?string
{
- return parse_url($this->getUrl(), PHP_URL_HOST);
+ return $this->getUrlAsUri()->getHost();
}
- public function getUrl(): ?string
+ public function getUrl(): string
{
return $this->url;
}
- public function setUrl(?string $url): void
+ public function getUrlAsUri(): UriInterface
+ {
+ return new Uri($this->url);
+ }
+
+ public function setUrl(string $url): void
{
if (!empty($url) && !str_starts_with($url, 'http')) {
/** @noinspection HttpUrlsUsage */
$url = 'http://' . $url;
}
- $this->url = $this->truncateNullableString($url);
+ $this->url = $this->truncateString($url);
}
/*
@@ -324,7 +329,7 @@ class StationRemote implements Stringable, Interfaces\StationMountInterface, Int
/** @inheritdoc */
public function getAutodjPort(): ?int
{
- return $this->getSourcePort() ?? parse_url($this->getUrl(), PHP_URL_PORT);
+ return $this->getSourcePort() ?? $this->getUrlAsUri()->getPort();
}
public function getSourcePort(): ?int
@@ -347,7 +352,7 @@ class StationRemote implements Stringable, Interfaces\StationMountInterface, Int
return self::PROTOCOL_ICY;
}
- $urlScheme = parse_url($this->getUrl(), PHP_URL_SCHEME);
+ $urlScheme = $this->getUrlAsUri()->getScheme();
return ('https' === $urlScheme)
? self::PROTOCOL_HTTPS
: self::PROTOCOL_HTTP;
@@ -406,7 +411,7 @@ class StationRemote implements Stringable, Interfaces\StationMountInterface, Int
): Api\StationRemote {
$response = new Api\StationRemote();
- $response->id = $this->id;
+ $response->id = $this->getIdRequired();
$response->name = $this->getDisplayName();
$response->url = $adapter->getPublicUrl($this);
@@ -430,7 +435,7 @@ class StationRemote implements Stringable, Interfaces\StationMountInterface, Int
}
if ($this->enable_autodj) {
- return $this->autodj_bitrate . 'kbps ' . strtoupper($this->autodj_format);
+ return $this->autodj_bitrate . 'kbps ' . strtoupper($this->autodj_format ?? '');
}
return Utilities\Strings::truncateUrl($this->url);
diff --git a/src/Entity/StationRequest.php b/src/Entity/StationRequest.php
index d2b24102c..147a34561 100644
--- a/src/Entity/StationRequest.php
+++ b/src/Entity/StationRequest.php
@@ -1,9 +1,10 @@
days = implode(',', (array)$days);
+ $this->days = implode(',', $days);
}
public function getLoopOnce(): bool
@@ -238,13 +239,13 @@ class StationSchedule
$now = CarbonImmutable::now(new DateTimeZone('UTC'));
}
- $timeCode = str_pad($timeCode, 4, '0', STR_PAD_LEFT);
+ $timeCode = str_pad((string)$timeCode, 4, '0', STR_PAD_LEFT);
return $now->setTime((int)substr($timeCode, 0, 2), (int)substr($timeCode, 2));
}
- public static function displayTimeCode($timeCode): string
+ public static function displayTimeCode(string|int $timeCode): string
{
- $timeCode = str_pad($timeCode, 4, '0', STR_PAD_LEFT);
+ $timeCode = str_pad((string)$timeCode, 4, '0', STR_PAD_LEFT);
$hours = (int)substr($timeCode, 0, 2);
$mins = substr($timeCode, 2);
diff --git a/src/Entity/StationStreamer.php b/src/Entity/StationStreamer.php
index 4845b750c..62ab43fcc 100644
--- a/src/Entity/StationStreamer.php
+++ b/src/Entity/StationStreamer.php
@@ -1,10 +1,9 @@
streamer_password = password_hash($streamer_password, PASSWORD_ARGON2ID);
diff --git a/src/Entity/StationStreamerBroadcast.php b/src/Entity/StationStreamerBroadcast.php
index 484569fb9..1c1d86a8a 100644
--- a/src/Entity/StationStreamerBroadcast.php
+++ b/src/Entity/StationStreamerBroadcast.php
@@ -1,9 +1,10 @@
'utf8mb4', 'collate' => 'utf8mb4_unicode_ci']),
Attributes\Auditable
]
-class StationWebhook implements Stringable, Interfaces\StationCloneAwareInterface
+class StationWebhook implements
+ Stringable,
+ Interfaces\StationCloneAwareInterface,
+ Interfaces\IdentifiableEntityInterface
{
use Traits\HasAutoIncrementId;
use Traits\TruncateStrings;
@@ -94,7 +97,7 @@ class StationWebhook implements Stringable, Interfaces\StationCloneAwareInterfac
#[Attributes\AuditIgnore]
protected ?array $metadata = null;
- public function __construct(Station $station, $type)
+ public function __construct(Station $station, string $type)
{
$this->station = $station;
$this->type = $type;
diff --git a/src/Entity/StorageLocation.php b/src/Entity/StorageLocation.php
index 6e0db44c6..97f3aa6e3 100644
--- a/src/Entity/StorageLocation.php
+++ b/src/Entity/StorageLocation.php
@@ -1,9 +1,10 @@
adapter;
}
- public function getPath(): ?string
+ public function getPath(): string
{
return $this->path;
}
- public function getFilteredPath(): ?string
+ public function getFilteredPath(): string
{
return match ($this->adapter) {
self::ADAPTER_S3, self::ADAPTER_DROPBOX => trim($this->path, '/'),
@@ -143,9 +144,9 @@ class StorageLocation implements Stringable
return $this->path . $suffix;
}
- public function setPath(?string $path): void
+ public function setPath(string $path): void
{
- $this->path = $this->truncateNullableString($path);
+ $this->path = $this->truncateString($path);
}
public function getS3CredentialKey(): ?string
@@ -383,14 +384,19 @@ class StorageLocation implements Stringable
{
$path = $this->applyPath($suffix);
+ $bucket = $this->s3Bucket;
+ if (null === $bucket) {
+ return 'No S3 Bucket Specified';
+ }
+
try {
$client = $this->getS3Client();
if (empty($path)) {
- $objectUrl = $client->getObjectUrl($this->s3Bucket, '/');
+ $objectUrl = $client->getObjectUrl($bucket, '/');
return rtrim($objectUrl, '/');
}
- return $client->getObjectUrl($this->s3Bucket, ltrim($path, '/'));
+ return $client->getObjectUrl($bucket, ltrim($path, '/'));
} catch (InvalidArgumentException $e) {
return 'Invalid URI (' . $e->getMessage() . ')';
}
@@ -416,11 +422,20 @@ class StorageLocation implements Stringable
{
$filteredPath = $this->getFilteredPath();
- return match ($this->adapter) {
- self::ADAPTER_S3 => new AwsS3Adapter($this->getS3Client(), $this->s3Bucket, $filteredPath),
- self::ADAPTER_DROPBOX => new DropboxAdapter($this->getDropboxClient(), $filteredPath),
- default => new LocalFilesystemAdapter($filteredPath)
- };
+ switch ($this->adapter) {
+ case self::ADAPTER_S3:
+ $bucket = $this->s3Bucket;
+ if (null === $bucket) {
+ throw new \RuntimeException('Amazon S3 bucket is empty.');
+ }
+ return new AwsS3Adapter($this->getS3Client(), $bucket, $filteredPath);
+
+ case self::ADAPTER_DROPBOX:
+ return new DropboxAdapter($this->getDropboxClient(), $filteredPath);
+
+ default:
+ return new LocalFilesystemAdapter($filteredPath);
+ }
}
protected function getS3Client(): S3Client
diff --git a/src/Entity/Traits/HasAutoIncrementId.php b/src/Entity/Traits/HasAutoIncrementId.php
index 38c4e0d78..6f02b92eb 100644
--- a/src/Entity/Traits/HasAutoIncrementId.php
+++ b/src/Entity/Traits/HasAutoIncrementId.php
@@ -1,5 +1,7 @@
id;
}
+
+ public function getIdRequired(): int
+ {
+ if (null === $this->id) {
+ throw new \RuntimeException('An ID was not generated for this object.');
+ }
+
+ return $this->id;
+ }
}
diff --git a/src/Entity/Traits/HasSongFields.php b/src/Entity/Traits/HasSongFields.php
index a25a211e2..312c8e1a9 100644
--- a/src/Entity/Traits/HasSongFields.php
+++ b/src/Entity/Traits/HasSongFields.php
@@ -1,5 +1,7 @@
song_id = Song::getSongHash($this->getText());
+ $text = $this->getText();
+ $this->song_id = (null !== $text)
+ ? Song::getSongHash($text)
+ : Song::createOffline()->getSongId();
}
public function getText(): ?string
@@ -65,7 +70,7 @@ trait HasSongFields
$oldText = $this->text;
$this->text = $this->truncateNullableString($text, 303);
- if (0 !== strcmp($oldText, $this->text)) {
+ if (0 !== strcmp($oldText ?? '', $this->text ?? '')) {
$this->updateSongId();
}
}
@@ -80,7 +85,7 @@ trait HasSongFields
$oldArtist = $this->artist;
$this->artist = $this->truncateNullableString($artist, 150);
- if (0 !== strcmp($oldArtist, $this->artist)) {
+ if (0 !== strcmp($oldArtist ?? '', $this->artist ?? '')) {
$this->setTextFromArtistAndTitle();
}
}
@@ -95,7 +100,7 @@ trait HasSongFields
$oldTitle = $this->title;
$this->title = $this->truncateNullableString($title, 150);
- if (0 !== strcmp($oldTitle, $this->title)) {
+ if (0 !== strcmp($oldTitle ?? '', $this->title ?? '')) {
$this->setTextFromArtistAndTitle();
}
}
diff --git a/src/Entity/Traits/HasSplitTokenFields.php b/src/Entity/Traits/HasSplitTokenFields.php
index d2ffdbbc3..d91d2436f 100644
--- a/src/Entity/Traits/HasSplitTokenFields.php
+++ b/src/Entity/Traits/HasSplitTokenFields.php
@@ -1,5 +1,7 @@
id;
}
+
+ public function getIdRequired(): string
+ {
+ if (null === $this->id) {
+ throw new \RuntimeException('An ID was not generated for this object.');
+ }
+
+ return $this->id;
+ }
}
diff --git a/src/Entity/Traits/TruncateInts.php b/src/Entity/Traits/TruncateInts.php
index 5a2a73811..4c0585944 100644
--- a/src/Entity/Traits/TruncateInts.php
+++ b/src/Entity/Traits/TruncateInts.php
@@ -1,5 +1,7 @@
name = $this->truncateNullableString($name, 100);
}
- public function getEmail(): ?string
+ public function getEmail(): string
{
return $this->email;
}
- public function setEmail(?string $email = null): void
+ public function setEmail(string $email): void
{
- $this->email = $this->truncateNullableString($email, 100);
+ $this->email = $this->truncateString($email, 100);
}
public function verifyPassword(string $password): bool
@@ -154,9 +156,9 @@ class User implements Stringable
return [PASSWORD_BCRYPT, []];
}
- public function setNewPassword(string $password): void
+ public function setNewPassword(?string $password): void
{
- if (trim($password)) {
+ if (null !== $password && trim($password)) {
[$algo, $algo_opts] = $this->getPasswordAlgorithm();
$this->auth_password = password_hash($password, $algo, $algo_opts);
}
@@ -164,7 +166,7 @@ class User implements Stringable
public function generateRandomPassword(): void
{
- $this->setNewPassword(bin2hex(random_bytes(20)));
+ $this->setNewPassword(Strings::generatePassword());
}
public function getLocale(): ?string
@@ -217,7 +219,7 @@ class User implements Stringable
}
/**
- * @return Collection|Role[]
+ * @return Collection
*/
public function getRoles(): Collection
{
@@ -225,7 +227,7 @@ class User implements Stringable
}
/**
- * @return Collection|ApiKey[]
+ * @return Collection
*/
public function getApiKeys(): Collection
{
diff --git a/src/Entity/UserLoginToken.php b/src/Entity/UserLoginToken.php
index d44a19f41..39a121bf1 100644
--- a/src/Entity/UserLoginToken.php
+++ b/src/Entity/UserLoginToken.php
@@ -1,6 +1,6 @@
data;
}
- protected function envToBool(string|bool $value): bool
+ protected function envToBool(string|bool|int $value): bool
{
if (is_bool($value)) {
return $value;
}
+ if (is_int($value)) {
+ return 0 !== $value;
+ }
return str_starts_with(strtolower($value), 'y')
|| 'true' === strtolower($value)
diff --git a/src/Event/AbstractBuildMenu.php b/src/Event/AbstractBuildMenu.php
index 413d7a0af..34e91dffe 100644
--- a/src/Event/AbstractBuildMenu.php
+++ b/src/Event/AbstractBuildMenu.php
@@ -1,5 +1,7 @@
The fully-qualified (::class) class name of the entity being managed. */
protected string $entityClass;
/** @var array The default context sent to form normalization/denormalization functions. */
@@ -42,11 +47,17 @@ class EntityForm extends Form
parent::__construct($options, $defaults);
}
+ /**
+ * @return class-string
+ */
public function getEntityClass(): string
{
return $this->entityClass;
}
+ /**
+ * @param class-string $entityClass
+ */
public function setEntityClass(string $entityClass): void
{
$this->entityClass = $entityClass;
@@ -57,6 +68,9 @@ class EntityForm extends Form
return $this->em;
}
+ /**
+ * @return ObjectRepository
+ */
public function getEntityRepository(): ObjectRepository
{
if (!isset($this->entityClass)) {
@@ -68,11 +82,11 @@ class EntityForm extends Form
/**
* @param ServerRequest $request
- * @param object|null $record
+ * @param TEntity|null $record
*
- * @return object|bool The modified object if edited/created, or `false` if not processed.
+ * @return TEntity|bool The modified object if edited/created, or `false` if not processed.
*/
- public function process(ServerRequest $request, $record = null): object|bool
+ public function process(ServerRequest $request, ?object $record = null): object|bool
{
if (!isset($this->entityClass)) {
throw new Exception('Entity class name is not specified.');
@@ -88,7 +102,7 @@ class EntityForm extends Form
}
// Handle submission.
- if ('POST' === $request->getMethod() && $this->isValid($request->getParsedBody())) {
+ if ($this->isValid($request)) {
$data = $this->getValues();
$record = $this->denormalizeToRecord($data, $record);
@@ -100,9 +114,9 @@ class EntityForm extends Form
$field_name = $error->getPropertyPath();
if (isset($this->fields[$field_name])) {
- $this->fields[$field_name]->addError($error->getMessage());
+ $this->fields[$field_name]->addError((string)$error->getMessage());
} else {
- $this->addError($error->getMessage());
+ $this->addError((string)$error->getMessage());
}
}
return false;
@@ -125,7 +139,7 @@ class EntityForm extends Form
/**
* The old ->toArray().
*
- * @param object $record
+ * @param TEntity $record
* @param array $context
*
* @return mixed[]
@@ -147,17 +161,17 @@ class EntityForm extends Form
) {
return $this->displayShortenedObject($innerObject);
},
- ObjectNormalizer::CIRCULAR_REFERENCE_HANDLER => function (
- $object,
- string $format = null,
- array $context = []
- ) {
- return $this->displayShortenedObject($object);
- },
+ ObjectNormalizer::CIRCULAR_REFERENCE_HANDLER => function (
+ $object,
+ string $format = null,
+ array $context = []
+ ) {
+ return $this->displayShortenedObject($object);
+ },
]
);
- return $this->serializer->normalize($record, null, $context);
+ return (array)$this->serializer->normalize($record, null, $context);
}
/**
@@ -170,17 +184,27 @@ class EntityForm extends Form
return $object->getName();
}
- return $object->getId();
+ if ($object instanceof IdentifiableEntityInterface) {
+ return $object->getIdRequired();
+ }
+
+ if ($object instanceof \Stringable) {
+ return (string)$object;
+ }
+
+ return get_class($object) . ': ' . spl_object_hash($object);
}
/**
* The old ->fromArray().
*
* @param array $data
- * @param object|null $record
+ * @param TEntity|null $record
* @param array $context
+ *
+ * @return TEntity
*/
- protected function denormalizeToRecord(array $data, $record = null, array $context = []): object
+ protected function denormalizeToRecord(array $data, ?object $record = null, array $context = []): object
{
$context = array_merge($this->defaultContext, $context);
diff --git a/src/Form/Field/Csrf.php b/src/Form/Field/Csrf.php
index 8a59f41bc..7f5d6de21 100644
--- a/src/Form/Field/Csrf.php
+++ b/src/Form/Field/Csrf.php
@@ -1,5 +1,7 @@
+ */
class PermissionsForm extends EntityForm
{
protected bool $set_permissions = true;
diff --git a/src/Form/SettingsForm.php b/src/Form/SettingsForm.php
index d06474278..b4619265f 100644
--- a/src/Form/SettingsForm.php
+++ b/src/Form/SettingsForm.php
@@ -1,5 +1,7 @@
getMethod() && $this->isValid($request->getParsedBody())) {
+ if ($this->isValid($request)) {
$data = $this->getValues();
$toClone = $data['clone'];
diff --git a/src/Form/StationForm.php b/src/Form/StationForm.php
index 85881b17f..451999737 100644
--- a/src/Form/StationForm.php
+++ b/src/Form/StationForm.php
@@ -1,5 +1,7 @@
+ */
class StationForm extends EntityForm
{
public function __construct(
@@ -75,7 +80,7 @@ class StationForm extends EntityForm
if (!isset($installedFrontends[Adapters::FRONTEND_SHOUTCAST])) {
$frontendDesc = __(
'Want to use SHOUTcast 2? Install it here , then reload this page.',
- $request->getRouter()->named('admin:install_shoutcast:index')
+ (string)$request->getRouter()->named('admin:install_shoutcast:index')
);
$this->getField('frontend_type')->setOption('description', $frontendDesc);
@@ -94,7 +99,7 @@ class StationForm extends EntityForm
if ($canSeeAdministration) {
$storageLocationsDesc = __(
'Manage storage locations and storage quota here .',
- $request->getRouter()->named('admin:storage_locations:index')
+ (string)$request->getRouter()->named('admin:storage_locations:index')
);
if ($this->hasField('media_storage_location_id')) {
@@ -137,7 +142,7 @@ class StationForm extends EntityForm
}
}
- if ('POST' === $request->getMethod() && $this->isValid($request->getParsedBody())) {
+ if ($this->isValid($request)) {
$data = $this->getValues();
/** @var Entity\Station $record */
@@ -145,28 +150,40 @@ class StationForm extends EntityForm
if ($canSeeAdministration) {
if (!empty($data['media_storage_location_id'])) {
- $record->setMediaStorageLocation(
- $this->storageLocationRepo->findByType(
- Entity\StorageLocation::TYPE_STATION_MEDIA,
- $data['media_storage_location_id']
- )
+ $sl = $this->storageLocationRepo->findByType(
+ Entity\StorageLocation::TYPE_STATION_MEDIA,
+ (int)$data['media_storage_location_id']
);
+
+ if (null === $sl) {
+ $this->addError('Media storage location not found.');
+ } else {
+ $record->setMediaStorageLocation($sl);
+ }
}
if (!empty($data['recordings_storage_location_id'])) {
- $record->setRecordingsStorageLocation(
- $this->storageLocationRepo->findByType(
- Entity\StorageLocation::TYPE_STATION_RECORDINGS,
- $data['recordings_storage_location_id']
- )
+ $sl = $this->storageLocationRepo->findByType(
+ Entity\StorageLocation::TYPE_STATION_RECORDINGS,
+ (int)$data['recordings_storage_location_id']
);
+
+ if (null === $sl) {
+ $this->addError('Recordings storage location not found.');
+ } else {
+ $record->setRecordingsStorageLocation($sl);
+ }
}
if (!empty($data['podcasts_storage_location_id'])) {
- $record->setPodcastsStorageLocation(
- $this->storageLocationRepo->findByType(
- Entity\StorageLocation::TYPE_STATION_PODCASTS,
- $data['podcasts_storage_location_id']
- )
+ $sl = $this->storageLocationRepo->findByType(
+ Entity\StorageLocation::TYPE_STATION_PODCASTS,
+ (int)$data['podcasts_storage_location_id']
);
+
+ if (null === $sl) {
+ $this->addError('Podcasts storage location not found.');
+ } else {
+ $record->setPodcastsStorageLocation($sl);
+ }
}
}
@@ -177,9 +194,9 @@ class StationForm extends EntityForm
$field_name = $error->getPropertyPath();
if (isset($this->fields[$field_name])) {
- $this->fields[$field_name]->addError($error->getMessage());
+ $this->fields[$field_name]->addError((string)$error->getMessage());
} else {
- $this->addError($error->getMessage());
+ $this->addError((string)$error->getMessage());
}
}
return false;
diff --git a/src/Form/StationRemoteForm.php b/src/Form/StationRemoteForm.php
index 02ae1f9c3..0f88a7841 100644
--- a/src/Form/StationRemoteForm.php
+++ b/src/Form/StationRemoteForm.php
@@ -1,5 +1,7 @@
getPrevious();
+ $exception = $exception->getPrevious() ?? $exception;
}
if ($exception instanceof Exception) {
@@ -253,12 +255,4 @@ class ErrorHandler extends \Slim\Handlers\ErrorHandler
return parent::respond();
}
}
-
- protected function withJson(ResponseInterface $response, $data): ResponseInterface
- {
- $json = (string)json_encode($data, JSON_THROW_ON_ERROR);
- $response->getBody()->write($json);
-
- return $response->withHeader('Content-Type', 'application/json;charset=utf-8');
- }
}
diff --git a/src/Http/Factory/ResponseFactory.php b/src/Http/Factory/ResponseFactory.php
index d87f13219..5cff1192e 100644
--- a/src/Http/Factory/ResponseFactory.php
+++ b/src/Http/Factory/ResponseFactory.php
@@ -1,5 +1,7 @@
withHeader('Pragma', 'public')
->withHeader('Expires', '0')
->withHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
- ->withHeader('Content-Type', mime_content_type($file_path))
+ ->withHeader('Content-Type', mime_content_type($file_path) ?: '')
->withHeader('Content-Length', (string)filesize($file_path))
->withHeader('Content-Disposition', 'attachment; filename=' . $file_name)
->withBody($stream);
@@ -117,7 +119,7 @@ final class Response extends \Slim\Http\Response
*
* @return static
*/
- public function renderStringAsFile(string $file_data, string $content_type, $file_name = null): static
+ public function renderStringAsFile(string $file_data, string $content_type, ?string $file_name = null): static
{
$response = $this->response
->withHeader('Pragma', 'public')
@@ -189,7 +191,7 @@ final class Response extends \Slim\Http\Response
}
$response = $this->withHeader('Content-Disposition', $disposition)
- ->withHeader('Content-Length', $fileMeta->fileSize())
+ ->withHeader('Content-Length', (string)$fileMeta->fileSize())
->withHeader('X-Accel-Buffering', 'no');
$adapter = $filesystem->getAdapter();
@@ -206,12 +208,12 @@ final class Response extends \Slim\Http\Response
if (str_starts_with($localPath, $diskPath)) {
$accelPath = str_replace($diskPath, $nginxPath, $localPath);
- return $response->withHeader('Content-Type', $fileMeta->mimeType())
+ return $response->withHeader('Content-Type', $fileMeta->mimeType() ?? '')
->withHeader('X-Accel-Redirect', $accelPath);
}
}
}
- return $response->withFile($filesystem->readStream($path), $fileMeta->mimeType());
+ return $response->withFile($filesystem->readStream($path), $fileMeta->mimeType() ?? '');
}
}
diff --git a/src/Http/Router.php b/src/Http/Router.php
index db54f5202..55ba0b952 100644
--- a/src/Http/Router.php
+++ b/src/Http/Router.php
@@ -1,5 +1,7 @@
request = $request;
$this->baseUrl = null;
- $this->currentRequest = $request;
}
public function getBaseUrl(bool $useRequest = true): UriInterface
@@ -62,8 +62,8 @@ class Router implements RouterInterface
$useHttps = $settings->getAlwaysUseSsl();
- if ($this->currentRequest instanceof ServerRequestInterface) {
- $currentUri = $this->currentRequest->getUri();
+ if ($this->request instanceof ServerRequestInterface) {
+ $currentUri = $this->request->getUri();
if ('https' === $currentUri->getScheme()) {
$useHttps = true;
@@ -99,54 +99,44 @@ class Router implements RouterInterface
}
/**
- * Same as $this->fromHere(), but merging the current GET query parameters into the request as well.
- *
- * @param null $route_name
- * @param array $route_params
- * @param array $query_params
- * @param bool $absolute
+ * @inheritDoc
*/
public function fromHereWithQuery(
- $route_name = null,
+ ?string $route_name = null,
array $route_params = [],
array $query_params = [],
- $absolute = false
- ): string {
- if ($this->currentRequest instanceof ServerRequestInterface) {
- $query_params = array_merge($this->currentRequest->getQueryParams(), $query_params);
+ bool $absolute = false
+ ): UriInterface {
+ if ($this->request instanceof ServerRequestInterface) {
+ $query_params = array_merge($this->request->getQueryParams(), $query_params);
}
return $this->fromHere($route_name, $route_params, $query_params, $absolute);
}
/**
- * Return a named route based on the current page and its route arguments.
- *
- * @param null $route_name
- * @param array $route_params
- * @param array $query_params
- * @param bool $absolute
+ * @inheritDoc
*/
public function fromHere(
- $route_name = null,
+ ?string $route_name = null,
array $route_params = [],
array $query_params = [],
- $absolute = false
- ): string {
- if ($this->currentRequest instanceof ServerRequestInterface) {
- $route = RouteContext::fromRequest($this->currentRequest)->getRoute();
+ bool $absolute = false
+ ): UriInterface {
+ if ($this->request instanceof ServerRequestInterface) {
+ $route = RouteContext::fromRequest($this->request)->getRoute();
} else {
$route = null;
}
- if ($route_name === null) {
- if ($route instanceof RouteInterface) {
- $route_name = $route->getName();
- } else {
- throw new InvalidArgumentException(
- 'Cannot specify a null route name if no existing route is configured.'
- );
- }
+ if (null === $route_name && $route instanceof RouteInterface) {
+ $route_name = $route->getName();
+ }
+
+ if (null === $route_name) {
+ throw new InvalidArgumentException(
+ 'Cannot specify a null route name if no existing route is configured.'
+ );
}
if ($route instanceof RouteInterface) {
@@ -157,18 +147,13 @@ class Router implements RouterInterface
}
/**
- * Simpler format for calling "named" routes with parameters.
- *
- * @param string $route_name
- * @param array $route_params
- * @param array $query_params
- * @param boolean $absolute Whether to include the full URL.
+ * @inheritDoc
*/
public function named(
string $route_name,
- $route_params = [],
+ array $route_params = [],
array $query_params = [],
- $absolute = false
+ bool $absolute = false
): UriInterface {
return self::resolveUri(
$this->getBaseUrl(),
diff --git a/src/Http/RouterInterface.php b/src/Http/RouterInterface.php
index 46f449a1e..7f3a73d60 100644
--- a/src/Http/RouterInterface.php
+++ b/src/Http/RouterInterface.php
@@ -1,5 +1,7 @@
fromHere(), but merging the current GET query parameters into the request as well.
@@ -52,9 +54,9 @@ interface RouterInterface
* @param bool $absolute
*/
public function fromHereWithQuery(
- $route_name = null,
+ ?string $route_name = null,
array $route_params = [],
array $query_params = [],
- $absolute = false
- ): string;
+ bool $absolute = false
+ ): UriInterface;
}
diff --git a/src/Http/ServerRequest.php b/src/Http/ServerRequest.php
index deb7de697..8dc1d5b70 100644
--- a/src/Http/ServerRequest.php
+++ b/src/Http/ServerRequest.php
@@ -1,5 +1,7 @@
serverRequest->getServerParams();
@@ -165,7 +167,11 @@ final class ServerRequest extends \Slim\Http\ServerRequest
?? $params['HTTP_FORWARDED_FOR']
?? $params['HTTP_FORWARDED']
?? $params['REMOTE_ADDR']
- ?? '';
+ ?? null;
+
+ if (null === $ip) {
+ throw new \RuntimeException('No IP address attached to this request.');
+ }
// Handle the IP being separated by commas.
$ipParts = explode(',', $ip);
diff --git a/src/Installer/Command/InstallCommand.php b/src/Installer/Command/InstallCommand.php
index be66f7bdc..c24fb87c7 100644
--- a/src/Installer/Command/InstallCommand.php
+++ b/src/Installer/Command/InstallCommand.php
@@ -1,5 +1,7 @@
ask(
$envConfig[$port]['name'] . ' - ' . $envConfig[$port]['description'],
- (int)$env[$port]
+ (string)$env[$port]
);
}
$azuracastEnv[Environment::AUTO_ASSIGN_PORT_MIN] = (int)$io->ask(
$azuracastEnvConfig[Environment::AUTO_ASSIGN_PORT_MIN]['name'],
- (int)$azuracastEnv[Environment::AUTO_ASSIGN_PORT_MIN]
+ (string)$azuracastEnv[Environment::AUTO_ASSIGN_PORT_MIN]
);
$azuracastEnv[Environment::AUTO_ASSIGN_PORT_MAX] = (int)$io->ask(
$azuracastEnvConfig[Environment::AUTO_ASSIGN_PORT_MAX]['name'],
- (int)$azuracastEnv[Environment::AUTO_ASSIGN_PORT_MAX]
+ (string)$azuracastEnv[Environment::AUTO_ASSIGN_PORT_MAX]
);
$stationPorts = Configuration::enumerateDefaultPorts(
diff --git a/src/Installer/EnvFiles/AbstractEnvFile.php b/src/Installer/EnvFiles/AbstractEnvFile.php
index 8f8f5afbe..001f93f8c 100644
--- a/src/Installer/EnvFiles/AbstractEnvFile.php
+++ b/src/Installer/EnvFiles/AbstractEnvFile.php
@@ -1,13 +1,19 @@
+ */
+abstract class AbstractEnvFile implements ArrayAccess
{
final public function __construct(
protected string $path,
@@ -39,22 +45,22 @@ abstract class AbstractEnvFile implements \ArrayAccess
$this->data = array_merge($defaults, $currentVars);
}
- public function offsetExists($offset): bool
+ public function offsetExists(mixed $offset): bool
{
return isset($this->data[$offset]);
}
- public function offsetGet($offset): mixed
+ public function offsetGet(mixed $offset): mixed
{
return $this->data[$offset];
}
- public function offsetSet($offset, $value): void
+ public function offsetSet(mixed $offset, mixed $value): void
{
$this->data[$offset] = $value;
}
- public function offsetUnset($offset): void
+ public function offsetUnset(mixed $offset): void
{
unset($this->data[$offset]);
}
diff --git a/src/Installer/EnvFiles/AzuraCastEnvFile.php b/src/Installer/EnvFiles/AzuraCastEnvFile.php
index 4a59d6f25..ab04bff7c 100644
--- a/src/Installer/EnvFiles/AzuraCastEnvFile.php
+++ b/src/Installer/EnvFiles/AzuraCastEnvFile.php
@@ -1,5 +1,7 @@
getServerParams();
- $browser_locale = \Locale::acceptFromHttp($server_params['HTTP_ACCEPT_LANGUAGE'] ?? null);
+ $browser_locale = \Locale::acceptFromHttp($server_params['HTTP_ACCEPT_LANGUAGE'] ?? '');
if (!empty($browser_locale)) {
if (2 === strlen($browser_locale)) {
@@ -136,7 +138,10 @@ class Locale
}
// Attempt to load from environment variable.
- $possibleLocales[] = $environment->getLang();
+ $envLang = $environment->getLang();
+ if (null !== $envLang) {
+ $possibleLocales[] = $envLang;
+ }
return new self($environment, $possibleLocales);
}
@@ -146,7 +151,7 @@ class Locale
): self {
return new self(
$environment,
- $environment->getLang()
+ $environment->getLang() ?? self::DEFAULT_LOCALE
);
}
diff --git a/src/LockFactory.php b/src/LockFactory.php
index c4d607432..81786a3f1 100644
--- a/src/LockFactory.php
+++ b/src/LockFactory.php
@@ -1,5 +1,7 @@
createLock($resource, $ttl, $autoRelease);
if ($force) {
diff --git a/src/Media/AlbumArtHandler/AbstractAlbumArtHandler.php b/src/Media/AlbumArtHandler/AbstractAlbumArtHandler.php
index 0e7ce5c5c..7301e206e 100644
--- a/src/Media/AlbumArtHandler/AbstractAlbumArtHandler.php
+++ b/src/Media/AlbumArtHandler/AbstractAlbumArtHandler.php
@@ -1,5 +1,7 @@
readMetadata($event->getPath());
$event->setMetadata($metadata);
} elseif ($event instanceof WriteMetadata) {
- if ($this->writeMetadata($event->getMetadata(), $event->getPath())) {
+ $metadata = $event->getMetadata();
+ if (null !== $metadata && $this->writeMetadata($metadata, $event->getPath())) {
$event->stopPropagation();
}
}
@@ -50,7 +53,9 @@ class GetId3MetadataService
$metadata = new Entity\Metadata();
if (is_numeric($info['playtime_seconds'])) {
- $metadata->setDuration($info['playtime_seconds']);
+ $metadata->setDuration(
+ Utilities\Time::displayTimeToSeconds($info['playtime_seconds']) ?? 0.0
+ );
}
$metaTags = $metadata->getTags();
diff --git a/src/Media/MimeType.php b/src/Media/MimeType.php
index fefc6c446..d741e1473 100644
--- a/src/Media/MimeType.php
+++ b/src/Media/MimeType.php
@@ -1,5 +1,7 @@
getText(), 'AzuraCast')) {
+ if (false !== mb_stripos($song->getText() ?? '', 'AzuraCast')) {
return null;
}
diff --git a/src/Message/AbstractMessage.php b/src/Message/AbstractMessage.php
index efd6d351a..60a86000f 100644
--- a/src/Message/AbstractMessage.php
+++ b/src/Message/AbstractMessage.php
@@ -1,5 +1,7 @@
*/
public array $triggers = [];
public function getIdentifier(): string
diff --git a/src/Message/ReprocessMediaMessage.php b/src/Message/ReprocessMediaMessage.php
index 78a9813cc..81cee262c 100644
--- a/src/Message/ReprocessMediaMessage.php
+++ b/src/Message/ReprocessMediaMessage.php
@@ -1,5 +1,7 @@
*/
public function getMessagesInTransport(string $queueName): Generator
{
foreach ($this->getTransport($queueName)->all() as $envelope) {
- yield $envelope->getMessage();
+ $message = $envelope->getMessage();
+ if ($message instanceof AbstractMessage) {
+ yield $message;
+ }
}
}
diff --git a/src/MessageQueue/ResetArrayCacheMiddleware.php b/src/MessageQueue/ResetArrayCacheMiddleware.php
index 9d7f9b416..3824b6a61 100644
--- a/src/MessageQueue/ResetArrayCacheMiddleware.php
+++ b/src/MessageQueue/ResetArrayCacheMiddleware.php
@@ -1,5 +1,7 @@
getRoute();
if ($current_route instanceof RouteInterface) {
- $route_parts = explode(':', $current_route->getName());
+ $route_parts = explode(':', $current_route->getName() ?? '');
$active_tab = $route_parts[1];
}
diff --git a/src/Middleware/Module/Api.php b/src/Middleware/Module/Api.php
index 9eb3edd7c..d4e56db23 100644
--- a/src/Middleware/Module/Api.php
+++ b/src/Middleware/Module/Api.php
@@ -1,5 +1,7 @@
getBaseUrl();
+
+ $baseUrl = $settings->getBaseUrl();
+ if (null !== $baseUrl) {
+ $rawOrigins[] = $baseUrl;
+ }
$origins = [];
foreach ($rawOrigins as $rawOrigin) {
diff --git a/src/Middleware/Module/StationFiles.php b/src/Middleware/Module/StationFiles.php
index faa673dcb..9ed5a2b6e 100644
--- a/src/Middleware/Module/StationFiles.php
+++ b/src/Middleware/Module/StationFiles.php
@@ -1,5 +1,7 @@
getRoute();
if ($current_route instanceof RouteInterface) {
- $route_parts = explode(':', $current_route->getName());
+ $route_parts = explode(':', $current_route->getName() ?? '');
$active_tab = $route_parts[1];
}
diff --git a/src/Middleware/Permissions.php b/src/Middleware/Permissions.php
index 150ebefc1..dc6fca747 100644
--- a/src/Middleware/Permissions.php
+++ b/src/Middleware/Permissions.php
@@ -1,5 +1,7 @@
inflector = InflectorFactory::create()->build();
}
- /**
- * @param DenormalizerInterface|NormalizerInterface|SerializerInterface $serializer
- */
- public function setSerializer($serializer): void
- {
- if (!$serializer instanceof DenormalizerInterface || !$serializer instanceof NormalizerInterface) {
- throw new InvalidArgumentException(
- 'Expected a serializer that also implements DenormalizerInterface and NormalizerInterface.'
- );
- }
-
- $this->serializer = $serializer;
- }
-
/**
* Replicates the "toArray" functionality previously present in Doctrine 1.
*
@@ -113,7 +97,7 @@ class DoctrineEntityNormalizer extends AbstractNormalizer
* Replicates the "fromArray" functionality previously present in Doctrine 1.
*
* @param mixed $data
- * @param string $type
+ * @param class-string $type
* @param string|null $format
* @param array $context
*/
@@ -184,15 +168,20 @@ class DoctrineEntityNormalizer extends AbstractNormalizer
}
/**
- * @param object|string $classOrObject
+ * @param object|class-string $classOrObject
* @param array $context
* @param bool $attributesAsString
*
- * @return bool|string[]|AttributeMetadataInterface[]
*/
- protected function getAllowedAttributes($classOrObject, array $context, $attributesAsString = false): bool|array
- {
- $meta = $this->classMetadataFactory->getMetadataFor($classOrObject)->getAttributesMetadata();
+ protected function getAllowedAttributes(
+ $classOrObject,
+ array $context,
+ bool $attributesAsString = false
+ ): array|false {
+ $meta = $this->classMetadataFactory?->getMetadataFor($classOrObject)?->getAttributesMetadata();
+ if (null === $meta) {
+ throw new \RuntimeException('Class metadata factory not specified.');
+ }
$props_raw = (new ReflectionClass($classOrObject))->getProperties(
ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED
@@ -222,15 +211,12 @@ class DoctrineEntityNormalizer extends AbstractNormalizer
return $allowedAttributes;
}
- /**
- * @param object $object
- * @param string $prop_name
- * @param null $format
- * @param array $context
- *
- */
- protected function getAttributeValue(object $object, string $prop_name, $format = null, array $context = []): mixed
- {
+ protected function getAttributeValue(
+ object $object,
+ string $prop_name,
+ string $format = null,
+ array $context = []
+ ): mixed {
$form_mode = $context[self::NORMALIZE_TO_IDENTIFIERS] ?? false;
if (isset($context[self::CLASS_METADATA]->associationMappings[$prop_name])) {
@@ -268,14 +254,14 @@ class DoctrineEntityNormalizer extends AbstractNormalizer
$return_val[] = $this->getProperty($val_obj, $id_field[0]);
}
} else {
- $return_val[] = $this->serializer->normalize($val_obj, $format, $context);
+ $return_val[] = $this->normalizer->normalize($val_obj, $format, $context);
}
}
}
return $return_val;
}
- return $this->serializer->normalize($prop_val, $format, $context);
+ return $this->normalizer->normalize($prop_val, $format, $context);
}
$value = $this->getProperty($object, $prop_name);
@@ -314,7 +300,7 @@ class DoctrineEntityNormalizer extends AbstractNormalizer
* @param string $var
* @param string $prefix
*/
- protected function getMethodName(string $var, $prefix = ''): string
+ protected function getMethodName(string $var, string $prefix = ''): string
{
return $this->inflector->camelize(($prefix ? $prefix . '_' : '') . $var);
}
@@ -330,7 +316,7 @@ class DoctrineEntityNormalizer extends AbstractNormalizer
object $object,
string $field,
mixed $value,
- $format = null,
+ ?string $format = null,
array $context = []
): void {
if (isset($context[self::ASSOCIATION_MAPPINGS][$field])) {
@@ -340,8 +326,12 @@ class DoctrineEntityNormalizer extends AbstractNormalizer
if ('one' === $mapping['type']) {
if (empty($value)) {
$this->setProperty($object, $field, null);
- } elseif (($field_item = $this->em->find($mapping['entity'], $value)) instanceof $mapping['entity']) {
- $this->setProperty($object, $field, $field_item);
+ } else {
+ /** @var class-string $entity */
+ $entity = $mapping['entity'];
+ if (($field_item = $this->em->find($entity, $value)) instanceof $entity) {
+ $this->setProperty($object, $field, $field_item);
+ }
}
} elseif ($mapping['is_owning_side']) {
$collection = $this->getProperty($object, $field);
@@ -351,8 +341,11 @@ class DoctrineEntityNormalizer extends AbstractNormalizer
if ($value) {
foreach ((array)$value as $field_id) {
- $field_item = $this->em->find($mapping['entity'], $field_id);
- if ($field_item instanceof $mapping['entity']) {
+ /** @var class-string $entity */
+ $entity = $mapping['entity'];
+
+ $field_item = $this->em->find($entity, $field_id);
+ if ($field_item instanceof $entity) {
$collection->add($field_item);
}
}
@@ -401,7 +394,7 @@ class DoctrineEntityNormalizer extends AbstractNormalizer
}
$dt = new DateTime();
- $dt->setTimestamp($value);
+ $dt->setTimestamp((int)$value);
$value = $dt;
}
break;
@@ -447,7 +440,9 @@ class DoctrineEntityNormalizer extends AbstractNormalizer
$class = ($class instanceof Proxy || $class instanceof GhostObjectInterface)
? get_parent_class($class)
: get_class($class);
- } elseif (!is_string($class)) {
+ }
+
+ if (!is_string($class)) {
return false;
}
diff --git a/src/Notification/Check/ComposeVersionCheck.php b/src/Notification/Check/ComposeVersionCheck.php
index 3101b262d..911d49ddc 100644
--- a/src/Notification/Check/ComposeVersionCheck.php
+++ b/src/Notification/Check/ComposeVersionCheck.php
@@ -1,5 +1,7 @@
inflector = InflectorFactory::create()
->build();
- $this->loadDirectory($base_dir);
+ $this->loadDirectory($baseDir);
}
- public function loadDirectory($dir): void
+ public function loadDirectory(string $dir): void
{
$plugins = (new Finder())
->ignoreUnreadableDirs()
diff --git a/src/Radio/AbstractAdapter.php b/src/Radio/AbstractAdapter.php
index d34a99d0c..22d012b82 100644
--- a/src/Radio/AbstractAdapter.php
+++ b/src/Radio/AbstractAdapter.php
@@ -1,5 +1,7 @@
getStation()->getNowplaying();
if ($np instanceof Entity\Api\NowPlaying) {
- $event->addAnnotations([
- 'title' => $np->now_playing->song->title,
- 'artist' => $np->now_playing->song->artist,
- 'playlist_id' => null,
- 'media_id' => null,
- ]);
+ $event->addAnnotations(
+ [
+ 'title' => $np->now_playing?->song?->title,
+ 'artist' => $np->now_playing?->song?->artist,
+ 'playlist_id' => null,
+ 'media_id' => null,
+ ]
+ );
}
} else {
$event->addAnnotations([
diff --git a/src/Radio/AutoDJ/Queue.php b/src/Radio/AutoDJ/Queue.php
index 4059edc34..19ed457ca 100644
--- a/src/Radio/AutoDJ/Queue.php
+++ b/src/Radio/AutoDJ/Queue.php
@@ -1,5 +1,7 @@
cache->get($queueCacheKey);
if (empty($mediaQueue)) {
- $playlistRaw = file_get_contents($playlist->getRemoteUrl());
- $mediaQueue = PlaylistParser::getSongs($playlistRaw);
+ $mediaQueue = [];
+
+ $playlistRemoteUrl = $playlist->getRemoteUrl();
+ if (null !== $playlistRemoteUrl) {
+ $playlistRaw = file_get_contents($playlistRemoteUrl);
+ if (false !== $playlistRaw) {
+ $mediaQueue = PlaylistParser::getSongs($playlistRaw);
+ }
+ }
}
$mediaId = null;
diff --git a/src/Radio/AutoDJ/Scheduler.php b/src/Radio/AutoDJ/Scheduler.php
index 726abbda6..cc5972c5a 100644
--- a/src/Radio/AutoDJ/Scheduler.php
+++ b/src/Radio/AutoDJ/Scheduler.php
@@ -1,5 +1,7 @@
equalTo($endTime)) {
- if (!$this->wasPlaylistPlayedInLastXMinutes($schedule->getPlaylist(), $now, 30)) {
+ $playlist = $schedule->getPlaylist();
+ if (null !== $playlist && !$this->wasPlaylistPlayedInLastXMinutes($playlist, $now, 30)) {
return true;
}
} else {
@@ -304,6 +307,11 @@ class Scheduler
$playlist = $schedule->getPlaylist();
+ if (null === $playlist) {
+ $this->logger->error('Attempting to check playlist loop status on a non-playlist-based schedule item.');
+ return false;
+ }
+
$playlistPlayedAt = CarbonImmutable::createFromTimestamp(
$playlist->getPlayedAt(),
$now->getTimezone()
@@ -358,20 +366,24 @@ class Scheduler
$endDate = $schedule->getEndDate();
if (!empty($startDate)) {
- $startDate = CarbonImmutable::createFromFormat('Y-m-d', $startDate, $now->getTimezone())
- ->setTime(0, 0);
+ $startDate = CarbonImmutable::createFromFormat('Y-m-d', $startDate, $now->getTimezone());
- if ($now->lt($startDate)) {
- return false;
+ if (false !== $startDate) {
+ $startDate = $startDate->setTime(0, 0);
+ if ($now->lt($startDate)) {
+ return false;
+ }
}
}
if (!empty($endDate)) {
- $endDate = CarbonImmutable::createFromFormat('Y-m-d', $endDate, $now->getTimezone())
- ->setTime(23, 59, 59);
+ $endDate = CarbonImmutable::createFromFormat('Y-m-d', $endDate, $now->getTimezone());
- if ($now->gt($endDate)) {
- return false;
+ if (false !== $endDate) {
+ $endDate = $endDate->setTime(23, 59, 59);
+ if ($now->gt($endDate)) {
+ return false;
+ }
}
}
diff --git a/src/Radio/Backend/AbstractBackend.php b/src/Radio/Backend/AbstractBackend.php
index 12cce8c64..327beccd7 100644
--- a/src/Radio/Backend/AbstractBackend.php
+++ b/src/Radio/Backend/AbstractBackend.php
@@ -1,5 +1,7 @@
command(
$station,
diff --git a/src/Radio/Backend/Liquidsoap/ConfigWriter.php b/src/Radio/Backend/Liquidsoap/ConfigWriter.php
index ff3b67d35..b46da01c7 100644
--- a/src/Radio/Backend/Liquidsoap/ConfigWriter.php
+++ b/src/Radio/Backend/Liquidsoap/ConfigWriter.php
@@ -1,5 +1,7 @@
getRemoteUrl();
- $remote_url_scheme = parse_url($remote_url, PHP_URL_SCHEME);
- $remote_url_function = ('https' === $remote_url_scheme) ? 'input.https' : 'input.http';
+ if (null !== $remote_url) {
+ $remote_url_scheme = parse_url($remote_url, PHP_URL_SCHEME);
+ $remote_url_function = ('https' === $remote_url_scheme) ? 'input.https' : 'input.http';
- $buffer = $playlist->getRemoteBuffer();
- $buffer = ($buffer < 1) ? Entity\StationPlaylist::DEFAULT_REMOTE_BUFFER : $buffer;
+ $buffer = $playlist->getRemoteBuffer();
+ $buffer = ($buffer < 1) ? Entity\StationPlaylist::DEFAULT_REMOTE_BUFFER : $buffer;
- $playlistConfigLines[] = $playlistVarName . ' = mksafe(' . $remote_url_function
+ $playlistConfigLines[] = $playlistVarName . ' = mksafe(' . $remote_url_function
. '(max=' . $buffer . '., "' . self::cleanUpString($remote_url) . '"))';
+ }
break;
}
}
@@ -533,9 +537,9 @@ class ConfigWriter implements EventSubscriberInterface
* @param Entity\StationPlaylist $playlist
* @param bool $notify
*
- * @return string The full path that was written to.
+ * @return string|null The full path that was written to.
*/
- public function writePlaylistFile(Entity\StationPlaylist $playlist, $notify = true): ?string
+ public function writePlaylistFile(Entity\StationPlaylist $playlist, bool $notify = true): ?string
{
$station = $playlist->getStation();
@@ -675,7 +679,7 @@ class ConfigWriter implements EventSubscriberInterface
* @param string $endpoint
* @param array $params
*/
- protected function getApiUrlCommand(Entity\Station $station, string $endpoint, $params = []): string
+ protected function getApiUrlCommand(Entity\Station $station, string $endpoint, array $params = []): string
{
// Docker cURL-based API URL call with API authentication.
if ($this->environment->isDocker()) {
@@ -1003,7 +1007,7 @@ class ConfigWriter implements EventSubscriberInterface
$charset = $station->getBackendConfig()->getCharset();
$output_format = $this->getOutputFormatString(
- $mount->getAutodjFormat(),
+ $mount->getAutodjFormat() ?? $mount::FORMAT_MP3,
$mount->getAutodjBitrate() ?? 128
);
@@ -1136,12 +1140,12 @@ class ConfigWriter implements EventSubscriberInterface
* @param float|int|string $number
* @param int $decimals
*/
- public static function toFloat(float|int|string $number, $decimals = 2): string
+ public static function toFloat(float|int|string $number, int $decimals = 2): string
{
return number_format((float)$number, $decimals, '.', '');
}
- public static function formatTimeCode($time_code): string
+ public static function formatTimeCode(int $time_code): string
{
$hours = floor($time_code / 100);
$mins = $time_code % 100;
@@ -1170,11 +1174,11 @@ class ConfigWriter implements EventSubscriberInterface
public static function cleanUpVarName(string $str): string
{
$str = strip_tags($str);
- $str = preg_replace(['/[\r\n\t ]+/', '/[\"\*\/\:\<\>\?\'\|]+/'], ' ', $str);
+ $str = preg_replace(['/[\r\n\t ]+/', '/[\"\*\/\:\<\>\?\'\|]+/'], ' ', $str) ?? '';
$str = strtolower($str);
$str = html_entity_decode($str, ENT_QUOTES, "utf-8");
$str = htmlentities($str, ENT_QUOTES, "utf-8");
- $str = preg_replace("/(&)([a-z])([a-z]+;)/i", '$2', $str);
+ $str = preg_replace("/(&)([a-z])([a-z]+;)/i", '$2', $str) ?? '';
$str = str_replace(' ', '_', $str);
$str = rawurlencode($str);
$str = str_replace(['%', '-'], ['', '_'], $str);
diff --git a/src/Radio/Backend/None.php b/src/Radio/Backend/None.php
index 4e7b5f5bb..f12f0dcfe 100644
--- a/src/Radio/Backend/None.php
+++ b/src/Radio/Backend/None.php
@@ -1,5 +1,7 @@
getId();
$affected_groups = $this->reloadSupervisor();
@@ -250,7 +252,7 @@ class Configuration
* @param Station $station
* @param bool $force
*/
- public function assignRadioPorts(Station $station, $force = false): void
+ public function assignRadioPorts(Station $station, bool $force = false): void
{
if (
$station->getFrontendType() !== Adapters::FRONTEND_REMOTE
@@ -388,13 +390,13 @@ class Configuration
protected function writeConfigurationSection(
Station $station,
AbstractAdapter $adapter,
- $priority
+ ?int $priority
): string {
[, $program_name] = explode(':', $adapter->getProgramName($station));
$config_lines = [
'user' => 'azuracast',
- 'priority' => $priority,
+ 'priority' => $priority ?? 50,
'command' => $adapter->getCommand($station),
'directory' => $station->getRadioConfigDir(),
'environment' => 'TZ="' . $station->getTimezone() . '"',
diff --git a/src/Radio/Frontend/AbstractFrontend.php b/src/Radio/Frontend/AbstractFrontend.php
index bed809d34..da067ba10 100644
--- a/src/Radio/Frontend/AbstractFrontend.php
+++ b/src/Radio/Frontend/AbstractFrontend.php
@@ -1,5 +1,7 @@
fromString('' . $custom_config_raw . ' ');
+ $xmlConfig = (new Reader())->fromString('' . $custom_config_raw . ' ');
+ return (false !== $xmlConfig)
+ ? (array)$xmlConfig
+ : false;
}
} catch (Exception $e) {
$this->logger->error(
diff --git a/src/Radio/Frontend/Icecast.php b/src/Radio/Frontend/Icecast.php
index 6ed4f3e8d..88b242bef 100644
--- a/src/Radio/Frontend/Icecast.php
+++ b/src/Radio/Frontend/Icecast.php
@@ -1,5 +1,7 @@
getRelayUrl()) {
- $relay_parts = parse_url($mount_row->getRelayUrl());
+ $mountRelayUrl = $mount_row->getRelayUrl();
+ if (null !== $mountRelayUrl) {
+ $mountRelayUri = new Uri($mountRelayUrl);
$config['relay'][] = [
- 'server' => $relay_parts['host'],
- 'port' => $relay_parts['port'],
- 'mount' => $relay_parts['path'],
+ 'server' => $mountRelayUri->getHost(),
+ 'port' => $mountRelayUri->getPort(),
+ 'mount' => $mountRelayUri->getPath(),
'local-mount' => $mount_row->getName(),
];
}
@@ -221,16 +224,16 @@ class Icecast extends AbstractFrontend
if (!empty($customConfig)) {
$customConfParsed = $this->processCustomConfig($customConfig);
- // Special handling for aliases.
- if (isset($customConfParsed['paths']['alias'])) {
- $alias = (array)$customConfParsed['paths']['alias'];
- if (!is_numeric(key($alias))) {
- $alias = [$alias];
- }
- $customConfParsed['paths']['alias'] = $alias;
- }
-
if (false !== $customConfParsed) {
+ // Special handling for aliases.
+ if (isset($customConfParsed['paths']['alias'])) {
+ $alias = (array)$customConfParsed['paths']['alias'];
+ if (!is_numeric(key($alias))) {
+ $alias = [$alias];
+ }
+ $customConfParsed['paths']['alias'] = $alias;
+ }
+
$config = Utilities\Arrays::arrayMergeRecursiveDistinct($config, $customConfParsed);
}
}
diff --git a/src/Radio/Frontend/Remote.php b/src/Radio/Frontend/Remote.php
index 868d29a8e..5df4cff9d 100644
--- a/src/Radio/Frontend/Remote.php
+++ b/src/Radio/Frontend/Remote.php
@@ -1,5 +1,7 @@
toInt();
}
- public static function getReadableSize(Math\BigInteger $bytes, $decimals = 1): string
+ public static function getReadableSize(Math\BigInteger $bytes, int $decimals = 1): string
{
$bytes_str = (string)$bytes;
@@ -39,7 +41,7 @@ class Quota
return $bytes_str;
}
- public static function convertFromReadableSize($size): ?Math\BigInteger
+ public static function convertFromReadableSize(Math\BigInteger|string|null $size): ?Math\BigInteger
{
if ($size instanceof Math\BigInteger) {
return $size;
@@ -50,18 +52,20 @@ class Quota
}
// Remove the non-unit characters from the size.
- $unit = preg_replace('/[^bkmgtpezy]/i', '', $size);
+ $unit = preg_replace('/[^bkmgtpezy]/i', '', $size) ?? '';
// Remove the non-numeric characters from the size.
- $size = preg_replace('/[^0-9\\.]/', '', $size);
+ $size = preg_replace('/[^0-9\\.]/', '', $size) ?? '';
if ($unit) {
// Find the position of the unit in the ordered string which is the power
// of magnitude to multiply a kilobyte by.
+
+ /** @noinspection StringFragmentMisplacedInspection */
$byte_power = stripos(
haystack: 'bkmgtpezy',
- needle: $unit[0]
- );
+ needle: $unit[0]
+ ) ?: 0;
$byte_multiplier = Math\BigInteger::of(1000)->power($byte_power);
return Math\BigDecimal::of($size)
diff --git a/src/Radio/Remote/AbstractRemote.php b/src/Radio/Remote/AbstractRemote.php
index c15b44a40..8768b5b5e 100644
--- a/src/Radio/Remote/AbstractRemote.php
+++ b/src/Radio/Remote/AbstractRemote.php
@@ -1,5 +1,7 @@
getUrl());
diff --git a/src/Radio/Remote/AdapterProxy.php b/src/Radio/Remote/AdapterProxy.php
index 42dabb7fe..898e7ff67 100644
--- a/src/Radio/Remote/AdapterProxy.php
+++ b/src/Radio/Remote/AdapterProxy.php
@@ -1,5 +1,7 @@
setListenersTotal($result->listeners->total);
- $remote->setListenersUnique($result->listeners->unique);
+ $remote->setListenersUnique($result->listeners->unique ?? 0);
$this->em->persist($remote);
$this->em->flush();
@@ -87,6 +89,6 @@ class AzuraRelay extends AbstractRemote
// Remove port number and other decorations.
return (string)$base_url
->withPort($radio_port)
- ->withPath($remote->getMount());
+ ->withPath($remote->getMount() ?? '');
}
}
diff --git a/src/Radio/Remote/Icecast.php b/src/Radio/Remote/Icecast.php
index a355abef6..d57250595 100644
--- a/src/Radio/Remote/Icecast.php
+++ b/src/Radio/Remote/Icecast.php
@@ -1,5 +1,7 @@
mustRun();
- if (!is_file($jsonOutPath)) {
- throw new RuntimeException('Audio waveform JSON was not generated.');
- }
-
- $inputRaw = file_get_contents($jsonOutPath);
- $input = json_decode($inputRaw, true, 512, JSON_THROW_ON_ERROR);
+ $input = Json::loadFromFile($jsonOutPath);
// Limit all input to a range from 0 to 1.
$data = $input['data'];
diff --git a/src/Service/Avatar.php b/src/Service/Avatar.php
index 6acdb7e73..915954f4b 100644
--- a/src/Service/Avatar.php
+++ b/src/Service/Avatar.php
@@ -1,5 +1,7 @@
getAvatarService();
$default = $this->settingsRepo->readSettings()->getAvatarDefaultUrl();
+ if (empty($email)) {
+ return $default;
+ }
+
return $avatarService->getAvatar($email, $size, $default);
}
}
diff --git a/src/Service/Avatar/AvatarServiceInterface.php b/src/Service/Avatar/AvatarServiceInterface.php
index 1261e2511..0e8df4f37 100644
--- a/src/Service/Avatar/AvatarServiceInterface.php
+++ b/src/Service/Avatar/AvatarServiceInterface.php
@@ -1,5 +1,7 @@
getUploadedPath();
$fp = fopen($finalPath, 'wb+');
+ if (false === $fp) {
+ throw new \RuntimeException(
+ sprintf(
+ 'Could not open final path "%s" for writing.',
+ $finalPath
+ )
+ );
+ }
+
for ($i = 1; $i <= $numChunks; $i++) {
- fwrite($fp, file_get_contents($chunkBaseDir . '/' . $chunkIdentifier . '.part' . $i));
+ $chunkContents = file_get_contents($chunkBaseDir . '/' . $chunkIdentifier . '.part' . $i);
+ if (empty($chunkContents)) {
+ throw new \RuntimeException(
+ sprintf(
+ 'Could not load chunk "%d" for writing.',
+ $i
+ )
+ );
+ }
+
+ fwrite($fp, $chunkContents);
}
fclose($fp);
@@ -215,7 +236,7 @@ class Flow
protected static function rrmdir(string $dir): void
{
if (is_dir($dir)) {
- $objects = array_diff(scandir($dir, SCANDIR_SORT_NONE), ['.', '..']);
+ $objects = array_diff(scandir($dir, SCANDIR_SORT_NONE) ?: [], ['.', '..']);
foreach ($objects as $object) {
if (is_dir($dir . '/' . $object)) {
self::rrmdir($dir . '/' . $object);
diff --git a/src/Service/Flow/UploadedFile.php b/src/Service/Flow/UploadedFile.php
index ff7810831..ec5408994 100644
--- a/src/Service/Flow/UploadedFile.php
+++ b/src/Service/Flow/UploadedFile.php
@@ -18,8 +18,12 @@ final class UploadedFile implements \JsonSerializable
?string $tempDir
) {
$tempDir ??= sys_get_temp_dir();
-
$originalFilename ??= tempnam($tempDir, 'upload');
+
+ if (!$originalFilename || !$tempDir) {
+ throw new \RuntimeException('Could not generate original filename.');
+ }
+
$this->originalFilename = self::filterOriginalFilename($originalFilename);
if (null === $uploadedPath) {
@@ -27,6 +31,9 @@ final class UploadedFile implements \JsonSerializable
$this->uploadedPath = $tempDir . '/' . $prefix . '_' . $originalFilename;
} else {
$uploadedPath = realpath($uploadedPath);
+ if (false === $uploadedPath) {
+ throw new \InvalidArgumentException('Could not determine real path of specified path.');
+ }
if (!str_starts_with($uploadedPath, $tempDir)) {
throw new \InvalidArgumentException('Uploaded path is not inside specified temporary directory.');
}
@@ -51,7 +58,12 @@ final class UploadedFile implements \JsonSerializable
public function getUploadedSize(): int
{
- return filesize($this->uploadedPath);
+ $size = filesize($this->uploadedPath);
+ if (false === $size) {
+ throw new \RuntimeException('Could not get file size of uploaded path.');
+ }
+
+ return $size;
}
public function readAndDeleteUploadedFile(): string
@@ -59,7 +71,7 @@ final class UploadedFile implements \JsonSerializable
$contents = file_get_contents($this->uploadedPath);
@unlink($this->uploadedPath);
- return $contents;
+ return $contents ?: '';
}
/** @return mixed[] */
diff --git a/src/Service/IpGeolocation.php b/src/Service/IpGeolocation.php
index c31fcf5c1..933374fff 100644
--- a/src/Service/IpGeolocation.php
+++ b/src/Service/IpGeolocation.php
@@ -1,5 +1,7 @@
initialize();
}
- if (null === $this->reader) {
+ $reader = $this->reader;
+ if (null === $reader) {
return [
'status' => 'error',
'message' => $this->getAttribution(),
@@ -86,12 +89,12 @@ class IpGeolocation
$ipInfo = $this->cache->get(
$cacheKey,
- function (CacheItem $item) use ($ip) {
+ function (CacheItem $item) use ($ip, $reader) {
/** @noinspection SummerTimeUnsafeTimeManipulationInspection */
$item->expiresAfter(86400 * 7);
try {
- $ipInfo = $this->reader->get($ip);
+ $ipInfo = $reader->get($ip);
if (!empty($ipInfo)) {
return $ipInfo;
}
@@ -125,7 +128,7 @@ class IpGeolocation
];
}
- protected function getLocalizedString($names, string $locale): string
+ protected function getLocalizedString(?array $names, string $locale): string
{
if (empty($names)) {
return '';
diff --git a/src/Service/IpGeolocator/AbstractIpGeolocator.php b/src/Service/IpGeolocator/AbstractIpGeolocator.php
index ebdfec6a6..fa37e0c70 100644
--- a/src/Service/IpGeolocator/AbstractIpGeolocator.php
+++ b/src/Service/IpGeolocator/AbstractIpGeolocator.php
@@ -1,5 +1,7 @@
quoteQuery($song->getTitle());
+ if (!empty($song->getTitle())) {
+ $query[] = $this->quoteQuery($song->getTitle());
+ }
if (!empty($song->getArtist())) {
$query[] = 'artist:' . $this->quoteQuery($song->getArtist());
@@ -114,6 +118,10 @@ class MusicBrainz
}
}
+ if (empty($query)) {
+ return [];
+ }
+
$response = $this->makeRequest(
'recording/',
[
diff --git a/src/Service/NChan.php b/src/Service/NChan.php
index 1e35a8f09..e3cfe0a0f 100644
--- a/src/Service/NChan.php
+++ b/src/Service/NChan.php
@@ -1,5 +1,7 @@
getSessionIdentifier($namespace);
if ($this->session->has($sessionKey)) {
- return $this->session->get($sessionKey);
+ $csrf = $this->session->get($sessionKey);
+ if (!empty($csrf)) {
+ return (string)$csrf;
+ }
}
$key = $this->randomString();
@@ -71,7 +76,7 @@ class Csrf
$sessionKey = $this->session->get($sessionIdentifier);
- if (0 !== strcmp($key, $sessionKey)) {
+ if (0 !== strcmp($key, (string)$sessionKey)) {
throw new Exception\CsrfValidationException('Invalid CSRF token supplied.');
}
}
diff --git a/src/Session/Flash.php b/src/Session/Flash.php
index 8aa1c508b..bbedd4582 100644
--- a/src/Session/Flash.php
+++ b/src/Session/Flash.php
@@ -1,5 +1,7 @@
addMessage($message, $level, $saveInSession);
}
@@ -45,7 +47,7 @@ class Flash
* @param string $level
* @param bool $saveInSession
*/
- public function addMessage(string $message, $level = self::INFO, $saveInSession = true): void
+ public function addMessage(string $message, string $level = self::INFO, bool $saveInSession = true): void
{
$colorChart = [
'green' => self::SUCCESS,
@@ -65,9 +67,7 @@ class Flash
'color' => $colorChart[$level] ?? $level,
];
- if (null === $this->messages) {
- $this->getMessages();
- }
+ $this->getMessages();
$this->messages[] = $messageRow;
if ($saveInSession) {
diff --git a/src/Sync/Runner.php b/src/Sync/Runner.php
index e50d8ffa5..9ae51dbcc 100644
--- a/src/Sync/Runner.php
+++ b/src/Sync/Runner.php
@@ -1,5 +1,7 @@
fileSize();
- $total_size = $total_size->plus($size);
+ if (null !== $size) {
+ $total_size = $total_size->plus($size);
+ }
} catch (UnableToRetrieveMetadata) {
continue;
}
@@ -209,10 +213,10 @@ class CheckMediaTask extends AbstractTask
unset($musicFiles[$pathHash]);
} else {
- $this->mediaRepo->remove(
- $this->em->find(Entity\StationMedia::class, $mediaRow['id']),
- false
- );
+ $media = $this->em->find(Entity\StationMedia::class, $mediaRow['id']);
+ if ($media instanceof Entity\StationMedia) {
+ $this->mediaRepo->remove($media, false);
+ }
$stats['deleted']++;
}
@@ -243,7 +247,7 @@ class CheckMediaTask extends AbstractTask
if (Entity\UnprocessableMedia::needsReprocessing($mtime, $unprocessableRow['mtime'] ?? 0)) {
$message = new Message\AddNewMediaMessage();
- $message->storage_location_id = $storageLocation->getId();
+ $message->storage_location_id = $storageLocation->getIdRequired();
$message->path = $unprocessableRow['path'];
$this->messageBus->dispatch($message);
@@ -285,7 +289,7 @@ class CheckMediaTask extends AbstractTask
$stats['already_queued']++;
} else {
$message = new Message\AddNewMediaMessage();
- $message->storage_location_id = $storageLocation->getId();
+ $message->storage_location_id = $storageLocation->getIdRequired();
$message->path = $path;
$this->messageBus->dispatch($message);
diff --git a/src/Sync/Task/CheckRequests.php b/src/Sync/Task/CheckRequests.php
index 6375eca8c..4745a995b 100644
--- a/src/Sync/Task/CheckRequests.php
+++ b/src/Sync/Task/CheckRequests.php
@@ -1,5 +1,7 @@
getRealPath();
- @unlink($file_path);
+ if (false !== $file_path) {
+ @unlink($file_path);
+ }
}
}
diff --git a/src/Sync/Task/NowPlayingTask.php b/src/Sync/Task/NowPlayingTask.php
index fcdfced4d..995c7990c 100644
--- a/src/Sync/Task/NowPlayingTask.php
+++ b/src/Sync/Task/NowPlayingTask.php
@@ -1,5 +1,7 @@
station_id = $station->getId();
+ $message->station_id = $station->getIdRequired();
$this->messageBus->dispatch(
$message,
@@ -305,7 +307,7 @@ class NowPlayingTask extends AbstractTask implements EventSubscriberInterface
];
if ($npOld instanceof Entity\Api\NowPlaying) {
- if ($npOld->now_playing->song->id !== $np->now_playing->song->id) {
+ if ($npOld->now_playing?->song?->id !== $np->now_playing?->song?->id) {
$triggers[] = Entity\StationWebhook::TRIGGER_SONG_CHANGED;
}
diff --git a/src/Sync/Task/ReactivateStreamerTask.php b/src/Sync/Task/ReactivateStreamerTask.php
index ef615bada..4e1a1687a 100644
--- a/src/Sync/Task/ReactivateStreamerTask.php
+++ b/src/Sync/Task/ReactivateStreamerTask.php
@@ -1,5 +1,7 @@
getRealPath();
- @unlink($file_path);
+ if ($file_path) {
+ @unlink($file_path);
+ }
}
}
}
diff --git a/src/Sync/Task/RunAnalyticsTask.php b/src/Sync/Task/RunAnalyticsTask.php
index ec76a8be6..56d16dba4 100644
--- a/src/Sync/Task/RunAnalyticsTask.php
+++ b/src/Sync/Task/RunAnalyticsTask.php
@@ -1,5 +1,7 @@
requiredFields = ['container'];
+ }
public function _initialize(): void
{
@@ -42,7 +49,12 @@ class Module extends Framework implements DoctrineProvider
]
);
- $this->container = $this->app->getContainer();
+ $container = $this->app->getContainer();
+ if (null === $container) {
+ throw new \RuntimeException('Container was not set on App.');
+ }
+
+ $this->container = $container;
$this->em = $this->container->get(ReloadableEntityManagerInterface::class);
parent::_initialize();
diff --git a/src/Traits/AvailableStaticallyTrait.php b/src/Traits/AvailableStaticallyTrait.php
index 034b69018..cd46fc7fe 100644
--- a/src/Traits/AvailableStaticallyTrait.php
+++ b/src/Traits/AvailableStaticallyTrait.php
@@ -1,5 +1,7 @@
$obj
+ */
public function fromParentObject(object|array $obj): void
{
if (is_object($obj)) {
diff --git a/src/Traits/RequestAwareTrait.php b/src/Traits/RequestAwareTrait.php
index b30e26137..96e805098 100644
--- a/src/Traits/RequestAwareTrait.php
+++ b/src/Traits/RequestAwareTrait.php
@@ -1,5 +1,7 @@
getRealPath();
+ if (false === $realPath) {
+ return false;
+ }
+
if ('link' !== $fileinfo->getType() && $fileinfo->isDir()) {
- if (!rmdir($fileinfo->getRealPath())) {
+ if (!rmdir($realPath)) {
return false;
}
- } elseif (!unlink($fileinfo->getRealPath())) {
+ } elseif (!unlink($realPath)) {
return false;
}
}
diff --git a/src/Utilities/Json.php b/src/Utilities/Json.php
new file mode 100644
index 000000000..7f36e793c
--- /dev/null
+++ b/src/Utilities/Json.php
@@ -0,0 +1,38 @@
+getMessage()
+ )
+ );
+ }
+ }
+ } elseif ($throwOnError) {
+ throw new \RuntimeException(sprintf('Error reading file: "%s"', $path));
+ }
+ } elseif ($throwOnError) {
+ throw new \RuntimeException(sprintf('File not found: "%s"', $path));
+ }
+
+ return [];
+ }
+}
diff --git a/src/Utilities/Strings.php b/src/Utilities/Strings.php
index 7a3b82c18..795c8ffa0 100644
--- a/src/Utilities/Strings.php
+++ b/src/Utilities/Strings.php
@@ -1,5 +1,7 @@
$v) {
+ $sec += (60 ** (int)$k) * (int)$v;
+ }
+
+ return $sec;
+ }
+
+ return (float)$seconds;
+ }
+}
diff --git a/src/Utilities/Xml.php b/src/Utilities/Xml.php
index 6b0da5589..ed89001fa 100644
--- a/src/Utilities/Xml.php
+++ b/src/Utilities/Xml.php
@@ -1,5 +1,7 @@
$value) {
+ foreach ($array as $key => $value) {
$key = is_numeric($key) ? "item$key" : $key;
if (is_array($value)) {
$subnode = $xml->addChild((string)$key);
self::arrToXml($value, $subnode);
} else {
- $xml->addChild((string)$key, htmlspecialchars($value));
+ $xml->addChild((string)$key, htmlspecialchars($value, ENT_QUOTES | ENT_HTML5));
}
}
}
diff --git a/src/Validator/Constraints/StationPortChecker.php b/src/Validator/Constraints/StationPortChecker.php
index e29f82a85..841672ffa 100644
--- a/src/Validator/Constraints/StationPortChecker.php
+++ b/src/Validator/Constraints/StationPortChecker.php
@@ -1,5 +1,7 @@
entityClass = $entityClass ?? $this->entityClass;
+
if (is_array($fields) && is_string(key($fields))) {
$options = array_merge($fields, $options);
} else {
@@ -53,11 +55,6 @@ class UniqueEntity extends Constraint
}
parent::__construct($options, $groups, $payload);
-
- $this->entityClass = $entityClass ?? $this->entityClass;
- $this->repositoryMethod = $repositoryMethod ?? $this->repositoryMethod;
- $this->errorPath = $errorPath ?? $this->errorPath;
- $this->ignoreNull = $ignoreNull ?? $this->ignoreNull;
}
/**
diff --git a/src/Validator/Constraints/UniqueEntityValidator.php b/src/Validator/Constraints/UniqueEntityValidator.php
index 4cfa0907b..99876d099 100644
--- a/src/Validator/Constraints/UniqueEntityValidator.php
+++ b/src/Validator/Constraints/UniqueEntityValidator.php
@@ -1,5 +1,7 @@
em = $em;
+ public function __construct(
+ protected EntityManagerInterface $em
+ ) {
}
/**
- * @param object $value
+ * @param mixed $value
*
* @throws UnexpectedTypeException
* @throws ConstraintDefinitionException
@@ -86,7 +87,7 @@ class UniqueEntityValidator extends ConstraintValidator
);
}
- $fieldValue = $class->reflFields[$fieldName]->getValue($value);
+ $fieldValue = $class->reflFields[$fieldName]?->getValue($value);
if (null === $fieldValue) {
$hasNullValue = true;
@@ -179,13 +180,13 @@ class UniqueEntityValidator extends ConstraintValidator
$this->context->buildViolation($message)
->atPath($errorPath)
- ->setParameter('{{ value }}', $this->formatWithIdentifiers($this->em, $class, $invalidValue))
+ ->setParameter('{{ value }}', $this->formatWithIdentifiers($class, $invalidValue))
->setInvalidValue($invalidValue)
->setCause($result)
->addViolation();
}
- private function formatWithIdentifiers($em, $class, $value): string
+ private function formatWithIdentifiers(ClassMetadata $class, mixed $value): string
{
if (!is_object($value) || $value instanceof DateTimeInterface) {
return $this->formatValue($value, self::PRETTY_DATE);
@@ -197,8 +198,8 @@ class UniqueEntityValidator extends ConstraintValidator
if ($class->getName() !== $idClass = get_class($value)) {
// non unique value might be a composite PK that consists of other entity objects
- if ($em->getMetadataFactory()->hasMetadataFor($idClass)) {
- $identifiers = $em->getClassMetadata($idClass)->getIdentifierValues($value);
+ if ($this->em->getMetadataFactory()->hasMetadataFor($idClass)) {
+ $identifiers = $this->em->getClassMetadata($idClass)->getIdentifierValues($value);
} else {
// this case might happen if the non unique column has a custom doctrine type and its value is an object
// in which case we cannot get any identifiers for it
diff --git a/src/Version.php b/src/Version.php
index 53b766529..8eb1f8bae 100644
--- a/src/Version.php
+++ b/src/Version.php
@@ -1,5 +1,7 @@
' . htmlspecialchars($dumpedValue) . '';
+ return '' . htmlspecialchars($dumpedValue ?? '', ENT_QUOTES | ENT_HTML5) . ' ';
}
);
diff --git a/src/Webhook/Connector/AbstractConnector.php b/src/Webhook/Connector/AbstractConnector.php
index a41221632..30ae343e6 100644
--- a/src/Webhook/Connector/AbstractConnector.php
+++ b/src/Webhook/Connector/AbstractConnector.php
@@ -1,11 +1,12 @@
webhookShouldTrigger($webhook, $triggers)) {
@@ -45,7 +49,12 @@ abstract class AbstractConnector implements ConnectorInterface
return true;
}
- #[Pure] protected function webhookShouldTrigger(Entity\StationWebhook $webhook, array $triggers = []): bool
+ /**
+ * @param Entity\StationWebhook $webhook
+ * @param array $triggers
+ *
+ */
+ protected function webhookShouldTrigger(Entity\StationWebhook $webhook, array $triggers = []): bool
{
$webhookTriggers = $webhook->getTriggers();
if (empty($webhookTriggers)) {
@@ -69,10 +78,10 @@ abstract class AbstractConnector implements ConnectorInterface
/**
* Replace variables in the format {{ blah }} with the flattened contents of the NowPlaying API array.
*
- * @param array $raw_vars
+ * @param array $raw_vars
* @param Entity\Api\NowPlaying $np
*
- * @return mixed[]
+ * @return array
*/
public function replaceVariables(array $raw_vars, Entity\Api\NowPlaying $np): array
{
@@ -101,7 +110,7 @@ abstract class AbstractConnector implements ConnectorInterface
*/
protected function getValidUrl(?string $url_string = null): ?string
{
- $url = trim($url_string);
+ $url = trim($url_string ?? '');
$pattern = sprintf(UrlValidator::PATTERN, 'http|https');
return (preg_match($pattern, $url)) ? $url : null;
}
diff --git a/src/Webhook/Connector/ConnectorInterface.php b/src/Webhook/Connector/ConnectorInterface.php
index 2b81dd5db..d63972377 100644
--- a/src/Webhook/Connector/ConnectorInterface.php
+++ b/src/Webhook/Connector/ConnectorInterface.php
@@ -1,5 +1,7 @@
$triggers
*
* @return bool Whether the given webhook should dispatch with these triggers.
*/
@@ -26,7 +28,7 @@ interface ConnectorInterface
* @param Entity\Station $station
* @param Entity\StationWebhook $webhook
* @param Entity\Api\NowPlaying $np
- * @param array $triggers
+ * @param array $triggers
*
* @return bool Whether the webhook actually dispatched.
*/
diff --git a/src/Webhook/Connector/Discord.php b/src/Webhook/Connector/Discord.php
index 5090779b6..3c062e68d 100644
--- a/src/Webhook/Connector/Discord.php
+++ b/src/Webhook/Connector/Discord.php
@@ -1,5 +1,7 @@
$config['partner_id'],
'partnerKey' => $config['partner_key'],
'id' => $config['station_id'],
- 'title' => $np->now_playing->song->title,
- 'artist' => $np->now_playing->song->artist,
- 'album' => $np->now_playing->song->album,
+ 'title' => $np->now_playing?->song?->title,
+ 'artist' => $np->now_playing?->song?->artist,
+ 'album' => $np->now_playing?->song?->album,
],
]
);
diff --git a/src/Webhook/Connector/Twitter.php b/src/Webhook/Connector/Twitter.php
index 4a594fe88..a39c5b316 100644
--- a/src/Webhook/Connector/Twitter.php
+++ b/src/Webhook/Connector/Twitter.php
@@ -1,5 +1,7 @@
writeElement($branchName, (string)$value);
}
- } elseif (is_array($value)) {
- $this->addBranch($key, $value, $writer);
- } elseif (str_starts_with($key, '@')) {
- $writer->writeAttribute(substr($key, 1), (string)$value);
} else {
- $writer->writeElement($key, (string)$value);
+ /** @var string $key */
+ if (is_array($value)) {
+ $this->addBranch($key, $value, $writer);
+ } elseif (str_starts_with($key, '@')) {
+ $writer->writeAttribute(substr($key, 1), (string)$value);
+ } else {
+ $writer->writeElement($key, (string)$value);
+ }
}
}
@@ -118,13 +123,13 @@ class Writer extends Xml
}
}
- protected function attributesFirst($a, $b): int
+ protected function attributesFirst(mixed $a, mixed $b): int
{
- if (str_starts_with($a, '@')) {
+ if (str_starts_with((string)$a, '@')) {
return -1;
}
- if (str_starts_with($b, '@')) {
+ if (str_starts_with((string)$b, '@')) {
return 1;
}
diff --git a/templates/admin/backups/run.phtml b/templates/admin/backups/run.phtml
index 8fce4a022..5d295b1ab 100644
--- a/templates/admin/backups/run.phtml
+++ b/templates/admin/backups/run.phtml
@@ -26,7 +26,7 @@ $this->layout('main', [
=$this->fetch('partials/log_inline', [
- 'url' => $router->fromHere('admin:backups:log', ['path' => $outputLog]),
+ 'url' => (string)$router->fromHere('admin:backups:log', ['path' => $outputLog]),
])?>
diff --git a/templates/admin/debug/sync.phtml b/templates/admin/debug/sync.phtml
index c680bff20..32f93ce20 100644
--- a/templates/admin/debug/sync.phtml
+++ b/templates/admin/debug/sync.phtml
@@ -25,7 +25,7 @@ $this->layout('main', [
=$this->fetch('partials/log_inline', [
- 'url' => $router->fromHere('admin:debug:log', ['path' => $outputLog]),
+ 'url' => (string)$router->fromHere('admin:debug:log', ['path' => $outputLog]),
])?>
diff --git a/templates/admin/storage_locations/index.phtml b/templates/admin/storage_locations/index.phtml
index 9171cdc32..1d7f41bae 100644
--- a/templates/admin/storage_locations/index.phtml
+++ b/templates/admin/storage_locations/index.phtml
@@ -8,8 +8,9 @@ $this->layout(
]
);
+/** @var App\Http\RouterInterface $router */
$props = [
- 'listUrl' => $router->fromHere('api:admin:storage_locations'),
+ 'listUrl' => (string)$router->fromHere('api:admin:storage_locations'),
];
/** @var \App\Assets $assets */
diff --git a/templates/frontend/account/login.phtml b/templates/frontend/account/login.phtml
index 3766f0dcc..a30a14ef6 100644
--- a/templates/frontend/account/login.phtml
+++ b/templates/frontend/account/login.phtml
@@ -70,7 +70,7 @@ $this->layout(
=__('Please log in to continue.')?> =sprintf(
'%s ',
- $router->named('account:forgot'),
+ (string)$router->named('account:forgot'),
__('Forgot your password?')
)?>
diff --git a/templates/frontend/public/index.phtml b/templates/frontend/public/index.phtml
index 5543368aa..ca017bfa7 100644
--- a/templates/frontend/public/index.phtml
+++ b/templates/frontend/public/index.phtml
@@ -53,7 +53,7 @@ $assets->addInlineJs(
$this->push('head');
?>
-
+
diff --git a/templates/frontend/public/ondemand.phtml b/templates/frontend/public/ondemand.phtml
index b60f52cd7..0e948c11b 100644
--- a/templates/frontend/public/ondemand.phtml
+++ b/templates/frontend/public/ondemand.phtml
@@ -15,7 +15,7 @@ $this->layout(
);
$props = [
- 'listUrl' => $router->fromHere('api:stations:ondemand:list'),
+ 'listUrl' => (string)$router->fromHere('api:stations:ondemand:list'),
'showDownloadButton' => $station->getEnableOnDemandDownload(),
'customFields' => $custom_fields,
'stationName' => $station->getName(),
diff --git a/templates/frontend/public/podcasts.phtml b/templates/frontend/public/podcasts.phtml
index febb83c13..b2c01645c 100644
--- a/templates/frontend/public/podcasts.phtml
+++ b/templates/frontend/public/podcasts.phtml
@@ -19,7 +19,7 @@ $this->layout('minimal', [
named(
+ $episodesPageLink = (string)$router->named(
'public:podcast:episodes',
[
'station_id' => $station->getId(),
@@ -27,7 +27,7 @@ $this->layout('minimal', [
]
) ?>
named(
+ $feedLink = (string)$router->named(
'public:podcast:feed',
['station_id' => $station->getId(), 'podcast_id' => $podcast->getId()]
) ?>
diff --git a/templates/stations/files/index.phtml b/templates/stations/files/index.phtml
index 303913ca0..c589310af 100644
--- a/templates/stations/files/index.phtml
+++ b/templates/stations/files/index.phtml
@@ -10,13 +10,14 @@ $this->layout(
]
);
+/** @var App\Http\RouterInterface $router */
$props = [
- 'listUrl' => $router->fromHere('api:stations:files:list'),
- 'batchUrl' => $router->fromHere('api:stations:files:batch'),
- 'uploadUrl' => $router->fromHere('api:stations:files:upload'),
- 'listDirectoriesUrl' => $router->fromHere('api:stations:files:directories'),
- 'mkdirUrl' => $router->fromHere('api:stations:files:mkdir'),
- 'renameUrl' => $router->fromHere('api:stations:files:rename'),
+ 'listUrl' => (string)$router->fromHere('api:stations:files:list'),
+ 'batchUrl' => (string)$router->fromHere('api:stations:files:batch'),
+ 'uploadUrl' => (string)$router->fromHere('api:stations:files:upload'),
+ 'listDirectoriesUrl' => (string)$router->fromHere('api:stations:files:directories'),
+ 'mkdirUrl' => (string)$router->fromHere('api:stations:files:mkdir'),
+ 'renameUrl' => (string)$router->fromHere('api:stations:files:rename'),
'initialPlaylists' => $playlists,
'customFields' => $custom_fields,
'validMimeTypes' => $mime_types,
diff --git a/templates/stations/mounts/index.phtml b/templates/stations/mounts/index.phtml
index 3d8c59a56..03fc9fe59 100644
--- a/templates/stations/mounts/index.phtml
+++ b/templates/stations/mounts/index.phtml
@@ -8,8 +8,9 @@ $this->layout(
]
);
+/** @var App\Http\RouterInterface $router */
$props = [
- 'listUrl' => $router->fromHere('api:stations:mounts'),
+ 'listUrl' => (string)$router->fromHere('api:stations:mounts'),
'stationFrontendType' => $station->getFrontendType(),
'enableAdvancedFeatures' => $enableAdvancedFeatures,
];
diff --git a/templates/stations/playlists/index.phtml b/templates/stations/playlists/index.phtml
index 93236e59a..107d59369 100644
--- a/templates/stations/playlists/index.phtml
+++ b/templates/stations/playlists/index.phtml
@@ -8,11 +8,12 @@ $this->layout(
]
);
+/** @var App\Http\RouterInterface $router */
$props = [
- 'listUrl' => $router->fromHere('api:stations:playlists'),
- 'scheduleUrl' => $router->fromHere('api:stations:playlists:schedule'),
+ 'listUrl' => (string)$router->fromHere('api:stations:playlists'),
+ 'scheduleUrl' => (string)$router->fromHere('api:stations:playlists:schedule'),
'locale' => substr($customization->getLocale()->getLocale(), 0, 2),
- 'filesUrl' => $router->fromHere('stations:files:index'),
+ 'filesUrl' => (string)$router->fromHere('stations:files:index'),
'stationTimeZone' => $station_tz,
'enableAdvancedFeatures' => $enableAdvancedFeatures,
];
diff --git a/templates/stations/podcasts/index.phtml b/templates/stations/podcasts/index.phtml
index f71b30178..9ea4d89ae 100644
--- a/templates/stations/podcasts/index.phtml
+++ b/templates/stations/podcasts/index.phtml
@@ -4,10 +4,11 @@ $this->layout('main', [
'manual' => true,
]);
+/** @var App\Http\RouterInterface $router */
$props = [
- 'listUrl' => $router->fromHere('api:stations:podcasts'),
- 'newArtUrl' => $router->fromHere('api:stations:podcasts:new-art'),
- 'stationUrl' => $router->fromHere('stations:index:index', [$stationId]),
+ 'listUrl' => (string)$router->fromHere('api:stations:podcasts'),
+ 'newArtUrl' => (string)$router->fromHere('api:stations:podcasts:new-art'),
+ 'stationUrl' => (string)$router->fromHere('stations:index:index', [$stationId]),
'locale' => substr($customization->getLocale(), 0, 2),
'stationTimeZone' => $stationTz,
'languageOptions' => $languageOptions,
diff --git a/templates/stations/profile/index.phtml b/templates/stations/profile/index.phtml
index 13b508827..e9a611dae 100644
--- a/templates/stations/profile/index.phtml
+++ b/templates/stations/profile/index.phtml
@@ -1,7 +1,8 @@
layout(
@@ -25,7 +26,7 @@ $props = [
'enableStreamers' => $station->getEnableStreamers(),
'enablePublicPage' => $station->getEnablePublicPage(),
'enableOnDemand' => $station->getEnableOnDemand(),
- 'profileApiUri' => $router->fromHere('api:stations:profile'),
+ 'profileApiUri' => (string)$router->fromHere('api:stations:profile'),
// ACL
'userCanManageMedia' => $acl->isAllowed(App\Acl::STATION_MEDIA, $station->getId()),
@@ -37,19 +38,25 @@ $props = [
// Header
'stationName' => $station->getName(),
'stationDescription' => $station->getDescription(),
- 'manageProfileUri' => $router->fromHere('stations:profile:edit'),
+ 'manageProfileUri' => (string)$router->fromHere('stations:profile:edit'),
// Now Playing
- 'backendSkipSongUri' => $router->fromHere('api:stations:backend', ['do' => 'skip']),
- 'backendDisconnectStreamerUri' => $router->fromHere('api:stations:backend', ['do' => 'disconnect']),
+ 'backendSkipSongUri' => (string)$router->fromHere('api:stations:backend', ['do' => 'skip']),
+ 'backendDisconnectStreamerUri' => (string)$router->fromHere('api:stations:backend', ['do' => 'disconnect']),
// Requests
- 'requestsViewUri' => $router->fromHere('stations:reports:requests'),
- 'requestsToggleUri' => $router->fromHere('stations:profile:toggle', ['feature' => 'requests', 'csrf' => $csrf]),
+ 'requestsViewUri' => (string)$router->fromHere('stations:reports:requests'),
+ 'requestsToggleUri' => (string)$router->fromHere(
+ 'stations:profile:toggle',
+ ['feature' => 'requests', 'csrf' => $csrf]
+ ),
// Streamers
- 'streamersViewUri' => $router->fromHere('stations:streamers:index'),
- 'streamersToggleUri' => $router->fromHere('stations:profile:toggle', ['feature' => 'streamers', 'csrf' => $csrf]),
+ 'streamersViewUri' => (string)$router->fromHere('stations:streamers:index'),
+ 'streamersToggleUri' => (string)$router->fromHere(
+ 'stations:profile:toggle',
+ ['feature' => 'streamers', 'csrf' => $csrf]
+ ),
// Public Pages
'publicPageUri' => (string)$router->named('public:index', ['station_id' => $station->getShortName()], [], true),
@@ -103,7 +110,7 @@ $props = [
true
),
- 'togglePublicPageUri' => $router->fromHere(
+ 'togglePublicPageUri' => (string)$router->fromHere(
'stations:profile:toggle',
['feature' => 'public', 'csrf' => $csrf]
),
@@ -113,18 +120,18 @@ $props = [
'frontendAdminPassword' => $frontendConfig->getAdminPassword(),
'frontendSourcePassword' => $frontendConfig->getSourcePassword(),
'frontendRelayPassword' => $frontendConfig->getRelayPassword(),
- 'frontendRestartUri' => $router->fromHere('api:stations:frontend', ['do' => 'restart']),
- 'frontendStartUri' => $router->fromHere('api:stations:frontend', ['do' => 'start']),
- 'frontendStopUri' => $router->fromHere('api:stations:frontend', ['do' => 'stop']),
+ 'frontendRestartUri' => (string)$router->fromHere('api:stations:frontend', ['do' => 'restart']),
+ 'frontendStartUri' => (string)$router->fromHere('api:stations:frontend', ['do' => 'start']),
+ 'frontendStopUri' => (string)$router->fromHere('api:stations:frontend', ['do' => 'stop']),
// Backend
'numSongs' => (int)$num_songs,
'numPlaylists' => (int)$num_playlists,
- 'manageMediaUri' => $router->fromHere('stations:files:index'),
- 'managePlaylistsUri' => $router->fromHere('stations:playlists:index'),
- 'backendRestartUri' => $router->fromHere('api:stations:backend', ['do' => 'restart']),
- 'backendStartUri' => $router->fromHere('api:stations:backend', ['do' => 'start']),
- 'backendStopUri' => $router->fromHere('api:stations:backend', ['do' => 'stop']),
+ 'manageMediaUri' => (string)$router->fromHere('stations:files:index'),
+ 'managePlaylistsUri' => (string)$router->fromHere('stations:playlists:index'),
+ 'backendRestartUri' => (string)$router->fromHere('api:stations:backend', ['do' => 'restart']),
+ 'backendStartUri' => (string)$router->fromHere('api:stations:backend', ['do' => 'start']),
+ 'backendStopUri' => (string)$router->fromHere('api:stations:backend', ['do' => 'stop']),
];
$assets
diff --git a/templates/stations/queue/index.phtml b/templates/stations/queue/index.phtml
index 65751abc4..989adbc8f 100644
--- a/templates/stations/queue/index.phtml
+++ b/templates/stations/queue/index.phtml
@@ -2,8 +2,9 @@
$this->layout('main', ['title' => __('Upcoming Song Queue'), 'manual' => true]);
+/** @var App\Http\RouterInterface $router */
$props = [
- 'listUrl' => $router->fromHere('api:stations:queue'),
+ 'listUrl' => (string)$router->fromHere('api:stations:queue'),
'locale' => substr($customization->getLocale(), 0, 2),
'stationTimeZone' => $stationTz,
];
diff --git a/templates/stations/reports/overview.phtml b/templates/stations/reports/overview.phtml
index b030859cd..4c7f8a7cf 100644
--- a/templates/stations/reports/overview.phtml
+++ b/templates/stations/reports/overview.phtml
@@ -6,6 +6,7 @@
$this->layout('main', ['title' => __('Statistics Overview'), 'manual' => true]);
+/** @var App\Http\RouterInterface $router */
$props = [
'chartsUrl' => (string)$router->fromHere('api:stations:reports:overview-charts'),
'bestAndWorstUrl' => (string)$router->fromHere('api:stations:reports:best-and-worst'),
diff --git a/templates/stations/streamers/index.phtml b/templates/stations/streamers/index.phtml
index ec83fbcd2..95afa2f99 100644
--- a/templates/stations/streamers/index.phtml
+++ b/templates/stations/streamers/index.phtml
@@ -2,14 +2,15 @@
$this->layout('main', ['title' => __('Streamer/DJ Accounts'), 'manual' => true]);
+/** @var App\Http\RouterInterface $router */
$props = [
- 'listUrl' => $router->fromHere('api:stations:streamers'),
- 'scheduleUrl' => $router->fromHere('api:stations:streamers:schedule'),
+ 'listUrl' => (string)$router->fromHere('api:stations:streamers'),
+ 'scheduleUrl' => (string)$router->fromHere('api:stations:streamers:schedule'),
'locale' => substr($customization->getLocale(), 0, 2),
'stationTimeZone' => $station_tz,
];
-/** @var \App\Assets $assets */
+/** @var App\Assets $assets */
$assets->addVueRender('Vue_StationsStreamers', '#station-streamers', $props);
?>
diff --git a/web/index.php b/web/index.php
index eaffdec72..40f8e8d2e 100755
--- a/web/index.php
+++ b/web/index.php
@@ -1,6 +1,9 @@