diff --git a/webapp/src/Command/CallApiActionCommand.php b/webapp/src/Command/CallApiActionCommand.php index a1b9aa6ba6..47d98c2671 100644 --- a/webapp/src/Command/CallApiActionCommand.php +++ b/webapp/src/Command/CallApiActionCommand.php @@ -4,6 +4,7 @@ use App\Entity\User; use App\Service\DOMJudgeService; +use App\Utils\Utils; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; @@ -114,7 +115,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } if ($json = $input->getOption('json')) { - $data = array_merge($data, $this->dj->jsonDecode($json)); + $data = array_merge($data, Utils::jsonDecode($json)); } foreach ($input->getOption('file') as $fileItem) { diff --git a/webapp/src/Controller/API/ContestController.php b/webapp/src/Controller/API/ContestController.php index 245490ae5c..388287c6b3 100644 --- a/webapp/src/Controller/API/ContestController.php +++ b/webapp/src/Controller/API/ContestController.php @@ -114,7 +114,7 @@ public function addContestAction(Request $request): string return $cid; } } elseif ($jsonFile) { - $data = $this->dj->jsonDecode(file_get_contents($jsonFile->getRealPath())); + $data = Utils::jsonDecode(file_get_contents($jsonFile->getRealPath())); if ($this->importExportService->importContestData($data, $message, $cid)) { return $cid; } @@ -870,7 +870,7 @@ public function getEventFeedAction( $result['time'] = Utils::absTime($event->getEventtime()); } - echo $this->dj->jsonEncode($result) . "\n"; + echo Utils::jsonEncode($result) . "\n"; ob_flush(); flush(); $lastUpdate = Utils::now(); diff --git a/webapp/src/Controller/API/JudgehostController.php b/webapp/src/Controller/API/JudgehostController.php index 23158e1c1a..705a08542d 100644 --- a/webapp/src/Controller/API/JudgehostController.php +++ b/webapp/src/Controller/API/JudgehostController.php @@ -734,7 +734,7 @@ public function internalErrorAction(Request $request): ?int } } - $disabled = $this->dj->jsonDecode($disabled); + $disabled = Utils::jsonDecode($disabled); /** @var Contest|null $contest */ $contest = null; @@ -770,7 +770,7 @@ public function internalErrorAction(Request $request): ?int ->andWhere('e.disabled = :disabled') ->andWhere('e.status = :status') ->setParameter('description', $description) - ->setParameter('disabled', $this->dj->jsonEncode($disabled)) + ->setParameter('disabled', Utils::jsonEncode($disabled)) ->setParameter('status', 'open') ->setMaxResults(1); diff --git a/webapp/src/Controller/API/LanguageController.php b/webapp/src/Controller/API/LanguageController.php index 0415d4120b..ecec18c1d9 100644 --- a/webapp/src/Controller/API/LanguageController.php +++ b/webapp/src/Controller/API/LanguageController.php @@ -5,6 +5,7 @@ use App\Entity\ExecutableFile; use App\Entity\ImmutableExecutable; use App\Entity\Language; +use App\Utils\Utils; use Doctrine\ORM\NonUniqueResultException; use Doctrine\ORM\QueryBuilder; use FOS\RestBundle\Controller\Annotations as Rest; @@ -139,7 +140,7 @@ public function configureLanguagesAction(Request $request): Response if (!$jsonFile) { throw new BadRequestHttpException('No JSON file supplied.'); } - $newLanguages = $this->dj->jsonDecode(file_get_contents($jsonFile->getRealPath())); + $newLanguages = Utils::jsonDecode(file_get_contents($jsonFile->getRealPath())); // Disable submission for all current languages, we will enable it for all new languages below. $curLanguages = $this->em->getRepository(Language::class)->findAll(); diff --git a/webapp/src/Controller/API/ProblemController.php b/webapp/src/Controller/API/ProblemController.php index a281237137..f4e07291b0 100644 --- a/webapp/src/Controller/API/ProblemController.php +++ b/webapp/src/Controller/API/ProblemController.php @@ -13,6 +13,7 @@ use App\Service\EventLogService; use App\Service\ImportExportService; use App\Service\ImportProblemService; +use App\Utils\Utils; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\NonUniqueResultException; use Doctrine\ORM\QueryBuilder; @@ -108,7 +109,7 @@ public function addProblemsAction(Request $request): array } $message = "Error while adding problems"; if (!empty($messages)) { - $message .= ': ' . $this->dj->jsonEncode($messages); + $message .= ': ' . Utils::jsonEncode($messages); } throw new BadRequestHttpException($message); } diff --git a/webapp/src/Controller/Jury/BalloonController.php b/webapp/src/Controller/Jury/BalloonController.php index 5726d5032f..9098772310 100644 --- a/webapp/src/Controller/Jury/BalloonController.php +++ b/webapp/src/Controller/Jury/BalloonController.php @@ -9,6 +9,7 @@ use App\Service\ConfigurationService; use App\Service\DOMJudgeService; use App\Service\EventLogService; +use App\Utils\Utils; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\ExpressionLanguage\Expression; @@ -77,7 +78,7 @@ public function indexAction(BalloonService $balloonService): Response } // Load preselected filters - $filters = $this->dj->jsonDecode((string)$this->dj->getCookie('domjudge_balloonsfilter') ?: '[]'); + $filters = Utils::jsonDecode((string)$this->dj->getCookie('domjudge_balloonsfilter') ?: '[]'); $haveFilters = $this->dj->getCookie('domjudge_balloonsfilter') != null; $filteredAffiliations = []; $filteredLocations = []; diff --git a/webapp/src/Controller/Jury/ContestController.php b/webapp/src/Controller/Jury/ContestController.php index aafed2ecfa..1e4c8b7bc9 100644 --- a/webapp/src/Controller/Jury/ContestController.php +++ b/webapp/src/Controller/Jury/ContestController.php @@ -708,7 +708,7 @@ public function prefetchAction(Request $request, int $contestId): Response // TODO: dedup here? $compareExec = $this->dj->getImmutableCompareExecutable($contestProblem); $runExec = $this->dj->getImmutableRunExecutable($contestProblem); - $runConfig = $this->dj->jsonEncode( + $runConfig = Utils::jsonEncode( [ 'hash' => $runExec->getHash(), 'combined_run_compare' => $problem->getCombinedRunCompare(), @@ -720,7 +720,7 @@ public function prefetchAction(Request $request, int $contestId): Response ->setJudgehost($judgehost) ->setPriority(JudgeTask::PRIORITY_DEFAULT) ->setCompareScriptId($compareExec->getImmutableExecId()) - ->setCompareConfig($this->dj->jsonEncode(['hash' => $compareExec->getHash()])) + ->setCompareConfig(Utils::jsonEncode(['hash' => $compareExec->getHash()])) ->setRunScriptId($runExec->getImmutableExecId()) ->setRunConfig($runConfig); $this->em->persist($judgeTask); @@ -741,7 +741,7 @@ public function prefetchAction(Request $request, int $contestId): Response ->setJudgehost($judgehost) ->setPriority(JudgeTask::PRIORITY_DEFAULT) ->setCompileScriptId($compileExec->getImmutableExecId()) - ->setCompileConfig($this->dj->jsonEncode(['hash' => $compileExec->getHash()])); + ->setCompileConfig(Utils::jsonEncode(['hash' => $compileExec->getHash()])); $this->em->persist($judgeTask); $cnt++; } diff --git a/webapp/src/Controller/Jury/ExternalContestController.php b/webapp/src/Controller/Jury/ExternalContestController.php index 4fe4a3c5e1..93096f18fd 100644 --- a/webapp/src/Controller/Jury/ExternalContestController.php +++ b/webapp/src/Controller/Jury/ExternalContestController.php @@ -102,7 +102,7 @@ public function indexAction(Request $request): Response $this->sourceService->setSource($externalContestSource); // Load preselected filters - $filters = $this->dj->jsonDecode((string)$this->dj->getCookie('domjudge_external_source_filter') ?: '[]'); + $filters = Utils::jsonDecode((string)$this->dj->getCookie('domjudge_external_source_filter') ?: '[]'); // Build the filter form. $form = $this->createForm(ExternalSourceWarningsFilterType::class, $filters); diff --git a/webapp/src/Controller/Jury/InternalErrorController.php b/webapp/src/Controller/Jury/InternalErrorController.php index ddb9ed840f..2b8bc03417 100644 --- a/webapp/src/Controller/Jury/InternalErrorController.php +++ b/webapp/src/Controller/Jury/InternalErrorController.php @@ -162,7 +162,7 @@ public function handleAction(Request $request, ?Profiler $profiler, int $errorId if ($request->isXmlHttpRequest()) { $profiler?->disable(); $progressReporter = function (int $progress, string $log, ?string $message = null) { - echo $this->dj->jsonEncode(['progress' => $progress, 'log' => htmlspecialchars($log), 'message' => $message]); + echo Utils::jsonEncode(['progress' => $progress, 'log' => htmlspecialchars($log), 'message' => $message]); ob_flush(); flush(); }; diff --git a/webapp/src/Controller/Jury/JuryMiscController.php b/webapp/src/Controller/Jury/JuryMiscController.php index c2f6018de9..8d3913989f 100644 --- a/webapp/src/Controller/Jury/JuryMiscController.php +++ b/webapp/src/Controller/Jury/JuryMiscController.php @@ -15,6 +15,7 @@ use App\Service\DOMJudgeService; use App\Service\EventLogService; use App\Service\ScoreboardService; +use App\Utils\Utils; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query\Expr\Join; use Symfony\Component\DependencyInjection\Attribute\Autowire; @@ -213,7 +214,7 @@ public function refreshCacheAction(Request $request, ScoreboardService $scoreboa if ($request->isXmlHttpRequest() && $request->isMethod('POST')) { $progressReporter = function (int $progress, string $log, ?string $message = null) { - echo $this->dj->jsonEncode(['progress' => $progress, 'log' => htmlspecialchars($log), 'message' => $message]); + echo Utils::jsonEncode(['progress' => $progress, 'log' => htmlspecialchars($log), 'message' => $message]); ob_flush(); flush(); }; diff --git a/webapp/src/Controller/Jury/RejudgingController.php b/webapp/src/Controller/Jury/RejudgingController.php index 1efa99c95d..4698e33a89 100644 --- a/webapp/src/Controller/Jury/RejudgingController.php +++ b/webapp/src/Controller/Jury/RejudgingController.php @@ -449,7 +449,7 @@ public function finishAction( if ($request->isXmlHttpRequest()) { $progressReporter = function (int $progress, string $log, ?string $message = null) { - echo $this->dj->jsonEncode(['progress' => $progress, 'log' => htmlspecialchars($log), 'message' => htmlspecialchars($message ?? '')]); + echo Utils::jsonEncode(['progress' => $progress, 'log' => htmlspecialchars($log), 'message' => htmlspecialchars($message ?? '')]); ob_flush(); flush(); }; @@ -537,7 +537,7 @@ public function addAction(Request $request, FormFactoryInterface $formFactory): } if ($isCreateRejudgingAjax) { $progressReporter = function (int $progress, string $log, ?string $redirect = null) { - echo $this->dj->jsonEncode(['progress' => $progress, 'log' => htmlspecialchars($log), 'redirect' => $redirect]); + echo Utils::jsonEncode(['progress' => $progress, 'log' => htmlspecialchars($log), 'redirect' => $redirect]); ob_flush(); flush(); }; @@ -722,7 +722,7 @@ public function createAction(Request $request): Response } $progressReporter = function (int $progress, string $log, ?string $redirect = null) { - echo $this->dj->jsonEncode(['progress' => $progress, 'log' => htmlspecialchars($log), 'redirect' => $redirect]); + echo Utils::jsonEncode(['progress' => $progress, 'log' => htmlspecialchars($log), 'redirect' => $redirect]); ob_flush(); flush(); }; diff --git a/webapp/src/Controller/Jury/SubmissionController.php b/webapp/src/Controller/Jury/SubmissionController.php index 20f40e2168..b91a82fb3b 100644 --- a/webapp/src/Controller/Jury/SubmissionController.php +++ b/webapp/src/Controller/Jury/SubmissionController.php @@ -136,7 +136,7 @@ public function indexAction( } // Load preselected filters - $filters = $this->dj->jsonDecode((string)$this->dj->getCookie('domjudge_submissionsfilter') ?: '[]'); + $filters = Utils::jsonDecode((string)$this->dj->getCookie('domjudge_submissionsfilter') ?: '[]'); $results = array_keys($this->dj->getVerdicts(['final', 'in_progress'])); @@ -257,7 +257,7 @@ public function viewAction( ->getQuery() ->getResult(); $timelimits = array_map(function (JudgeTask $task) { - return $this->dj->jsonDecode($task->getRunConfig())['time_limit']; + return Utils::jsonDecode($task->getRunConfig())['time_limit']; }, $judgeTasks); } @@ -626,7 +626,7 @@ public function requestFullDebug(Request $request, Judging $jid): RedirectRespon ->setJobId($jid->getJudgingid()) ->setUuid($jid->getUuid()) ->setRunScriptId($executable->getImmutableExecId()) - ->setRunConfig($this->dj->jsonEncode(['hash' => $executable->getHash()])); + ->setRunConfig(Utils::jsonEncode(['hash' => $executable->getHash()])); $this->em->persist($judgeTask); } $this->em->flush(); @@ -1257,8 +1257,8 @@ public function createJudgeTasks(string $submitId): RedirectResponse */ private function maybeGetErrors(string $type, string $expectedConfigString, string $observedConfigString, array &$allErrors): void { - $expectedConfig = $this->dj->jsonDecode($expectedConfigString); - $observedConfig = $this->dj->jsonDecode($observedConfigString); + $expectedConfig = Utils::jsonDecode($expectedConfigString); + $observedConfig = Utils::jsonDecode($observedConfigString); $errors = []; foreach (array_keys($expectedConfig) as $k) { if (!array_key_exists($k, $observedConfig)) { @@ -1271,7 +1271,7 @@ private function maybeGetErrors(string $type, string $expectedConfigString, stri // Silently ignore. } else { $errors[] = '- ' . preg_replace('/_/', ' ', $k) . ': ' - . $this->dj->jsonEncode($observedConfig[$k]) . ' → ' . $this->dj->jsonEncode($expectedConfig[$k]); + . Utils::jsonEncode($observedConfig[$k]) . ' → ' . Utils::jsonEncode($expectedConfig[$k]); } } } diff --git a/webapp/src/Service/ConfigurationService.php b/webapp/src/Service/ConfigurationService.php index 6fe72c340f..c215a154d8 100644 --- a/webapp/src/Service/ConfigurationService.php +++ b/webapp/src/Service/ConfigurationService.php @@ -7,6 +7,7 @@ use App\Entity\Configuration; use App\Entity\Executable; use App\Entity\Judging; +use App\Utils\Utils; use BackedEnum; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\NonUniqueResultException; @@ -269,7 +270,7 @@ public function saveChanges( } if (!isset($errors[$specName])) { if ($optionToSet->getValue() != $oldValue) { - $valJson = $dj->jsonEncode($optionToSet->getValue()); + $valJson = Utils::jsonEncode($optionToSet->getValue()); $dj->auditlog('configuration', $specName, 'updated', $valJson); if ($optionIsNew) { $this->em->persist($optionToSet); diff --git a/webapp/src/Service/DOMJudgeService.php b/webapp/src/Service/DOMJudgeService.php index b2914afef2..1c0a90a4df 100644 --- a/webapp/src/Service/DOMJudgeService.php +++ b/webapp/src/Service/DOMJudgeService.php @@ -522,23 +522,6 @@ public function alert(string $messageType, string $description = ''): void system(sprintf('%s %s %s &', $alert, escapeshellarg($messageType), escapeshellarg($description))); } - /** - * Decode a JSON string with our preferred settings. - * @return mixed - */ - public function jsonDecode(string $str) - { - return json_decode($str, true, 512, JSON_THROW_ON_ERROR); - } - - /** - * Encode a JSON string with our preferred settings. - */ - public function jsonEncode(mixed $data): string - { - return json_encode($data, JSON_PRESERVE_ZERO_FRACTION | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR); - } - /** * Dis- or re-enable what caused an internal error. * @@ -664,7 +647,7 @@ public function internalApiRequest(string $url, string $method = Request::METHOD return null; } - return $this->jsonDecode($content); + return Utils::jsonDecode($content); } public function getDomjudgeEtcDir(): string @@ -1454,7 +1437,7 @@ public function getRunConfig(ContestProblem $problem, Submission $submission, in } $runExecutable = $this->getImmutableRunExecutable($problem); - return $this->jsonEncode( + return Utils::jsonEncode( [ 'time_limit' => $problem->getProblem()->getTimelimit() * $submission->getLanguage()->getTimeFactor(), 'memory_limit' => $memoryLimit, @@ -1471,7 +1454,7 @@ public function getRunConfig(ContestProblem $problem, Submission $submission, in public function getCompareConfig(ContestProblem $problem): string { $compareExecutable = $this->getImmutableCompareExecutable($problem); - return $this->jsonEncode( + return Utils::jsonEncode( [ 'script_timelimit' => $this->config->get('script_timelimit'), 'script_memory_limit' => $this->config->get('script_memory_limit'), @@ -1486,7 +1469,7 @@ public function getCompareConfig(ContestProblem $problem): string public function getCompileConfig(Submission $submission): string { $compileExecutable = $submission->getLanguage()->getCompileExecutable()->getImmutableExecutable(); - return $this->jsonEncode( + return Utils::jsonEncode( [ 'script_timelimit' => $this->config->get('script_timelimit'), 'script_memory_limit' => $this->config->get('script_memory_limit'), diff --git a/webapp/src/Service/EventLogService.php b/webapp/src/Service/EventLogService.php index f059e09e5e..1a21f5929a 100644 --- a/webapp/src/Service/EventLogService.php +++ b/webapp/src/Service/EventLogService.php @@ -183,8 +183,8 @@ public function log( $jsonPassed = isset($json); // Make a combined string to keep track of the data ID's - $dataidsCombined = $this->dj->jsonEncode($dataIds); - $idsCombined = $ids === null ? null : (is_array($ids) ? $this->dj->jsonEncode($ids) : $ids); + $dataidsCombined = Utils::jsonEncode($dataIds); + $idsCombined = $ids === null ? null : (is_array($ids) ? Utils::jsonEncode($ids) : $ids); $this->logger->debug( "EventLogService::log arguments: '%s' '%s' '%s' '%s' '%s' '%s'", @@ -224,7 +224,7 @@ public function log( } if ($ids === [null] && $json !== null) { - $data = $this->dj->jsonDecode($json); + $data = Utils::jsonDecode($json); if (!empty($data['id'])) { $ids = [$data['id']]; } @@ -234,14 +234,14 @@ public function log( $ids = [$ids]; } - $idsCombined = $this->dj->jsonEncode($ids); + $idsCombined = Utils::jsonEncode($ids); // State is a special case, as it works without an ID if ($type !== 'state' && count(array_filter($ids)) !== count($dataIds)) { $this->logger->warning( "EventLogService::log API ID not specified or inferred ". "from data for type %s and data ID's '%s'", - [ $type, $this->dj->jsonEncode($dataIds) ] + [ $type, Utils::jsonEncode($dataIds) ] ); return; } @@ -337,7 +337,7 @@ public function log( $now = sprintf('%.3f', microtime(true)); if ($jsonPassed) { - $json = $this->dj->jsonDecode($json); + $json = Utils::jsonDecode($json); } elseif (!in_array($type, ['contests', 'state'])) { // Re-index JSON so we can look up the elements by ID. $tmp = $json; @@ -557,8 +557,8 @@ protected function insertEvents( $existingEvent = $existingEvents[$event->getEndpointid()] ?? null; $existingData = $existingEvent === null ? null : - $this->dj->jsonEncode($existingEvent->getContent()); - $data = $this->dj->jsonEncode($event->getContent()); + Utils::jsonEncode($existingEvent->getContent()); + $data = Utils::jsonEncode($event->getContent()); if ($existingEvent === null || $existingData !== $data) { // Special case for state: this is always an update event if ($endpointType === 'state') { diff --git a/webapp/src/Service/ImportExportService.php b/webapp/src/Service/ImportExportService.php index 6e175668f3..493695d369 100644 --- a/webapp/src/Service/ImportExportService.php +++ b/webapp/src/Service/ImportExportService.php @@ -665,7 +665,7 @@ public function importJson(string $type, UploadedFile $file, ?string &$message = { $content = file_get_contents($file->getRealPath()); try { - $data = $this->dj->jsonDecode($content); + $data = Utils::jsonDecode($content); } catch (JsonException $e) { // Check if we can parse it as YAML try { diff --git a/webapp/src/Service/ImportProblemService.php b/webapp/src/Service/ImportProblemService.php index 7f3a503022..f341187c93 100644 --- a/webapp/src/Service/ImportProblemService.php +++ b/webapp/src/Service/ImportProblemService.php @@ -658,7 +658,7 @@ public function importZippedProblem( // Read submission details from optional file. $submission_file_string = $zip->getFromName($submission_file); $submission_details = $submission_file_string===false ? [] : - $this->dj->jsonDecode($submission_file_string); + Utils::jsonDecode($submission_file_string); $numJurySolutions = 0; for ($j = 0; $j < $zip->numFiles; $j++) { @@ -913,7 +913,7 @@ public function importProblemFromRequest(Request $request, ?int $contestId = nul $zip?->close(); } if (!empty($errors)) { - throw new BadRequestHttpException($this->dj->jsonEncode($errors)); + throw new BadRequestHttpException(Utils::jsonEncode($errors)); } return [ 'problem_id' => $probId, diff --git a/webapp/src/Service/ScoreboardService.php b/webapp/src/Service/ScoreboardService.php index 35ab9f15cc..0c1167d425 100644 --- a/webapp/src/Service/ScoreboardService.php +++ b/webapp/src/Service/ScoreboardService.php @@ -713,7 +713,7 @@ public function initializeScoreboardFilter(Request $request, ?Response $response { $scoreFilter = []; if ($this->dj->getCookie('domjudge_scorefilter')) { - $scoreFilter = $this->dj->jsonDecode((string)$this->dj->getCookie('domjudge_scorefilter')); + $scoreFilter = Utils::jsonDecode((string)$this->dj->getCookie('domjudge_scorefilter')); } if ($request->query->has('clear')) { @@ -731,7 +731,7 @@ public function initializeScoreboardFilter(Request $request, ?Response $response $this->dj->setCookie( 'domjudge_scorefilter', - $this->dj->jsonEncode($scoreFilter), + Utils::jsonEncode($scoreFilter), 0, null, '', false, false, $response ); diff --git a/webapp/src/Utils/Utils.php b/webapp/src/Utils/Utils.php index c1ec4acb9a..6b59489979 100644 --- a/webapp/src/Utils/Utils.php +++ b/webapp/src/Utils/Utils.php @@ -949,4 +949,21 @@ public static function getTextStreamedResponse( return $response; } + + /** + * Decode a JSON string with our preferred settings. + * @return mixed + */ + public static function jsonDecode(string $str) + { + return json_decode($str, true, 512, JSON_THROW_ON_ERROR); + } + + /** + * Encode a JSON string with our preferred settings. + */ + public static function jsonEncode(mixed $data): string + { + return json_encode($data, JSON_PRESERVE_ZERO_FRACTION | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR); + } }