Skip to content

Fix all PHPStan reported errors #3013

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 60 additions & 4 deletions webapp/src/Command/ScoreboardMergeCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Entity\Contest;
use App\Entity\ContestProblem;
use App\Entity\Problem;
use App\Entity\RankCache;
use App\Entity\ScoreCache;
use App\Entity\Team;
use App\Entity\TeamAffiliation;
Expand All @@ -14,6 +15,7 @@
use App\Service\ScoreboardService;
use App\Utils\FreezeData;
use App\Utils\Scoreboard\Scoreboard;
use App\Utils\Utils;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
Expand Down Expand Up @@ -131,6 +133,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$problems = [];
$problemNameToIdMap = [];
$scoreCache = [];
/** @var RankCache[] $rankCache */
$rankCache = [];
$penaltyTime = (int)$this->config->get('penalty_time');
$scoreIsInSeconds = (bool)$this->config->get('score_in_seconds');
$timeOfLastCorrect = [];
$affiliations = [];
$firstSolve = [];
$contest = (new Contest())
Expand Down Expand Up @@ -261,6 +268,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$id = count($problems);
$problemObj = (new Problem())
->setProbid($id)
->setExternalid((string)$id)
->setName($name);
$contestProblemObj = (new ContestProblem())
->setProblem($problemObj)
Expand All @@ -280,10 +288,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$scoreCacheObj
->setSolveTimePublic($problem['time'] * 60)
->setSolveTimeRestricted($problem['time'] * 60);
if (
$firstSolve[$name] === null or
$problem['time'] * 60 < $firstSolve[$name]
) {
if ($firstSolve[$name] === null ||
$problem['time'] * 60 < $firstSolve[$name]) {
$firstSolve[$name] = $problem['time'] * 60;
}
}
Expand All @@ -302,14 +308,64 @@ protected function execute(InputInterface $input, OutputInterface $output): int
if ($scoreCacheObj->getSolveTimeRestricted() == $firstSolve[$scoreCacheObj->getProblem()->getName()]) {
$scoreCacheObj->setIsFirstToSolve(true);
}

$teamId = $scoreCacheObj->getTeam()->getTeamid();
if (isset($rankCache[$teamId])) {
$rankCacheObj = $rankCache[$teamId];
} else {
$rankCacheObj = (new RankCache())
->setTeam($scoreCacheObj->getTeam());
$rankCache[$teamId] = $rankCacheObj;
}

$problem = $problems[$scoreCacheObj->getProblem()->getProbid()];
if ($scoreCacheObj->getIsCorrectRestricted()) {
$rankCacheObj->setPointsRestricted($rankCacheObj->getPointsRestricted() + $problem->getPoints());
$solveTime = Utils::scoretime(
(float)$scoreCacheObj->getSolvetimeRestricted(),
$scoreIsInSeconds
);
$penalty = Utils::calcPenaltyTime($scoreCacheObj->getIsCorrectRestricted(),
$scoreCacheObj->getSubmissionsRestricted(),
$penaltyTime, $scoreIsInSeconds);
$rankCacheObj->setTotaltimeRestricted($rankCacheObj->getTotaltimeRestricted() + $solveTime + $penalty);
$rankCacheObj->setTotalruntimeRestricted($rankCacheObj->getTotalruntimeRestricted() + $scoreCacheObj->getRuntimeRestricted());
$timeOfLastCorrect[$teamId] = max(
$timeOfLastCorrect[$teamId] ?? 0,
Utils::scoretime(
(float)$scoreCacheObj->getSolvetimeRestricted(),
$scoreIsInSeconds
),
);
}
}

foreach ($rankCache as $rankCacheObj) {
$teamId = $rankCacheObj->getTeam()->getTeamid();
$rankCacheObj->setSortKeyRestricted(ScoreboardService::getICPCScoreKey(
$rankCacheObj->getPointsRestricted(),
$rankCacheObj->getTotaltimeRestricted(), $timeOfLastCorrect[$teamId] ?? 0
));
}

usort($teams, function (Team $a, Team $b) use ($rankCache) {
$rankCacheA = $rankCache[$a->getTeamid()];
$rankCacheB = $rankCache[$b->getTeamid()];
$rankCacheSort = $rankCacheB->getSortKeyRestricted() <=> $rankCacheA->getSortKeyRestricted();
if ($rankCacheSort === 0) {
return $a->getEffectiveName() <=> $b->getEffectiveName();
}

return $rankCacheSort;
});

