diff --git a/app/Makefile b/app/Makefile
index 9aed537..6423518 100644
--- a/app/Makefile
+++ b/app/Makefile
@@ -41,9 +41,11 @@ composer: ## Install PHP dependencies
cache: ## Clear Symfony cache
@docker compose exec php bin/console c:c
+test-build:
+ @$(TEST_COMPOSE) build
test: ## Run PHPUnit tests (isolated test containers)
@echo "Starting test containers..."
- @$(TEST_COMPOSE) up -d --build --wait
+ @$(TEST_COMPOSE) up -d
@echo "Setting up test database..."
@$(TEST_COMPOSE) exec php bin/console d:d:c -n --if-not-exists
@$(TEST_COMPOSE) exec php bin/console d:m:m -n
@@ -55,7 +57,7 @@ test: ## Run PHPUnit tests (isolated test containers)
@$(TEST_COMPOSE) down
test-up: ## Start test containers (keep running)
- @$(TEST_COMPOSE) up -d --build --wait
+ @$(TEST_COMPOSE) up -d
@$(TEST_COMPOSE) exec php bin/console d:d:c -n --if-not-exists
@$(TEST_COMPOSE) exec php bin/console d:m:m -n
@$(TEST_COMPOSE) exec php bin/console app:populate:users -n
@@ -90,7 +92,7 @@ coverage-text: ## Show code coverage in terminal
@$(TEST_COMPOSE) up -d --build --wait
@$(TEST_COMPOSE) exec php bin/console d:d:c -n --if-not-exists
@$(TEST_COMPOSE) exec php bin/console d:m:m -n
- @$(TEST_COMPOSE) exec php bin/console app:populate:users -n
+ @$(TEST_COMPOSE) exec php bin/console app:populate:users -n 1 -f Test -l User
@$(TEST_COMPOSE) exec php bin/console app:populate:capitals --purge -n
@$(TEST_COMPOSE) exec php bin/console app:populate:flags --purge -n
@$(TEST_COMPOSE) exec php php -d pcov.enabled=1 vendor/bin/phpunit --coverage-text
@@ -103,7 +105,7 @@ qa: ## Run full quality assurance pipeline CS-FIXER PSALM PHPUNIT
@$(TEST_COMPOSE) up -d --build --wait
@$(TEST_COMPOSE) exec php bin/console d:d:c -n --if-not-exists
@$(TEST_COMPOSE) exec php bin/console d:m:m -n
- @$(TEST_COMPOSE) exec php bin/console app:populate:users -n
+ @$(TEST_COMPOSE) exec php bin/console app:populate:users -n 1 -f Test -l User
@$(TEST_COMPOSE) exec php bin/console app:populate:capitals --purge -n
@$(TEST_COMPOSE) exec php bin/console app:populate:flags --purge -n
@echo ""
@@ -127,6 +129,9 @@ pipeline: qa ## Alias for qa (run full pipeline like GitHub Actions)
psalm: ## Run Psalm static analysis
@docker compose exec php vendor/bin/psalm --no-cache
+psalm-baseline-update: ## Update baseline file (new errors will not be added)
+ @docker compose exec php vendor/bin/psalm --no-cache --update-baseline
+
cs-fix: ## Fix code style (PHP CS Fixer + PHPCS)
@echo "Fixing code style with PHP CS Fixer..."
@docker compose exec php vendor/bin/php-cs-fixer fix
diff --git a/app/psalm-baseline.xml b/app/psalm-baseline.xml
index 2a817f0..0f300d4 100644
--- a/app/psalm-baseline.xml
+++ b/app/psalm-baseline.xml
@@ -94,8 +94,6 @@
-
-
@@ -129,8 +127,6 @@
-
-
@@ -180,7 +176,6 @@
-
@@ -218,7 +213,6 @@
-
@@ -228,8 +222,6 @@
-
-
diff --git a/app/src/Flags/ConsoleCommand/SetWebhookCommand.php b/app/src/Flags/ConsoleCommand/SetWebhookCommand.php
index fa23974..a827b14 100644
--- a/app/src/Flags/ConsoleCommand/SetWebhookCommand.php
+++ b/app/src/Flags/ConsoleCommand/SetWebhookCommand.php
@@ -9,6 +9,10 @@
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\HttpFoundation\Request;
+use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
+use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
+use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
+use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class SetWebhookCommand extends Command
@@ -41,12 +45,22 @@ protected function configure()
;
}
+ /**
+ * @throws TransportExceptionInterface
+ * @throws ServerExceptionInterface
+ * @throws RedirectionExceptionInterface
+ * @throws ClientExceptionInterface
+ */
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$arg1 = $input->getArgument('arg1');
- // $r = $this->client->request(Request::METHOD_GET, 'https://api.telegram.org/bot' . $this->botToken . '/setWebhook?url=' . $arg1);
- $r = $this->client->request(Request::METHOD_GET, 'https://api.telegram.org/bot' . $this->botToken . '/setWebhook?url=' . $arg1);
+ // $r = $this->client->request(
+ // Request::METHOD_GET, 'https://api.telegram.org/bot' . $this->botToken . '/setWebhook?url=' . $arg1);
+ $r = $this->client->request(
+ Request::METHOD_GET,
+ 'https://api.telegram.org/bot' . $this->botToken . '/setWebhook?url=' . $arg1,
+ );
if ($arg1) {
$io->note(sprintf('You passed an argument: %s', $arg1));
}
diff --git a/app/src/Flags/Controller/CapitalsController.php b/app/src/Flags/Controller/CapitalsController.php
index fddf346..9e08621 100644
--- a/app/src/Flags/Controller/CapitalsController.php
+++ b/app/src/Flags/Controller/CapitalsController.php
@@ -59,8 +59,12 @@ public function gameOver(Request $request, CapitalsGameService $service): JsonRe
}
#[Route('/capitals/answer/{game}/{countryCode}/{answer}', name: 'get_question_for_game', methods: ['GET'])]
- public function getQuestion(Game $game, string $countryCode, string $answer, CapitalsGameService $service): JsonResponse
- {
+ public function getQuestion(
+ Game $game,
+ string $countryCode,
+ string $answer,
+ CapitalsGameService $service,
+ ): JsonResponse {
return $this->json($service->giveAnswer($countryCode, base64_decode($answer), $game));
}
diff --git a/app/src/Flags/Controller/FlagsController.php b/app/src/Flags/Controller/FlagsController.php
index b093364..07127f3 100644
--- a/app/src/Flags/Controller/FlagsController.php
+++ b/app/src/Flags/Controller/FlagsController.php
@@ -107,8 +107,11 @@ public function getHighScores(UserRepository $repository): Response
* @throws \JsonException
*/
#[Route('/scores', name: 'submit_game_results', methods: ['POST'])]
- public function postScore(Request $request, EntityManagerInterface $entityManager, #[CurrentUser] User $user): Response
- {
+ public function postScore(
+ Request $request,
+ EntityManagerInterface $entityManager,
+ #[CurrentUser] User $user,
+ ): Response {
$requestArray = json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR);
$scoreDTO = new ScoreDTO($requestArray);
$score = new Score()->fromDTO($scoreDTO);
diff --git a/app/src/Flags/Entity/CapitalsStat.php b/app/src/Flags/Entity/CapitalsStat.php
index 58cfad1..21ce594 100644
--- a/app/src/Flags/Entity/CapitalsStat.php
+++ b/app/src/Flags/Entity/CapitalsStat.php
@@ -3,9 +3,10 @@
namespace App\Flags\Entity;
use App\Flags\Entity\Enum\GameType;
+use App\Flags\Repository\CapitalsStatRepository;
use Doctrine\ORM\Mapping as ORM;
-#[ORM\Entity(repositoryClass: "App\Flags\Repository\CapitalsStatRepository")]
+#[ORM\Entity(repositoryClass: CapitalsStatRepository::class)]
class CapitalsStat
{
#[ORM\Id]
@@ -19,7 +20,7 @@ class CapitalsStat
#[ORM\Column(type: 'integer', length: 255)]
protected int $score;
- #[ORM\ManyToOne(targetEntity: "App\Flags\Entity\User")]
+ #[ORM\ManyToOne(targetEntity: User::class)]
protected readonly User $user;
#[ORM\Column(type: 'string', length: 255)]
diff --git a/app/src/Flags/Repository/AnswerRepository.php b/app/src/Flags/Repository/AnswerRepository.php
index 0e6513c..add8762 100644
--- a/app/src/Flags/Repository/AnswerRepository.php
+++ b/app/src/Flags/Repository/AnswerRepository.php
@@ -22,7 +22,8 @@ public function __construct(ManagerRegistry $registry)
public function findIncorrectGuesses(string $userId): array
{
- // SELECT COUNT(answer.flag_code) as incorrect, answer.flag_code FROM answer WHERE answer.user_id = 6 AND answer.correct = 0
+ // SELECT COUNT(answer.flag_code) as incorrect,
+ // answer.flag_code FROM answer WHERE answer.user_id = 6 AND answer.correct = 0
// GROUP BY answer.flag_code ORDER BY incorrect DESC;
return $this->createQueryBuilder('a')
@@ -41,7 +42,8 @@ public function findIncorrectGuesses(string $userId): array
public function findCorrectGuesses(string $userId): array
{
- // SELECT COUNT(answer.flag_code) as incorrect, answer.flag_code FROM answer WHERE answer.user_id = 6 AND answer.correct = 0
+ // SELECT COUNT(answer.flag_code) as incorrect,
+ // answer.flag_code FROM answer WHERE answer.user_id = 6 AND answer.correct = 0
// GROUP BY answer.flag_code ORDER BY incorrect DESC;
return $this->createQueryBuilder('a')
@@ -60,7 +62,8 @@ public function findCorrectGuesses(string $userId): array
public function findAllGuesses(string $userId): array
{
- // SELECT COUNT(answer.flag_code) as incorrect, answer.flag_code FROM answer WHERE answer.user_id = 6 AND answer.correct = 0
+ // SELECT COUNT(answer.flag_code) as incorrect,
+ // answer.flag_code FROM answer WHERE answer.user_id = 6 AND answer.correct = 0
// GROUP BY answer.flag_code ORDER BY incorrect DESC;
return $this->createQueryBuilder('a')
@@ -74,33 +77,4 @@ public function findAllGuesses(string $userId): array
->getArrayResult()
;
}
-
- // /**
- // * @return Flag[] Returns an array of Flag objects
- // */
- /*
- public function findByExampleField($value)
- {
- return $this->createQueryBuilder('f')
- ->andWhere('f.exampleField = :val')
- ->setParameter('val', $value)
- ->orderBy('f.id', 'ASC')
- ->setMaxResults(10)
- ->getQuery()
- ->getResult()
- ;
- }
- */
-
- /*
- public function findOneBySomeField($value): ?Flag
- {
- return $this->createQueryBuilder('f')
- ->andWhere('f.exampleField = :val')
- ->setParameter('val', $value)
- ->getQuery()
- ->getOneOrNullResult()
- ;
- }
- */
}
diff --git a/app/src/Flags/Repository/GameRepository.php b/app/src/Flags/Repository/GameRepository.php
index f593b86..bac5d33 100644
--- a/app/src/Flags/Repository/GameRepository.php
+++ b/app/src/Flags/Repository/GameRepository.php
@@ -4,6 +4,7 @@
use App\Flags\Entity\Game;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
+use Doctrine\ORM\EntityNotFoundException;
use Doctrine\Persistence\ManagerRegistry;
/**
@@ -19,32 +20,10 @@ public function __construct(ManagerRegistry $registry)
parent::__construct($registry, Game::class);
}
- // /**
- // * @return Game[] Returns an array of Game objects
- // */
- /*
- public function findByExampleField($value)
+ /** @psalm-suppress PossiblyUnusedParam */
+ public function getById(int $id): Game
{
- return $this->createQueryBuilder('g')
- ->andWhere('g.exampleField = :val')
- ->setParameter('val', $value)
- ->orderBy('g.id', 'ASC')
- ->setMaxResults(10)
- ->getQuery()
- ->getResult()
- ;
+ return $this->findOneBy(['id' => $id])
+ ?? throw EntityNotFoundException::fromClassNameAndIdentifier(className: Game::class, id: [$id]);
}
- */
-
- /*
- public function findOneBySomeField($value): ?Game
- {
- return $this->createQueryBuilder('g')
- ->andWhere('g.exampleField = :val')
- ->setParameter('val', $value)
- ->getQuery()
- ->getOneOrNullResult()
- ;
- }
- */
}
diff --git a/app/src/Flags/Repository/UserRepository.php b/app/src/Flags/Repository/UserRepository.php
index 0ae23fa..f15ad99 100644
--- a/app/src/Flags/Repository/UserRepository.php
+++ b/app/src/Flags/Repository/UserRepository.php
@@ -4,7 +4,6 @@
use App\Flags\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
-use Doctrine\Common\Collections\Criteria;
use Doctrine\Persistence\ManagerRegistry;
use League\OAuth2\Client\Provider\GenericResourceOwner;
use Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface;
@@ -13,6 +12,7 @@
/**
* @method User|null find($id, $lockMode = null, $lockVersion = null)
* @method User[] findAll()
+ * @method User|null findOneBy(array $criteria)
* @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class UserRepository extends ServiceEntityRepository implements UserLoaderInterface
@@ -22,7 +22,7 @@ public function __construct(ManagerRegistry $registry)
parent::__construct($registry, User::class);
}
- public function getHighScores()
+ public function getHighScores(): array
{
return $this->createQueryBuilder('u')
->select('u.firstName')
@@ -32,53 +32,11 @@ public function getHighScores()
->addSelect('u.gamesTotal')
->addOrderBy('u.highScore', 'DESC')
->addOrderBy('u.bestTime', 'ASC')
- ->setMaxResults(5)
- ->getQuery()
- ->getScalarResult()
- ;
- }
-
- public function getAnyUser(): User
- {
- return $this->matching(
- ($criteria = new Criteria())
- ->where(
- $criteria
- ->expr()
- ->gt('id', 0)
- )
- ->setMaxResults(1)
- )->get(0);
- }
-
- // /**
- // * @return User[] Returns an array of User objects
- // */
- /*
- public function findByExampleField($value)
- {
- return $this->createQueryBuilder('u')
- ->andWhere('u.exampleField = :val')
- ->setParameter('val', $value)
- ->orderBy('u.id', 'ASC')
->setMaxResults(10)
->getQuery()
- ->getResult()
- ;
- }
- */
-
- /*
- public function findOneBySomeField($value): ?User
- {
- return $this->createQueryBuilder('u')
- ->andWhere('u.exampleField = :val')
- ->setParameter('val', $value)
- ->getQuery()
- ->getOneOrNullResult()
+ ->getScalarResult()
;
}
- */
public function loadUserByIdentifier(string $identifier): ?UserInterface
{
diff --git a/app/src/Flags/Security/HqAuthAuthenticator.php b/app/src/Flags/Security/HqAuthAuthenticator.php
index fac7b11..26565f3 100644
--- a/app/src/Flags/Security/HqAuthAuthenticator.php
+++ b/app/src/Flags/Security/HqAuthAuthenticator.php
@@ -1,92 +1,11 @@
attributes->get('_route') === 'oauth_check';
-// }
-//
-// public function authenticate(Request $request): Passport
-// {
-// $client = $this->clientRegistry->getClient('flags_app');
-// $accessToken = $this->fetchAccessToken($client);
-//
-// // Store the access token in the request for later use
-// $request->attributes->set('oauth_access_token', $accessToken->getToken());
-// $request->attributes->set('oauth_refresh_token', $accessToken->getRefreshToken());
-// $request->attributes->set('oauth_expires_in', $accessToken->getExpires());
-//
-// return new SelfValidatingPassport(
-// new UserBadge($accessToken->getToken(), function () use ($accessToken, $client) {
-// $userInfo = $client->fetchUserFromToken($accessToken);
-// return $this->userRepository->loadOrCreateFromOAuth($userInfo);
-// })
-// );
-// }
-//
-// public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
-// {
-// // Get the JWT access token
-// $accessToken = $request->attributes->get('oauth_access_token');
-// $refreshToken = $request->attributes->get('oauth_refresh_token');
-// $expiresIn = $request->attributes->get('oauth_expires_in');
-//
-// // Return JSON with the tokens for the frontend
-// return new JsonResponse([
-// 'success' => true,
-// 'access_token' => $accessToken,
-// 'refresh_token' => $refreshToken,
-// 'expires_in' => $expiresIn,
-// 'token_type' => 'Bearer',
-// 'user' => [
-// // 'email' => $token->getUser()->getEmail(),
-// 'roles' => $token->getUser()->getRoles(),
-// ]
-// ]);
-// }
-//
-// public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
-// {
-// return new JsonResponse([
-// 'success' => false,
-// 'error' => $exception->getMessage()
-// ], Response::HTTP_UNAUTHORIZED);
-// }
-// }
-
namespace App\Flags\Security;
use App\Flags\Repository\UserRepository;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use KnpU\OAuth2ClientBundle\Security\Authenticator\OAuth2Authenticator;
-use Lcobucci\JWT\Parser;
use Symfony\Component\HttpFoundation\JsonResponse;
-use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
@@ -102,7 +21,6 @@ public function __construct(
private ClientRegistry $clientRegistry,
private RouterInterface $router,
private UserRepository $userRepository,
- // private Parser $jwtParser,
) {
}
@@ -140,12 +58,17 @@ public function authenticate(Request $request): Passport
);
}
- // public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
+ // public function onAuthenticationSuccess(R
+ // equest $request,
+ // TokenInterface $token,
+ // string $firewallName
+ // ): ?Response
// {
// return new JsonResponse([$token->getUser()->getUserIdentifier(), implode($token->getUser()->getRoles())]);
// // return new RedirectResponse($this->router->generate('app_dashboard'));
// }
+ #[\Override]
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
// dd($request->toArray());
@@ -177,11 +100,21 @@ public function onAuthenticationSuccess(Request $request, TokenInterface $token,
// ]);
}
- public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
- {
+ #[\Override]
+ public function onAuthenticationFailure(
+ Request $request,
+ AuthenticationException $exception,
+ ): ?Response {
// DEBUG: Log the actual error
error_log('OAuth authentication failed: ' . $exception->getMessage());
- error_log('Previous exception: ' . ($exception->getPrevious() ? $exception->getPrevious()->getMessage() : 'none'));
+ error_log(
+ sprintf(
+ 'Previous exception: %s',
+ $exception->getPrevious()
+ ? $exception->getPrevious()->getMessage()
+ : 'none'
+ )
+ );
// Temporarily return error instead of redirect loop
return new JsonResponse([
diff --git a/app/src/Flags/Security/JwksJwtEncoder.php b/app/src/Flags/Security/JwksJwtEncoder.php
index b1a6d45..18b3861 100644
--- a/app/src/Flags/Security/JwksJwtEncoder.php
+++ b/app/src/Flags/Security/JwksJwtEncoder.php
@@ -7,13 +7,14 @@
use App\Flags\Service\JwksService;
use Lexik\Bundle\JWTAuthenticationBundle\Encoder\JWTEncoderInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
+use Psr\Cache\InvalidArgumentException;
use Psr\Log\LoggerInterface;
-class JwksJwtEncoder implements JWTEncoderInterface
+readonly class JwksJwtEncoder implements JWTEncoderInterface
{
public function __construct(
- private readonly JwksService $jwksService,
- private readonly ?LoggerInterface $logger = null,
+ private JwksService $jwksService,
+ private ?LoggerInterface $logger = null,
) {
}
@@ -22,6 +23,11 @@ public function encode(array $data): string
throw new \LogicException('This application does not issue tokens. Use the auth server.');
}
+ /**
+ * @throws InvalidArgumentException
+ * @throws JWTDecodeFailureException
+ * @throws \JsonException
+ */
public function decode($token): array
{
$this->logger?->notice('JwksJwtEncoder: CUSTOM ENCODER ACTIVE - decoding token');
@@ -29,7 +35,10 @@ public function decode($token): array
$publicKey = $this->jwksService->getPublicKey();
if (!$publicKey) {
- $this->logger?->error('JwksJwtEncoder: Unable to fetch public key from JWKS endpoint');
+ $this->logger?->error(
+ 'JwksJwtEncoder: Unable to fetch public key from JWKS endpoint',
+ );
+
throw new JWTDecodeFailureException(JWTDecodeFailureException::INVALID_TOKEN, 'Unable to fetch public key from JWKS endpoint');
}
@@ -43,7 +52,7 @@ public function decode($token): array
[$headerB64, $payloadB64, $signatureB64] = $parts;
// Decode header
- $header = json_decode($this->base64UrlDecode($headerB64), true);
+ $header = json_decode($this->base64UrlDecode($headerB64), true, 512, JSON_THROW_ON_ERROR);
if (!$header || ($header['alg'] ?? '') !== 'RS256') {
throw new JWTDecodeFailureException(JWTDecodeFailureException::INVALID_TOKEN, 'Unsupported algorithm or invalid header');
}
@@ -65,7 +74,12 @@ public function decode($token): array
if ($publicKey) {
$publicKeyResource = openssl_pkey_get_public($publicKey);
if ($publicKeyResource) {
- $isValid = openssl_verify($dataToVerify, $signature, $publicKeyResource, OPENSSL_ALGO_SHA256);
+ $isValid = openssl_verify(
+ $dataToVerify,
+ $signature,
+ $publicKeyResource,
+ OPENSSL_ALGO_SHA256
+ );
}
}
@@ -75,7 +89,12 @@ public function decode($token): array
}
// Decode payload
- $payload = json_decode($this->base64UrlDecode($payloadB64), true);
+ $payload = json_decode(
+ $this->base64UrlDecode($payloadB64),
+ true,
+ 512,
+ JSON_THROW_ON_ERROR,
+ );
if (!$payload) {
throw new JWTDecodeFailureException(JWTDecodeFailureException::INVALID_TOKEN, 'Invalid payload');
}
diff --git a/app/src/Flags/Service/CapitalsGameService.php b/app/src/Flags/Service/CapitalsGameService.php
index e425bfa..bf51ffe 100644
--- a/app/src/Flags/Service/CapitalsGameService.php
+++ b/app/src/Flags/Service/CapitalsGameService.php
@@ -8,6 +8,7 @@
use App\Flags\Entity\Game;
use App\Flags\Entity\User;
use App\Flags\Repository\CapitalRepository;
+use App\Flags\Repository\CapitalsStatRepository;
use App\Flags\Repository\GameRepository;
use Doctrine\ORM\EntityManagerInterface;
use Rteeom\FlagsGenerator\FlagsGenerator;
@@ -21,6 +22,7 @@
public function __construct(
private CapitalRepository $repository,
private GameRepository $gameRepository,
+ private CapitalsStatRepository $capitalsStatRepository,
private TokenStorageInterface $tokenStorage,
private EntityManagerInterface $entityManager,
) {
@@ -44,7 +46,7 @@ public function getQuestion(?Game $game = null): array
$countries = $this->repository->findBy(['region' => $region], ['id' => 'ASC']);
if (!$countries) {
- throw new \Exception('no countries found');
+ throw new \RuntimeException('no countries found');
}
$totalQuestions = count($countries);
@@ -74,10 +76,26 @@ public function getQuestion(?Game $game = null): array
$correct = array_pop($options);
$options = [
- ['option' => $correct->getName(), 'country' => $correct->getCountry(), 'flag' => $this->isoFlags->getEmojiFlag(strtolower($correct->getCode()))],
- ['option' => ($entry = array_pop($options))->getName(), 'country' => $entry->getCountry(), 'flag' => $this->isoFlags->getEmojiFlag(strtolower($entry->getCode()))],
- ['option' => ($entry = array_pop($options))->getName(), 'country' => $entry->getCountry(), 'flag' => $this->isoFlags->getEmojiFlag(strtolower($entry->getCode()))],
- ['option' => ($entry = array_pop($options))->getName(), 'country' => $entry->getCountry(), 'flag' => $this->isoFlags->getEmojiFlag(strtolower($entry->getCode()))],
+ [
+ 'option' => $correct->getName(),
+ 'country' => $correct->getCountry(),
+ 'flag' => $this->isoFlags->getEmojiFlag(strtolower($correct->getCode())),
+ ],
+ [
+ 'option' => ($entry = array_pop($options))->getName(),
+ 'country' => $entry->getCountry(),
+ 'flag' => $this->isoFlags->getEmojiFlag(strtolower($entry->getCode())),
+ ],
+ [
+ 'option' => ($entry = array_pop($options))->getName(),
+ 'country' => $entry->getCountry(),
+ 'flag' => $this->isoFlags->getEmojiFlag(strtolower($entry->getCode())),
+ ],
+ [
+ 'option' => ($entry = array_pop($options))->getName(),
+ 'country' => $entry->getCountry(),
+ 'flag' => $this->isoFlags->getEmojiFlag(strtolower($entry->getCode())),
+ ],
];
shuffle($options);
try {
@@ -109,18 +127,20 @@ public function giveAnswer(string $questionCountryCode, string $answer, Game $ga
];
}
+ /**
+ * @throws \JsonException
+ */
public function handleGameOver(Request $request): array
{
[
'sessionTimer' => $sessionTimer,
'score' => $score,
'gameId' => $gameId,
- ] = json_decode($request->getContent(), true);
+ ] = json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR);
/** @var User $user */
- $user = $this->tokenStorage->getToken()->getUser();
- /** @var Game $game */
- $game = $this->gameRepository->findOneById((int) $gameId);
+ $user = $this->tokenStorage->getToken()?->getUser();
+ $game = $this->gameRepository->getById((int) $gameId);
$entity = new CapitalsStat($sessionTimer, $score, $user, $game->getType());
$this->entityManager->persist($entity);
@@ -132,18 +152,18 @@ public function handleGameOver(Request $request): array
public function getHighScores(string $gameType): array
{
return array_map(
- fn (array $item) => [
+ static fn (array $item) => [
'userName' => $item['firstName'] . ' ' . $item['lastName'], 'score' => $item['score'],
'sessionTimer' => $item['sessionTimer'],
],
- $this->entityManager->getRepository(CapitalsStat::class)->getHighScores($gameType)
+ $this->capitalsStatRepository->getHighScores($gameType)
);
}
public function startGame(GameType $gameType): Game
{
/** @var User $user */
- $user = $this->tokenStorage->getToken()->getUser();
+ $user = $this->tokenStorage->getToken()?->getUser();
$this->entityManager->persist($game = new Game($user, $gameType));
$this->entityManager->flush();
diff --git a/app/tests/Functional/Flags/CorrectFlagEndpointTest.php b/app/tests/Functional/Flags/CorrectFlagEndpointTest.php
index 5739ff8..0ded8f5 100644
--- a/app/tests/Functional/Flags/CorrectFlagEndpointTest.php
+++ b/app/tests/Functional/Flags/CorrectFlagEndpointTest.php
@@ -7,7 +7,7 @@
use App\Flags\Entity\Flag;
use App\Tests\Functional\ApiTestCase;
use App\Tests\Support\DataProvider\FlagDataProvider;
-use Doctrine\ORM\Exception\ORMException;
+use Doctrine\DBAL\Exception;
final class CorrectFlagEndpointTest extends ApiTestCase
{
@@ -57,7 +57,7 @@ public function testCorrectEndpointRequiresAuthentication(): void
}
/**
- * @throws ORMException
+ * @throws Exception
*/
public function testCorrectEndpointIncrementsCounter(): void
{