Code quality cleanup sweep.

This commit is contained in:
Buster "Silver Eagle" Neece 2022-05-08 13:05:02 -05:00
parent fe61967c50
commit a9f066602c
No known key found for this signature in database
GPG Key ID: 9FC8B9E008872109
87 changed files with 245 additions and 283 deletions

View File

@ -6,10 +6,9 @@ declare(strict_types=1);
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT); error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT);
ini_set('display_errors', '1'); ini_set('display_errors', '1');
$autoloader = require dirname(__DIR__) . '/vendor/autoload.php'; require dirname(__DIR__) . '/vendor/autoload.php';
$cli = App\AppFactory::createCli( $cli = App\AppFactory::createCli(
$autoloader,
[ [
App\Environment::BASE_DIR => dirname(__DIR__), App\Environment::BASE_DIR => dirname(__DIR__),
] ]

View File

@ -18,6 +18,7 @@
"ext-iconv": "*", "ext-iconv": "*",
"ext-intl": "*", "ext-intl": "*",
"ext-json": "*", "ext-json": "*",
"ext-libxml": "*",
"ext-maxminddb": "*", "ext-maxminddb": "*",
"ext-mbstring": "*", "ext-mbstring": "*",
"ext-redis": "*", "ext-redis": "*",

View File

@ -8,10 +8,8 @@ use App\Console\Application;
use App\Enums\SupportedLocales; use App\Enums\SupportedLocales;
use App\Http\Factory\ResponseFactory; use App\Http\Factory\ResponseFactory;
use App\Http\Factory\ServerRequestFactory; use App\Http\Factory\ServerRequestFactory;
use Composer\Autoload\ClassLoader;
use DI; use DI;
use DI\Bridge\Slim\ControllerInvoker; use DI\Bridge\Slim\ControllerInvoker;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Invoker\Invoker; use Invoker\Invoker;
use Invoker\ParameterResolver\AssociativeArrayResolver; use Invoker\ParameterResolver\AssociativeArrayResolver;
use Invoker\ParameterResolver\Container\TypeHintContainerResolver; use Invoker\ParameterResolver\Container\TypeHintContainerResolver;
@ -31,33 +29,19 @@ use const E_USER_ERROR;
class AppFactory class AppFactory
{ {
/**
* @param ClassLoader|null $autoloader
* @param array<string, mixed> $appEnvironment
* @param array<string, mixed> $diDefinitions
*
*/
public static function createApp( public static function createApp(
?ClassLoader $autoloader = null,
array $appEnvironment = [], array $appEnvironment = [],
array $diDefinitions = [] array $diDefinitions = []
): App { ): App {
$di = self::buildContainer($autoloader, $appEnvironment, $diDefinitions); $di = self::buildContainer($appEnvironment, $diDefinitions);
return self::buildAppFromContainer($di); return self::buildAppFromContainer($di);
} }
/**
* @param ClassLoader|null $autoloader
* @param array<string, mixed> $appEnvironment
* @param array<string, mixed> $diDefinitions
*
*/
public static function createCli( public static function createCli(
?ClassLoader $autoloader = null,
array $appEnvironment = [], array $appEnvironment = [],
array $diDefinitions = [] array $diDefinitions = []
): Application { ): Application {
$di = self::buildContainer($autoloader, $appEnvironment, $diDefinitions); $di = self::buildContainer($appEnvironment, $diDefinitions);
self::buildAppFromContainer($di); self::buildAppFromContainer($di);
$env = $di->get(Environment::class); $env = $di->get(Environment::class);
@ -106,24 +90,10 @@ class AppFactory
return $app; return $app;
} }
/**
* @param ClassLoader|null $autoloader
* @param array<string, mixed> $appEnvironment
* @param array<string, mixed> $diDefinitions
*
* @noinspection SummerTimeUnsafeTimeManipulationInspection
*
*/
public static function buildContainer( public static function buildContainer(
?ClassLoader $autoloader = null,
array $appEnvironment = [], array $appEnvironment = [],
array $diDefinitions = [] array $diDefinitions = []
): DI\Container { ): DI\Container {
// Register Annotation autoloader
if (null !== $autoloader) {
AnnotationRegistry::registerLoader([$autoloader, 'loadClass']);
}
$environment = self::buildEnvironment($appEnvironment); $environment = self::buildEnvironment($appEnvironment);
Environment::setInstance($environment); Environment::setInstance($environment);
@ -132,22 +102,20 @@ class AppFactory
// Override DI definitions for settings. // Override DI definitions for settings.
$diDefinitions[Environment::class] = $environment; $diDefinitions[Environment::class] = $environment;
if ($autoloader) { $plugins = new Plugins($environment->getBaseDirectory() . '/plugins');
$plugins = new Plugins($environment->getBaseDirectory() . '/plugins');
$diDefinitions[Plugins::class] = $plugins; $diDefinitions[Plugins::class] = $plugins;
$diDefinitions = $plugins->registerServices($diDefinitions); $diDefinitions = $plugins->registerServices($diDefinitions);
}
$containerBuilder = new DI\ContainerBuilder(); $containerBuilder = new DI\ContainerBuilder();
$containerBuilder->useAutowiring(true); $containerBuilder->useAutowiring(true);
/* // TODO Implement APCu
$containerBuilder->enableDefinitionCache(); // $containerBuilder->enableDefinitionCache();
if ($environment->isProduction()) { if ($environment->isProduction()) {
$containerBuilder->enableCompilation($environment->getTempDirectory()); $containerBuilder->enableCompilation($environment->getTempDirectory());
} }
*/
$containerBuilder->addDefinitions($diDefinitions); $containerBuilder->addDefinitions($diDefinitions);

View File

@ -253,9 +253,9 @@ class Assets
$this->addInlineJs( $this->addInlineJs(
<<<JS <<<JS
let ${name}; let {$name};
$(function () { $(function () {
${name} = ${nameWithoutPrefix}.default('${elementId}', ${propsJson}); {$name} = {$nameWithoutPrefix}.default('{$elementId}', {$propsJson});
}); });
JS JS
); );

View File

@ -15,6 +15,7 @@ use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Path; use Symfony\Component\Filesystem\Path;
use Throwable;
use const PATHINFO_EXTENSION; use const PATHINFO_EXTENSION;
@ -101,7 +102,7 @@ class BackupCommand extends AbstractBackupCommand
$tmp_dir_mariadb = '/tmp/azuracast_backup_mariadb'; $tmp_dir_mariadb = '/tmp/azuracast_backup_mariadb';
try { try {
$fsUtils->mkdir($tmp_dir_mariadb); $fsUtils->mkdir($tmp_dir_mariadb);
} catch (\Throwable $e) { } catch (Throwable $e) {
$io->error($e->getMessage()); $io->error($e->getMessage());
return 1; return 1;
} }

View File

@ -9,6 +9,7 @@ use App\Entity;
use App\Entity\Repository\StationRepository; use App\Entity\Repository\StationRepository;
use App\Radio\Backend\Liquidsoap\Command\AbstractCommand; use App\Radio\Backend\Liquidsoap\Command\AbstractCommand;
use App\Radio\Enums\LiquidsoapCommands; use App\Radio\Enums\LiquidsoapCommands;
use LogicException;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
@ -17,6 +18,7 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Style\SymfonyStyle;
use Throwable;
#[AsCommand( #[AsCommand(
name: 'azuracast:internal:liquidsoap', name: 'azuracast:internal:liquidsoap',
@ -62,12 +64,12 @@ class LiquidsoapCommand extends CommandAbstract
try { try {
$station = $this->stationRepo->findByIdentifier($stationId); $station = $this->stationRepo->findByIdentifier($stationId);
if (!($station instanceof Entity\Station)) { if (!($station instanceof Entity\Station)) {
throw new \LogicException('Station not found.'); throw new LogicException('Station not found.');
} }
$command = LiquidsoapCommands::tryFrom($action); $command = LiquidsoapCommands::tryFrom($action);
if (null === $command || !$this->di->has($command->getClass())) { if (null === $command || !$this->di->has($command->getClass())) {
throw new \LogicException('Command not found.'); throw new LogicException('Command not found.');
} }
/** @var AbstractCommand $commandObj */ /** @var AbstractCommand $commandObj */
@ -75,7 +77,7 @@ class LiquidsoapCommand extends CommandAbstract
$result = $commandObj->run($station, $asAutoDj, $payload); $result = $commandObj->run($station, $asAutoDj, $payload);
$io->writeln($result); $io->writeln($result);
} catch (\Throwable $e) { } catch (Throwable $e) {
$this->logger->error( $this->logger->error(
sprintf( sprintf(
'Liquidsoap command "%s" error: %s', 'Liquidsoap command "%s" error: %s',

View File

@ -65,7 +65,7 @@ class SftpEventCommand extends CommandAbstract
$storageLocation = $sftpUser->getStation()->getMediaStorageLocation(); $storageLocation = $sftpUser->getStation()->getMediaStorageLocation();
if (!$storageLocation->isLocal()) { if (!$storageLocation->isLocal()) {
$this->logger->error(sprintf('Storage location "%s" is not local.', (string)$storageLocation)); $this->logger->error(sprintf('Storage location "%s" is not local.', $storageLocation));
return 1; return 1;
} }

View File

@ -14,6 +14,7 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Style\SymfonyStyle;
use Throwable;
#[AsCommand( #[AsCommand(
name: 'azuracast:radio:restart', name: 'azuracast:radio:restart',
@ -73,7 +74,7 @@ class RestartRadioCommand extends CommandAbstract
reloadSupervisor: !$noSupervisorRestart, reloadSupervisor: !$noSupervisorRestart,
forceRestart: true forceRestart: true
); );
} catch (\Throwable $e) { } catch (Throwable $e) {
$io->error([ $io->error([
$station . ': ' . $e->getMessage(), $station . ': ' . $e->getMessage(),
]); ]);

View File

@ -12,6 +12,8 @@ use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Lock\Lock; use Symfony\Component\Lock\Lock;
use Symfony\Component\Process\Process; use Symfony\Component\Process\Process;
use function random_int;
abstract class AbstractSyncCommand extends CommandAbstract abstract class AbstractSyncCommand extends CommandAbstract
{ {
protected array $processes = []; protected array $processes = [];
@ -34,7 +36,7 @@ abstract class AbstractSyncCommand extends CommandAbstract
$process = $processGroup['process']; $process = $processGroup['process'];
// 10% chance that refresh will be called // 10% chance that refresh will be called
if (\random_int(1, 100) <= 10) { if (random_int(1, 100) <= 10) {
$lock->refresh(); $lock->refresh();
} }

View File

@ -15,6 +15,8 @@ use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Style\SymfonyStyle;
use function random_int;
#[AsCommand( #[AsCommand(
name: 'azuracast:sync:nowplaying', name: 'azuracast:sync:nowplaying',
description: 'Task to run the Now Playing worker task.' description: 'Task to run the Now Playing worker task.'
@ -81,7 +83,7 @@ class NowPlayingCommand extends AbstractSyncCommand
if (!isset($this->processes[$shortName])) { if (!isset($this->processes[$shortName])) {
$npTimestamp = (int)$activeStation['nowplaying_timestamp']; $npTimestamp = (int)$activeStation['nowplaying_timestamp'];
if (time() > $npTimestamp + \random_int(5, 15)) { if (time() > $npTimestamp + random_int(5, 15)) {
$this->start($io, $shortName); $this->start($io, $shortName);
usleep(250000); usleep(250000);

View File

@ -15,6 +15,7 @@ use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Style\SymfonyStyle;
use Throwable;
#[AsCommand( #[AsCommand(
name: 'azuracast:sync:nowplaying:station', name: 'azuracast:sync:nowplaying:station',
@ -63,7 +64,7 @@ class NowPlayingPerStationCommand extends CommandAbstract
try { try {
$this->buildQueueTask->run($station); $this->buildQueueTask->run($station);
} catch (\Throwable $e) { } catch (Throwable $e) {
$this->logger->error( $this->logger->error(
'Queue builder error: ' . $e->getMessage(), 'Queue builder error: ' . $e->getMessage(),
['exception' => $e] ['exception' => $e]
@ -72,7 +73,7 @@ class NowPlayingPerStationCommand extends CommandAbstract
try { try {
$this->nowPlayingTask->run($station); $this->nowPlayingTask->run($station);
} catch (\Throwable $e) { } catch (Throwable $e) {
$this->logger->error( $this->logger->error(
'Now Playing error: ' . $e->getMessage(), 'Now Playing error: ' . $e->getMessage(),
['exception' => $e] ['exception' => $e]

View File

@ -8,8 +8,10 @@ use App\Entity\Repository\SettingsRepository;
use App\Environment; use App\Environment;
use App\Event\GetSyncTasks; use App\Event\GetSyncTasks;
use App\LockFactory; use App\LockFactory;
use App\Sync\Task\AbstractTask;
use Carbon\CarbonImmutable; use Carbon\CarbonImmutable;
use Cron\CronExpression; use Cron\CronExpression;
use DateTimeZone;
use Psr\EventDispatcher\EventDispatcherInterface; use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
@ -17,6 +19,8 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Style\SymfonyStyle;
use function usleep;
#[AsCommand( #[AsCommand(
name: 'azuracast:sync:run', name: 'azuracast:sync:run',
description: 'Task to run the minute\'s synchronized tasks.' description: 'Task to run the minute\'s synchronized tasks.'
@ -46,7 +50,7 @@ class RunnerCommand extends AbstractSyncCommand
$syncTasksEvent = new GetSyncTasks(); $syncTasksEvent = new GetSyncTasks();
$this->dispatcher->dispatch($syncTasksEvent); $this->dispatcher->dispatch($syncTasksEvent);
$now = CarbonImmutable::now(new \DateTimeZone('UTC')); $now = CarbonImmutable::now(new DateTimeZone('UTC'));
foreach ($syncTasksEvent->getTasks() as $taskClass) { foreach ($syncTasksEvent->getTasks() as $taskClass) {
$schedulePattern = $taskClass::getSchedulePattern(); $schedulePattern = $taskClass::getSchedulePattern();
@ -71,12 +75,12 @@ class RunnerCommand extends AbstractSyncCommand
$this->checkRunningProcesses(); $this->checkRunningProcesses();
} }
\usleep(250000); usleep(250000);
} }
/** /**
* @param SymfonyStyle $io * @param SymfonyStyle $io
* @param class-string $taskClass * @param class-string<AbstractTask> $taskClass
*/ */
protected function start( protected function start(
SymfonyStyle $io, SymfonyStyle $io,

View File

@ -6,9 +6,11 @@ namespace App\Console\Command\Sync;
use App\Console\Command\CommandAbstract; use App\Console\Command\CommandAbstract;
use App\Sync\Task\AbstractTask; use App\Sync\Task\AbstractTask;
use InvalidArgumentException;
use Monolog\Logger; use Monolog\Logger;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
use Psr\SimpleCache\CacheInterface; use Psr\SimpleCache\CacheInterface;
use ReflectionClass;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
@ -43,7 +45,7 @@ class SingleTaskCommand extends CommandAbstract
try { try {
$this->runTask($task); $this->runTask($task);
} catch (\InvalidArgumentException $e) { } catch (InvalidArgumentException $e) {
$io->error($e->getMessage()); $io->error($e->getMessage());
return 1; return 1;
} }
@ -60,12 +62,12 @@ class SingleTaskCommand extends CommandAbstract
bool $force = false bool $force = false
): void { ): void {
if (!$this->di->has($task)) { if (!$this->di->has($task)) {
throw new \InvalidArgumentException('Task not found.'); throw new InvalidArgumentException('Task not found.');
} }
$taskClass = $this->di->get($task); $taskClass = $this->di->get($task);
if (!($taskClass instanceof AbstractTask)) { if (!($taskClass instanceof AbstractTask)) {
throw new \InvalidArgumentException('Specified class is not a synchronized task.'); throw new InvalidArgumentException('Specified class is not a synchronized task.');
} }
$taskShortName = self::getClassShortName($task); $taskShortName = self::getClassShortName($task);
@ -106,6 +108,6 @@ class SingleTaskCommand extends CommandAbstract
*/ */
public static function getClassShortName(string $taskClass): string public static function getClassShortName(string $taskClass): string
{ {
return (new \ReflectionClass($taskClass))->getShortName(); return (new ReflectionClass($taskClass))->getShortName();
} }
} }

View File

@ -19,6 +19,7 @@ use App\Session\Flash;
use App\Sync\NowPlaying\Task\NowPlayingTask; use App\Sync\NowPlaying\Task\NowPlayingTask;
use Carbon\CarbonImmutable; use Carbon\CarbonImmutable;
use Cron\CronExpression; use Cron\CronExpression;
use DateTimeZone;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Monolog\Handler\TestHandler; use Monolog\Handler\TestHandler;
use Monolog\Logger; use Monolog\Logger;
@ -55,7 +56,7 @@ class DebugController extends AbstractLogViewerController
} }
$syncTimes = []; $syncTimes = [];
$now = CarbonImmutable::now(new \DateTimeZone('UTC')); $now = CarbonImmutable::now(new DateTimeZone('UTC'));
$syncTasksEvent = new GetSyncTasks(); $syncTasksEvent = new GetSyncTasks();
$dispatcher->dispatch($syncTasksEvent); $dispatcher->dispatch($syncTasksEvent);

View File

@ -7,6 +7,7 @@ namespace App\Controller\Admin;
use App\Http\Response; use App\Http\Response;
use App\Http\ServerRequest; use App\Http\ServerRequest;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use RuntimeException;
class ShoutcastAction class ShoutcastAction
{ {
@ -15,7 +16,7 @@ class ShoutcastAction
Response $response Response $response
): ResponseInterface { ): ResponseInterface {
if ('x86_64' !== php_uname('m')) { if ('x86_64' !== php_uname('m')) {
throw new \RuntimeException('SHOUTcast cannot be installed on non-X86_64 systems.'); throw new RuntimeException('SHOUTcast cannot be installed on non-X86_64 systems.');
} }
$router = $request->getRouter(); $router = $request->getRouter();

View File

@ -28,9 +28,9 @@ class StationsAction
props: array_merge( props: array_merge(
$stationFormComponent->getProps($request), $stationFormComponent->getProps($request),
[ [
'listUrl' => (string)$router->fromHere('api:admin:stations'), 'listUrl' => (string)$router->fromHere('api:admin:stations'),
'frontendTypes' => $adapters->listFrontendAdapters(false), 'frontendTypes' => $adapters->listFrontendAdapters(),
'backendTypes' => $adapters->listBackendAdapters(false), 'backendTypes' => $adapters->listBackendAdapters(),
] ]
) )
); );

View File

@ -21,6 +21,6 @@ class GetLogAction
): ResponseInterface { ): ResponseInterface {
$logPath = File::validateTempPath($path); $logPath = File::validateTempPath($path);
return $this->streamLogToResponse($request, $response, $logPath, true); return $this->streamLogToResponse($request, $response, $logPath);
} }
} }

View File

@ -10,6 +10,7 @@ use App\Http\Response;
use App\Http\ServerRequest; use App\Http\ServerRequest;
use App\Service\Flow; use App\Service\Flow;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use RuntimeException;
use Symfony\Component\Process\Process; use Symfony\Component\Process\Process;
class PostAction class PostAction
@ -20,7 +21,7 @@ class PostAction
Environment $environment Environment $environment
): ResponseInterface { ): ResponseInterface {
if ('x86_64' !== php_uname('m')) { if ('x86_64' !== php_uname('m')) {
throw new \RuntimeException('SHOUTcast cannot be installed on non-X86_64 systems.'); throw new RuntimeException('SHOUTcast cannot be installed on non-X86_64 systems.');
} }
$flowResponse = Flow::process($request, $response); $flowResponse = Flow::process($request, $response);

View File

@ -12,6 +12,7 @@ use App\Http\ServerRequest;
use DeepCopy; use DeepCopy;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Throwable;
class CloneAction extends StationsController class CloneAction extends StationsController
{ {
@ -186,7 +187,7 @@ class CloneAction extends StationsController
try { try {
$this->configuration->writeConfiguration($newStation); $this->configuration->writeConfiguration($newStation);
} catch (\Throwable $e) { } catch (Throwable $e) {
} }
$this->em->flush(); $this->em->flush();

View File

@ -20,6 +20,7 @@ use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Component\Validator\Validator\ValidatorInterface;
use Throwable;
/** @extends AbstractAdminApiCrudController<Entity\Station> */ /** @extends AbstractAdminApiCrudController<Entity\Station> */
#[ #[
@ -331,7 +332,7 @@ class StationsController extends AbstractAdminApiCrudController
station: $station, station: $station,
forceRestart: true forceRestart: true
); );
} catch (\Throwable $e) { } catch (Throwable $e) {
} }
} }

View File

@ -17,7 +17,7 @@ class DeleteTwoFactorAction extends UsersController
$user = $request->getUser(); $user = $request->getUser();
$user = $this->em->refetch($user); $user = $this->em->refetch($user);
$user->setTwoFactorSecret(null); $user->setTwoFactorSecret();
$this->em->persist($user); $this->em->persist($user);
$this->em->flush(); $this->em->flush();

View File

@ -9,9 +9,12 @@ use App\Http\Response;
use App\Http\ServerRequest; use App\Http\ServerRequest;
use App\Radio\Backend\Liquidsoap\Command\AbstractCommand; use App\Radio\Backend\Liquidsoap\Command\AbstractCommand;
use App\Radio\Enums\LiquidsoapCommands; use App\Radio\Enums\LiquidsoapCommands;
use InvalidArgumentException;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use RuntimeException;
use Throwable;
class LiquidsoapAction class LiquidsoapAction
{ {
@ -31,13 +34,13 @@ class LiquidsoapAction
if (!$acl->isAllowed(StationPermissions::View, $station->getIdRequired())) { if (!$acl->isAllowed(StationPermissions::View, $station->getIdRequired())) {
$authKey = $request->getHeaderLine('X-Liquidsoap-Api-Key'); $authKey = $request->getHeaderLine('X-Liquidsoap-Api-Key');
if (!$station->validateAdapterApiKey($authKey)) { if (!$station->validateAdapterApiKey($authKey)) {
throw new \RuntimeException('Invalid API key.'); throw new RuntimeException('Invalid API key.');
} }
} }
$command = LiquidsoapCommands::tryFrom($action); $command = LiquidsoapCommands::tryFrom($action);
if (null === $command || !$di->has($command->getClass())) { if (null === $command || !$di->has($command->getClass())) {
throw new \InvalidArgumentException('Command not found.'); throw new InvalidArgumentException('Command not found.');
} }
/** @var AbstractCommand $commandObj */ /** @var AbstractCommand $commandObj */
@ -45,7 +48,7 @@ class LiquidsoapAction
$result = $commandObj->run($station, $asAutoDj, $payload); $result = $commandObj->run($station, $asAutoDj, $payload);
$response->getBody()->write((string)$result); $response->getBody()->write((string)$result);
} catch (\Throwable $e) { } catch (Throwable $e) {
$logger->error( $logger->error(
sprintf( sprintf(
'Liquidsoap command "%s" error: %s', 'Liquidsoap command "%s" error: %s',

View File

@ -24,6 +24,7 @@ use InvalidArgumentException;
use League\Flysystem\StorageAttributes; use League\Flysystem\StorageAttributes;
use Psr\EventDispatcher\EventDispatcherInterface; use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use RuntimeException;
use Symfony\Component\Messenger\MessageBus; use Symfony\Component\Messenger\MessageBus;
use Throwable; use Throwable;
@ -306,7 +307,7 @@ class BatchAction
$result = $this->parseRequest($request, $fs, true); $result = $this->parseRequest($request, $fs, true);
if (BackendAdapters::Liquidsoap !== $station->getBackendTypeEnum()) { if (BackendAdapters::Liquidsoap !== $station->getBackendTypeEnum()) {
throw new \RuntimeException('This functionality can only be used on stations that use Liquidsoap.'); throw new RuntimeException('This functionality can only be used on stations that use Liquidsoap.');
} }
/** @var Liquidsoap $backend */ /** @var Liquidsoap $backend */
@ -336,7 +337,7 @@ class BatchAction
$newQueue = Entity\StationQueue::fromMedia($station, $media); $newQueue = Entity\StationQueue::fromMedia($station, $media);
$newQueue->setTimestampCued($cuedTimestamp); $newQueue->setTimestampCued($cuedTimestamp);
$newQueue->setIsPlayed(true); $newQueue->setIsPlayed();
$this->em->persist($newQueue); $this->em->persist($newQueue);
$event = AnnotateNextSong::fromStationQueue($newQueue, true); $event = AnnotateNextSong::fromStationQueue($newQueue, true);

View File

@ -13,6 +13,7 @@ use App\Radio\Backend\Liquidsoap;
use App\Radio\Backend\Liquidsoap\ConfigWriter; use App\Radio\Backend\Liquidsoap\ConfigWriter;
use Psr\EventDispatcher\EventDispatcherInterface; use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Throwable;
class PutAction class PutAction
{ {
@ -45,7 +46,7 @@ class PutAction
$config = $event->buildConfiguration(); $config = $event->buildConfiguration();
$liquidsoap->verifyConfig($config); $liquidsoap->verifyConfig($config);
} catch (\Throwable $e) { } catch (Throwable $e) {
return $response->withStatus(500)->withJson(Entity\Api\Error::fromException($e)); return $response->withStatus(500)->withJson(Entity\Api\Error::fromException($e));
} }

View File

@ -333,7 +333,7 @@ class PodcastEpisodesController extends AbstractApiCrudController
/** /**
* @inheritDoc * @inheritDoc
*/ */
protected function viewRecord(object $record, ServerRequest $request): mixed protected function viewRecord(object $record, ServerRequest $request): Entity\Api\PodcastEpisode
{ {
if (!($record instanceof Entity\PodcastEpisode)) { if (!($record instanceof Entity\PodcastEpisode)) {
throw new InvalidArgumentException(sprintf('Record must be an instance of %s.', $this->entityClass)); throw new InvalidArgumentException(sprintf('Record must be an instance of %s.', $this->entityClass));

View File

@ -268,16 +268,10 @@ class PodcastsController extends AbstractApiCrudController
*/ */
protected function getRecord(Entity\Station $station, string $id): ?object protected function getRecord(Entity\Station $station, string $id): ?object
{ {
$record = $this->podcastRepository->fetchPodcastForStation($station, $id); return $this->podcastRepository->fetchPodcastForStation($station, $id);
return $record;
} }
/** protected function viewRecord(object $record, ServerRequest $request): Entity\Api\Podcast
* @param Entity\Podcast $record
* @param ServerRequest $request
*
*/
protected function viewRecord(object $record, ServerRequest $request): mixed
{ {
if (!($record instanceof Entity\Podcast)) { if (!($record instanceof Entity\Podcast)) {
throw new InvalidArgumentException(sprintf('Record must be an instance of %s.', $this->entityClass)); throw new InvalidArgumentException(sprintf('Record must be an instance of %s.', $this->entityClass));

View File

@ -179,7 +179,7 @@ class RemotesController extends AbstractStationApiCrudController
return $this->listPaginatedFromQuery($request, $response, $qb->getQuery()); return $this->listPaginatedFromQuery($request, $response, $qb->getQuery());
} }
protected function viewRecord(object $record, ServerRequest $request): mixed protected function viewRecord(object $record, ServerRequest $request): Entity\Api\StationRemote
{ {
if (!($record instanceof Entity\StationRemote)) { if (!($record instanceof Entity\StationRemote)) {
throw new InvalidArgumentException( throw new InvalidArgumentException(

View File

@ -14,6 +14,7 @@ use App\Radio\Configuration;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use OpenApi\Attributes as OA; use OpenApi\Attributes as OA;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Throwable;
#[ #[
OA\Get( OA\Get(
@ -136,7 +137,7 @@ class ServicesController
station: $station, station: $station,
forceRestart: true forceRestart: true
); );
} catch (\Throwable $e) { } catch (Throwable $e) {
return $response->withJson( return $response->withJson(
new Entity\Api\Error( new Entity\Api\Error(
500, 500,
@ -159,7 +160,7 @@ class ServicesController
forceRestart: true, forceRestart: true,
attemptReload: false attemptReload: false
); );
} catch (\Throwable $e) { } catch (Throwable $e) {
return $response->withJson( return $response->withJson(
new Entity\Api\Error( new Entity\Api\Error(
500, 500,

View File

@ -37,8 +37,7 @@ class TestLogAction extends AbstractWebhooksAction
return $this->streamLogToResponse( return $this->streamLogToResponse(
$request, $request,
$response, $response,
$tempPath, $tempPath
true
); );
} }
} }

View File

@ -38,7 +38,7 @@ class Customization
$this->user = $request->getAttribute(ServerRequest::ATTR_USER); $this->user = $request->getAttribute(ServerRequest::ATTR_USER);
// Register current theme // Register current theme
$this->theme = $this->determineTheme($request, false); $this->theme = $this->determineTheme($request);
$this->publicTheme = $this->determineTheme($request, true); $this->publicTheme = $this->determineTheme($request, true);
// Register locale // Register locale
@ -114,7 +114,7 @@ class Customization
$publicCss .= <<<CSS $publicCss .= <<<CSS
[data-theme] body.page-minimal { [data-theme] body.page-minimal {
background-image: url('${backgroundUrl}'); background-image: url('{$backgroundUrl}');
} }
CSS; CSS;
} }

View File

@ -16,7 +16,7 @@ class DecoratedEntityManager extends EntityManagerDecorator implements Reloadabl
public function __construct(callable $createEm) public function __construct(callable $createEm)
{ {
parent::__construct($createEm()); parent::__construct($createEm());
$this->createEm = Closure::fromCallable($createEm); $this->createEm = $createEm(...);
} }
/** /**

View File

@ -67,9 +67,7 @@ class NowPlaying implements ResolvableUrlInterface
*/ */
public function resolveUrls(UriInterface $base): void public function resolveUrls(UriInterface $base): void
{ {
if ($this->station instanceof ResolvableUrlInterface) { $this->station->resolveUrls($base);
$this->station->resolveUrls($base);
}
if ($this->now_playing instanceof ResolvableUrlInterface) { if ($this->now_playing instanceof ResolvableUrlInterface) {
$this->now_playing->resolveUrls($base); $this->now_playing->resolveUrls($base);

View File

@ -60,8 +60,6 @@ class SongHistory implements ResolvableUrlInterface
*/ */
public function resolveUrls(UriInterface $base): void public function resolveUrls(UriInterface $base): void
{ {
if ($this->song instanceof ResolvableUrlInterface) { $this->song->resolveUrls($base);
$this->song->resolveUrls($base);
}
} }
} }

View File

@ -55,8 +55,6 @@ class StationQueue implements ResolvableUrlInterface
*/ */
public function resolveUrls(UriInterface $base): void public function resolveUrls(UriInterface $base): void
{ {
if ($this->song instanceof ResolvableUrlInterface) { $this->song->resolveUrls($base);
$this->song->resolveUrls($base);
}
} }
} }

View File

@ -126,11 +126,7 @@ class NowPlayingApiGenerator
?UriInterface $baseUri = null ?UriInterface $baseUri = null
): Entity\Api\NowPlaying\NowPlaying { ): Entity\Api\NowPlaying\NowPlaying {
$np = $station->getNowplaying(); $np = $station->getNowplaying();
if (null !== $np) { return $np ?? $this->offlineApi($station, $baseUri);
return $np;
}
return $this->offlineApi($station, $baseUri);
} }
protected function offlineApi( protected function offlineApi(

View File

@ -5,9 +5,10 @@ declare(strict_types=1);
namespace App\Entity; namespace App\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use JsonSerializable;
#[ORM\Embeddable] #[ORM\Embeddable]
class ListenerDevice implements \JsonSerializable class ListenerDevice implements JsonSerializable
{ {
#[ORM\Column(length: 255)] #[ORM\Column(length: 255)]
protected ?string $client = null; protected ?string $client = null;
@ -57,7 +58,7 @@ class ListenerDevice implements \JsonSerializable
return $this->os_family; return $this->os_family;
} }
public function jsonSerialize(): mixed public function jsonSerialize(): array
{ {
return [ return [
'client' => $this->client, 'client' => $this->client,

View File

@ -5,9 +5,10 @@ declare(strict_types=1);
namespace App\Entity; namespace App\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use JsonSerializable;
#[ORM\Embeddable] #[ORM\Embeddable]
class ListenerLocation implements \JsonSerializable class ListenerLocation implements JsonSerializable
{ {
#[ORM\Column(length: 255, nullable: false)] #[ORM\Column(length: 255, nullable: false)]
protected string $description = 'Unknown'; protected string $description = 'Unknown';
@ -57,7 +58,7 @@ class ListenerLocation implements \JsonSerializable
return $this->lon; return $this->lon;
} }
public function jsonSerialize(): mixed public function jsonSerialize(): array
{ {
return [ return [
'description' => $this->description, 'description' => $this->description,

View File

@ -17,17 +17,17 @@ final class Version20180425050351 extends AbstractMigration
*/ */
public function up(Schema $schema): void public function up(Schema $schema): void
{ {
$this->changeCharset('utf8mb4', 'utf8mb4_bin'); $this->changeCharset('utf8mb4_bin');
} }
private function changeCharset(string $charset, string $collate): void private function changeCharset(string $collate): void
{ {
$sqlLines = [ $sqlLines = [
'ALTER TABLE `station_media` DROP FOREIGN KEY FK_32AADE3AA0BDB2F3', 'ALTER TABLE `station_media` DROP FOREIGN KEY FK_32AADE3AA0BDB2F3',
'ALTER TABLE `song_history` DROP FOREIGN KEY FK_2AD16164A0BDB2F3', 'ALTER TABLE `song_history` DROP FOREIGN KEY FK_2AD16164A0BDB2F3',
'ALTER TABLE `station_media` CONVERT TO CHARACTER SET ' . $charset . ' COLLATE ' . $collate, 'ALTER TABLE `station_media` CONVERT TO CHARACTER SET utf8mb4 COLLATE ' . $collate,
'ALTER TABLE `song_history` CONVERT TO CHARACTER SET ' . $charset . ' COLLATE ' . $collate, 'ALTER TABLE `song_history` CONVERT TO CHARACTER SET utf8mb4 COLLATE ' . $collate,
'ALTER TABLE `songs` CONVERT TO CHARACTER SET ' . $charset . ' COLLATE ' . $collate, 'ALTER TABLE `songs` CONVERT TO CHARACTER SET utf8mb4 COLLATE ' . $collate,
'ALTER TABLE `song_history` ADD CONSTRAINT FK_2AD16164A0BDB2F3 FOREIGN KEY (song_id) REFERENCES songs (id) ON DELETE CASCADE', 'ALTER TABLE `song_history` ADD CONSTRAINT FK_2AD16164A0BDB2F3 FOREIGN KEY (song_id) REFERENCES songs (id) ON DELETE CASCADE',
'ALTER TABLE `station_media` ADD CONSTRAINT FK_32AADE3AA0BDB2F3 FOREIGN KEY (song_id) REFERENCES songs (id) ON DELETE SET NULL', 'ALTER TABLE `station_media` ADD CONSTRAINT FK_32AADE3AA0BDB2F3 FOREIGN KEY (song_id) REFERENCES songs (id) ON DELETE SET NULL',
]; ];
@ -42,6 +42,6 @@ final class Version20180425050351 extends AbstractMigration
*/ */
public function down(Schema $schema): void public function down(Schema $schema): void
{ {
$this->changeCharset('utf8mb4', 'utf8mb4_unicode_ci'); $this->changeCharset('utf8mb4_unicode_ci');
} }
} }

View File

@ -17,10 +17,10 @@ final class Version20180826043500 extends AbstractMigration
*/ */
public function up(Schema $schema): void public function up(Schema $schema): void
{ {
$this->changeCharset('utf8mb4', 'utf8mb4_general_ci'); $this->changeCharset('utf8mb4_general_ci');
} }
private function changeCharset(string $charset, string $collate): void private function changeCharset(string $collate): void
{ {
$db_name = $this->connection->getDatabase() ?? 'azuracast'; $db_name = $this->connection->getDatabase() ?? 'azuracast';
@ -51,7 +51,9 @@ final class Version20180826043500 extends AbstractMigration
]; ];
$sqlLines = [ $sqlLines = [
'ALTER DATABASE ' . $this->connection->quoteIdentifier($db_name) . ' CHARACTER SET = ' . $charset . ' COLLATE = ' . $collate, 'ALTER DATABASE ' . $this->connection->quoteIdentifier(
$db_name
) . ' CHARACTER SET = utf8mb4 COLLATE = ' . $collate,
'ALTER TABLE `song_history` DROP FOREIGN KEY FK_2AD16164A0BDB2F3', 'ALTER TABLE `song_history` DROP FOREIGN KEY FK_2AD16164A0BDB2F3',
'ALTER TABLE `station_media` DROP FOREIGN KEY FK_32AADE3AA0BDB2F3', 'ALTER TABLE `station_media` DROP FOREIGN KEY FK_32AADE3AA0BDB2F3',
]; ];
@ -60,7 +62,11 @@ final class Version20180826043500 extends AbstractMigration
} }
foreach ($tables as $table_name) { foreach ($tables as $table_name) {
$this->addSql('ALTER TABLE ' . $this->connection->quoteIdentifier($table_name) . ' CONVERT TO CHARACTER SET ' . $charset . ' COLLATE ' . $collate); $this->addSql(
'ALTER TABLE ' . $this->connection->quoteIdentifier(
$table_name
) . ' CONVERT TO CHARACTER SET utf8mb4 COLLATE ' . $collate
);
} }
$sqlLines = [ $sqlLines = [
@ -77,6 +83,6 @@ final class Version20180826043500 extends AbstractMigration
*/ */
public function down(Schema $schema): void public function down(Schema $schema): void
{ {
$this->changeCharset('utf8mb4', 'utf8mb4_unicode_ci'); $this->changeCharset('utf8mb4_unicode_ci');
} }
} }

View File

@ -7,25 +7,20 @@ namespace App\Entity\Migration;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration; use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20210620131126 extends AbstractMigration final class Version20210620131126 extends AbstractMigration
{ {
public function getDescription(): string public function getDescription(): string
{ {
return ''; return 'Add "max_listener_duration" to station_mounts table.';
} }
public function up(Schema $schema): void public function up(Schema $schema): void
{ {
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE station_mounts ADD max_listener_duration INT NOT NULL'); $this->addSql('ALTER TABLE station_mounts ADD max_listener_duration INT NOT NULL');
} }
public function down(Schema $schema): void public function down(Schema $schema): void
{ {
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE station_mounts DROP max_listener_duration'); $this->addSql('ALTER TABLE station_mounts DROP max_listener_duration');
} }
} }

View File

@ -7,26 +7,21 @@ namespace App\Entity\Migration;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration; use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20210703185549 extends AbstractMigration final class Version20210703185549 extends AbstractMigration
{ {
public function getDescription(): string public function getDescription(): string
{ {
return ''; return 'Add columns to facilitate "loop once" functionality.';
} }
public function up(Schema $schema): void public function up(Schema $schema): void
{ {
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE station_playlists ADD queue_reset_at INT NOT NULL'); $this->addSql('ALTER TABLE station_playlists ADD queue_reset_at INT NOT NULL');
$this->addSql('ALTER TABLE station_schedules ADD loop_once TINYINT(1) NOT NULL'); $this->addSql('ALTER TABLE station_schedules ADD loop_once TINYINT(1) NOT NULL');
} }
public function down(Schema $schema): void public function down(Schema $schema): void
{ {
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE station_playlists DROP queue_reset_at'); $this->addSql('ALTER TABLE station_playlists DROP queue_reset_at');
$this->addSql('ALTER TABLE station_schedules DROP loop_once'); $this->addSql('ALTER TABLE station_schedules DROP loop_once');
} }

View File

@ -7,25 +7,20 @@ namespace App\Entity\Migration;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration; use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20210805004608 extends AbstractMigration final class Version20210805004608 extends AbstractMigration
{ {
public function getDescription(): string public function getDescription(): string
{ {
return ''; return 'Add author and e-mail to podcast table.';
} }
public function up(Schema $schema): void public function up(Schema $schema): void
{ {
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE podcast ADD author VARCHAR(255) NOT NULL, ADD email VARCHAR(255) NOT NULL'); $this->addSql('ALTER TABLE podcast ADD author VARCHAR(255) NOT NULL, ADD email VARCHAR(255) NOT NULL');
} }
public function down(Schema $schema): void public function down(Schema $schema): void
{ {
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE podcast DROP author, DROP email'); $this->addSql('ALTER TABLE podcast DROP author, DROP email');
} }
} }

View File

@ -7,26 +7,21 @@ namespace App\Entity\Migration;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration; use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20211227232320 extends AbstractMigration final class Version20211227232320 extends AbstractMigration
{ {
public function getDescription(): string public function getDescription(): string
{ {
return ''; return 'Change on-delete behavior of media on song_history table.';
} }
public function up(Schema $schema): void public function up(Schema $schema): void
{ {
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE song_history DROP FOREIGN KEY FK_2AD16164EA9FDD75'); $this->addSql('ALTER TABLE song_history DROP FOREIGN KEY FK_2AD16164EA9FDD75');
$this->addSql('ALTER TABLE song_history ADD CONSTRAINT FK_2AD16164EA9FDD75 FOREIGN KEY (media_id) REFERENCES station_media (id) ON DELETE SET NULL'); $this->addSql('ALTER TABLE song_history ADD CONSTRAINT FK_2AD16164EA9FDD75 FOREIGN KEY (media_id) REFERENCES station_media (id) ON DELETE SET NULL');
} }
public function down(Schema $schema): void public function down(Schema $schema): void
{ {
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE song_history DROP FOREIGN KEY FK_2AD16164EA9FDD75'); $this->addSql('ALTER TABLE song_history DROP FOREIGN KEY FK_2AD16164EA9FDD75');
$this->addSql('ALTER TABLE song_history ADD CONSTRAINT FK_2AD16164EA9FDD75 FOREIGN KEY (media_id) REFERENCES station_media (id) ON DELETE CASCADE'); $this->addSql('ALTER TABLE song_history ADD CONSTRAINT FK_2AD16164EA9FDD75 FOREIGN KEY (media_id) REFERENCES station_media (id) ON DELETE CASCADE');
} }

View File

@ -16,6 +16,7 @@ use Doctrine\DBAL\Connection;
use NowPlaying\Result\Client; use NowPlaying\Result\Client;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Serializer;
use Throwable;
/** /**
* @extends Repository<Entity\Listener> * @extends Repository<Entity\Listener>
@ -188,7 +189,7 @@ class ListenerRepository extends Repository
$record['device_is_bot'] = $browserResult->isBot ? 1 : 0; $record['device_is_bot'] = $browserResult->isBot ? 1 : 0;
$record['device_browser_family'] = $this->truncateNullableString($browserResult->browserFamily, 150); $record['device_browser_family'] = $this->truncateNullableString($browserResult->browserFamily, 150);
$record['device_os_family'] = $this->truncateNullableString($browserResult->osFamily, 150); $record['device_os_family'] = $this->truncateNullableString($browserResult->osFamily, 150);
} catch (\Throwable $e) { } catch (Throwable $e) {
$this->logger->error('Device Detector error: ' . $e->getMessage(), [ $this->logger->error('Device Detector error: ' . $e->getMessage(), [
'user_agent' => $userAgent, 'user_agent' => $userAgent,
'exception' => $e, 'exception' => $e,
@ -211,7 +212,7 @@ class ListenerRepository extends Repository
$record['location_country'] = $this->truncateNullableString($ipInfo->country, 2); $record['location_country'] = $this->truncateNullableString($ipInfo->country, 2);
$record['location_lat'] = $ipInfo->lat; $record['location_lat'] = $ipInfo->lat;
$record['location_lon'] = $ipInfo->lon; $record['location_lon'] = $ipInfo->lon;
} catch (\Throwable $e) { } catch (Throwable $e) {
$this->logger->error('IP Geolocation error: ' . $e->getMessage(), [ $this->logger->error('IP Geolocation error: ' . $e->getMessage(), [
'ip' => $ip, 'ip' => $ip,
'exception' => $e, 'exception' => $e,

View File

@ -191,9 +191,9 @@ class StationPlaylistMediaRepository extends Repository
)->setParameter('playlist', $playlist) )->setParameter('playlist', $playlist)
->execute(); ->execute();
} elseif (Entity\Enums\PlaylistOrders::Shuffle === $playlist->getOrderEnum()) { } elseif (Entity\Enums\PlaylistOrders::Shuffle === $playlist->getOrderEnum()) {
$this->em->transactional( $this->em->wrapInTransaction(
function () use ($playlist): void { function (ReloadableEntityManagerInterface $em) use ($playlist): void {
$allSpmRecordsQuery = $this->em->createQuery( $allSpmRecordsQuery = $em->createQuery(
<<<'DQL' <<<'DQL'
SELECT spm.id SELECT spm.id
FROM App\Entity\StationPlaylistMedia spm FROM App\Entity\StationPlaylistMedia spm
@ -202,7 +202,7 @@ class StationPlaylistMediaRepository extends Repository
DQL DQL
)->setParameter('playlist', $playlist); )->setParameter('playlist', $playlist);
$updateSpmWeightQuery = $this->em->createQuery( $updateSpmWeightQuery = $em->createQuery(
<<<'DQL' <<<'DQL'
UPDATE App\Entity\StationPlaylistMedia spm UPDATE App\Entity\StationPlaylistMedia spm
SET spm.weight=:weight, spm.is_queued=1 SET spm.weight=:weight, spm.is_queued=1

View File

@ -166,11 +166,7 @@ class StationRepository extends Repository
} }
$customUrl = $this->settingsRepo->readSettings()->getDefaultAlbumArtUrlAsUri(); $customUrl = $this->settingsRepo->readSettings()->getDefaultAlbumArtUrlAsUri();
if (null !== $customUrl) { return $customUrl ?? AssetFactory::createAlbumArt($this->environment)->getUri();
return $customUrl;
}
return AssetFactory::createAlbumArt($this->environment)->getUri();
} }
public function setFallback( public function setFallback(

View File

@ -12,6 +12,7 @@ use App\Service\Avatar;
use App\Utilities\Urls; use App\Utilities\Urls;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use GuzzleHttp\Psr7\Uri; use GuzzleHttp\Psr7\Uri;
use InvalidArgumentException;
use OpenApi\Attributes as OA; use OpenApi\Attributes as OA;
use Psr\Http\Message\UriInterface; use Psr\Http\Message\UriInterface;
use RuntimeException; use RuntimeException;
@ -245,7 +246,7 @@ class Settings implements Stringable
public function setAnalytics(?string $analytics): void public function setAnalytics(?string $analytics): void
{ {
if (null !== $analytics && null === Entity\Enums\AnalyticsLevel::tryFrom($analytics)) { if (null !== $analytics && null === Entity\Enums\AnalyticsLevel::tryFrom($analytics)) {
throw new \InvalidArgumentException('Invalid analytics level.'); throw new InvalidArgumentException('Invalid analytics level.');
} }
$this->analytics = $analytics; $this->analytics = $analytics;
@ -337,7 +338,7 @@ class Settings implements Stringable
public function setPublicTheme(?string $publicTheme): void public function setPublicTheme(?string $publicTheme): void
{ {
if (null !== $publicTheme && null === SupportedThemes::tryFrom($publicTheme)) { if (null !== $publicTheme && null === SupportedThemes::tryFrom($publicTheme)) {
throw new \InvalidArgumentException('Unsupported theme specified.'); throw new InvalidArgumentException('Unsupported theme specified.');
} }
$this->public_theme = $publicTheme; $this->public_theme = $publicTheme;

View File

@ -19,10 +19,12 @@ use DateTimeZone;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use InvalidArgumentException;
use League\Flysystem\UnixVisibility\PortableVisibilityConverter; use League\Flysystem\UnixVisibility\PortableVisibilityConverter;
use League\Flysystem\Visibility; use League\Flysystem\Visibility;
use OpenApi\Attributes as OA; use OpenApi\Attributes as OA;
use Psr\Http\Message\UriInterface; use Psr\Http\Message\UriInterface;
use RuntimeException;
use Stringable; use Stringable;
use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Path; use Symfony\Component\Filesystem\Path;
@ -456,7 +458,7 @@ class Station implements Stringable, IdentifiableEntityInterface
public function setFrontendType(?string $frontend_type = null): void public function setFrontendType(?string $frontend_type = null): void
{ {
if (null !== $frontend_type && null === FrontendAdapters::tryFrom($frontend_type)) { if (null !== $frontend_type && null === FrontendAdapters::tryFrom($frontend_type)) {
throw new \InvalidArgumentException('Invalid frontend type specified.'); throw new InvalidArgumentException('Invalid frontend type specified.');
} }
$this->frontend_type = $frontend_type; $this->frontend_type = $frontend_type;
@ -511,7 +513,7 @@ class Station implements Stringable, IdentifiableEntityInterface
public function setBackendType(string $backend_type = null): void public function setBackendType(string $backend_type = null): void
{ {
if (null !== $backend_type && null === BackendAdapters::tryFrom($backend_type)) { if (null !== $backend_type && null === BackendAdapters::tryFrom($backend_type)) {
throw new \InvalidArgumentException('Invalid frontend type specified.'); throw new InvalidArgumentException('Invalid frontend type specified.');
} }
$this->backend_type = $backend_type; $this->backend_type = $backend_type;
@ -962,7 +964,7 @@ class Station implements Stringable, IdentifiableEntityInterface
public function getMediaStorageLocation(): StorageLocation public function getMediaStorageLocation(): StorageLocation
{ {
if (null === $this->media_storage_location) { if (null === $this->media_storage_location) {
throw new \RuntimeException('Media storage location not initialized.'); throw new RuntimeException('Media storage location not initialized.');
} }
return $this->media_storage_location; return $this->media_storage_location;
} }
@ -970,7 +972,7 @@ class Station implements Stringable, IdentifiableEntityInterface
public function setMediaStorageLocation(?StorageLocation $storageLocation = null): void public function setMediaStorageLocation(?StorageLocation $storageLocation = null): void
{ {
if (null !== $storageLocation && StorageLocationTypes::StationMedia !== $storageLocation->getTypeEnum()) { if (null !== $storageLocation && StorageLocationTypes::StationMedia !== $storageLocation->getTypeEnum()) {
throw new \RuntimeException('Invalid storage location.'); throw new RuntimeException('Invalid storage location.');
} }
$this->media_storage_location = $storageLocation; $this->media_storage_location = $storageLocation;
@ -979,7 +981,7 @@ class Station implements Stringable, IdentifiableEntityInterface
public function getRecordingsStorageLocation(): StorageLocation public function getRecordingsStorageLocation(): StorageLocation
{ {
if (null === $this->recordings_storage_location) { if (null === $this->recordings_storage_location) {
throw new \RuntimeException('Recordings storage location not initialized.'); throw new RuntimeException('Recordings storage location not initialized.');
} }
return $this->recordings_storage_location; return $this->recordings_storage_location;
} }
@ -987,7 +989,7 @@ class Station implements Stringable, IdentifiableEntityInterface
public function setRecordingsStorageLocation(?StorageLocation $storageLocation = null): void public function setRecordingsStorageLocation(?StorageLocation $storageLocation = null): void
{ {
if (null !== $storageLocation && StorageLocationTypes::StationRecordings !== $storageLocation->getTypeEnum()) { if (null !== $storageLocation && StorageLocationTypes::StationRecordings !== $storageLocation->getTypeEnum()) {
throw new \RuntimeException('Invalid storage location.'); throw new RuntimeException('Invalid storage location.');
} }
$this->recordings_storage_location = $storageLocation; $this->recordings_storage_location = $storageLocation;
@ -996,7 +998,7 @@ class Station implements Stringable, IdentifiableEntityInterface
public function getPodcastsStorageLocation(): StorageLocation public function getPodcastsStorageLocation(): StorageLocation
{ {
if (null === $this->podcasts_storage_location) { if (null === $this->podcasts_storage_location) {
throw new \RuntimeException('Podcasts storage location not initialized.'); throw new RuntimeException('Podcasts storage location not initialized.');
} }
return $this->podcasts_storage_location; return $this->podcasts_storage_location;
} }
@ -1004,7 +1006,7 @@ class Station implements Stringable, IdentifiableEntityInterface
public function setPodcastsStorageLocation(?StorageLocation $storageLocation = null): void public function setPodcastsStorageLocation(?StorageLocation $storageLocation = null): void
{ {
if (null !== $storageLocation && StorageLocationTypes::StationPodcasts !== $storageLocation->getTypeEnum()) { if (null !== $storageLocation && StorageLocationTypes::StationPodcasts !== $storageLocation->getTypeEnum()) {
throw new \RuntimeException('Invalid storage location.'); throw new RuntimeException('Invalid storage location.');
} }
$this->podcasts_storage_location = $storageLocation; $this->podcasts_storage_location = $storageLocation;
@ -1016,7 +1018,7 @@ class Station implements Stringable, IdentifiableEntityInterface
StorageLocationTypes::StationMedia => $this->getMediaStorageLocation(), StorageLocationTypes::StationMedia => $this->getMediaStorageLocation(),
StorageLocationTypes::StationRecordings => $this->getRecordingsStorageLocation(), StorageLocationTypes::StationRecordings => $this->getRecordingsStorageLocation(),
StorageLocationTypes::StationPodcasts => $this->getPodcastsStorageLocation(), StorageLocationTypes::StationPodcasts => $this->getPodcastsStorageLocation(),
default => throw new \InvalidArgumentException('Invalid storage location.') default => throw new InvalidArgumentException('Invalid storage location.')
}; };
} }

View File

@ -7,6 +7,7 @@ namespace App\Entity;
use App\Entity\Enums\StationBackendPerformanceModes; use App\Entity\Enums\StationBackendPerformanceModes;
use App\Radio\Enums\StreamFormats; use App\Radio\Enums\StreamFormats;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use InvalidArgumentException;
class StationBackendConfiguration extends ArrayCollection class StationBackendConfiguration extends ArrayCollection
{ {
@ -80,7 +81,7 @@ class StationBackendConfiguration extends ArrayCollection
} }
if (null !== $format && null === StreamFormats::tryFrom($format)) { if (null !== $format && null === StreamFormats::tryFrom($format)) {
throw new \InvalidArgumentException('Invalid recording type specified.'); throw new InvalidArgumentException('Invalid recording type specified.');
} }
$this->set(self::RECORD_STREAMS_FORMAT, $format); $this->set(self::RECORD_STREAMS_FORMAT, $format);

View File

@ -13,6 +13,7 @@ use Azura\Normalizer\Attributes\DeepNormalize;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use InvalidArgumentException;
use OpenApi\Attributes as OA; use OpenApi\Attributes as OA;
use Stringable; use Stringable;
use Symfony\Component\Serializer\Annotation as Serializer; use Symfony\Component\Serializer\Annotation as Serializer;
@ -251,7 +252,7 @@ class StationPlaylist implements
public function setType(string $type): void public function setType(string $type): void
{ {
if (null === PlaylistTypes::tryFrom($type)) { if (null === PlaylistTypes::tryFrom($type)) {
throw new \InvalidArgumentException('Invalid playlist type.'); throw new InvalidArgumentException('Invalid playlist type.');
} }
$this->type = $type; $this->type = $type;
@ -270,7 +271,7 @@ class StationPlaylist implements
public function setSource(string $source): void public function setSource(string $source): void
{ {
if (null === PlaylistSources::tryFrom($source)) { if (null === PlaylistSources::tryFrom($source)) {
throw new \InvalidArgumentException('Invalid playlist source.'); throw new InvalidArgumentException('Invalid playlist source.');
} }
$this->source = $source; $this->source = $source;
@ -289,7 +290,7 @@ class StationPlaylist implements
public function setOrder(string $order): void public function setOrder(string $order): void
{ {
if (null === PlaylistOrders::tryFrom($order)) { if (null === PlaylistOrders::tryFrom($order)) {
throw new \InvalidArgumentException('Invalid playlist order.'); throw new InvalidArgumentException('Invalid playlist order.');
} }
$this->order = $order; $this->order = $order;
@ -318,7 +319,7 @@ class StationPlaylist implements
public function setRemoteType(?string $remote_type): void public function setRemoteType(?string $remote_type): void
{ {
if (null !== $remote_type && null === PlaylistRemoteTypes::tryFrom($remote_type)) { if (null !== $remote_type && null === PlaylistRemoteTypes::tryFrom($remote_type)) {
throw new \InvalidArgumentException('Invalid playlist remote type.'); throw new InvalidArgumentException('Invalid playlist remote type.');
} }
$this->remote_type = $remote_type; $this->remote_type = $remote_type;

View File

@ -12,6 +12,7 @@ use App\Radio\Remote\AbstractRemote;
use App\Utilities; use App\Utilities;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use GuzzleHttp\Psr7\Uri; use GuzzleHttp\Psr7\Uri;
use InvalidArgumentException;
use Psr\Http\Message\UriInterface; use Psr\Http\Message\UriInterface;
use Stringable; use Stringable;
@ -234,7 +235,7 @@ class StationRemote implements
public function setType(string $type): void public function setType(string $type): void
{ {
if (null === RemoteAdapters::tryFrom($type)) { if (null === RemoteAdapters::tryFrom($type)) {
throw new \InvalidArgumentException('Invalid type specified.'); throw new InvalidArgumentException('Invalid type specified.');
} }
$this->type = $type; $this->type = $type;

View File

@ -222,7 +222,7 @@ class StationSchedule implements IdentifiableEntityInterface
7 => 'Sun', 7 => 'Sun',
]; ];
if (null !== $days) { if ([] !== $days) {
$displayDays = []; $displayDays = [];
foreach ($days as $day) { foreach ($days as $day) {
$displayDays[] = $daysOfWeek[$day]; $displayDays[] = $daysOfWeek[$day];

View File

@ -17,7 +17,7 @@ trait HasSongFields
use TruncateStrings; use TruncateStrings;
#[ #[
OA\Property(), OA\Property,
ORM\Column(length: 50), ORM\Column(length: 50),
Groups([EntityGroupsInterface::GROUP_GENERAL, EntityGroupsInterface::GROUP_ALL]) Groups([EntityGroupsInterface::GROUP_GENERAL, EntityGroupsInterface::GROUP_ALL])
] ]

View File

@ -11,6 +11,7 @@ use App\Http\ServerRequest;
use Gettext\GettextTranslator; use Gettext\GettextTranslator;
use Gettext\TranslatorFunctions; use Gettext\TranslatorFunctions;
use Gettext\TranslatorInterface; use Gettext\TranslatorInterface;
use Locale;
use PhpMyAdmin\MoTranslator\Loader; use PhpMyAdmin\MoTranslator\Loader;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
@ -124,7 +125,7 @@ enum SupportedLocales: string
} }
$server_params = $request->getServerParams(); $server_params = $request->getServerParams();
$browser_locale = \Locale::acceptFromHttp($server_params['HTTP_ACCEPT_LANGUAGE'] ?? ''); $browser_locale = Locale::acceptFromHttp($server_params['HTTP_ACCEPT_LANGUAGE'] ?? '');
if (!empty($browser_locale)) { if (!empty($browser_locale)) {
if (2 === strlen($browser_locale)) { if (2 === strlen($browser_locale)) {

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace App\Event\Radio; namespace App\Event\Radio;
use App\Entity; use App\Entity;
use RuntimeException;
use Symfony\Contracts\EventDispatcher\Event; use Symfony\Contracts\EventDispatcher\Event;
/** /**
@ -80,7 +81,7 @@ class AnnotateNextSong extends Event
public function buildAnnotations(): string public function buildAnnotations(): string
{ {
if (empty($this->songPath)) { if (empty($this->songPath)) {
throw new \RuntimeException('No valid path for song.'); throw new RuntimeException('No valid path for song.');
} }
$this->annotations = array_filter($this->annotations); $this->annotations = array_filter($this->annotations);

View File

@ -19,6 +19,8 @@ use Symfony\Component\Lock\Exception\LockConflictedException;
use Symfony\Component\Lock\Key; use Symfony\Component\Lock\Key;
use Symfony\Component\Lock\PersistingStoreInterface; use Symfony\Component\Lock\PersistingStoreInterface;
use const PHP_INT_MAX;
/** /**
* Copied from Symfony 5.x as it was deprecated in 6.x with no suitable replacement. * Copied from Symfony 5.x as it was deprecated in 6.x with no suitable replacement.
* *
@ -38,7 +40,7 @@ final class RetryTillSaveStore implements BlockingStoreInterface, LoggerAwareInt
public function __construct( public function __construct(
private PersistingStoreInterface $decorated, private PersistingStoreInterface $decorated,
private int $retrySleep = 100, private int $retrySleep = 100,
private int $retryCount = \PHP_INT_MAX private int $retryCount = PHP_INT_MAX
) { ) {
$this->logger = new NullLogger(); $this->logger = new NullLogger();
} }
@ -68,12 +70,10 @@ final class RetryTillSaveStore implements BlockingStoreInterface, LoggerAwareInt
} }
} while (++$retry < $this->retryCount); } while (++$retry < $this->retryCount);
if (null !== $this->logger) { $this->logger?->warning(
$this->logger->warning( 'Failed to store the "{resource}" lock. Abort after {retry} retry.',
'Failed to store the "{resource}" lock. Abort after {retry} retry.', ['resource' => $key, 'retry' => $retry]
['resource' => $key, 'retry' => $retry] );
);
}
throw new LockConflictedException(); throw new LockConflictedException();
} }

View File

@ -14,7 +14,7 @@ class MimeTypeExtensionMap extends GeneratedExtensionToMimeTypeMap
public function lookupMimeType(string $extension): ?string public function lookupMimeType(string $extension): ?string
{ {
return self::MIME_TYPES_FOR_EXTENSIONS[$extension] return parent::lookupMimeType($extension)
?? self::ADDED_MIME_TYPES[$extension] ?? self::ADDED_MIME_TYPES[$extension]
?? null; ?? null;
} }

View File

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace App\Message; namespace App\Message;
use App\Entity\Api\NowPlaying\NowPlaying; use App\Entity\Api\NowPlaying\NowPlaying;
use App\MessageQueue\QueueManager;
use App\MessageQueue\QueueManagerInterface; use App\MessageQueue\QueueManagerInterface;
class DispatchWebhookMessage extends AbstractUniqueMessage class DispatchWebhookMessage extends AbstractUniqueMessage

View File

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace App\Message; namespace App\Message;
use App\Environment; use App\Environment;
use App\MessageQueue\QueueManagerInterface;
class TestWebhookMessage extends AbstractUniqueMessage class TestWebhookMessage extends AbstractUniqueMessage
{ {
@ -23,9 +22,4 @@ class TestWebhookMessage extends AbstractUniqueMessage
{ {
return Environment::getInstance()->getSyncLongExecutionTime(); return Environment::getInstance()->getSyncLongExecutionTime();
} }
public function getQueue(): string
{
return QueueManagerInterface::QUEUE_NORMAL_PRIORITY;
}
} }

View File

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace App; namespace App;
use App\Http\Response; use App\Http\Response;
use App\Http\Router;
use App\Http\RouterInterface; use App\Http\RouterInterface;
use App\Http\ServerRequest; use App\Http\ServerRequest;
use Countable; use Countable;
@ -179,23 +178,21 @@ class Paginator implements IteratorAggregate, Countable
} }
$pageLinks = []; $pageLinks = [];
if ($this->router instanceof Router) { $pageLinks['first'] = (string)$this->router->fromHereWithQuery(null, [], ['page' => 1]);
$pageLinks['first'] = (string)$this->router->fromHereWithQuery(null, [], ['page' => 1]);
$prevPage = $this->paginator->hasPreviousPage() $prevPage = $this->paginator->hasPreviousPage()
? $this->paginator->getPreviousPage() ? $this->paginator->getPreviousPage()
: 1; : 1;
$pageLinks['previous'] = (string)$this->router->fromHereWithQuery(null, [], ['page' => $prevPage]); $pageLinks['previous'] = (string)$this->router->fromHereWithQuery(null, [], ['page' => $prevPage]);
$nextPage = $this->paginator->hasNextPage() $nextPage = $this->paginator->hasNextPage()
? $this->paginator->getNextPage() ? $this->paginator->getNextPage()
: $this->paginator->getNbPages(); : $this->paginator->getNbPages();
$pageLinks['next'] = (string)$this->router->fromHereWithQuery(null, [], ['page' => $nextPage]); $pageLinks['next'] = (string)$this->router->fromHereWithQuery(null, [], ['page' => $nextPage]);
$pageLinks['last'] = (string)$this->router->fromHereWithQuery(null, [], ['page' => $totalPages]); $pageLinks['last'] = (string)$this->router->fromHereWithQuery(null, [], ['page' => $totalPages]);
}
return $response->withJson( return $response->withJson(
[ [

View File

@ -15,7 +15,6 @@ use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Supervisor\Exception\Fault; use Supervisor\Exception\Fault;
use Supervisor\Exception\SupervisorException as SupervisorLibException; use Supervisor\Exception\SupervisorException as SupervisorLibException;
use Supervisor\Process;
use Supervisor\SupervisorInterface; use Supervisor\SupervisorInterface;
abstract class AbstractAdapter abstract class AbstractAdapter
@ -106,9 +105,7 @@ abstract class AbstractAdapter
$program_name = $this->getProgramName($station); $program_name = $this->getProgramName($station);
try { try {
$process = $this->supervisor->getProcess($program_name); return $this->supervisor->getProcess($program_name)->isRunning();
return $process instanceof Process && $process->isRunning();
} catch (Fault\BadNameException) { } catch (Fault\BadNameException) {
return false; return false;
} }

View File

@ -10,6 +10,7 @@ use App\Radio\Adapters;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Psr\EventDispatcher\EventDispatcherInterface; use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use RuntimeException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class Annotations implements EventSubscriberInterface class Annotations implements EventSubscriberInterface
@ -52,7 +53,7 @@ class Annotations implements EventSubscriberInterface
$queueRow = $this->queueRepo->getNextToSendToAutoDj($station); $queueRow = $this->queueRepo->getNextToSendToAutoDj($station);
if (null === $queueRow) { if (null === $queueRow) {
throw new \RuntimeException('Queue is empty for station.'); throw new RuntimeException('Queue is empty for station.');
} }
$event = AnnotateNextSong::fromStationQueue($queueRow, $asAutoDj); $event = AnnotateNextSong::fromStationQueue($queueRow, $asAutoDj);

View File

@ -8,6 +8,7 @@ use App\Entity;
use App\Event\Radio\WriteLiquidsoapConfiguration; use App\Event\Radio\WriteLiquidsoapConfiguration;
use App\Exception; use App\Exception;
use App\Radio\Enums\LiquidsoapQueues; use App\Radio\Enums\LiquidsoapQueues;
use LogicException;
use Psr\Http\Message\UriInterface; use Psr\Http\Message\UriInterface;
use Symfony\Component\Process\Process; use Symfony\Component\Process\Process;
@ -329,7 +330,7 @@ class Liquidsoap extends AbstractBackend
$process->run(); $process->run();
if (1 === $process->getExitCode()) { if (1 === $process->getExitCode()) {
throw new \LogicException($process->getOutput()); throw new LogicException($process->getOutput());
} }
} }
} }

View File

@ -7,6 +7,8 @@ namespace App\Radio\Backend\Liquidsoap\Command;
use App\Entity; use App\Entity;
use App\Radio\Enums\BackendAdapters; use App\Radio\Enums\BackendAdapters;
use Monolog\Logger; use Monolog\Logger;
use ReflectionClass;
use Throwable;
abstract class AbstractCommand abstract class AbstractCommand
{ {
@ -30,7 +32,7 @@ abstract class AbstractCommand
} }
); );
$className = (new \ReflectionClass(static::class))->getShortName(); $className = (new ReflectionClass(static::class))->getShortName();
$this->logger->debug( $this->logger->debug(
sprintf('Running Internal Command %s', $className), sprintf('Running Internal Command %s', $className),
[ [
@ -54,7 +56,7 @@ abstract class AbstractCommand
} else { } else {
return (string)$result; return (string)$result;
} }
} catch (\Throwable $e) { } catch (Throwable $e) {
$this->logger->error( $this->logger->error(
sprintf( sprintf(
'Error with Internal Command %s: %s', 'Error with Internal Command %s: %s',

View File

@ -6,13 +6,14 @@ namespace App\Radio\Backend\Liquidsoap\Command;
use App\Entity; use App\Entity;
use App\Flysystem\StationFilesystems; use App\Flysystem\StationFilesystems;
use RuntimeException;
class CopyCommand extends AbstractCommand class CopyCommand extends AbstractCommand
{ {
protected function doRun(Entity\Station $station, bool $asAutoDj = false, array $payload = []): string protected function doRun(Entity\Station $station, bool $asAutoDj = false, array $payload = []): string
{ {
if (empty($payload['uri'])) { if (empty($payload['uri'])) {
throw new \RuntimeException('No URI provided.'); throw new RuntimeException('No URI provided.');
} }
$uri = $payload['uri']; $uri = $payload['uri'];

View File

@ -6,6 +6,7 @@ namespace App\Radio\Backend\Liquidsoap\Command;
use App\Entity; use App\Entity;
use Monolog\Logger; use Monolog\Logger;
use RuntimeException;
class DjAuthCommand extends AbstractCommand class DjAuthCommand extends AbstractCommand
{ {
@ -22,7 +23,7 @@ class DjAuthCommand extends AbstractCommand
array $payload = [] array $payload = []
): bool { ): bool {
if (!$station->getEnableStreamers()) { if (!$station->getEnableStreamers()) {
throw new \RuntimeException('Attempted DJ authentication when streamers are disabled on this station.'); throw new RuntimeException('Attempted DJ authentication when streamers are disabled on this station.');
} }
$user = $payload['user'] ?? ''; $user = $payload['user'] ?? '';

View File

@ -8,6 +8,7 @@ use App\Entity;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Monolog\Logger; use Monolog\Logger;
use Psr\SimpleCache\CacheInterface; use Psr\SimpleCache\CacheInterface;
use RuntimeException;
class FeedbackCommand extends AbstractCommand class FeedbackCommand extends AbstractCommand
{ {
@ -24,12 +25,12 @@ class FeedbackCommand extends AbstractCommand
{ {
// Process extra metadata sent by Liquidsoap (if it exists). // Process extra metadata sent by Liquidsoap (if it exists).
if (empty($payload['media_id'])) { if (empty($payload['media_id'])) {
throw new \RuntimeException('No payload provided.'); throw new RuntimeException('No payload provided.');
} }
$media = $this->em->find(Entity\StationMedia::class, $payload['media_id']); $media = $this->em->find(Entity\StationMedia::class, $payload['media_id']);
if (!$media instanceof Entity\StationMedia) { if (!$media instanceof Entity\StationMedia) {
throw new \RuntimeException('Media ID does not exist for station.'); throw new RuntimeException('Media ID does not exist for station.');
} }
$sq = $this->queueRepo->findRecentlyCuedSong($station, $media); $sq = $this->queueRepo->findRecentlyCuedSong($station, $media);

View File

@ -116,7 +116,7 @@ class ConfigWriter implements EventSubscriberInterface
$event->appendBlock( $event->appendBlock(
<<<EOF <<<EOF
init.daemon.set(false) init.daemon.set(false)
init.daemon.pidfile.path.set("${pidfile}") init.daemon.pidfile.path.set("{$pidfile}")
log.stdout.set(true) log.stdout.set(true)
log.file.set(false) log.file.set(false)
@ -125,14 +125,14 @@ class ConfigWriter implements EventSubscriberInterface
settings.server.socket.set(true) settings.server.socket.set(true)
settings.server.socket.permissions.set(0o660) settings.server.socket.permissions.set(0o660)
settings.server.socket.path.set("${socketFile}") settings.server.socket.path.set("{$socketFile}")
settings.harbor.bind_addrs.set(["0.0.0.0"]) settings.harbor.bind_addrs.set(["0.0.0.0"])
settings.tag.encodings.set(["UTF-8","ISO-8859-1"]) settings.tag.encodings.set(["UTF-8","ISO-8859-1"])
settings.encoder.metadata.export.set(["artist","title","album","song"]) settings.encoder.metadata.export.set(["artist","title","album","song"])
setenv("TZ", "${stationTz}") setenv("TZ", "{$stationTz}")
autodj_is_loading = ref(true) autodj_is_loading = ref(true)
ignore(autodj_is_loading) ignore(autodj_is_loading)
@ -154,8 +154,8 @@ class ConfigWriter implements EventSubscriberInterface
$event->appendBlock( $event->appendBlock(
<<<EOF <<<EOF
azuracast_api_url = "${stationApiUrl}" azuracast_api_url = "{$stationApiUrl}"
azuracast_api_key = "${stationApiAuth}" azuracast_api_key = "{$stationApiAuth}"
def azuracast_api_call(~timeout_ms=2000, url, payload) = def azuracast_api_call(~timeout_ms=2000, url, payload) =
full_url = "#{azuracast_api_url}/#{url}" full_url = "#{azuracast_api_url}/#{url}"
@ -189,7 +189,7 @@ class ConfigWriter implements EventSubscriberInterface
$event->appendBlock( $event->appendBlock(
<<<EOF <<<EOF
station_media_dir = "${stationMediaDir}" station_media_dir = "{$stationMediaDir}"
def azuracast_media_protocol(~rlog=_,~maxtime=_,arg) = def azuracast_media_protocol(~rlog=_,~maxtime=_,arg) =
["#{station_media_dir}/#{arg}"] ["#{station_media_dir}/#{arg}"]
end end
@ -240,7 +240,7 @@ class ConfigWriter implements EventSubscriberInterface
<<<EOF <<<EOF
# Optimize Performance # Optimize Performance
runtime.gc.set(runtime.gc.get().{ runtime.gc.set(runtime.gc.get().{
space_overhead = ${gcSpaceOverhead}, space_overhead = {$gcSpaceOverhead},
allocation_policy = 2 allocation_policy = 2
}) })
EOF EOF
@ -552,12 +552,12 @@ class ConfigWriter implements EventSubscriberInterface
$event->appendBlock( $event->appendBlock(
<<< EOF <<< EOF
requests = request.queue(id="${requestsQueueName}") requests = request.queue(id="{$requestsQueueName}")
requests = cue_cut(id="cue_${requestsQueueName}", requests) requests = cue_cut(id="cue_{$requestsQueueName}", requests)
radio = fallback(id="requests_fallback", track_sensitive = true, [requests, radio]) radio = fallback(id="requests_fallback", track_sensitive = true, [requests, radio])
interrupting_queue = request.queue(id="${interruptingQueueName}") interrupting_queue = request.queue(id="{$interruptingQueueName}")
interrupting_queue = cue_cut(id="cue_${interruptingQueueName}", interrupting_queue) interrupting_queue = cue_cut(id="cue_{$interruptingQueueName}", interrupting_queue)
radio = fallback(id="interrupting_fallback", track_sensitive = false, [interrupting_queue, radio]) radio = fallback(id="interrupting_fallback", track_sensitive = false, [interrupting_queue, radio])
add_skip_command(radio) add_skip_command(radio)
@ -800,7 +800,7 @@ class ConfigWriter implements EventSubscriberInterface
ignore(radio_without_live) ignore(radio_without_live)
# Live Broadcasting # Live Broadcasting
live = input.harbor(${harborParams}) live = input.harbor({$harborParams})
def insert_missing(m) = def insert_missing(m) =
if m == [] then if m == [] then
@ -827,14 +827,14 @@ class ConfigWriter implements EventSubscriberInterface
$event->appendBlock( $event->appendBlock(
<<< EOF <<< EOF
# Record Live Broadcasts # Record Live Broadcasts
recording_base_path = "${recordBasePath}" recording_base_path = "{$recordBasePath}"
recording_extension = "${recordExtension}" recording_extension = "{$recordExtension}"
output.file( output.file(
{$formatString}, {$formatString},
fun () -> begin fun () -> begin
if (!live_enabled) then if (!live_enabled) then
"#{recording_base_path}/#{!live_dj}/${recordPathPrefix}_%Y%m%d-%H%M%S.#{recording_extension}.tmp" "#{recording_base_path}/#{!live_dj}/{$recordPathPrefix}_%Y%m%d-%H%M%S.#{recording_extension}.tmp"
else else
"" ""
end end
@ -897,7 +897,7 @@ class ConfigWriter implements EventSubscriberInterface
$event->appendBlock( $event->appendBlock(
<<<EOF <<<EOF
radio = fallback(id="safe_fallback", track_sensitive = false, [radio, single(id="error_jingle", "${errorFile}")]) radio = fallback(id="safe_fallback", track_sensitive = false, [radio, single(id="error_jingle", "{$errorFile}")])
EOF EOF
); );

View File

@ -17,6 +17,7 @@ use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Filesystem;
use Throwable;
class PlaylistFileWriter implements EventSubscriberInterface class PlaylistFileWriter implements EventSubscriberInterface
{ {
@ -136,7 +137,7 @@ class PlaylistFileWriter implements EventSubscriberInterface
try { try {
$this->eventDispatcher->dispatch($event); $this->eventDispatcher->dispatch($event);
$playlistFile[] = $event->buildAnnotations(); $playlistFile[] = $event->buildAnnotations();
} catch (\Throwable $e) { } catch (Throwable $e) {
} }
} }

View File

@ -14,6 +14,7 @@ use App\Radio\Enums\BackendAdapters;
use App\Radio\Enums\FrontendAdapters; use App\Radio\Enums\FrontendAdapters;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Monolog\Logger; use Monolog\Logger;
use RuntimeException;
use Supervisor\Exception\SupervisorException; use Supervisor\Exception\SupervisorException;
use Supervisor\SupervisorInterface; use Supervisor\SupervisorInterface;
@ -104,7 +105,7 @@ class Configuration
if (!$station->getIsEnabled()) { if (!$station->getIsEnabled()) {
$this->unlinkAndStopStation($station, $reloadSupervisor); $this->unlinkAndStopStation($station, $reloadSupervisor);
throw new \RuntimeException('Station is disabled.'); throw new RuntimeException('Station is disabled.');
} }
$frontend = $this->adapters->getFrontendAdapter($station); $frontend = $this->adapters->getFrontendAdapter($station);
@ -113,7 +114,7 @@ class Configuration
// If no processes need to be managed, remove any existing config. // If no processes need to be managed, remove any existing config.
if (!$frontend->hasCommand($station) && !$backend->hasCommand($station)) { if (!$frontend->hasCommand($station) && !$backend->hasCommand($station)) {
$this->unlinkAndStopStation($station, $reloadSupervisor); $this->unlinkAndStopStation($station, $reloadSupervisor);
throw new \RuntimeException('Station has no local services.'); throw new RuntimeException('Station has no local services.');
} }
// If using AutoDJ and there is no media, don't spin up services. // If using AutoDJ and there is no media, don't spin up services.
@ -122,7 +123,7 @@ class Configuration
&& !$this->stationPlaylistRepo->stationHasActivePlaylists($station) && !$this->stationPlaylistRepo->stationHasActivePlaylists($station)
) { ) {
$this->unlinkAndStopStation($station, $reloadSupervisor); $this->unlinkAndStopStation($station, $reloadSupervisor);
throw new \RuntimeException('Station has no media assigned to playlists.'); throw new RuntimeException('Station has no media assigned to playlists.');
} }
// Get group information // Get group information
@ -174,8 +175,8 @@ class Configuration
$backend->reload($station); $backend->reload($station);
$frontend->reload($station); $frontend->reload($station);
} else { } else {
$this->supervisor->stopProcessGroup($backend_group, true); $this->supervisor->stopProcessGroup($backend_group);
$this->supervisor->startProcessGroup($backend_group, true); $this->supervisor->startProcessGroup($backend_group);
} }
} catch (SupervisorException) { } catch (SupervisorException) {
} }
@ -483,7 +484,7 @@ class Configuration
// Try forcing the group to stop, but don't hard-fail if it doesn't. // Try forcing the group to stop, but don't hard-fail if it doesn't.
try { try {
$this->supervisor->stopProcessGroup($station_group, true); $this->supervisor->stopProcessGroup($station_group);
$this->supervisor->removeProcessGroup($station_group); $this->supervisor->removeProcessGroup($station_group);
} catch (SupervisorException) { } catch (SupervisorException) {
} }

View File

@ -7,6 +7,7 @@ namespace App\Radio\Frontend\Blocklist;
use App\Entity; use App\Entity;
use App\Radio\Enums\FrontendAdapters; use App\Radio\Enums\FrontendAdapters;
use App\Service\IpGeolocation; use App\Service\IpGeolocation;
use InvalidArgumentException;
use PhpIP\IP; use PhpIP\IP;
use PhpIP\IPBlock; use PhpIP\IPBlock;
@ -84,7 +85,7 @@ class BlocklistParser
} }
} }
} }
} catch (\InvalidArgumentException) { } catch (InvalidArgumentException) {
} }
} }

View File

@ -70,7 +70,7 @@ class RateLimit
$rateLimiterFactory = new RateLimiterFactory($config, $cacheStore, $this->lockFactory); $rateLimiterFactory = new RateLimiterFactory($config, $cacheStore, $this->lockFactory);
$rateLimiter = $rateLimiterFactory->create($key); $rateLimiter = $rateLimiterFactory->create($key);
if (false === $rateLimiter->consume(1)->isAccepted()) { if (false === $rateLimiter->consume()->isAccepted()) {
throw new Exception\RateLimitExceededException(); throw new Exception\RateLimitExceededException();
} }
} }

View File

@ -65,13 +65,13 @@ class Flow
return self::handleStandardUpload($request, $tempDir); return self::handleStandardUpload($request, $tempDir);
} }
$flowIdentifier = $params['flowIdentifier'] ?? ''; $flowIdentifier = $params['flowIdentifier'];
$flowChunkNumber = (int)($params['flowChunkNumber'] ?? 1); $flowChunkNumber = (int)($params['flowChunkNumber'] ?? 1);
$targetSize = (int)($params['flowTotalSize'] ?? 0); $targetSize = (int)($params['flowTotalSize'] ?? 0);
$targetChunks = (int)($params['flowTotalChunks'] ?? 1); $targetChunks = (int)($params['flowTotalChunks']);
$flowFilename = $params['flowFilename'] ?? ($flowIdentifier ?: ('upload-' . date('Ymd'))); $flowFilename = $params['flowFilename'] ?? ($flowIdentifier);
// init the destination file (format <filename.ext>.part<#chunk> // init the destination file (format <filename.ext>.part<#chunk>
$chunkBaseDir = $tempDir . '/' . $flowIdentifier; $chunkBaseDir = $tempDir . '/' . $flowIdentifier;

View File

@ -8,6 +8,7 @@ use App\Service\IpGeolocator;
use Exception; use Exception;
use MaxMind\Db\Reader; use MaxMind\Db\Reader;
use Psr\Cache\CacheItemPoolInterface; use Psr\Cache\CacheItemPoolInterface;
use RuntimeException;
use Symfony\Component\Cache\Adapter\ProxyAdapter; use Symfony\Component\Cache\Adapter\ProxyAdapter;
use Symfony\Component\Cache\CacheItem; use Symfony\Component\Cache\CacheItem;
use Symfony\Contracts\Cache\CacheInterface; use Symfony\Contracts\Cache\CacheInterface;
@ -76,7 +77,7 @@ class IpGeolocation
$reader = $this->reader; $reader = $this->reader;
if (null === $reader) { if (null === $reader) {
throw new \RuntimeException('No IP Geolocation reader available.'); throw new RuntimeException('No IP Geolocation reader available.');
} }
$cacheKey = $this->readerShortName . '_' . str_replace([':', '.'], '_', $ip); $cacheKey = $this->readerShortName . '_' . str_replace([':', '.'], '_', $ip);

View File

@ -74,7 +74,7 @@ class CheckMediaTask extends AbstractTask
$this->logger->info( $this->logger->info(
sprintf( sprintf(
'Processing media for storage location %s...', 'Processing media for storage location %s...',
(string)$storageLocation $storageLocation
) )
); );
@ -111,7 +111,7 @@ class CheckMediaTask extends AbstractTask
); );
} catch (FilesystemException $e) { } catch (FilesystemException $e) {
$this->logger->error( $this->logger->error(
sprintf('Flysystem Error for Storage Space %s', (string)$storageLocation), sprintf('Flysystem Error for Storage Space %s', $storageLocation),
[ [
'exception' => $e, 'exception' => $e,
] ]
@ -171,7 +171,7 @@ class CheckMediaTask extends AbstractTask
$this->processNewFiles($storageLocation, $queuedNewFiles, $musicFiles, $stats); $this->processNewFiles($storageLocation, $queuedNewFiles, $musicFiles, $stats);
$this->logger->debug(sprintf('Media processed for "%s".', (string)$storageLocation), $stats); $this->logger->debug(sprintf('Media processed for "%s".', $storageLocation), $stats);
} }
protected function processExistingMediaRows( protected function processExistingMediaRows(
@ -221,7 +221,7 @@ class CheckMediaTask extends AbstractTask
} else { } else {
$media = $this->em->find(Entity\StationMedia::class, $mediaRow['id']); $media = $this->em->find(Entity\StationMedia::class, $mediaRow['id']);
if ($media instanceof Entity\StationMedia) { if ($media instanceof Entity\StationMedia) {
$this->mediaRepo->remove($media, false); $this->mediaRepo->remove($media);
} }
$stats['deleted']++; $stats['deleted']++;

View File

@ -8,6 +8,7 @@ use App\Entity;
use Exception; use Exception;
use League\Flysystem\StorageAttributes; use League\Flysystem\StorageAttributes;
use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\Finder;
use Throwable;
class CleanupStorageTask extends AbstractTask class CleanupStorageTask extends AbstractTask
{ {
@ -22,7 +23,7 @@ class CleanupStorageTask extends AbstractTask
try { try {
/** @var Entity\Station $station */ /** @var Entity\Station $station */
$this->cleanStationTempFiles($station); $this->cleanStationTempFiles($station);
} catch (\Throwable $e) { } catch (Throwable $e) {
$this->logger->error($e->getMessage(), [ $this->logger->error($e->getMessage(), [
'station' => (string)$station, 'station' => (string)$station,
]); ]);
@ -34,7 +35,7 @@ class CleanupStorageTask extends AbstractTask
try { try {
/** @var Entity\StorageLocation $storageLocation */ /** @var Entity\StorageLocation $storageLocation */
$this->cleanMediaStorageLocation($storageLocation); $this->cleanMediaStorageLocation($storageLocation);
} catch (\Throwable $e) { } catch (Throwable $e) {
$this->logger->error($e->getMessage(), [ $this->logger->error($e->getMessage(), [
'storageLocation' => (string)$storageLocation, 'storageLocation' => (string)$storageLocation,
]); ]);
@ -80,7 +81,7 @@ class CleanupStorageTask extends AbstractTask
if (0 === count($allUniqueIds)) { if (0 === count($allUniqueIds)) {
$this->logger->notice( $this->logger->notice(
sprintf('Skipping storage location %s: no media found.', (string)$storageLocation) sprintf('Skipping storage location %s: no media found.', $storageLocation)
); );
return; return;
} }

View File

@ -8,6 +8,7 @@ use App\Doctrine\ReloadableEntityManagerInterface;
use App\Entity; use App\Entity;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\Finder;
use Throwable;
class MoveBroadcastsTask extends AbstractTask class MoveBroadcastsTask extends AbstractTask
{ {
@ -35,7 +36,7 @@ class MoveBroadcastsTask extends AbstractTask
try { try {
/** @var Entity\StorageLocation $storageLocation */ /** @var Entity\StorageLocation $storageLocation */
$this->processForStorageLocation($storageLocation); $this->processForStorageLocation($storageLocation);
} catch (\Throwable $e) { } catch (Throwable $e) {
$this->logger->error($e->getMessage(), [ $this->logger->error($e->getMessage(), [
'storageLocation' => (string)$storageLocation, 'storageLocation' => (string)$storageLocation,
]); ]);

View File

@ -12,6 +12,7 @@ use League\Flysystem\StorageAttributes;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Supervisor\SupervisorInterface; use Supervisor\SupervisorInterface;
use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\Finder;
use Throwable;
class RotateLogsTask extends AbstractTask class RotateLogsTask extends AbstractTask
{ {
@ -43,7 +44,7 @@ class RotateLogsTask extends AbstractTask
try { try {
$this->rotateStationLogs($station); $this->rotateStationLogs($station);
} catch (\Throwable $e) { } catch (Throwable $e) {
$this->logger->error($e->getMessage(), [ $this->logger->error($e->getMessage(), [
'station' => (string)$station, 'station' => (string)$station,
]); ]);

View File

@ -8,6 +8,7 @@ declare(strict_types=1);
namespace App\Tests; namespace App\Tests;
use App\AppFactory;
use App\Doctrine\ReloadableEntityManagerInterface; use App\Doctrine\ReloadableEntityManagerInterface;
use App\Enums\ApplicationEnvironment; use App\Enums\ApplicationEnvironment;
use App\Environment; use App\Environment;
@ -38,16 +39,10 @@ class Module extends Framework implements DoctrineProvider
public function _initialize(): void public function _initialize(): void
{ {
/** @var string $container_class The fully qualified name of the container class. */ $this->app = AppFactory::createApp(
$container_class = $this->config['container'];
$autoloader = $GLOBALS['autoloader'];
$this->app = $container_class::createApp(
$autoloader,
[ [
Environment::BASE_DIR => Configuration::projectDir(), Environment::BASE_DIR => Configuration::projectDir(),
Environment::APP_ENV => ApplicationEnvironment::Testing->value, Environment::APP_ENV => ApplicationEnvironment::Testing->value,
] ]
); );

View File

@ -8,8 +8,11 @@ declare(strict_types=1);
namespace App\Xml; namespace App\Xml;
use RuntimeException;
use XMLReader; use XMLReader;
use const LIBXML_XINCLUDE;
/** /**
* XML config reader. * XML config reader.
*/ */
@ -32,14 +35,14 @@ class Reader
} }
/** @var XMLReader|false $reader */ /** @var XMLReader|false $reader */
$reader = XMLReader::XML($string, null, \LIBXML_XINCLUDE); $reader = XMLReader::XML($string, null, LIBXML_XINCLUDE);
if (false === $reader) { if (false === $reader) {
return false; return false;
} }
set_error_handler( set_error_handler(
function ($error, $message = '') { function ($error, $message = '') {
throw new \RuntimeException( throw new RuntimeException(
sprintf('Error reading XML string: %s', $message), sprintf('Error reading XML string: %s', $message),
$error $error
); );

View File

@ -8,6 +8,7 @@ declare(strict_types=1);
namespace App\Xml; namespace App\Xml;
use RuntimeException;
use XMLWriter; use XMLWriter;
class Writer class Writer
@ -71,7 +72,7 @@ class Writer
$branchType = 'string'; $branchType = 'string';
} }
} elseif ($branchType !== (is_numeric($key) ? 'numeric' : 'string')) { } elseif ($branchType !== (is_numeric($key) ? 'numeric' : 'string')) {
throw new \RuntimeException('Mixing of string and numeric keys is not allowed'); throw new RuntimeException('Mixing of string and numeric keys is not allowed');
} }
if ($branchType === 'numeric') { if ($branchType === 'numeric') {

View File

@ -2,8 +2,7 @@ actor: FunctionalTester
suite_namespace: \Functional suite_namespace: \Functional
modules: modules:
enabled: enabled:
- \App\Tests\Module: - \App\Tests\Module
container: \App\AppFactory
- Doctrine2: - Doctrine2:
depends: \App\Tests\Module depends: \App\Tests\Module
- REST: - REST:

View File

@ -2,7 +2,6 @@ actor: UnitTester
suite_namespace: \Unit suite_namespace: \Unit
modules: modules:
enabled: enabled:
- \App\Tests\Module: - \App\Tests\Module
container: \App\AppFactory
error_level: "E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED" error_level: "E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED"

View File

@ -5,12 +5,8 @@ $autoloader->addClassMap([
'Functional\CestAbstract' => __DIR__ . '/Functional/CestAbstract.php', 'Functional\CestAbstract' => __DIR__ . '/Functional/CestAbstract.php',
]); ]);
\Doctrine\Common\Annotations\AnnotationRegistry::registerLoader([$autoloader, 'loadClass']);
$GLOBALS['autoloader'] = $autoloader;
if (!function_exists('__')) { if (!function_exists('__')) {
\PhpMyAdmin\MoTranslator\Loader::loadFunctions(); PhpMyAdmin\MoTranslator\Loader::loadFunctions();
} }
// Clear output directory // Clear output directory

View File

@ -6,14 +6,13 @@
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT); error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT);
ini_set('display_errors', 1); ini_set('display_errors', 1);
$autoloader = require dirname(__DIR__) . '/vendor/autoload.php'; require dirname(__DIR__) . '/vendor/autoload.php';
const AZURACAST_VERSION = App\Version::FALLBACK_VERSION; const AZURACAST_VERSION = App\Version::FALLBACK_VERSION;
const AZURACAST_API_URL = 'https://localhost/api'; const AZURACAST_API_URL = 'https://localhost/api';
const AZURACAST_API_NAME = 'Testing API'; const AZURACAST_API_NAME = 'Testing API';
App\AppFactory::createCli( App\AppFactory::createCli(
$autoloader,
[ [
App\Environment::BASE_DIR => dirname(__DIR__), App\Environment::BASE_DIR => dirname(__DIR__),
] ]

View File

@ -6,9 +6,9 @@ use App\Environment;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
$autoloader = require dirname(__DIR__) . '/vendor/autoload.php'; require dirname(__DIR__) . '/vendor/autoload.php';
$di = App\AppFactory::buildContainer($autoloader, $di = App\AppFactory::buildContainer(
[ [
App\Environment::BASE_DIR => dirname(__DIR__), App\Environment::BASE_DIR => dirname(__DIR__),
App\Environment::LOG_LEVEL => LogLevel::DEBUG, App\Environment::LOG_LEVEL => LogLevel::DEBUG,

View File

@ -5,10 +5,9 @@ declare(strict_types=1);
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT); error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT);
ini_set('display_errors', '1'); ini_set('display_errors', '1');
$autoloader = require dirname(__DIR__) . '/vendor/autoload.php'; require dirname(__DIR__) . '/vendor/autoload.php';
$app = App\AppFactory::createApp( $app = App\AppFactory::createApp(
$autoloader,
[ [
App\Environment::BASE_DIR => dirname(__DIR__), App\Environment::BASE_DIR => dirname(__DIR__),
] ]