3v4l.org

run code in 300+ PHP versions simultaneously
<?php declare(strict_types=1); namespace GameDev\Platform\Command\Aggregator; use DateTimeImmutable; use GameDev\Component\Filter\FilterCondition; use GameDev\Platform\Command\Common\IOTrait; use GameDev\Platform\Filter\Catalog\AggregatorFilter; use GameDev\Platform\Filter\Statistic\AggregatorStatisticFilter; use GameDev\Platform\Filter\Statistic\AggregatorStatisticGroupBy; use GameDev\Platform\Model\Repository\Statistic\AggregatorStatisticItem; use GameDev\Platform\Model\Shared\Session\Player\PlayerMode; use GameDev\Platform\Repository\Catalog\AggregatorRepository; use GameDev\Platform\Repository\Statistic\AggregatorStatisticRepository; use GameDev\Platform\Utils\EmailSender; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use Shared\DaemonCommandBundle\Command\AbstractDaemon; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Mailer\Exception\TransportExceptionInterface; use Throwable; use Twig\Error\LoaderError; use Twig\Error\RuntimeError; use Twig\Error\SyntaxError; use Webmozart\Assert\Assert; #[AsCommand( name: 'platform:aggregator:aggregators-without-statistic-report-job', description: 'Check aggregators without statistics', )] class AggregatorsWithoutStatisticReportJob extends AbstractDaemon { use IOTrait; public const CATALOG_AGGREGATORS_DO_NOT_EXIST = 'Aggregators do not exist'; public const STATISTIC_AGGREGATOR_ID_MUST_BE_NOT_NULL = 'Aggregator ID must not be empty'; public const MESSAGE_TEMPLATE = 'email/aggregators_without_statistic.txt.twig'; private const ARGUMENT_EMAILS = 'emails'; private array $emails = []; public function __construct( private readonly AggregatorRepository $aggregatorRepository, private readonly AggregatorStatisticRepository $aggregatorStatisticRepository, private readonly EmailSender $emailSender, private readonly LoggerInterface $logger = new NullLogger(), ) { parent::__construct(); } protected function configure(): void { parent::configure(); $this ->setDescription('Arguments aggregators without statistic report') ->addArgument( self::ARGUMENT_EMAILS, InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'Separate multiple emails with a space', ) ->addUsage('hello@world.com'); } /** * @throws Throwable */ protected function execute(InputInterface $input, OutputInterface $output): int { $this->setIO($input, $output); $this->initParameters($input); $dateStart = new DateTimeImmutable('-2 days'); $dateEnd = new DateTimeImmutable('-1 day'); try { $aggregatorsMap = $this->getAggregatorNamesById(); if (count($aggregatorsMap) === 0) { $this->printError(self::CATALOG_AGGREGATORS_DO_NOT_EXIST); return self::FAILURE; } $messages = $this->buildMessageData($aggregatorsMap, $dateStart, $dateEnd); if (count($messages) === 0) { $this->printInfo('Statistics for the aggregator for yesterday and the day before yesterday exist'); return self::SUCCESS; } $this->sendNotifications($messages, $dateEnd); $this->logInfo($messages, $output->getVerbosity()); $this->addContext(['messages' => $messages]); } catch (Throwable $exception) { $this->logError($exception); return self::FAILURE; } return self::SUCCESS; } private function initParameters(InputInterface $input): void { $this->emails = $input->getArgument(self::ARGUMENT_EMAILS); } /** * @return array<int, string> [id => name] */ private function getAggregatorNamesById(): array { $result = []; $aggregators = $this->aggregatorRepository->findAllByFilter($this->createAggregatorsFilter()); foreach ($aggregators as $aggregator) { $result[$aggregator->getId()] = $aggregator->getName(); } return $result; } private function createAggregatorsFilter(): AggregatorFilter { return AggregatorFilter::create() ->addCondition(FilterCondition::eq(AggregatorFilter::PRODUCTION, true)) ->addCondition(FilterCondition::eq(AggregatorFilter::DISABLED, false)) ->addOrdering(AggregatorFilter::ID, true); } /** * @param array<int> $aggregatorsIds * @return array<int> */ private function getActiveAggregatorsOnDate(array $aggregatorsIds, DateTimeImmutable $date): array { $statistics = $this->aggregatorStatisticRepository->getAggregatedAggregatorStatistic( $this->createAggregatorStatisticFilter($aggregatorsIds, $date), ); return array_map(function (AggregatorStatisticItem $aggregatorStatisticItem) { Assert::notNull($aggregatorStatisticItem->aggregatorId, self::STATISTIC_AGGREGATOR_ID_MUST_BE_NOT_NULL); return $aggregatorStatisticItem->aggregatorId; }, $statistics); } /** * @param array<int, string> $aggregatorsMap */ private function buildMessageData( array $aggregatorsMap, DateTimeImmutable $dateStart, DateTimeImmutable $dateEnd, ): array { $messages = []; $aggregatorsIds = array_keys($aggregatorsMap); $activeAggregatorsOnDateStart = $this->getActiveAggregatorsOnDate($aggregatorsIds, $dateStart); $activeAggregatorsOnDateEnd = $this->getActiveAggregatorsOnDate($aggregatorsIds, $dateEnd); foreach ($activeAggregatorsOnDateStart as $aggregatorId) { if ( !in_array($aggregatorId, $activeAggregatorsOnDateEnd, true) && array_key_exists($aggregatorId, $aggregatorsMap) ) { $messages[] = [ 'aggregator_id' => $aggregatorId, 'aggregator_name' => $aggregatorsMap[$aggregatorId], ]; } } return $messages; } /** * @param array<int, array{aggregator_id: int, aggregator_name: string}> $messages * @throws Throwable * @throws TransportExceptionInterface * @throws LoaderError * @throws RuntimeError * @throws SyntaxError */ private function sendNotifications(array $messages, DateTimeImmutable $dateEnd): void { $date = $dateEnd->format('Y-m-d'); $this->emailSender->send( $this->emails, sprintf('Aggregators lost traffic %s', $date), self::MESSAGE_TEMPLATE, [ 'messages' => $messages, 'date' => $date, ], ); } /** * @param array<int> $aggregatorsIds */ private function createAggregatorStatisticFilter( array $aggregatorsIds, DateTimeImmutable $date, ): AggregatorStatisticFilter { $filter = AggregatorStatisticFilter::create(); $filter->addCondition(FilterCondition::eq(AggregatorStatisticFilter::DATE, $date->format('Y-m-d'))); $filter->addCondition( FilterCondition::in(AggregatorStatisticFilter::AGGREGATOR_ID, $aggregatorsIds), ); $filter->addCondition(FilterCondition::eq(AggregatorStatisticFilter::MODE, PlayerMode::MODE_REAL)); $filter->groupBy(AggregatorStatisticGroupBy::Aggregator); $filter->addOrdering(AggregatorStatisticFilter::AGGREGATOR_ID, true); return $filter; } private function logInfo(array $messages, int $verbosity): void { if (OutputInterface::VERBOSITY_NORMAL <= $verbosity) { $messageToLog = json_encode($messages); $this->printInfo($messageToLog); $this->logger->info($messageToLog); } } private function logError(Throwable $exception): void { $message = sprintf('An error: %s', $exception->getMessage()); $this->printError($message); $this->logger->error($message, ['exception' => $exception]); } } <?php declare(strict_types=1); namespace GameDev\Platform\Command\Aggregator; use DateTimeImmutable; use GameDev\Component\Filter\FilterCondition; use GameDev\Platform\Command\Common\IOTrait; use GameDev\Platform\Filter\Catalog\AggregatorFilter; use GameDev\Platform\Filter\Statistic\AggregatorStatisticFilter; use GameDev\Platform\Filter\Statistic\AggregatorStatisticGroupBy; use GameDev\Platform\Model\Repository\Statistic\AggregatorStatisticItem; use GameDev\Platform\Model\Shared\Session\Player\PlayerMode; use GameDev\Platform\Repository\Catalog\AggregatorRepository; use GameDev\Platform\Repository\Statistic\AggregatorStatisticRepository; use GameDev\Platform\Utils\EmailSender; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use Shared\DaemonCommandBundle\Command\AbstractDaemon; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Mailer\Exception\TransportExceptionInterface; use Throwable; use Twig\Error\LoaderError; use Twig\Error\RuntimeError; use Twig\Error\SyntaxError; use Webmozart\Assert\Assert; #[AsCommand( name: 'platform:aggregator:aggregators-without-statistic-report-job', description: 'Check aggregators without statistics', )] class AggregatorsWithoutStatisticReportJob extends AbstractDaemon { use IOTrait; public const CATALOG_AGGREGATORS_DO_NOT_EXIST = 'Aggregators do not exist'; public const STATISTIC_AGGREGATOR_ID_MUST_BE_NOT_NULL = 'Aggregator ID must not be empty'; public const MESSAGE_TEMPLATE = 'email/aggregators_without_statistic.txt.twig'; private const ARGUMENT_EMAILS = 'emails'; private array $emails = []; public function __construct( private readonly AggregatorRepository $aggregatorRepository, private readonly AggregatorStatisticRepository $aggregatorStatisticRepository, private readonly EmailSender $emailSender, private readonly LoggerInterface $logger = new NullLogger(), ) { parent::__construct(); } protected function configure(): void { parent::configure(); $this ->setDescription('Arguments aggregators without statistic report') ->addArgument( self::ARGUMENT_EMAILS, InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'Separate multiple emails with a space', ) ->addUsage('hello@world.com'); } /** * @throws Throwable */ protected function execute(InputInterface $input, OutputInterface $output): int { $this->setIO($input, $output); $this->initParameters($input); $dateStart = new DateTimeImmutable('-2 days'); $dateEnd = new DateTimeImmutable('-1 day'); try { $aggregatorsMap = $this->getAggregatorNamesById(); if (count($aggregatorsMap) === 0) { $this->printError(self::CATALOG_AGGREGATORS_DO_NOT_EXIST); return self::FAILURE; } $messages = $this->buildMessageData($aggregatorsMap, $dateStart, $dateEnd); if (count($messages) === 0) { $this->printInfo('Statistics for the aggregator for yesterday and the day before yesterday exist'); return self::SUCCESS; } $this->sendNotifications($messages, $dateEnd); $this->logInfo($messages, $output->getVerbosity()); $this->addContext(['messages' => $messages]); } catch (Throwable $exception) { $this->logError($exception); return self::FAILURE; } return self::SUCCESS; } private function initParameters(InputInterface $input): void { $this->emails = $input->getArgument(self::ARGUMENT_EMAILS); } /** * @return array<int, string> [id => name] */ private function getAggregatorNamesById(): array { $result = []; $aggregators = $this->aggregatorRepository->findAllByFilter($this->createAggregatorsFilter()); foreach ($aggregators as $aggregator) { $result[$aggregator->getId()] = $aggregator->getName(); } return $result; } private function createAggregatorsFilter(): AggregatorFilter { return AggregatorFilter::create() ->addCondition(FilterCondition::eq(AggregatorFilter::PRODUCTION, true)) ->addCondition(FilterCondition::eq(AggregatorFilter::DISABLED, false)) ->addOrdering(AggregatorFilter::ID, true); } /** * @param array<int> $aggregatorsIds * @return array<int> */ private function getActiveAggregatorsOnDate(array $aggregatorsIds, DateTimeImmutable $date): array { $statistics = $this->aggregatorStatisticRepository->getAggregatedAggregatorStatistic( $this->createAggregatorStatisticFilter($aggregatorsIds, $date), ); return array_map(function (AggregatorStatisticItem $aggregatorStatisticItem) { Assert::notNull($aggregatorStatisticItem->aggregatorId, self::STATISTIC_AGGREGATOR_ID_MUST_BE_NOT_NULL); return $aggregatorStatisticItem->aggregatorId; }, $statistics); } /** * @param array<int, string> $aggregatorsMap */ private function buildMessageData( array $aggregatorsMap, DateTimeImmutable $dateStart, DateTimeImmutable $dateEnd, ): array { $messages = []; $aggregatorsIds = array_keys($aggregatorsMap); $activeAggregatorsOnDateStart = $this->getActiveAggregatorsOnDate($aggregatorsIds, $dateStart); $activeAggregatorsOnDateEnd = $this->getActiveAggregatorsOnDate($aggregatorsIds, $dateEnd); foreach ($activeAggregatorsOnDateStart as $aggregatorId) { if ( !in_array($aggregatorId, $activeAggregatorsOnDateEnd, true) && array_key_exists($aggregatorId, $aggregatorsMap) ) { $messages[] = [ 'aggregator_id' => $aggregatorId, 'aggregator_name' => $aggregatorsMap[$aggregatorId], ]; } } return $messages; } /** * @param array<int, array{aggregator_id: int, aggregator_name: string}> $messages * @throws Throwable * @throws TransportExceptionInterface * @throws LoaderError * @throws RuntimeError * @throws SyntaxError */ private function sendNotifications(array $messages, DateTimeImmutable $dateEnd): void { $date = $dateEnd->format('Y-m-d'); $this->emailSender->send( $this->emails, sprintf('Aggregators lost traffic %s', $date), self::MESSAGE_TEMPLATE, [ 'messages' => $messages, 'date' => $date, ], ); } /** * @param array<int> $aggregatorsIds */ private function createAggregatorStatisticFilter( array $aggregatorsIds, DateTimeImmutable $date, ): AggregatorStatisticFilter { $filter = AggregatorStatisticFilter::create(); $filter->addCondition(FilterCondition::eq(AggregatorStatisticFilter::DATE, $date->format('Y-m-d'))); $filter->addCondition( FilterCondition::in(AggregatorStatisticFilter::AGGREGATOR_ID, $aggregatorsIds), ); $filter->addCondition(FilterCondition::eq(AggregatorStatisticFilter::MODE, PlayerMode::MODE_REAL)); $filter->groupBy(AggregatorStatisticGroupBy::Aggregator); $filter->addOrdering(AggregatorStatisticFilter::AGGREGATOR_ID, true); return $filter; } private function logInfo(array $messages, int $verbosity): void { if (OutputInterface::VERBOSITY_NORMAL <= $verbosity) { $messageToLog = json_encode($messages); $this->printInfo($messageToLog); $this->logger->info($messageToLog); } } private function logError(Throwable $exception): void { $message = sprintf('An error: %s', $exception->getMessage()); $this->printError($message); $this->logger->error($message, ['exception' => $exception]); } }