$scoreboard = new Scoreboard(
$contest,
$teams,
[$category],
$problems,
$scoreCache,
array_values($rankCache),
$freezeData,
false,
(int)$this->config->get('penalty_time'),
Expand Down
4 changes: 1 addition & 3 deletions webapp/src/Controller/API/JudgehostController.php
Original file line number Diff line number Diff line change
Expand Up @@ -1656,9 +1656,7 @@ public function getJudgeTasksAction(Request $request): array
$judgetasks = [['type' => 'try_again']];
}
}
if (!empty($judgetasks)) {
return $judgetasks;
}
return $judgetasks;
}

if ($this->config->get('enable_parallel_judging')) {
Expand Down
1 change: 1 addition & 0 deletions webapp/src/Controller/API/PrintController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
Expand Down
2 changes: 2 additions & 0 deletions webapp/src/Controller/Jury/ProblemController.php
Original file line number Diff line number Diff line change
Expand Up @@ -593,8 +593,10 @@ public function testcasesAction(Request $request, int $probId): Response
$content = file_get_contents($file->getRealPath());
if ($type === 'image') {
if (mime_content_type($file->getRealPath()) === 'image/svg+xml') {
$originalContent = $content;
$content = Utils::sanitizeSvg($content);
if ($content === false) {
$imageType = Utils::getImageType($originalContent, $error);
$this->addFlash('danger', sprintf('image: %s', $error));
return $this->redirectToRoute('jury_problem_testcases', ['probId' => $probId]);
}
Expand Down
3 changes: 3 additions & 0 deletions webapp/src/Entity/Contest.php
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,9 @@ public function removeLanguage(Language $language): void
$this->languages->removeElement($language);
}

/**
* @return Collection<int, Language>
*/
public function getLanguages(): Collection
{
return $this->languages;
Expand Down
6 changes: 6 additions & 0 deletions webapp/src/Entity/Language.php
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,9 @@ public function removeContest(Contest $contest): Language
return $this;
}

/**
* @return Collection<int, Contest>
*/
public function getContests(): Collection
{
return $this->contests;
Expand All @@ -466,6 +469,9 @@ public function removeProblem(Problem $problem): Language
return $this;
}

/**
* @return Collection<int, Problem>
*/
public function getProblems(): Collection
{
return $this->problems;
Expand Down
13 changes: 13 additions & 0 deletions webapp/src/Entity/Problem.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ class Problem extends BaseApiEntity implements
public const TYPE_INTERACTIVE = 8;
public const TYPE_SUBMIT_ANSWER = 16;

/**
* @var array<int, string>
*/
private array $typesToString = [
self::TYPE_PASS_FAIL => 'pass-fail',
self::TYPE_SCORING => 'scoring',
Expand Down Expand Up @@ -292,8 +295,12 @@ public function getSpecialCompareArgs(): ?string
return $this->special_compare_args;
}

/**
* @param list<string> $types
*/
public function setTypesAsString(array $types): Problem
{
/** @var array<string, int> $stringToTypes */
$stringToTypes = array_flip($this->typesToString);
$typeConstants = [];
foreach ($types as $type) {
Expand All @@ -320,6 +327,9 @@ public function getTypesAsString(): string
return implode(', ', $typeStrings);
}

/**
* @return list<int>
*/
public function getTypes(): array
{
$ret = [];
Expand All @@ -331,6 +341,9 @@ public function getTypes(): array
return $ret;
}

/**
* @param array<int> $types
*/
public function setTypes(array $types): Problem
{
$types = array_unique($types);
Expand Down
1 change: 1 addition & 0 deletions webapp/src/Service/ConfigurationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ public function addOptions(ConfigurationSpecification $item): ConfigurationSpeci
* identifier key/value pairs. The identifiers try to adhere to
* https://ccs-specs.icpc.io/draft/contest_api#known-judgement-types
*
* @param list<string> $groups
* @return array<string, string>
*/
public function getVerdicts(array $groups = ['final']): array
Expand Down
3 changes: 2 additions & 1 deletion webapp/src/Service/DOMJudgeService.php
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,7 @@ public function openZipFile(string $filename): ZipArchive
* @param string $origname The original filename as submitted by the team
* @param string|null $language Langid of the programming language this file is in
* @param bool $asTeam Print the file as the team associated with the user
* @return array{0: bool, 1: string}
*/
public function printUserFile(
string $filename,
Expand Down Expand Up @@ -1543,7 +1544,7 @@ public function getScoreboardZip(
}

/**
* @return array{'backgroundColors', array<TeamCategory>}
* @return array{backgroundColors: array<string>}
*/
public function getScoreboardCategoryColorCss(): array {
$backgroundColors = array_map(fn($x) => ( $x->getColor() ?? '#FFFFFF' ), $this->em->getRepository(TeamCategory::class)->findAll());
Expand Down
6 changes: 5 additions & 1 deletion webapp/src/Service/ImportProblemService.php
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,11 @@ private function searchAndAddValidator(ZipArchive $zip, ?array &$messages, strin
return true;
}

// Returns true iff the yaml could be parsed correctly.
/**
* Returns true iff the yaml could be parsed correctly.
*
* @param array{danger: string[], info: string[]} $messages
*/
public static function parseYaml(bool|string $problemYaml, array &$messages, string &$validationMode, PropertyAccessor $propertyAccessor, Problem $problem): bool
{
if ($problemYaml === false) {
Expand Down
1 change: 1 addition & 0 deletions webapp/src/Utils/Scoreboard/Scoreboard.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class Scoreboard
* @param TeamCategory[] $categories
* @param ContestProblem[] $problems
* @param ScoreCache[] $scoreCache
* @param RankCache[] $rankCache
*/
public function __construct(
protected readonly Contest $contest,
Expand Down
1 change: 1 addition & 0 deletions webapp/src/Utils/Scoreboard/SingleTeamScoreboard.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class SingleTeamScoreboard extends Scoreboard
/**
* @param ContestProblem[] $problems
* @param ScoreCache[] $scoreCache
* @param RankCache[] $rankCache
*/
public function __construct(
Contest $contest,
Expand Down
29 changes: 16 additions & 13 deletions webapp/tests/Unit/Service/ImportProblemServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ protected function setUp(): void
self::bootKernel();
}

public function testEmptyYaml()
public function testEmptyYaml(): void
{
$yaml = '';
$messages = [];
Expand All @@ -26,7 +26,7 @@ public function testEmptyYaml()
$this->assertEquals('Unknown name', $problem->getName());
}

public function testMinimalYamlTest()
public function testMinimalYamlTest(): void
{
$yaml = <<<YAML
name: test
Expand All @@ -53,7 +53,7 @@ public function testMinimalYamlTest()
$this->assertEquals(null, $problem->getSpecialCompareArgs());
}

public function testTypesYamlTest()
public function testTypesYamlTest(): void
{
foreach ([
'pass-fail',
Expand Down Expand Up @@ -89,7 +89,7 @@ public function testTypesYamlTest()
}
}

public function testUnknownProblemType()
public function testUnknownProblemType(): void
{
$yaml = <<<YAML
name: test
Expand All @@ -105,7 +105,8 @@ public function testUnknownProblemType()
$this->assertStringContainsString('Unknown problem type', $messagesString);
}

public function testInvalidProblemType() {
public function testInvalidProblemType(): void
{
foreach ([
'pass-fail scoring',
'submit-answer multi-pass',
Expand All @@ -126,7 +127,7 @@ public function testInvalidProblemType() {
}
}

public function testValidatorFlags()
public function testValidatorFlags(): void
{
$yaml = <<<YAML
name: test
Expand All @@ -143,7 +144,7 @@ public function testValidatorFlags()
$this->assertEquals('float_tolerance 1E-6', $problem->getSpecialCompareArgs());
}

public function testCustomValidation()
public function testCustomValidation(): void
{
foreach (['custom', 'custom interactive', 'custom multi-pass'] as $mode) {
$yaml = <<<YAML
Expand All @@ -169,7 +170,7 @@ public function testCustomValidation()
}
}

public function testMemoryLimit()
public function testMemoryLimit(): void
{
$yaml = <<<YAML
name: test
Expand All @@ -187,7 +188,7 @@ public function testMemoryLimit()
$this->assertEquals(1234*1024, $problem->getMemlimit());
}

public function testOutputLimit()
public function testOutputLimit(): void
{
$yaml = <<<YAML
name: test
Expand All @@ -205,7 +206,7 @@ public function testOutputLimit()
$this->assertEquals(4223*1024, $problem->getOutputlimit());
}

public function testMultipassLimit()
public function testMultipassLimit(): void
{
$yaml = <<<YAML
name: test
Expand All @@ -223,7 +224,8 @@ public function testMultipassLimit()
$this->assertEquals(7, $problem->getMultipassLimit());
}

public function testMaximalProblem() {
public function testMaximalProblem(): void
{
$yaml = <<<YAML
name: test
type: pass-fail
Expand All @@ -249,7 +251,8 @@ public function testMaximalProblem() {
$this->assertEquals('special flags', $problem->getSpecialCompareArgs());
}

public function testMultipleLanguages() {
public function testMultipleLanguages(): void
{
$yaml = <<<YAML
name:
de: deutsch
Expand All @@ -265,7 +268,7 @@ public function testMultipleLanguages() {
$this->assertEquals('english', $problem->getName());
}

public function testKattisExample()
public function testKattisExample(): void
{
$yaml = <<<YAML
problem_format_version: 2023-07-draft
Expand Down
Loading