Here you find the average performance (time & memory) of each version. A grayed out version indicates it didn't complete successfully (based on exit-code).

VersionSystem time (s)User time (s)Memory (MiB)
8.4.100.0370.01417.98
8.4.90.0390.01217.71
8.4.80.0280.00618.00
8.4.70.0230.00617.97
8.4.60.0270.00517.75
8.4.50.0370.01017.59
8.4.40.0390.00817.86
8.4.30.0460.00917.19
8.4.20.0460.00617.65
8.4.10.0440.00617.82
8.3.230.0380.00816.79
8.3.220.0310.00916.58
8.3.210.0320.01216.59
8.3.200.0360.01116.58
8.3.190.0380.00916.73
8.3.180.0370.00716.37
8.3.170.0400.01016.63
8.3.160.0400.01016.81
8.3.150.0230.00616.80
8.3.140.0160.00716.74
8.3.130.0150.00616.71
8.3.120.0340.00516.41
8.3.110.0390.00416.44
8.3.100.0430.01016.38
8.3.90.0420.00816.59
8.3.80.0400.00516.55
8.3.70.0400.00916.74
8.3.60.0430.00816.94
8.3.50.0420.00916.79
8.3.40.0420.00817.76
8.3.30.0350.00717.69
8.3.20.0330.01217.36
8.3.10.0330.00817.52
8.3.00.0330.01117.67
8.2.290.0260.00716.89
8.2.280.0270.00916.47
8.2.270.0300.01016.88
8.2.260.0310.00716.39
8.2.250.0160.00716.55
8.2.240.0270.00716.27
8.2.230.0230.00316.41
8.2.220.0220.00416.71
8.2.210.0340.00616.21
8.2.200.0320.00916.14
8.2.190.0320.00916.00
8.2.180.0360.00516.39
8.2.170.0370.00717.72
8.2.160.0360.00817.53
8.2.150.0310.01217.21
8.2.140.0360.01017.26
8.2.130.0340.01017.50
8.2.120.0190.00217.59
8.2.110.0200.00617.58
8.2.100.0170.00517.65
8.2.90.0140.00517.72
8.2.80.0130.00617.39
8.2.70.0340.00917.50
8.2.60.0310.00917.41
8.2.50.0450.00717.39
8.2.40.0300.00717.14
8.2.30.0340.00617.42
8.2.20.0330.01017.17
8.2.10.0320.00917.44
8.2.00.0270.01217.36

preferences:
136.96 ms | 1000 KiB | 7 Q