Merge pull request 'MAPG-243 use soko-web framework' (!56) from feature/MAPG-243-use-soko-web-framework into develop
Reviewed-on: #56
This commit is contained in:
		
						commit
						7b4743b2b3
					
				@ -1,4 +1,5 @@
 | 
			
		||||
APP_NAME=MapGuesser
 | 
			
		||||
APP_URL=mapguesser.dev
 | 
			
		||||
DEV=1
 | 
			
		||||
DB_HOST=mariadb
 | 
			
		||||
DB_USER=mapguesser
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								Jenkinsfile
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								Jenkinsfile
									
									
									
									
										vendored
									
									
								
							@ -8,6 +8,9 @@ pipeline {
 | 
			
		||||
 | 
			
		||||
    stages {
 | 
			
		||||
        stage('Install composer') {
 | 
			
		||||
            environment {
 | 
			
		||||
                COMPOSER_HOME="${WORKSPACE}/.composer"
 | 
			
		||||
            }
 | 
			
		||||
            agent {
 | 
			
		||||
                dockerfile {
 | 
			
		||||
                    filename 'docker/Dockerfile-test'
 | 
			
		||||
 | 
			
		||||
@ -3,15 +3,19 @@
 | 
			
		||||
  "type": "project",
 | 
			
		||||
  "description": "MapGuesser Application",
 | 
			
		||||
  "license": "GNU GPL 3.0",
 | 
			
		||||
  "repositories": [
 | 
			
		||||
    {
 | 
			
		||||
        "url": "https://git.esoko.eu/esoko/soko-web.git",
 | 
			
		||||
        "type": "git"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "require": {
 | 
			
		||||
    "vlucas/phpdotenv": "^4.1",
 | 
			
		||||
    "symfony/console": "^5.1",
 | 
			
		||||
    "phpmailer/phpmailer": "^6.1",
 | 
			
		||||
    "esoko/soko-web": "0.1",
 | 
			
		||||
    "fzaninotto/faker": "^1.9"
 | 
			
		||||
  },
 | 
			
		||||
  "require-dev": {
 | 
			
		||||
    "phpunit/phpunit": "^9",
 | 
			
		||||
    "phpstan/phpstan": "^0.12.32"
 | 
			
		||||
    "phpunit/phpunit": "^9.6",
 | 
			
		||||
    "phpstan/phpstan": "^1.10"
 | 
			
		||||
  },
 | 
			
		||||
  "autoload": {
 | 
			
		||||
    "psr-4": {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1758
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1758
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,8 +1,8 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Database\Query\Modify;
 | 
			
		||||
use MapGuesser\Database\Query\Select;
 | 
			
		||||
use MapGuesser\Interfaces\Database\IResultSet;
 | 
			
		||||
use SokoWeb\Database\Query\Modify;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
use SokoWeb\Interfaces\Database\IResultSet;
 | 
			
		||||
use MapGuesser\Util\Geo\Bounds;
 | 
			
		||||
 | 
			
		||||
$select = new Select(\Container::$dbConnection, 'maps');
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,8 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Database\Query\Modify;
 | 
			
		||||
use MapGuesser\Database\Query\Select;
 | 
			
		||||
use MapGuesser\Interfaces\Database\IResultSet;
 | 
			
		||||
use SokoWeb\Database\Query\Modify;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
use SokoWeb\Interfaces\Database\IResultSet;
 | 
			
		||||
 | 
			
		||||
$select = new Select(\Container::$dbConnection, 'users');
 | 
			
		||||
$select->columns(['id']);
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,6 @@
 | 
			
		||||
FROM php:7.4.7-cli-buster
 | 
			
		||||
FROM ubuntu:focal
 | 
			
		||||
 | 
			
		||||
RUN apt-get update && apt-get install -y unzip
 | 
			
		||||
ENV DEBIAN_FRONTEND noninteractive
 | 
			
		||||
 | 
			
		||||
RUN apt update && apt install -y curl git unzip php7.4-cli php7.4-mbstring php7.4-xml
 | 
			
		||||
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								main.php
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								main.php
									
									
									
									
									
								
							@ -14,10 +14,10 @@ $dotenv->load();
 | 
			
		||||
 | 
			
		||||
class Container
 | 
			
		||||
{
 | 
			
		||||
    static MapGuesser\Interfaces\Database\IConnection $dbConnection;
 | 
			
		||||
    static MapGuesser\Routing\RouteCollection $routeCollection;
 | 
			
		||||
    static MapGuesser\Interfaces\Session\ISessionHandler $sessionHandler;
 | 
			
		||||
    static MapGuesser\Interfaces\Request\IRequest $request;
 | 
			
		||||
    static SokoWeb\Interfaces\Database\IConnection $dbConnection;
 | 
			
		||||
    static SokoWeb\Routing\RouteCollection $routeCollection;
 | 
			
		||||
    static SokoWeb\Interfaces\Session\ISessionHandler $sessionHandler;
 | 
			
		||||
    static SokoWeb\Interfaces\Request\IRequest $request;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Container::$dbConnection = new MapGuesser\Database\Mysql\Connection($_ENV['DB_HOST'], $_ENV['DB_USER'], $_ENV['DB_PASSWORD'], $_ENV['DB_NAME']);
 | 
			
		||||
Container::$dbConnection = new SokoWeb\Database\Mysql\Connection($_ENV['DB_HOST'], $_ENV['DB_USER'], $_ENV['DB_PASSWORD'], $_ENV['DB_NAME']);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								mapg
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								mapg
									
									
									
									
									
								
							@ -8,6 +8,6 @@ $app = new Symfony\Component\Console\Application('MapGuesser Console', '');
 | 
			
		||||
$app->add(new MapGuesser\Cli\MigrateDatabaseCommand());
 | 
			
		||||
$app->add(new MapGuesser\Cli\AddUserCommand());
 | 
			
		||||
$app->add(new MapGuesser\Cli\LinkViewCommand());
 | 
			
		||||
$app->add(new \MapGuesser\Cli\MaintainDatabaseCommand());
 | 
			
		||||
$app->add(new MapGuesser\Cli\MaintainDatabaseCommand());
 | 
			
		||||
 | 
			
		||||
$app->run();
 | 
			
		||||
 | 
			
		||||
@ -19,14 +19,14 @@ if ($match !== null) {
 | 
			
		||||
    $handler = $route->getHandler();
 | 
			
		||||
    $controller = new $handler[0](Container::$request);
 | 
			
		||||
 | 
			
		||||
    if ($controller instanceof MapGuesser\Interfaces\Authorization\ISecured) {
 | 
			
		||||
    if ($controller instanceof SokoWeb\Interfaces\Authorization\ISecured) {
 | 
			
		||||
        $authorized = $controller->authorize();
 | 
			
		||||
    } else {
 | 
			
		||||
        $authorized = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ($method === 'post' && Container::$request->post('anti_csrf_token') !== Container::$request->session()->get('anti_csrf_token')) {
 | 
			
		||||
        $content = new MapGuesser\Response\JsonContent(['error' => 'no_valid_anti_csrf_token']);
 | 
			
		||||
        $content = new SokoWeb\Response\JsonContent(['error' => 'no_valid_anti_csrf_token']);
 | 
			
		||||
        header('Content-Type: text/html; charset=UTF-8', true, 403);
 | 
			
		||||
        $content->render();
 | 
			
		||||
        return;
 | 
			
		||||
@ -35,12 +35,12 @@ if ($match !== null) {
 | 
			
		||||
    if ($authorized) {
 | 
			
		||||
        $response = call_user_func([$controller, $handler[1]]);
 | 
			
		||||
 | 
			
		||||
        if ($response instanceof MapGuesser\Interfaces\Response\IContent) {
 | 
			
		||||
        if ($response instanceof SokoWeb\Interfaces\Response\IContent) {
 | 
			
		||||
            header('Content-Type: ' . $response->getContentType() . '; charset=UTF-8');
 | 
			
		||||
            $response->render();
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        } elseif ($response instanceof MapGuesser\Interfaces\Response\IRedirect) {
 | 
			
		||||
        } elseif ($response instanceof SokoWeb\Interfaces\Response\IRedirect) {
 | 
			
		||||
            header('Location: ' . $response->getUrl(), true, $response->getHttpCode());
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
@ -48,6 +48,6 @@ if ($match !== null) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$content = new MapGuesser\Response\HtmlContent('error/404');
 | 
			
		||||
$content = new SokoWeb\Response\HtmlContent('error/404');
 | 
			
		||||
header('Content-Type: text/html; charset=UTF-8', true, 404);
 | 
			
		||||
$content->render();
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
<?php namespace MapGuesser\Cli;
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
use MapGuesser\PersistentData\Model\User;
 | 
			
		||||
use Symfony\Component\Console\Command\Command;
 | 
			
		||||
use Symfony\Component\Console\Input\InputArgument;
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
<?php namespace MapGuesser\Cli;
 | 
			
		||||
 | 
			
		||||
use FilesystemIterator;
 | 
			
		||||
use MapGuesser\View\Linker;
 | 
			
		||||
use SokoWeb\View\Linker;
 | 
			
		||||
use RecursiveDirectoryIterator;
 | 
			
		||||
use RecursiveIteratorIterator;
 | 
			
		||||
use Symfony\Component\Console\Command\Command;
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,10 @@
 | 
			
		||||
<?php namespace MapGuesser\Cli;
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use MapGuesser\Database\Query\Modify;
 | 
			
		||||
use MapGuesser\Database\Query\Select;
 | 
			
		||||
use MapGuesser\Interfaces\Database\IResultSet;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\Database\Query\Modify;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
use SokoWeb\Interfaces\Database\IResultSet;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
use MapGuesser\Repository\MultiRoomRepository;
 | 
			
		||||
use MapGuesser\Repository\UserConfirmationRepository;
 | 
			
		||||
use MapGuesser\Repository\UserPasswordResetterRepository;
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,8 @@
 | 
			
		||||
<?php namespace MapGuesser\Cli;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Database\Query\Modify;
 | 
			
		||||
use MapGuesser\Database\Query\Select;
 | 
			
		||||
use MapGuesser\Interfaces\Database\IResultSet;
 | 
			
		||||
use SokoWeb\Database\Query\Modify;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
use SokoWeb\Interfaces\Database\IResultSet;
 | 
			
		||||
use Symfony\Component\Console\Command\Command;
 | 
			
		||||
use Symfony\Component\Console\Input\InputInterface;
 | 
			
		||||
use Symfony\Component\Console\Output\OutputInterface;
 | 
			
		||||
 | 
			
		||||
@ -2,24 +2,24 @@
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use Faker\Factory;
 | 
			
		||||
use MapGuesser\Interfaces\Authorization\ISecured;
 | 
			
		||||
use MapGuesser\Interfaces\Request\IRequest;
 | 
			
		||||
use MapGuesser\Response\HtmlContent;
 | 
			
		||||
use MapGuesser\Response\JsonContent;
 | 
			
		||||
use MapGuesser\Interfaces\Response\IContent;
 | 
			
		||||
use MapGuesser\Interfaces\Response\IRedirect;
 | 
			
		||||
use SokoWeb\Interfaces\Authorization\ISecured;
 | 
			
		||||
use SokoWeb\Interfaces\Request\IRequest;
 | 
			
		||||
use SokoWeb\Response\HtmlContent;
 | 
			
		||||
use SokoWeb\Response\JsonContent;
 | 
			
		||||
use SokoWeb\Interfaces\Response\IContent;
 | 
			
		||||
use SokoWeb\Interfaces\Response\IRedirect;
 | 
			
		||||
use MapGuesser\Multi\MultiConnector;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Challenge;
 | 
			
		||||
use MapGuesser\PersistentData\Model\MultiRoom;
 | 
			
		||||
use MapGuesser\PersistentData\Model\PlaceInChallenge;
 | 
			
		||||
use MapGuesser\PersistentData\Model\UserInChallenge;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
use MapGuesser\Repository\ChallengeRepository;
 | 
			
		||||
use MapGuesser\Repository\MapRepository;
 | 
			
		||||
use MapGuesser\Repository\MultiRoomRepository;
 | 
			
		||||
use MapGuesser\Repository\PlaceRepository;
 | 
			
		||||
use MapGuesser\Repository\UserInChallengeRepository;
 | 
			
		||||
use MapGuesser\Response\Redirect;
 | 
			
		||||
use SokoWeb\Response\Redirect;
 | 
			
		||||
 | 
			
		||||
class GameController implements ISecured
 | 
			
		||||
{
 | 
			
		||||
@ -103,7 +103,7 @@ class GameController implements ISecured
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getChallenge(): IContent
 | 
			
		||||
    {   
 | 
			
		||||
    {
 | 
			
		||||
        $challengeToken = $this->request->query('challengeToken');
 | 
			
		||||
 | 
			
		||||
        return new HtmlContent('game', ['challengeToken' => $challengeToken]);
 | 
			
		||||
@ -120,7 +120,7 @@ class GameController implements ISecured
 | 
			
		||||
        $challenge = new Challenge();
 | 
			
		||||
        $challenge->setToken($challengeToken);
 | 
			
		||||
        $challenge->setCreatedDate(new DateTime());
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if ($this->request->post('timerEnabled') !== null && $this->request->post('timeLimit') !== null) {
 | 
			
		||||
            $challenge->setTimeLimit($this->request->post('timeLimit'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -1,13 +1,13 @@
 | 
			
		||||
<?php namespace MapGuesser\Controller;
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use MapGuesser\Interfaces\Authorization\ISecured;
 | 
			
		||||
use MapGuesser\Interfaces\Request\IRequest;
 | 
			
		||||
use SokoWeb\Interfaces\Authorization\ISecured;
 | 
			
		||||
use SokoWeb\Interfaces\Request\IRequest;
 | 
			
		||||
use MapGuesser\Util\Geo\Position;
 | 
			
		||||
use MapGuesser\Response\JsonContent;
 | 
			
		||||
use MapGuesser\Interfaces\Response\IContent;
 | 
			
		||||
use SokoWeb\Response\JsonContent;
 | 
			
		||||
use SokoWeb\Interfaces\Response\IContent;
 | 
			
		||||
use MapGuesser\Multi\MultiConnector;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Challenge;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Guess;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Map;
 | 
			
		||||
@ -40,14 +40,8 @@ class GameFlowController implements ISecured
 | 
			
		||||
 | 
			
		||||
    private PlaceRepository $placeRepository;
 | 
			
		||||
 | 
			
		||||
    private MapRepository $mapRepository;
 | 
			
		||||
 | 
			
		||||
    private UserRepository $userRepository;
 | 
			
		||||
 | 
			
		||||
    private UserPlayedPlaceRepository $userPlayedPlaceRepository;
 | 
			
		||||
 | 
			
		||||
    private ChallengeRepository $challengeRepository;
 | 
			
		||||
 | 
			
		||||
    private UserInChallengeRepository $userInChallengeRepository;
 | 
			
		||||
 | 
			
		||||
    private PlaceInChallengeRepository $placeInChallengeRepository;
 | 
			
		||||
@ -61,10 +55,7 @@ class GameFlowController implements ISecured
 | 
			
		||||
        $this->multiConnector = new MultiConnector();
 | 
			
		||||
        $this->multiRoomRepository = new MultiRoomRepository();
 | 
			
		||||
        $this->placeRepository = new PlaceRepository();
 | 
			
		||||
        $this->mapRepository = new MapRepository();
 | 
			
		||||
        $this->userRepository = new UserRepository();
 | 
			
		||||
        $this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
 | 
			
		||||
        $this->challengeRepository = new ChallengeRepository();
 | 
			
		||||
        $this->userInChallengeRepository = new UserInChallengeRepository();
 | 
			
		||||
        $this->placeInChallengeRepository = new PlaceInChallengeRepository();
 | 
			
		||||
        $this->guessRepository = new GuessRepository();
 | 
			
		||||
@ -154,16 +145,16 @@ class GameFlowController implements ISecured
 | 
			
		||||
    private function prepareChallengeResponse(int $userId, Challenge $challenge, int $currentRound, bool $withHistory = false): array
 | 
			
		||||
    {
 | 
			
		||||
        $currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        // if the last round was played ($currentPlace == null) or history is explicitly requested (for initializing)
 | 
			
		||||
        if (!isset($currentPlace) || $withHistory) {
 | 
			
		||||
 | 
			
		||||
            $withRelations = [User::class, PlaceInChallenge::class, Place::class];
 | 
			
		||||
            foreach ($this->guessRepository->getAllInChallenge($challenge, $withRelations) as $guess) { 
 | 
			
		||||
            foreach ($this->guessRepository->getAllInChallenge($challenge, $withRelations) as $guess) {
 | 
			
		||||
                $round = $guess->getPlaceInChallenge()->getRound();
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
                if ($guess->getUser()->getId() === $userId) {
 | 
			
		||||
                    $response['history'][$round]['position'] = 
 | 
			
		||||
                    $response['history'][$round]['position'] =
 | 
			
		||||
                            $guess->getPlaceInChallenge()->getPlace()->getPosition()->toArray();
 | 
			
		||||
                    $response['history'][$round]['result'] = [
 | 
			
		||||
                            'guessPosition' => $guess->getPosition()->toArray(),
 | 
			
		||||
@ -188,8 +179,8 @@ class GameFlowController implements ISecured
 | 
			
		||||
                            'distance' => null,
 | 
			
		||||
                            'score' => 0
 | 
			
		||||
                    ];
 | 
			
		||||
                    
 | 
			
		||||
                    $response['history'][$i]['position'] = 
 | 
			
		||||
 | 
			
		||||
                    $response['history'][$i]['position'] =
 | 
			
		||||
                            $this->placeRepository->getByRoundInChallenge($challenge, $i)->getPosition()->toArray();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@ -250,7 +241,7 @@ class GameFlowController implements ISecured
 | 
			
		||||
        if ($challenge->getTimeLimitType() === 'game' && $challenge->getTimeLimit() !== null && $userInChallenge->getCurrentRound() > 0) {
 | 
			
		||||
            $timeLimit = max(10, $userInChallenge->getTimeLeft());
 | 
			
		||||
            $response['restrictions']['timeLimit'] = $timeLimit * 1000;
 | 
			
		||||
        } 
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new JsonContent($response);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,10 @@
 | 
			
		||||
<?php namespace MapGuesser\Controller;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Interfaces\Request\IRequest;
 | 
			
		||||
use MapGuesser\Interfaces\Response\IContent;
 | 
			
		||||
use MapGuesser\Interfaces\Response\IRedirect;
 | 
			
		||||
use MapGuesser\Response\JsonContent;
 | 
			
		||||
use MapGuesser\Response\Redirect;
 | 
			
		||||
use SokoWeb\Interfaces\Request\IRequest;
 | 
			
		||||
use SokoWeb\Interfaces\Response\IContent;
 | 
			
		||||
use SokoWeb\Interfaces\Response\IRedirect;
 | 
			
		||||
use SokoWeb\Response\JsonContent;
 | 
			
		||||
use SokoWeb\Response\Redirect;
 | 
			
		||||
 | 
			
		||||
class HomeController
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -2,25 +2,25 @@
 | 
			
		||||
 | 
			
		||||
use DateInterval;
 | 
			
		||||
use DateTime;
 | 
			
		||||
use MapGuesser\Http\Request;
 | 
			
		||||
use MapGuesser\Interfaces\Request\IRequest;
 | 
			
		||||
use MapGuesser\Interfaces\Response\IContent;
 | 
			
		||||
use MapGuesser\Interfaces\Response\IRedirect;
 | 
			
		||||
use MapGuesser\Mailing\Mail;
 | 
			
		||||
use MapGuesser\OAuth\GoogleOAuth;
 | 
			
		||||
use SokoWeb\Http\Request;
 | 
			
		||||
use SokoWeb\Interfaces\Request\IRequest;
 | 
			
		||||
use SokoWeb\Interfaces\Response\IContent;
 | 
			
		||||
use SokoWeb\Interfaces\Response\IRedirect;
 | 
			
		||||
use SokoWeb\Mailing\Mail;
 | 
			
		||||
use SokoWeb\OAuth\GoogleOAuth;
 | 
			
		||||
use MapGuesser\PersistentData\Model\User;
 | 
			
		||||
use MapGuesser\PersistentData\Model\UserConfirmation;
 | 
			
		||||
use MapGuesser\PersistentData\Model\UserPasswordResetter;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
use MapGuesser\Repository\UserConfirmationRepository;
 | 
			
		||||
use MapGuesser\Repository\UserPasswordResetterRepository;
 | 
			
		||||
use MapGuesser\Repository\UserPlayedPlaceRepository;
 | 
			
		||||
use MapGuesser\Repository\UserRepository;
 | 
			
		||||
use MapGuesser\Response\HtmlContent;
 | 
			
		||||
use MapGuesser\Response\JsonContent;
 | 
			
		||||
use MapGuesser\Response\Redirect;
 | 
			
		||||
use MapGuesser\Util\CaptchaValidator;
 | 
			
		||||
use MapGuesser\Util\JwtParser;
 | 
			
		||||
use SokoWeb\Response\HtmlContent;
 | 
			
		||||
use SokoWeb\Response\JsonContent;
 | 
			
		||||
use SokoWeb\Response\Redirect;
 | 
			
		||||
use SokoWeb\Util\CaptchaValidator;
 | 
			
		||||
use SokoWeb\Util\JwtParser;
 | 
			
		||||
 | 
			
		||||
class LoginController
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,15 @@
 | 
			
		||||
<?php namespace MapGuesser\Controller;
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use MapGuesser\Interfaces\Authentication\IUser;
 | 
			
		||||
use MapGuesser\Interfaces\Authorization\ISecured;
 | 
			
		||||
use MapGuesser\Interfaces\Request\IRequest;
 | 
			
		||||
use MapGuesser\Interfaces\Response\IContent;
 | 
			
		||||
use SokoWeb\Interfaces\Authentication\IUser;
 | 
			
		||||
use SokoWeb\Interfaces\Authorization\ISecured;
 | 
			
		||||
use SokoWeb\Interfaces\Request\IRequest;
 | 
			
		||||
use SokoWeb\Interfaces\Response\IContent;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Challenge;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Map;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Place;
 | 
			
		||||
use MapGuesser\PersistentData\Model\PlaceInChallenge;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
use MapGuesser\Repository\ChallengeRepository;
 | 
			
		||||
use MapGuesser\Repository\GuessRepository;
 | 
			
		||||
use MapGuesser\Repository\MapRepository;
 | 
			
		||||
@ -17,8 +17,8 @@ use MapGuesser\Repository\PlaceInChallengeRepository;
 | 
			
		||||
use MapGuesser\Repository\PlaceRepository;
 | 
			
		||||
use MapGuesser\Repository\UserInChallengeRepository;
 | 
			
		||||
use MapGuesser\Repository\UserPlayedPlaceRepository;
 | 
			
		||||
use MapGuesser\Response\HtmlContent;
 | 
			
		||||
use MapGuesser\Response\JsonContent;
 | 
			
		||||
use SokoWeb\Response\HtmlContent;
 | 
			
		||||
use SokoWeb\Response\JsonContent;
 | 
			
		||||
use MapGuesser\Util\Geo\Bounds;
 | 
			
		||||
use MapGuesser\Util\Panorama\Pov;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,12 @@
 | 
			
		||||
<?php namespace MapGuesser\Controller;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Database\Query\Select;
 | 
			
		||||
use MapGuesser\Database\RawExpression;
 | 
			
		||||
use MapGuesser\Interfaces\Authentication\IUser;
 | 
			
		||||
use MapGuesser\Interfaces\Database\IResultSet;
 | 
			
		||||
use MapGuesser\Interfaces\Request\IRequest;
 | 
			
		||||
use MapGuesser\Interfaces\Response\IContent;
 | 
			
		||||
use MapGuesser\Response\HtmlContent;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
use SokoWeb\Database\RawExpression;
 | 
			
		||||
use SokoWeb\Interfaces\Authentication\IUser;
 | 
			
		||||
use SokoWeb\Interfaces\Database\IResultSet;
 | 
			
		||||
use SokoWeb\Interfaces\Request\IRequest;
 | 
			
		||||
use SokoWeb\Interfaces\Response\IContent;
 | 
			
		||||
use SokoWeb\Response\HtmlContent;
 | 
			
		||||
 | 
			
		||||
class MapsController
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -1,24 +1,23 @@
 | 
			
		||||
<?php namespace MapGuesser\Controller;
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use MapGuesser\Http\Request;
 | 
			
		||||
use MapGuesser\Interfaces\Authorization\ISecured;
 | 
			
		||||
use MapGuesser\Interfaces\Request\IRequest;
 | 
			
		||||
use MapGuesser\Interfaces\Response\IContent;
 | 
			
		||||
use MapGuesser\Interfaces\Response\IRedirect;
 | 
			
		||||
use MapGuesser\OAuth\GoogleOAuth;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\Http\Request;
 | 
			
		||||
use SokoWeb\Interfaces\Authorization\ISecured;
 | 
			
		||||
use SokoWeb\Interfaces\Request\IRequest;
 | 
			
		||||
use SokoWeb\Interfaces\Response\IContent;
 | 
			
		||||
use SokoWeb\Interfaces\Response\IRedirect;
 | 
			
		||||
use SokoWeb\OAuth\GoogleOAuth;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
use MapGuesser\PersistentData\Model\User;
 | 
			
		||||
use MapGuesser\PersistentData\Model\UserInChallenge;
 | 
			
		||||
use MapGuesser\Repository\GuessRepository;
 | 
			
		||||
use MapGuesser\Repository\UserConfirmationRepository;
 | 
			
		||||
use MapGuesser\Repository\UserInChallengeRepository;
 | 
			
		||||
use MapGuesser\Repository\UserPasswordResetterRepository;
 | 
			
		||||
use MapGuesser\Repository\UserPlayedPlaceRepository;
 | 
			
		||||
use MapGuesser\Response\HtmlContent;
 | 
			
		||||
use MapGuesser\Response\JsonContent;
 | 
			
		||||
use MapGuesser\Response\Redirect;
 | 
			
		||||
use MapGuesser\Util\JwtParser;
 | 
			
		||||
use SokoWeb\Response\HtmlContent;
 | 
			
		||||
use SokoWeb\Response\JsonContent;
 | 
			
		||||
use SokoWeb\Response\Redirect;
 | 
			
		||||
use SokoWeb\Util\JwtParser;
 | 
			
		||||
 | 
			
		||||
class UserController implements ISecured
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -1,114 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Database\Mysql;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Interfaces\Database\IConnection;
 | 
			
		||||
use MapGuesser\Interfaces\Database\IResultSet;
 | 
			
		||||
use MapGuesser\Interfaces\Database\IStatement;
 | 
			
		||||
use mysqli;
 | 
			
		||||
 | 
			
		||||
class Connection implements IConnection
 | 
			
		||||
{
 | 
			
		||||
    private mysqli $connection;
 | 
			
		||||
 | 
			
		||||
    public function __construct(string $host, string $user, string $password, string $db, int $port = -1, string $socket = null)
 | 
			
		||||
    {
 | 
			
		||||
        if ($port < 0) {
 | 
			
		||||
            $port = (int) ini_get('mysqli.default_port');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($socket === null) {
 | 
			
		||||
            $socket = (string) ini_get('mysqli.default_socket');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->connection = new mysqli($host, $user, $password, $db, $port, $socket);
 | 
			
		||||
 | 
			
		||||
        if ($this->connection->connect_error) {
 | 
			
		||||
            throw new \Exception('Connection failed: ' . $this->connection->connect_error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!$this->connection->set_charset('utf8mb4')) {
 | 
			
		||||
            throw new \Exception($this->connection->error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function __destruct()
 | 
			
		||||
    {
 | 
			
		||||
        $this->connection->close();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function startTransaction(): void
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->connection->autocommit(false)) {
 | 
			
		||||
            throw new \Exception($this->connection->error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function commit(): void
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->connection->commit() || !$this->connection->autocommit(true)) {
 | 
			
		||||
            throw new \Exception($this->connection->error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function rollback(): void
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->connection->rollback() || !$this->connection->autocommit(true)) {
 | 
			
		||||
            throw new \Exception($this->connection->error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function query(string $query): ?IResultSet
 | 
			
		||||
    {
 | 
			
		||||
        if (!($result = $this->connection->query($query))) {
 | 
			
		||||
            throw new \Exception($this->connection->error . '. Query: ' . $query);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($result !== true) {
 | 
			
		||||
            return new ResultSet($result);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function multiQuery(string $query): array
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->connection->multi_query($query)) {
 | 
			
		||||
            throw new \Exception($this->connection->error . '. Query: ' . $query);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $ret = [];
 | 
			
		||||
        do {
 | 
			
		||||
            if ($result = $this->connection->store_result()) {
 | 
			
		||||
                $ret[] = new ResultSet($result);
 | 
			
		||||
            } else {
 | 
			
		||||
                $ret[] = null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $this->connection->more_results();
 | 
			
		||||
        } while ($this->connection->next_result());
 | 
			
		||||
 | 
			
		||||
        if ($this->connection->error) {
 | 
			
		||||
            throw new \Exception($this->connection->error  . '. Query: ' . $query);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function prepare(string $query): IStatement
 | 
			
		||||
    {
 | 
			
		||||
        if (!($stmt = $this->connection->prepare($query))) {
 | 
			
		||||
            throw new \Exception($this->connection->error . '. Query: ' . $query);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new Statement($stmt);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function lastId(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->connection->insert_id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getAffectedRows(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->connection->affected_rows;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,62 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Database\Mysql;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Interfaces\Database\IResultSet;
 | 
			
		||||
use mysqli_result;
 | 
			
		||||
 | 
			
		||||
class ResultSet implements IResultSet
 | 
			
		||||
{
 | 
			
		||||
    private mysqli_result $result;
 | 
			
		||||
 | 
			
		||||
    public function __construct(mysqli_result $result)
 | 
			
		||||
    {
 | 
			
		||||
        $this->result = $result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function fetch(int $type = IResultSet::FETCH_ASSOC): ?array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->result->fetch_array($this->convertFetchType($type));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function fetchAll(int $type = IResultSet::FETCH_ASSOC): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->result->fetch_all($this->convertFetchType($type));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function fetchOneColumn(string $valueName, string $keyName = null): array
 | 
			
		||||
    {
 | 
			
		||||
        $array = [];
 | 
			
		||||
 | 
			
		||||
        while ($r = $this->fetch(IResultSet::FETCH_ASSOC)) {
 | 
			
		||||
            if (isset($keyName)) {
 | 
			
		||||
                $array[$r[$keyName]] = $r[$valueName];
 | 
			
		||||
            } else {
 | 
			
		||||
                $array[] = $r[$valueName];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $array;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function convertFetchType(int $type): int
 | 
			
		||||
    {
 | 
			
		||||
        switch ($type) {
 | 
			
		||||
            case IResultSet::FETCH_ASSOC:
 | 
			
		||||
                $internal_type = MYSQLI_ASSOC;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case IResultSet::FETCH_BOTH:
 | 
			
		||||
                $internal_type = MYSQLI_BOTH;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case IResultSet::FETCH_NUM:
 | 
			
		||||
                $internal_type = MYSQLI_NUM;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                $internal_type = MYSQLI_BOTH;
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $internal_type;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,79 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Database\Mysql;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Interfaces\Database\IResultSet;
 | 
			
		||||
use MapGuesser\Interfaces\Database\IStatement;
 | 
			
		||||
use mysqli_stmt;
 | 
			
		||||
 | 
			
		||||
class Statement implements IStatement
 | 
			
		||||
{
 | 
			
		||||
    private mysqli_stmt $stmt;
 | 
			
		||||
 | 
			
		||||
    public function __construct(mysqli_stmt $stmt)
 | 
			
		||||
    {
 | 
			
		||||
        $this->stmt = $stmt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function __destruct()
 | 
			
		||||
    {
 | 
			
		||||
        $this->stmt->close();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function execute(array $params = []): ?IResultSet
 | 
			
		||||
    {
 | 
			
		||||
        if ($params) {
 | 
			
		||||
            $ref_params = [''];
 | 
			
		||||
 | 
			
		||||
            foreach ($params as &$param) {
 | 
			
		||||
                $type = gettype($param);
 | 
			
		||||
 | 
			
		||||
                switch ($type) {
 | 
			
		||||
                    case 'integer':
 | 
			
		||||
                    case 'double':
 | 
			
		||||
                    case 'string':
 | 
			
		||||
                        $t = $type[0];
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
                    case 'NULL':
 | 
			
		||||
                        $t = 's';
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
                    case 'boolean':
 | 
			
		||||
                        $param = (string) (int) $param;
 | 
			
		||||
                        $t = 's';
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
                    case 'array':
 | 
			
		||||
                        $param = json_encode($param);
 | 
			
		||||
                        $t = 's';
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (!isset($t)) {
 | 
			
		||||
                    throw new \Exception('Data type ' . $type . ' not supported!');
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $ref_params[] = &$param;
 | 
			
		||||
                $ref_params[0] .= $t;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!call_user_func_array([$this->stmt, 'bind_param'], $ref_params)) {
 | 
			
		||||
                throw new \Exception($this->stmt->error);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!$this->stmt->execute()) {
 | 
			
		||||
            throw new \Exception($this->stmt->error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($result_set = $this->stmt->get_result()) {
 | 
			
		||||
            return new ResultSet($result_set);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getAffectedRows(): int
 | 
			
		||||
    {
 | 
			
		||||
        return $this->stmt->affected_rows;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,140 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Database\Query;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Interfaces\Database\IConnection;
 | 
			
		||||
use MapGuesser\Database\Utils;
 | 
			
		||||
 | 
			
		||||
class Modify
 | 
			
		||||
{
 | 
			
		||||
    private IConnection $connection;
 | 
			
		||||
 | 
			
		||||
    private string $table;
 | 
			
		||||
 | 
			
		||||
    private string $idName = 'id';
 | 
			
		||||
 | 
			
		||||
    private array $attributes = [];
 | 
			
		||||
 | 
			
		||||
    private ?string $externalId = null;
 | 
			
		||||
 | 
			
		||||
    private bool $autoIncrement = true;
 | 
			
		||||
 | 
			
		||||
    public function __construct(IConnection $connection, string $table)
 | 
			
		||||
    {
 | 
			
		||||
        $this->connection = $connection;
 | 
			
		||||
        $this->table = $table;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setIdName(string $idName): Modify
 | 
			
		||||
    {
 | 
			
		||||
        $this->idName = $idName;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setExternalId($id): Modify
 | 
			
		||||
    {
 | 
			
		||||
        $this->externalId = $id;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setAutoIncrement(bool $autoIncrement = true): Modify
 | 
			
		||||
    {
 | 
			
		||||
        $this->autoIncrement = $autoIncrement;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function fill(array $attributes): Modify
 | 
			
		||||
    {
 | 
			
		||||
        $this->attributes = array_merge($this->attributes, $attributes);
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function set(string $name, $value): Modify
 | 
			
		||||
    {
 | 
			
		||||
        $this->attributes[$name] = $value;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setId($id): Modify
 | 
			
		||||
    {
 | 
			
		||||
        $this->attributes[$this->idName] = $id;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getId()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->attributes[$this->idName];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function save(): void
 | 
			
		||||
    {
 | 
			
		||||
        if (isset($this->attributes[$this->idName])) {
 | 
			
		||||
            $this->update();
 | 
			
		||||
        } else {
 | 
			
		||||
            $this->insert();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function delete(): void
 | 
			
		||||
    {
 | 
			
		||||
        if (!isset($this->attributes[$this->idName])) {
 | 
			
		||||
            throw new \Exception('No primary key specified!');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $query = 'DELETE FROM ' . Utils::backtick($this->table) . ' WHERE ' . Utils::backtick($this->idName) . '=?';
 | 
			
		||||
 | 
			
		||||
        $stmt = $this->connection->prepare($query);
 | 
			
		||||
        $stmt->execute([$this->idName => $this->attributes[$this->idName]]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function insert(): void
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->externalId !== null) {
 | 
			
		||||
            $this->attributes[$this->idName] = $this->externalId;
 | 
			
		||||
        } elseif (!$this->autoIncrement) {
 | 
			
		||||
            $this->attributes[$this->idName] = $this->generateKey();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $set = $this->generateColumnsWithBinding(array_keys($this->attributes));
 | 
			
		||||
 | 
			
		||||
        $query = 'INSERT INTO ' . Utils::backtick($this->table) . ' SET ' . $set;
 | 
			
		||||
 | 
			
		||||
        $stmt = $this->connection->prepare($query);
 | 
			
		||||
        $stmt->execute($this->attributes);
 | 
			
		||||
 | 
			
		||||
        if ($this->autoIncrement) {
 | 
			
		||||
            $this->attributes[$this->idName] = $this->connection->lastId();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function update(): void
 | 
			
		||||
    {
 | 
			
		||||
        $attributes = $this->attributes;
 | 
			
		||||
        unset($attributes[$this->idName]);
 | 
			
		||||
 | 
			
		||||
        $set = $this->generateColumnsWithBinding(array_keys($attributes));
 | 
			
		||||
 | 
			
		||||
        $query = 'UPDATE ' . Utils::backtick($this->table) . ' SET ' . $set . ' WHERE ' . Utils::backtick($this->idName) . '=?';
 | 
			
		||||
 | 
			
		||||
        $stmt = $this->connection->prepare($query);
 | 
			
		||||
        $stmt->execute(array_merge($attributes, [$this->idName => $this->attributes[$this->idName]]));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function generateColumnsWithBinding(array $columns): string
 | 
			
		||||
    {
 | 
			
		||||
        array_walk($columns, function(&$value, $key) {
 | 
			
		||||
            $value = Utils::backtick($value) . '=?';
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return implode(',', $columns);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function generateKey(): string
 | 
			
		||||
    {
 | 
			
		||||
        return substr(hash('sha256', serialize($this->attributes) . random_bytes(5) . microtime()), 0, 7);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,445 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Database\Query;
 | 
			
		||||
 | 
			
		||||
use Closure;
 | 
			
		||||
use MapGuesser\Interfaces\Database\IConnection;
 | 
			
		||||
use MapGuesser\Interfaces\Database\IResultSet;
 | 
			
		||||
use MapGuesser\Database\RawExpression;
 | 
			
		||||
use MapGuesser\Database\Utils;
 | 
			
		||||
 | 
			
		||||
class Select
 | 
			
		||||
{
 | 
			
		||||
    const CONDITION_WHERE = 0;
 | 
			
		||||
 | 
			
		||||
    const CONDITION_HAVING = 1;
 | 
			
		||||
 | 
			
		||||
    const DERIVED_TABLE_KEY = 'DERIVED';
 | 
			
		||||
 | 
			
		||||
    private IConnection $connection;
 | 
			
		||||
 | 
			
		||||
    private string $table;
 | 
			
		||||
 | 
			
		||||
    private string $idName = 'id';
 | 
			
		||||
 | 
			
		||||
    private array $tableAliases = [];
 | 
			
		||||
 | 
			
		||||
    private array $joins = [];
 | 
			
		||||
 | 
			
		||||
    private array $columns = [];
 | 
			
		||||
 | 
			
		||||
    private array $conditions = [self::CONDITION_WHERE => [], self::CONDITION_HAVING => []];
 | 
			
		||||
 | 
			
		||||
    private array $groups = [];
 | 
			
		||||
 | 
			
		||||
    private array $orders = [];
 | 
			
		||||
 | 
			
		||||
    private ?array $limit;
 | 
			
		||||
 | 
			
		||||
    public function __construct(IConnection $connection, ?string $table = null)
 | 
			
		||||
    {
 | 
			
		||||
        $this->connection = $connection;
 | 
			
		||||
 | 
			
		||||
        if ($table !== null) {
 | 
			
		||||
            $this->table = $table;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setIdName(string $idName): Select
 | 
			
		||||
    {
 | 
			
		||||
        $this->idName = $idName;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setTableAliases(array $tableAliases): Select
 | 
			
		||||
    {
 | 
			
		||||
        $this->tableAliases = array_merge($this->tableAliases, $tableAliases);
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setDerivedTableAlias(string $tableAlias): Select
 | 
			
		||||
    {
 | 
			
		||||
        return $this->setTableAliases([Select::DERIVED_TABLE_KEY => $tableAlias]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function from(string $table): Select
 | 
			
		||||
    {
 | 
			
		||||
        $this->table = $table;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function columns(array $columns): Select
 | 
			
		||||
    {
 | 
			
		||||
        $this->columns = array_merge($this->columns, $columns);
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function innerJoin($table, $column1, string $relation, $column2): Select
 | 
			
		||||
    {
 | 
			
		||||
        $this->addJoin('INNER', $table, $column1, $relation, $column2);
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function leftJoin($table, $column1, string $relation, $column2): Select
 | 
			
		||||
    {
 | 
			
		||||
        $this->addJoin('LEFT', $table, $column1, $relation, $column2);
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function whereId($value): Select
 | 
			
		||||
    {
 | 
			
		||||
        $this->addWhereCondition('AND', $this->idName, '=', $value);
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function where($column, string $relation = null, $value = null): Select
 | 
			
		||||
    {
 | 
			
		||||
        $this->addWhereCondition('AND', $column, $relation, $value);
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function orWhere($column, string $relation = null, $value = null): Select
 | 
			
		||||
    {
 | 
			
		||||
        $this->addWhereCondition('OR', $column, $relation, $value);
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function having($column, string $relation = null, $value = null): Select
 | 
			
		||||
    {
 | 
			
		||||
        $this->addHavingCondition('AND', $column, $relation, $value);
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function orHaving($column, string $relation = null, $value = null): Select
 | 
			
		||||
    {
 | 
			
		||||
        $this->addHavingCondition('OR', $column, $relation, $value);
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function groupBy($column): Select
 | 
			
		||||
    {
 | 
			
		||||
        $this->groups[] = $column;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function orderBy($column, string $type = 'ASC'): Select
 | 
			
		||||
    {
 | 
			
		||||
        $this->orders[] = [$column, $type];
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function limit(int $limit, int $offset = 0): Select
 | 
			
		||||
    {
 | 
			
		||||
        $this->limit = [$limit, $offset];
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function resetLimit(): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->limit = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function paginate(int $page, int $itemsPerPage): Select
 | 
			
		||||
    {
 | 
			
		||||
        $this->limit($itemsPerPage, ($page - 1) * $itemsPerPage);
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function execute(): IResultSet
 | 
			
		||||
    {
 | 
			
		||||
        list($query, $params) = $this->generateQuery();
 | 
			
		||||
 | 
			
		||||
        return $this->connection->prepare($query)->execute($params);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function count(): int
 | 
			
		||||
    {
 | 
			
		||||
        if (count($this->groups) > 0 || count($this->conditions[self::CONDITION_HAVING]) > 0) {
 | 
			
		||||
            $orders = $this->orders;
 | 
			
		||||
 | 
			
		||||
            $this->orders = [];
 | 
			
		||||
 | 
			
		||||
            list($query, $params) = $this->generateQuery();
 | 
			
		||||
 | 
			
		||||
            $result = $this->connection->prepare('SELECT COUNT(*) num_rows FROM (' . $query . ') x')
 | 
			
		||||
                ->execute($params)
 | 
			
		||||
                ->fetch(IResultSet::FETCH_NUM);
 | 
			
		||||
 | 
			
		||||
            $this->orders = $orders;
 | 
			
		||||
 | 
			
		||||
            return $result[0];
 | 
			
		||||
        } else {
 | 
			
		||||
            $columns = $this->columns;
 | 
			
		||||
            $orders = $this->orders;
 | 
			
		||||
 | 
			
		||||
            $this->columns = [new RawExpression('COUNT(*) num_rows')];
 | 
			
		||||
            $this->orders = [];
 | 
			
		||||
 | 
			
		||||
            list($query, $params) = $this->generateQuery();
 | 
			
		||||
 | 
			
		||||
            $result = $this->connection->prepare($query)
 | 
			
		||||
                ->execute($params)
 | 
			
		||||
                ->fetch(IResultSet::FETCH_NUM);
 | 
			
		||||
 | 
			
		||||
            $this->columns = $columns;
 | 
			
		||||
            $this->orders = $orders;
 | 
			
		||||
 | 
			
		||||
            return $result[0];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function isDerivedTable(): bool
 | 
			
		||||
    {
 | 
			
		||||
        return array_key_exists(Select::DERIVED_TABLE_KEY, $this->tableAliases);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function addJoin(string $type, $table, $column1, string $relation, $column2): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->joins[] = [$type, $table, $column1, $relation, $column2];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function addWhereCondition(string $logic, $column, string $relation, $value): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->conditions[self::CONDITION_WHERE][] = [$logic, $column, $relation, $value];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function addHavingCondition(string $logic, $column, string $relation, $value): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->conditions[self::CONDITION_HAVING][] = [$logic, $column, $relation, $value];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function generateQuery(): array
 | 
			
		||||
    {
 | 
			
		||||
        list($tableQuery, $tableParams) = $this->generateTable($this->table, true);
 | 
			
		||||
        $queryString = 'SELECT ' . $this->generateColumns() . ' FROM ' . $tableQuery;
 | 
			
		||||
 | 
			
		||||
        if (count($this->joins) > 0) {
 | 
			
		||||
            list($joinQuery, $joinParams) = $this->generateJoins();
 | 
			
		||||
            $queryString .= ' ' . $joinQuery;
 | 
			
		||||
        } else {
 | 
			
		||||
            $joinParams = [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (count($this->conditions[self::CONDITION_WHERE]) > 0) {
 | 
			
		||||
            list($wheres, $whereParams) = $this->generateConditions(self::CONDITION_WHERE);
 | 
			
		||||
 | 
			
		||||
            $queryString .= ' WHERE ' .  $wheres;
 | 
			
		||||
        } else {
 | 
			
		||||
            $whereParams = [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (count($this->groups) > 0) {
 | 
			
		||||
            $queryString .= ' GROUP BY ' . $this->generateGroupBy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (count($this->conditions[self::CONDITION_HAVING]) > 0) {
 | 
			
		||||
            list($havings, $havingParams) = $this->generateConditions(self::CONDITION_HAVING);
 | 
			
		||||
 | 
			
		||||
            $queryString .= ' HAVING ' .  $havings;
 | 
			
		||||
        } else {
 | 
			
		||||
            $havingParams = [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (count($this->orders) > 0) {
 | 
			
		||||
            $queryString .= ' ORDER BY ' . $this->generateOrderBy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isset($this->limit)) {
 | 
			
		||||
            $queryString .= ' LIMIT ' . $this->limit[1] . ', ' . $this->limit[0];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($this->isDerivedTable()) {
 | 
			
		||||
            $queryString = '(' . $queryString . ') AS ' . $this->tableAliases[Select::DERIVED_TABLE_KEY];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [$queryString, array_merge($tableParams, $joinParams, $whereParams, $havingParams)];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function generateTable($table, bool $defineAlias = false): array
 | 
			
		||||
    {
 | 
			
		||||
        $params = [];
 | 
			
		||||
 | 
			
		||||
        if ($table instanceof RawExpression) {
 | 
			
		||||
            return [(string) $table, $params];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($table instanceof Select)
 | 
			
		||||
        {
 | 
			
		||||
            return $table->generateQuery();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isset($this->tableAliases[$table])) {
 | 
			
		||||
            $queryString = ($defineAlias ? Utils::backtick($this->tableAliases[$table]) . ' ' . Utils::backtick($table) : Utils::backtick($table));
 | 
			
		||||
            return [$queryString, $params];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [Utils::backtick($table), $params];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function generateColumn($column): string
 | 
			
		||||
    {
 | 
			
		||||
        if ($column instanceof RawExpression) {
 | 
			
		||||
            return (string) $column;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (is_array($column)) {
 | 
			
		||||
            $out = '';
 | 
			
		||||
 | 
			
		||||
            if ($column[0]) {
 | 
			
		||||
                list($tableName, $params) = $this->generateTable($column[0]);
 | 
			
		||||
                $out .= $tableName . '.';
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $out .= Utils::backtick($column[1]);
 | 
			
		||||
 | 
			
		||||
            if (!empty($column[2])) {
 | 
			
		||||
                $out .= ' ' . Utils::backtick($column[2]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return $out;
 | 
			
		||||
        } else {
 | 
			
		||||
            return Utils::backtick($column);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function generateColumns(): string
 | 
			
		||||
    {
 | 
			
		||||
        $columns = $this->columns;
 | 
			
		||||
 | 
			
		||||
        array_walk($columns, function (&$value, $key) {
 | 
			
		||||
            $value = $this->generateColumn($value);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return implode(',', $columns);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function generateJoins(): array
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $joinQueries = [];
 | 
			
		||||
        $params = [];
 | 
			
		||||
 | 
			
		||||
        foreach ($this->joins as $join) {
 | 
			
		||||
            list($joinQueryFragment, $paramsFragment) = $this->generateTable($join[1], true);
 | 
			
		||||
            $joinQueries[] = $join[0] . ' JOIN ' . $joinQueryFragment . ' ON ' . $this->generateColumn($join[2]) . ' ' . $join[3] . ' ' . $this->generateColumn($join[4]);
 | 
			
		||||
            $params = array_merge($params, $paramsFragment);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [implode(' ', $joinQueries), $params];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function generateConditions(int $type): array
 | 
			
		||||
    {
 | 
			
		||||
        $conditions = '';
 | 
			
		||||
        $params = [];
 | 
			
		||||
 | 
			
		||||
        foreach ($this->conditions[$type] as $condition) {
 | 
			
		||||
            list($logic, $column, $relation, $value) = $condition;
 | 
			
		||||
 | 
			
		||||
            if ($column instanceof Closure) {
 | 
			
		||||
                list($conditionsStringFragment, $paramsFragment) = $this->generateComplexConditionFragment($type, $column);
 | 
			
		||||
            } else {
 | 
			
		||||
                list($conditionsStringFragment, $paramsFragment) = $this->generateConditionFragment($condition);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($conditions !== '') {
 | 
			
		||||
                $conditions .= ' ' . $logic . ' ';
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $conditions .= $conditionsStringFragment;
 | 
			
		||||
            $params = array_merge($params, $paramsFragment);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [$conditions, $params];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function generateConditionFragment(array $condition): array
 | 
			
		||||
    {
 | 
			
		||||
        list($logic, $column, $relation, $value) = $condition;
 | 
			
		||||
 | 
			
		||||
        if ($column instanceof RawExpression) {
 | 
			
		||||
            return [(string) $column, []];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $conditionsString = $this->generateColumn($column) . ' ';
 | 
			
		||||
 | 
			
		||||
        if ($value === null) {
 | 
			
		||||
            return [$conditionsString . ($relation == '=' ? 'IS NULL' : 'IS NOT NULL'), []];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $conditionsString .= strtoupper($relation) . ' ';;
 | 
			
		||||
 | 
			
		||||
        switch ($relation = strtolower($relation)) {
 | 
			
		||||
            case 'between':
 | 
			
		||||
                $params = [$value[0], $value[1]];
 | 
			
		||||
 | 
			
		||||
                $conditionsString .= '? AND ?';
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case 'in':
 | 
			
		||||
            case 'not in':
 | 
			
		||||
                $params = $value;
 | 
			
		||||
 | 
			
		||||
                if (count($value) > 0) {
 | 
			
		||||
                    $conditionsString .= '(' . implode(', ', array_fill(0, count($value), '?')) . ')';
 | 
			
		||||
                } else {
 | 
			
		||||
                    $conditionsString = $relation == 'in' ? '0' : '1';
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                $params = [$value];
 | 
			
		||||
 | 
			
		||||
                $conditionsString .= '?';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [$conditionsString, $params];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function generateComplexConditionFragment(int $type, Closure $conditionCallback): array
 | 
			
		||||
    {
 | 
			
		||||
        $instance = new self($this->connection, $this->table);
 | 
			
		||||
        $instance->tableAliases = $this->tableAliases;
 | 
			
		||||
 | 
			
		||||
        $conditionCallback($instance);
 | 
			
		||||
 | 
			
		||||
        list($conditions, $params) = $instance->generateConditions($type);
 | 
			
		||||
 | 
			
		||||
        return ['(' . $conditions . ')', $params];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function generateGroupBy(): string
 | 
			
		||||
    {
 | 
			
		||||
        $groups = $this->groups;
 | 
			
		||||
 | 
			
		||||
        array_walk($groups, function (&$value, $key) {
 | 
			
		||||
            $value = $this->generateColumn($value);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return implode(',', $groups);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function generateOrderBy(): string
 | 
			
		||||
    {
 | 
			
		||||
        $orders = $this->orders;
 | 
			
		||||
 | 
			
		||||
        array_walk($orders, function (&$value, $key) {
 | 
			
		||||
            $value = $this->generateColumn($value[0]) . ' ' . strtoupper($value[1]);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return implode(',', $orders);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,16 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Database;
 | 
			
		||||
 | 
			
		||||
class RawExpression
 | 
			
		||||
{
 | 
			
		||||
    private string $expression;
 | 
			
		||||
 | 
			
		||||
    public function __construct(string $expression)
 | 
			
		||||
    {
 | 
			
		||||
        $this->expression = $expression;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function __toString(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->expression;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,8 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Database;
 | 
			
		||||
 | 
			
		||||
class Utils {
 | 
			
		||||
    public static function backtick(string $name): string
 | 
			
		||||
    {
 | 
			
		||||
        return '`' . $name . '`';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,102 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Http;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Interfaces\Http\IRequest;
 | 
			
		||||
use MapGuesser\Interfaces\Http\IResponse;
 | 
			
		||||
 | 
			
		||||
class Request implements IRequest
 | 
			
		||||
{
 | 
			
		||||
    private string $url;
 | 
			
		||||
 | 
			
		||||
    private int $method;
 | 
			
		||||
 | 
			
		||||
    private string $query = '';
 | 
			
		||||
 | 
			
		||||
    private array $headers = [];
 | 
			
		||||
 | 
			
		||||
    public function __construct(string $url = '', int $method = self::HTTP_GET)
 | 
			
		||||
    {
 | 
			
		||||
        $this->url = $url;
 | 
			
		||||
        $this->method = $method;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setUrl(string $url): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->url = $url;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setMethod(int $method): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->method = $method;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setQuery($query): void
 | 
			
		||||
    {
 | 
			
		||||
        if (is_string($query)) {
 | 
			
		||||
            $this->query = $query;
 | 
			
		||||
        } else {
 | 
			
		||||
            $this->query = http_build_query($query);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setHeaders(array $headers): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->headers = array_merge($this->headers, $headers);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function send(): IResponse
 | 
			
		||||
    {
 | 
			
		||||
        $ch = curl_init();
 | 
			
		||||
 | 
			
		||||
        if ($this->method === self::HTTP_POST) {
 | 
			
		||||
            $url = $this->url;
 | 
			
		||||
 | 
			
		||||
            curl_setopt($ch, CURLOPT_POST, 1);
 | 
			
		||||
            curl_setopt($ch, CURLOPT_POSTFIELDS, $this->query);
 | 
			
		||||
        } else {
 | 
			
		||||
            $url = $this->url . '?' . $this->query;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        curl_setopt($ch, CURLOPT_URL, $url);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_TIMEOUT, 20);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_USERAGENT, 'MapGuesser cURL/1.0');
 | 
			
		||||
 | 
			
		||||
        if (count($this->headers) > 0) {
 | 
			
		||||
            curl_setopt($ch, CURLOPT_HTTPHEADER, $this->headers);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $responseHeaders = [];
 | 
			
		||||
        curl_setopt(
 | 
			
		||||
            $ch,
 | 
			
		||||
            CURLOPT_HEADERFUNCTION,
 | 
			
		||||
            function ($ch, $header) use (&$responseHeaders) {
 | 
			
		||||
                $len = strlen($header);
 | 
			
		||||
                $header = explode(':', $header, 2);
 | 
			
		||||
 | 
			
		||||
                if (count($header) < 2) {
 | 
			
		||||
                    return $len;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $responseHeaders[strtolower(trim($header[0]))][] = trim($header[1]);
 | 
			
		||||
 | 
			
		||||
                return $len;
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $responseBody = curl_exec($ch);
 | 
			
		||||
 | 
			
		||||
        if ($responseBody === false) {
 | 
			
		||||
            $error = curl_error($ch);
 | 
			
		||||
 | 
			
		||||
            curl_close($ch);
 | 
			
		||||
 | 
			
		||||
            throw new \Exception($error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        curl_close($ch);
 | 
			
		||||
 | 
			
		||||
        return new Response($responseBody, $responseHeaders);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,26 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Http;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Interfaces\Http\IResponse;
 | 
			
		||||
 | 
			
		||||
class Response implements IResponse
 | 
			
		||||
{
 | 
			
		||||
    private string $body;
 | 
			
		||||
 | 
			
		||||
    private array $headers;
 | 
			
		||||
 | 
			
		||||
    public function __construct(string $body, array $headers)
 | 
			
		||||
    {
 | 
			
		||||
        $this->body = $body;
 | 
			
		||||
        $this->headers = $headers;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getBody(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->body;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getHeaders(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->headers;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,16 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Interfaces\Authentication;
 | 
			
		||||
 | 
			
		||||
interface IUser
 | 
			
		||||
{
 | 
			
		||||
    const PERMISSION_NORMAL = 0;
 | 
			
		||||
 | 
			
		||||
    const PERMISSION_ADMIN = 1;
 | 
			
		||||
 | 
			
		||||
    public function hasPermission(int $permission): bool;
 | 
			
		||||
 | 
			
		||||
    public function getUniqueId();
 | 
			
		||||
 | 
			
		||||
    public function getDisplayName(): string;
 | 
			
		||||
 | 
			
		||||
    public function checkPassword(string $password): bool;
 | 
			
		||||
}
 | 
			
		||||
@ -1,6 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Interfaces\Authorization;
 | 
			
		||||
 | 
			
		||||
interface ISecured
 | 
			
		||||
{
 | 
			
		||||
    public function authorize(): bool;
 | 
			
		||||
}
 | 
			
		||||
@ -1,20 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Interfaces\Database;
 | 
			
		||||
 | 
			
		||||
interface IConnection
 | 
			
		||||
{
 | 
			
		||||
    public function startTransaction(): void;
 | 
			
		||||
 | 
			
		||||
    public function commit(): void;
 | 
			
		||||
 | 
			
		||||
    public function rollback(): void;
 | 
			
		||||
 | 
			
		||||
    public function query(string $query): ?IResultSet;
 | 
			
		||||
 | 
			
		||||
    public function multiQuery(string $query): array;
 | 
			
		||||
 | 
			
		||||
    public function prepare(string $query): IStatement;
 | 
			
		||||
 | 
			
		||||
    public function lastId(): int;
 | 
			
		||||
 | 
			
		||||
    public function getAffectedRows(): int;
 | 
			
		||||
}
 | 
			
		||||
@ -1,16 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Interfaces\Database;
 | 
			
		||||
 | 
			
		||||
interface IResultSet
 | 
			
		||||
{
 | 
			
		||||
    const FETCH_ASSOC = 0;
 | 
			
		||||
 | 
			
		||||
    const FETCH_NUM = 1;
 | 
			
		||||
 | 
			
		||||
    const FETCH_BOTH = 2;
 | 
			
		||||
 | 
			
		||||
    public function fetch(int $type): ?array;
 | 
			
		||||
 | 
			
		||||
    public function fetchAll(int $type): array;
 | 
			
		||||
 | 
			
		||||
    public function fetchOneColumn(string $valueName, string $keyName): array;
 | 
			
		||||
}
 | 
			
		||||
@ -1,8 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Interfaces\Database;
 | 
			
		||||
 | 
			
		||||
interface IStatement
 | 
			
		||||
{
 | 
			
		||||
    public function execute(array $params): ?IResultSet;
 | 
			
		||||
 | 
			
		||||
    public function getAffectedRows(): int;
 | 
			
		||||
}
 | 
			
		||||
@ -1,18 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Interfaces\Http;
 | 
			
		||||
 | 
			
		||||
interface IRequest
 | 
			
		||||
{
 | 
			
		||||
    const HTTP_GET = 0;
 | 
			
		||||
 | 
			
		||||
    const HTTP_POST = 1;
 | 
			
		||||
 | 
			
		||||
    public function setUrl(string $url): void;
 | 
			
		||||
 | 
			
		||||
    public function setMethod(int $method): void;
 | 
			
		||||
 | 
			
		||||
    public function setQuery($query): void;
 | 
			
		||||
 | 
			
		||||
    public function setHeaders(array $headers): void;
 | 
			
		||||
 | 
			
		||||
    public function send(): IResponse;
 | 
			
		||||
}
 | 
			
		||||
@ -1,8 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Interfaces\Http;
 | 
			
		||||
 | 
			
		||||
interface IResponse
 | 
			
		||||
{
 | 
			
		||||
    public function getBody(): string;
 | 
			
		||||
 | 
			
		||||
    public function getHeaders(): array;
 | 
			
		||||
}
 | 
			
		||||
@ -1,20 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Interfaces\Request;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Interfaces\Authentication\IUser;
 | 
			
		||||
 | 
			
		||||
interface IRequest
 | 
			
		||||
{
 | 
			
		||||
    public function setParsedRouteParams(array &$routeParams): void;
 | 
			
		||||
 | 
			
		||||
    public function getBase(): string;
 | 
			
		||||
 | 
			
		||||
    public function query(string $key);
 | 
			
		||||
 | 
			
		||||
    public function post(string $key);
 | 
			
		||||
 | 
			
		||||
    public function session(): ISession;
 | 
			
		||||
 | 
			
		||||
    public function setUser(?IUser $user): void;
 | 
			
		||||
 | 
			
		||||
    public function user(): ?IUser;
 | 
			
		||||
}
 | 
			
		||||
@ -1,12 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Interfaces\Request;
 | 
			
		||||
 | 
			
		||||
interface ISession
 | 
			
		||||
{
 | 
			
		||||
    public function has(string $key): bool;
 | 
			
		||||
 | 
			
		||||
    public function get(string $key);
 | 
			
		||||
 | 
			
		||||
    public function set(string $key, $value): void;
 | 
			
		||||
 | 
			
		||||
    public function delete(string $key): void;
 | 
			
		||||
}
 | 
			
		||||
@ -1,12 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Interfaces\Response;
 | 
			
		||||
 | 
			
		||||
interface IContent
 | 
			
		||||
{
 | 
			
		||||
    public function setData(array $data): void;
 | 
			
		||||
 | 
			
		||||
    public function getData(): array;
 | 
			
		||||
 | 
			
		||||
    public function render(): void;
 | 
			
		||||
 | 
			
		||||
    public function getContentType(): string;
 | 
			
		||||
}
 | 
			
		||||
@ -1,12 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Interfaces\Response;
 | 
			
		||||
 | 
			
		||||
interface IRedirect
 | 
			
		||||
{
 | 
			
		||||
    const PERMANENT = 1;
 | 
			
		||||
 | 
			
		||||
    const TEMPORARY = 2;
 | 
			
		||||
 | 
			
		||||
    public function getUrl(): string;
 | 
			
		||||
 | 
			
		||||
    public function getHttpCode(): int;
 | 
			
		||||
}
 | 
			
		||||
@ -1,9 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Interfaces\Session;
 | 
			
		||||
 | 
			
		||||
use SessionHandlerInterface;
 | 
			
		||||
use SessionIdInterface;
 | 
			
		||||
use SessionUpdateTimestampHandlerInterface;
 | 
			
		||||
 | 
			
		||||
interface ISessionHandler extends SessionHandlerInterface, SessionIdInterface, SessionUpdateTimestampHandlerInterface
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@ -1,87 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Mailing;
 | 
			
		||||
 | 
			
		||||
use PHPMailer\PHPMailer\PHPMailer;
 | 
			
		||||
 | 
			
		||||
class Mail
 | 
			
		||||
{
 | 
			
		||||
    private array $recipients = [];
 | 
			
		||||
 | 
			
		||||
    public string $subject = '';
 | 
			
		||||
 | 
			
		||||
    public string $body = '';
 | 
			
		||||
 | 
			
		||||
    public function addRecipient(string $mail, ?string $name = null): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->recipients[] = [$mail, $name];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setSubject(string $subject): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->subject = $subject;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setBody(string $body): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->body = $body;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setBodyFromTemplate(string $template, array $params = []): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->body = file_get_contents(ROOT . '/mail/' . $template . '.html');
 | 
			
		||||
 | 
			
		||||
        $baseParameters = [
 | 
			
		||||
            'APP_NAME' => $_ENV['APP_NAME'],
 | 
			
		||||
            'BASE_URL' => \Container::$request->getBase(),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $params = array_merge($baseParameters, $params);
 | 
			
		||||
 | 
			
		||||
        foreach ($params as $key => $param) {
 | 
			
		||||
            $this->body = str_replace('{{' . $key . '}}', $param, $this->body);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function send(): void
 | 
			
		||||
    {
 | 
			
		||||
        $mailer = new PHPMailer(true);
 | 
			
		||||
 | 
			
		||||
        $mailer->CharSet = 'utf-8';
 | 
			
		||||
        $mailer->Hostname = substr($_ENV['MAIL_FROM'], strpos($_ENV['MAIL_FROM'], '@') + 1);
 | 
			
		||||
 | 
			
		||||
        if (!empty($_ENV['MAIL_HOST'])) {
 | 
			
		||||
            $mailer->Mailer = 'smtp';
 | 
			
		||||
            $mailer->Host = $_ENV['MAIL_HOST'];
 | 
			
		||||
            $mailer->Port = !empty($_ENV['MAIL_PORT']) ? $_ENV['MAIL_PORT'] : 25;
 | 
			
		||||
            $mailer->SMTPSecure = !empty($_ENV['MAIL_SECURE']) ? $_ENV['MAIL_SECURE'] : '';
 | 
			
		||||
 | 
			
		||||
            if (!empty($_ENV['MAIL_USER'])) {
 | 
			
		||||
                $mailer->SMTPAuth = true;
 | 
			
		||||
                $mailer->Username = $_ENV['MAIL_USER'];
 | 
			
		||||
                $mailer->Password = $_ENV['MAIL_PASSWORD'];
 | 
			
		||||
            } else {
 | 
			
		||||
                $mailer->SMTPAuth = false;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            $mailer->Mailer = 'mail';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $mailer->setFrom($_ENV['MAIL_FROM'], $_ENV['APP_NAME']);
 | 
			
		||||
        $mailer->addReplyTo($_ENV['MAIL_FROM'], $_ENV['APP_NAME']);
 | 
			
		||||
 | 
			
		||||
        $mailer->Sender = !empty($_ENV['MAIL_BOUNCE']) ? $_ENV['MAIL_BOUNCE'] : $_ENV['MAIL_FROM'];
 | 
			
		||||
        $mailer->Subject = $this->subject;
 | 
			
		||||
        $mailer->msgHTML($this->body);
 | 
			
		||||
 | 
			
		||||
        foreach ($this->recipients as $recipient) {
 | 
			
		||||
            $this->sendMail($mailer, $recipient);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function sendMail(PHPMailer $mailer, array $recipient): void
 | 
			
		||||
    {
 | 
			
		||||
        $mailer->clearAddresses();
 | 
			
		||||
        $mailer->addAddress($recipient[0], $recipient[1]);
 | 
			
		||||
 | 
			
		||||
        $mailer->send();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,56 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\OAuth;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Interfaces\Http\IRequest;
 | 
			
		||||
 | 
			
		||||
class GoogleOAuth
 | 
			
		||||
{
 | 
			
		||||
    private static string $dialogUrlBase = 'https://accounts.google.com/o/oauth2/v2/auth';
 | 
			
		||||
 | 
			
		||||
    private static string $tokenUrlBase = 'https://oauth2.googleapis.com/token';
 | 
			
		||||
 | 
			
		||||
    private IRequest $request;
 | 
			
		||||
 | 
			
		||||
    public function __construct(IRequest $request)
 | 
			
		||||
    {
 | 
			
		||||
        $this->request = $request;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getDialogUrl(string $state, string $redirectUrl, ?string $nonce = null, ?string $loginHint = null): string
 | 
			
		||||
    {
 | 
			
		||||
        $oauthParams = [
 | 
			
		||||
            'response_type' => 'code',
 | 
			
		||||
            'client_id' => $_ENV['GOOGLE_OAUTH_CLIENT_ID'],
 | 
			
		||||
            'scope' => 'openid email',
 | 
			
		||||
            'redirect_uri' => $redirectUrl,
 | 
			
		||||
            'state' => $state,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        if ($nonce !== null) {
 | 
			
		||||
            $oauthParams['nonce'] = $nonce;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($loginHint !== null) {
 | 
			
		||||
            $oauthParams['login_hint'] = $loginHint;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return self::$dialogUrlBase . '?' . http_build_query($oauthParams);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getToken(string $code, string $redirectUrl): array
 | 
			
		||||
    {
 | 
			
		||||
        $tokenParams = [
 | 
			
		||||
            'code' => $code,
 | 
			
		||||
            'client_id' => $_ENV['GOOGLE_OAUTH_CLIENT_ID'],
 | 
			
		||||
            'client_secret' => $_ENV['GOOGLE_OAUTH_CLIENT_SECRET'],
 | 
			
		||||
            'redirect_uri' => $redirectUrl,
 | 
			
		||||
            'grant_type' => 'authorization_code',
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $this->request->setUrl(self::$tokenUrlBase);
 | 
			
		||||
        $this->request->setMethod(IRequest::HTTP_POST);
 | 
			
		||||
        $this->request->setQuery($tokenParams);
 | 
			
		||||
        $response = $this->request->send();
 | 
			
		||||
 | 
			
		||||
        return json_decode($response->getBody(), true);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
<?php namespace MapGuesser\PersistentData\Model;
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use SokoWeb\PersistentData\Model\Model;
 | 
			
		||||
 | 
			
		||||
class Challenge extends Model
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
<?php namespace MapGuesser\PersistentData\Model;
 | 
			
		||||
 | 
			
		||||
use SokoWeb\PersistentData\Model\Model;
 | 
			
		||||
use MapGuesser\Util\Geo\Position;
 | 
			
		||||
 | 
			
		||||
class Guess extends Model
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
<?php namespace MapGuesser\PersistentData\Model;
 | 
			
		||||
 | 
			
		||||
use SokoWeb\PersistentData\Model\Model;
 | 
			
		||||
use MapGuesser\Util\Geo\Bounds;
 | 
			
		||||
 | 
			
		||||
class Map extends Model
 | 
			
		||||
 | 
			
		||||
@ -1,69 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\PersistentData\Model;
 | 
			
		||||
 | 
			
		||||
abstract class Model
 | 
			
		||||
{
 | 
			
		||||
    protected static string $table;
 | 
			
		||||
 | 
			
		||||
    protected static array $fields;
 | 
			
		||||
 | 
			
		||||
    protected static array $relations = [];
 | 
			
		||||
 | 
			
		||||
    protected $id = null;
 | 
			
		||||
 | 
			
		||||
    private array $snapshot = [];
 | 
			
		||||
 | 
			
		||||
    public static function getTable(): string
 | 
			
		||||
    {
 | 
			
		||||
        return static::$table;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function getFields(): array
 | 
			
		||||
    {
 | 
			
		||||
        return array_merge(['id'], static::$fields);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function getRelations(): array
 | 
			
		||||
    {
 | 
			
		||||
        return static::$relations;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setId($id): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->id = $id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getId()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function toArray(): array
 | 
			
		||||
    {
 | 
			
		||||
        $array = [];
 | 
			
		||||
 | 
			
		||||
        foreach (self::getFields() as $key) {
 | 
			
		||||
            $method = 'get' . str_replace('_', '', ucwords($key, '_'));
 | 
			
		||||
 | 
			
		||||
            if (method_exists($this, $method)) {
 | 
			
		||||
                $array[$key] = $this->$method();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $array;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function saveSnapshot(): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->snapshot = $this->toArray();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function resetSnapshot(): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->snapshot = [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getSnapshot(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->snapshot;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
<?php namespace MapGuesser\PersistentData\Model;
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use SokoWeb\PersistentData\Model\Model;
 | 
			
		||||
 | 
			
		||||
class MultiRoom extends Model
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -2,8 +2,9 @@
 | 
			
		||||
 | 
			
		||||
use DateInterval;
 | 
			
		||||
use DateTime;
 | 
			
		||||
use MapGuesser\Http\Request;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\PersistentData\Model\Model;
 | 
			
		||||
use SokoWeb\Http\Request;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
use MapGuesser\Util\Geo\Position;
 | 
			
		||||
use MapGuesser\Util\Panorama\Pov;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,7 @@
 | 
			
		||||
<?php namespace MapGuesser\PersistentData\Model;
 | 
			
		||||
 | 
			
		||||
use SokoWeb\PersistentData\Model\Model;
 | 
			
		||||
 | 
			
		||||
class PlaceInChallenge extends Model
 | 
			
		||||
{
 | 
			
		||||
    protected static string $table = 'place_in_challenge';
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,8 @@
 | 
			
		||||
<?php namespace MapGuesser\PersistentData\Model;
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use MapGuesser\Interfaces\Authentication\IUser;
 | 
			
		||||
use SokoWeb\PersistentData\Model\Model;
 | 
			
		||||
use SokoWeb\Interfaces\Authentication\IUser;
 | 
			
		||||
 | 
			
		||||
class User extends Model implements IUser
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
<?php namespace MapGuesser\PersistentData\Model;
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use SokoWeb\PersistentData\Model\Model;
 | 
			
		||||
 | 
			
		||||
class UserConfirmation extends Model
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,7 @@
 | 
			
		||||
<?php namespace MapGuesser\PersistentData\Model;
 | 
			
		||||
 | 
			
		||||
use SokoWeb\PersistentData\Model\Model;
 | 
			
		||||
 | 
			
		||||
class UserInChallenge extends Model
 | 
			
		||||
{
 | 
			
		||||
    protected static string $table = 'user_in_challenge';
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
<?php namespace MapGuesser\PersistentData\Model;
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use SokoWeb\PersistentData\Model\Model;
 | 
			
		||||
 | 
			
		||||
class UserPasswordResetter extends Model
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
<?php namespace MapGuesser\PersistentData\Model;
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use SokoWeb\PersistentData\Model\Model;
 | 
			
		||||
 | 
			
		||||
class UserPlayedPlace extends Model
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -1,235 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\PersistentData;
 | 
			
		||||
 | 
			
		||||
use Generator;
 | 
			
		||||
use MapGuesser\Database\Query\Modify;
 | 
			
		||||
use MapGuesser\Database\Query\Select;
 | 
			
		||||
use MapGuesser\Interfaces\Database\IResultSet;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Model;
 | 
			
		||||
 | 
			
		||||
class PersistentDataManager
 | 
			
		||||
{
 | 
			
		||||
    public function selectFromDb(Select $select, string $type, bool $useRelations = false, array $withRelations = [])
 | 
			
		||||
    {
 | 
			
		||||
        $select = $this->createSelect($select, $type, $useRelations, $withRelations);
 | 
			
		||||
 | 
			
		||||
        $data = $select->execute()->fetch(IResultSet::FETCH_ASSOC);
 | 
			
		||||
 | 
			
		||||
        if ($data === null) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $model = new $type();
 | 
			
		||||
        $this->fillWithData($data, $model, $withRelations);
 | 
			
		||||
 | 
			
		||||
        return $model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function selectMultipleFromDb(Select $select, string $type, bool $useRelations = false, array $withRelations = []): Generator
 | 
			
		||||
    {
 | 
			
		||||
        $select = $this->createSelect($select, $type, $useRelations, $withRelations);
 | 
			
		||||
        $result = $select->execute();
 | 
			
		||||
 | 
			
		||||
        while ($data = $result->fetch(IResultSet::FETCH_ASSOC)) {
 | 
			
		||||
            $model = new $type();
 | 
			
		||||
            $this->fillWithData($data, $model, $withRelations);
 | 
			
		||||
 | 
			
		||||
            yield $model;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function selectFromDbById($id, string $type, bool $useRelations = false)
 | 
			
		||||
    {
 | 
			
		||||
        $select = new Select(\Container::$dbConnection);
 | 
			
		||||
        $select->whereId($id);
 | 
			
		||||
 | 
			
		||||
        return $this->selectFromDb($select, $type, $useRelations);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function fillWithData(array &$data, Model $model, array $withRelations = [], ?string $modelKey = null): void
 | 
			
		||||
    {
 | 
			
		||||
        $relations = $model::getRelations();
 | 
			
		||||
        if (count($withRelations)) {
 | 
			
		||||
            $relations = array_intersect($relations, $withRelations);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        while (key($data)) {
 | 
			
		||||
            $key = key($data);
 | 
			
		||||
            $value = current($data);
 | 
			
		||||
            $relation = key($relations);
 | 
			
		||||
 | 
			
		||||
            if (strpos($key, '__') == false) {
 | 
			
		||||
                $method = 'set' . str_replace('_', '', ucwords($key, '_'));
 | 
			
		||||
 | 
			
		||||
                if (method_exists($model, $method) && isset($value)) {
 | 
			
		||||
                    $model->$method($value);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                next($data);
 | 
			
		||||
            } else if (isset($modelKey) && substr($key, 0, strlen($modelKey . '__')) === $modelKey . '__') {
 | 
			
		||||
                $key = substr($key, strlen($modelKey) + 2);
 | 
			
		||||
 | 
			
		||||
                $method = 'set' . str_replace('_', '', ucwords($key, '_'));
 | 
			
		||||
 | 
			
		||||
                if (method_exists($model, $method) && isset($value)) {
 | 
			
		||||
                    $model->$method($value);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                next($data);
 | 
			
		||||
            } else if (substr($key, 0, strlen($relation . '__')) === $relation . '__') {
 | 
			
		||||
                $relationType = current($relations);
 | 
			
		||||
                $relationModel = new $relationType();
 | 
			
		||||
                $this->fillWithData($data, $relationModel, $withRelations, $relation);
 | 
			
		||||
 | 
			
		||||
                $method = 'set' . str_replace('_', '', ucwords($relation, '_'));
 | 
			
		||||
                $model->$method($relationModel);
 | 
			
		||||
 | 
			
		||||
                next($relations);
 | 
			
		||||
            } else {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $model->saveSnapshot();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function loadRelationsFromDb(Model $model, bool $recursive): void
 | 
			
		||||
    {
 | 
			
		||||
        foreach ($model::getRelations() as $relation => $relationType) {
 | 
			
		||||
            $camel = str_replace('_', '', ucwords($relation, '_'));
 | 
			
		||||
 | 
			
		||||
            $methodGet = 'get' . $camel . 'Id';
 | 
			
		||||
            $methodSet = 'set' . $camel;
 | 
			
		||||
 | 
			
		||||
            $relationId = $model->$methodGet();
 | 
			
		||||
 | 
			
		||||
            if ($relationId !== null) {
 | 
			
		||||
                $relationModel = $this->selectFromDbById($relationId, $relationType, $recursive);
 | 
			
		||||
 | 
			
		||||
                $model->$methodSet($relationModel);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function saveToDb(Model $model): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->syncRelations($model);
 | 
			
		||||
 | 
			
		||||
        $modified = $model->toArray();
 | 
			
		||||
        $id = $model->getId();
 | 
			
		||||
 | 
			
		||||
        $modify = new Modify(\Container::$dbConnection, $model::getTable());
 | 
			
		||||
 | 
			
		||||
        if ($id !== null) {
 | 
			
		||||
            $original = $model->getSnapshot();
 | 
			
		||||
 | 
			
		||||
            foreach ($original as $key => $value) {
 | 
			
		||||
                if ($value === $modified[$key]) {
 | 
			
		||||
                    unset($modified[$key]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (count($modified) > 0) {
 | 
			
		||||
                $modify->setId($id);
 | 
			
		||||
                $modify->fill($modified);
 | 
			
		||||
                $modify->save();
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            $modify->fill($modified);
 | 
			
		||||
            $modify->save();
 | 
			
		||||
 | 
			
		||||
            $model->setId($modify->getId());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $model->saveSnapshot();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function deleteFromDb(Model $model): void
 | 
			
		||||
    {
 | 
			
		||||
        $modify = new Modify(\Container::$dbConnection, $model::getTable());
 | 
			
		||||
        $modify->setId($model->getId());
 | 
			
		||||
        $modify->delete();
 | 
			
		||||
 | 
			
		||||
        $model->setId(null);
 | 
			
		||||
        $model->resetSnapshot();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function createSelect(Select $select, string $type, bool $useRelations = false, array $withRelations = []): Select
 | 
			
		||||
    {
 | 
			
		||||
        $table = call_user_func([$type, 'getTable']);
 | 
			
		||||
        $fields = call_user_func([$type, 'getFields']);
 | 
			
		||||
 | 
			
		||||
        $columns = [];
 | 
			
		||||
 | 
			
		||||
        foreach ($fields as $field) {
 | 
			
		||||
            $columns[] = [$table, $field];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $select->from($table);
 | 
			
		||||
 | 
			
		||||
        if ($useRelations) {
 | 
			
		||||
            $relations = call_user_func([$type, 'getRelations']);
 | 
			
		||||
            if (count($withRelations)) {
 | 
			
		||||
                $relations = array_intersect($relations, $withRelations);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $columns = array_merge($columns, $this->getRelationColumns($relations, $withRelations));
 | 
			
		||||
 | 
			
		||||
            $this->leftJoinRelations($select, $table, $relations, $withRelations);
 | 
			
		||||
            $select->columns($columns);
 | 
			
		||||
        } else {
 | 
			
		||||
            $select->columns($columns);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $select;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function getRelationColumns(array $relations, array $withRelations): array
 | 
			
		||||
    {
 | 
			
		||||
        $columns = [];
 | 
			
		||||
 | 
			
		||||
        foreach ($relations as $relation => $relationType) {
 | 
			
		||||
            $relationTable = call_user_func([$relationType, 'getTable']);
 | 
			
		||||
            foreach (call_user_func([$relationType, 'getFields']) as $relationField) {
 | 
			
		||||
                $columns[] = [$relationTable, $relationField, $relation . '__' . $relationField];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $nextOrderRelations = call_user_func([$relationType, 'getRelations']);
 | 
			
		||||
            if (count($withRelations)) {
 | 
			
		||||
                $nextOrderRelations = array_intersect($nextOrderRelations, $withRelations);
 | 
			
		||||
            }
 | 
			
		||||
            $columns = array_merge($columns, $this->getRelationColumns($nextOrderRelations, $withRelations));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $columns;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function leftJoinRelations(Select $select, string $table, array $relations, array $withRelations): void
 | 
			
		||||
    {
 | 
			
		||||
        foreach ($relations as $relation => $relationType) {
 | 
			
		||||
            $relationTable = call_user_func([$relationType, 'getTable']);
 | 
			
		||||
            $select->leftJoin($relationTable, [$relationTable, 'id'], '=', [$table, $relation . '_id']);
 | 
			
		||||
 | 
			
		||||
            $nextOrderRelations = call_user_func([$relationType, 'getRelations']);
 | 
			
		||||
            if (count($withRelations)) {
 | 
			
		||||
                $nextOrderRelations = array_intersect($nextOrderRelations, $withRelations);
 | 
			
		||||
            }
 | 
			
		||||
            $this->leftJoinRelations($select, $relationTable, $nextOrderRelations, $withRelations);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function syncRelations(Model $model): void
 | 
			
		||||
    {
 | 
			
		||||
        foreach ($model::getRelations() as $relation => $relationType) {
 | 
			
		||||
            $camel = str_replace('_', '', ucwords($relation, '_'));
 | 
			
		||||
 | 
			
		||||
            $methodGet = 'get' . $camel;
 | 
			
		||||
            $methodSet = 'set' . $camel . 'Id';
 | 
			
		||||
 | 
			
		||||
            $relationModel = $model->$methodGet();
 | 
			
		||||
 | 
			
		||||
            if ($relationModel !== null) {
 | 
			
		||||
                $model->$methodSet($relationModel->getId());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,11 +1,11 @@
 | 
			
		||||
<?php namespace MapGuesser\Repository;
 | 
			
		||||
 | 
			
		||||
use Generator;
 | 
			
		||||
use MapGuesser\Database\Query\Select;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Challenge;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Place;
 | 
			
		||||
use MapGuesser\PersistentData\Model\User;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
 | 
			
		||||
class ChallengeRepository
 | 
			
		||||
{
 | 
			
		||||
@ -25,7 +25,7 @@ class ChallengeRepository
 | 
			
		||||
    {
 | 
			
		||||
        $select = new Select(\Container::$dbConnection);
 | 
			
		||||
        $select->where('token', '=', $token);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        return $this->pdm->selectFromDb($select, Challenge::class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -72,4 +72,4 @@ class ChallengeRepository
 | 
			
		||||
 | 
			
		||||
        yield from $this->pdm->selectMultipleFromDb($select, Challenge::class);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,13 @@
 | 
			
		||||
<?php namespace MapGuesser\Repository;
 | 
			
		||||
 | 
			
		||||
use Generator;
 | 
			
		||||
use MapGuesser\Database\Query\Select;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Challenge;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Guess;
 | 
			
		||||
use MapGuesser\PersistentData\Model\User;
 | 
			
		||||
use MapGuesser\PersistentData\Model\UserInChallenge;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Place;
 | 
			
		||||
use MapGuesser\PersistentData\Model\PlaceInChallenge;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
 | 
			
		||||
class GuessRepository
 | 
			
		||||
{
 | 
			
		||||
@ -79,7 +78,7 @@ class GuessRepository
 | 
			
		||||
            $necessaryRelations = [PlaceInChallenge::class];
 | 
			
		||||
            $withRelations = array_unique(array_merge($withRelations, $necessaryRelations));
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $select = new Select(\Container::$dbConnection);
 | 
			
		||||
        $select->where('challenge_id', '=', $challenge->getId());
 | 
			
		||||
        $select->where('round', '=', $round);
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,10 @@
 | 
			
		||||
<?php namespace MapGuesser\Repository;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Database\Query\Select;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Challenge;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Map;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Place;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
 | 
			
		||||
class MapRepository
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -2,9 +2,9 @@
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use Generator;
 | 
			
		||||
use MapGuesser\Database\Query\Select;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
use MapGuesser\PersistentData\Model\MultiRoom;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
 | 
			
		||||
class MultiRoomRepository
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,11 @@
 | 
			
		||||
<?php namespace MapGuesser\Repository;
 | 
			
		||||
 | 
			
		||||
use Generator;
 | 
			
		||||
use MapGuesser\Database\Query\Select;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Challenge;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Map;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Place;
 | 
			
		||||
use MapGuesser\PersistentData\Model\PlaceInChallenge;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
 | 
			
		||||
class PlaceInChallengeRepository
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,11 @@
 | 
			
		||||
<?php namespace MapGuesser\Repository;
 | 
			
		||||
 | 
			
		||||
use Generator;
 | 
			
		||||
use MapGuesser\Database\Query\Select;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Challenge;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Map;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Place;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
 | 
			
		||||
class PlaceRepository
 | 
			
		||||
{
 | 
			
		||||
@ -41,7 +41,7 @@ class PlaceRepository
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $oldPlaces = $this->getRandomOldNForMapWithValidPano($mapId, $n - count($unvisitedPlaces), $userId);
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            return array_merge($unvisitedPlaces, $oldPlaces);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -198,5 +198,5 @@ class PlaceRepository
 | 
			
		||||
 | 
			
		||||
        yield from $this->pdm->selectMultipleFromDb($select, Place::class);
 | 
			
		||||
    }
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,9 @@
 | 
			
		||||
<?php namespace MapGuesser\Repository;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Database\Query\Select;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
use MapGuesser\PersistentData\Model\User;
 | 
			
		||||
use MapGuesser\PersistentData\Model\UserConfirmation;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
 | 
			
		||||
class UserConfirmationRepository
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,11 @@
 | 
			
		||||
<?php namespace MapGuesser\Repository;
 | 
			
		||||
 | 
			
		||||
use Generator;
 | 
			
		||||
use MapGuesser\Database\Query\Select;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Challenge;
 | 
			
		||||
use MapGuesser\PersistentData\Model\User;
 | 
			
		||||
use MapGuesser\PersistentData\Model\UserInChallenge;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
 | 
			
		||||
class UserInChallengeRepository
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -2,10 +2,10 @@
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use Generator;
 | 
			
		||||
use MapGuesser\Database\Query\Select;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
use MapGuesser\PersistentData\Model\User;
 | 
			
		||||
use MapGuesser\PersistentData\Model\UserPasswordResetter;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
 | 
			
		||||
class UserPasswordResetterRepository
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,11 @@
 | 
			
		||||
<?php namespace MapGuesser\Repository;
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use Generator;
 | 
			
		||||
use MapGuesser\Database\Query\Select;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
use MapGuesser\PersistentData\Model\User;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Place;
 | 
			
		||||
use MapGuesser\PersistentData\Model\UserPlayedPlace;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
 | 
			
		||||
class UserPlayedPlaceRepository
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -2,12 +2,13 @@
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use Generator;
 | 
			
		||||
use MapGuesser\Database\Query\Select;
 | 
			
		||||
use SokoWeb\Interfaces\Repository\IUserRepository;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
use MapGuesser\PersistentData\Model\Guess;
 | 
			
		||||
use MapGuesser\PersistentData\Model\User;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use SokoWeb\PersistentData\PersistentDataManager;
 | 
			
		||||
 | 
			
		||||
class UserRepository
 | 
			
		||||
class UserRepository implements IUserRepository
 | 
			
		||||
{
 | 
			
		||||
    private PersistentDataManager $pdm;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,93 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Request;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Interfaces\Authentication\IUser;
 | 
			
		||||
use MapGuesser\Interfaces\Request\IRequest;
 | 
			
		||||
use MapGuesser\Interfaces\Request\ISession;
 | 
			
		||||
use MapGuesser\PersistentData\Model\User;
 | 
			
		||||
use MapGuesser\PersistentData\PersistentDataManager;
 | 
			
		||||
use MapGuesser\Repository\UserRepository;
 | 
			
		||||
 | 
			
		||||
class Request implements IRequest
 | 
			
		||||
{
 | 
			
		||||
    private string $base;
 | 
			
		||||
 | 
			
		||||
    private array $get;
 | 
			
		||||
 | 
			
		||||
    private array $routeParams = [];
 | 
			
		||||
 | 
			
		||||
    private array $post;
 | 
			
		||||
 | 
			
		||||
    private Session $session;
 | 
			
		||||
 | 
			
		||||
    private UserRepository $userRepository;
 | 
			
		||||
 | 
			
		||||
    private ?User $user = null;
 | 
			
		||||
 | 
			
		||||
    public function __construct(string $base, array &$get, array &$post, array &$session)
 | 
			
		||||
    {
 | 
			
		||||
        $this->base = $base;
 | 
			
		||||
        $this->get = &$get;
 | 
			
		||||
        $this->post = &$post;
 | 
			
		||||
        $this->session = new Session($session);
 | 
			
		||||
 | 
			
		||||
        $this->userRepository = new UserRepository();
 | 
			
		||||
 | 
			
		||||
        $userId = $this->session->get('userId');
 | 
			
		||||
 | 
			
		||||
        if ($userId !== null) {
 | 
			
		||||
            $this->user = $this->userRepository->getById($userId);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setParsedRouteParams(array &$routeParams): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->routeParams = &$routeParams;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getBase(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->base;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function query($key)
 | 
			
		||||
    {
 | 
			
		||||
        if (isset($this->get[$key])) {
 | 
			
		||||
            return $this->get[$key];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isset($this->routeParams[$key])) {
 | 
			
		||||
            return $this->routeParams[$key];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function post($key)
 | 
			
		||||
    {
 | 
			
		||||
        if (isset($this->post[$key])) {
 | 
			
		||||
            return $this->post[$key];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function session(): ISession
 | 
			
		||||
    {
 | 
			
		||||
        return $this->session;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setUser(?IUser $user): void
 | 
			
		||||
    {
 | 
			
		||||
        if ($user === null) {
 | 
			
		||||
            $this->session->delete('userId');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->session->set('userId', $user->getUniqueId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function user(): ?IUser
 | 
			
		||||
    {
 | 
			
		||||
        return $this->user;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,37 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Request;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Interfaces\Request\ISession;
 | 
			
		||||
 | 
			
		||||
class Session implements ISession
 | 
			
		||||
{
 | 
			
		||||
    private array $data;
 | 
			
		||||
 | 
			
		||||
    public function __construct(array &$data)
 | 
			
		||||
    {
 | 
			
		||||
        $this->data = &$data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function has(string $key): bool
 | 
			
		||||
    {
 | 
			
		||||
        return isset($this->data[$key]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function get(string $key)
 | 
			
		||||
    {
 | 
			
		||||
        if (isset($this->data[$key])) {
 | 
			
		||||
            return $this->data[$key];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function set(string $key, $value): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->data[$key] = $value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function delete(string $key): void
 | 
			
		||||
    {
 | 
			
		||||
        unset($this->data[$key]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,22 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Response;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Interfaces\Response\IContent;
 | 
			
		||||
 | 
			
		||||
abstract class ContentBase implements IContent
 | 
			
		||||
{
 | 
			
		||||
    protected array $data;
 | 
			
		||||
 | 
			
		||||
    public function setData(array $data): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->data = $data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getData(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    abstract public function render(): void;
 | 
			
		||||
 | 
			
		||||
    abstract public function getContentType(): string;
 | 
			
		||||
}
 | 
			
		||||
@ -1,34 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Response;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\View\Linker;
 | 
			
		||||
 | 
			
		||||
class HtmlContent extends ContentBase
 | 
			
		||||
{
 | 
			
		||||
    private string $view;
 | 
			
		||||
 | 
			
		||||
    public function __construct(string $view, array $data = [])
 | 
			
		||||
    {
 | 
			
		||||
        $this->view = $view;
 | 
			
		||||
        $this->data = $data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function render(): void
 | 
			
		||||
    {
 | 
			
		||||
        if (!empty($_ENV['DEV'])) {
 | 
			
		||||
            $generator = new Linker($this->view);
 | 
			
		||||
            $generator->generate();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        extract($this->data);
 | 
			
		||||
 | 
			
		||||
        require ROOT . '/cache/views/' . $this->view . '.php';
 | 
			
		||||
 | 
			
		||||
        // @phpstan-ignore-next-line - SCRIPT_STARTED is defined in main.php
 | 
			
		||||
        echo '<!-- __debug__runtime: ' . round((hrtime(true) - SCRIPT_STARTED) / 1e+6, 1) . ' -->';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getContentType(): string
 | 
			
		||||
    {
 | 
			
		||||
        return 'text/html';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,22 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Response;
 | 
			
		||||
 | 
			
		||||
class JsonContent extends ContentBase
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(array $data = [])
 | 
			
		||||
    {
 | 
			
		||||
        $this->data = $data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function render(): void
 | 
			
		||||
    {
 | 
			
		||||
        // @phpstan-ignore-next-line - SCRIPT_STARTED is defined in main.php
 | 
			
		||||
        $this->data['__debug__runtime'] = round((hrtime(true) - SCRIPT_STARTED) / 1e+6, 1);
 | 
			
		||||
 | 
			
		||||
        echo json_encode($this->data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getContentType(): string
 | 
			
		||||
    {
 | 
			
		||||
        return 'application/json';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,41 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Response;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Interfaces\Response\IRedirect;
 | 
			
		||||
 | 
			
		||||
class Redirect implements IRedirect
 | 
			
		||||
{
 | 
			
		||||
    private string $target;
 | 
			
		||||
 | 
			
		||||
    private int $type;
 | 
			
		||||
 | 
			
		||||
    public function __construct(string $target, int $type = IRedirect::TEMPORARY)
 | 
			
		||||
    {
 | 
			
		||||
        $this->target = $target;
 | 
			
		||||
        $this->type = $type;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getUrl(): string
 | 
			
		||||
    {
 | 
			
		||||
        if (preg_match('/^http(s)?/', $this->target) === 1) {
 | 
			
		||||
            $link = $this->target;
 | 
			
		||||
        } else {
 | 
			
		||||
            $link = \Container::$request->getBase() . '/' . $this->target;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $link;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getHttpCode(): int
 | 
			
		||||
    {
 | 
			
		||||
        switch ($this->type) {
 | 
			
		||||
            case IRedirect::PERMANENT:
 | 
			
		||||
                return 301;
 | 
			
		||||
 | 
			
		||||
            case IRedirect::TEMPORARY:
 | 
			
		||||
                return 302;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                return 302;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,73 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Routing;
 | 
			
		||||
 | 
			
		||||
class Route
 | 
			
		||||
{
 | 
			
		||||
    private string $id;
 | 
			
		||||
 | 
			
		||||
    private array $pattern;
 | 
			
		||||
 | 
			
		||||
    private array $handler;
 | 
			
		||||
 | 
			
		||||
    public function __construct(string $id, array $pattern, array $handler)
 | 
			
		||||
    {
 | 
			
		||||
        $this->id = $id;
 | 
			
		||||
        $this->pattern = $pattern;
 | 
			
		||||
        $this->handler = $handler;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getId(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getHandler(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->handler;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function generateLink(array $parameters = []): string
 | 
			
		||||
    {
 | 
			
		||||
        $link = [];
 | 
			
		||||
 | 
			
		||||
        foreach ($this->pattern as $fragment) {
 | 
			
		||||
            if (preg_match('/^{(\\w+)(\\?)?}$/', $fragment, $matches) === 1) {
 | 
			
		||||
                if (isset($parameters[$matches[1]])) {
 | 
			
		||||
                    $link[] = $parameters[$matches[1]];
 | 
			
		||||
                    unset($parameters[$matches[1]]);
 | 
			
		||||
                } elseif (!isset($matches[2])) {//TODO: why? parameter not found but not optional
 | 
			
		||||
                    $link[] = $fragment;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                $link[] = $fragment;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $queryParams = [];
 | 
			
		||||
        foreach ($parameters as $key => $value) {
 | 
			
		||||
            if ($value === null) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $queryParams[$key] = $value;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $query = count($queryParams) > 0 ? '?' . http_build_query($queryParams) : '';
 | 
			
		||||
 | 
			
		||||
        return implode('/', $link) . $query;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testAgainst(array $path): ?array
 | 
			
		||||
    {
 | 
			
		||||
        $parameters = [];
 | 
			
		||||
 | 
			
		||||
        foreach ($path as $i => $fragment) {
 | 
			
		||||
            if (preg_match('/^{(\\w+)(?:\\?)?}$/', $this->pattern[$i], $matches) === 1) {
 | 
			
		||||
                $parameters[$matches[1]] = $fragment;
 | 
			
		||||
            } elseif ($fragment != $this->pattern[$i]) {
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $parameters;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,88 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Routing;
 | 
			
		||||
 | 
			
		||||
use Closure;
 | 
			
		||||
 | 
			
		||||
class RouteCollection
 | 
			
		||||
{
 | 
			
		||||
    private array $routes = [];
 | 
			
		||||
 | 
			
		||||
    private array $searchTable = [
 | 
			
		||||
        'get' => [],
 | 
			
		||||
        'post' => []
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    private array $groupStack = [];
 | 
			
		||||
 | 
			
		||||
    public function get(string $id, string $pattern, array $handler): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->addRoute('get', $id, $pattern, $handler);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function post(string $id, string $pattern, array $handler): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->addRoute('post', $id, $pattern, $handler);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function group(string $pattern, Closure $group): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->groupStack[] = $pattern;
 | 
			
		||||
 | 
			
		||||
        $group($this);
 | 
			
		||||
 | 
			
		||||
        array_pop($this->groupStack);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getRoute(string $id): ?Route
 | 
			
		||||
    {
 | 
			
		||||
        if (!isset($this->routes[$id])) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->routes[$id];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function match(string $method, array $uri): ?array
 | 
			
		||||
    {
 | 
			
		||||
        $groupNumber = count($uri);
 | 
			
		||||
 | 
			
		||||
        // response to HEAD request with the GET content
 | 
			
		||||
        if ($method === 'head') {
 | 
			
		||||
            $method = 'get';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!isset($this->searchTable[$method][$groupNumber])) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach ($this->searchTable[$method][$groupNumber] as $route) {
 | 
			
		||||
            if (($parameters = $route->testAgainst($uri)) !== null) {
 | 
			
		||||
                return [$route, $parameters];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function addRoute(string $method, string $id, string $pattern, array $handler): void
 | 
			
		||||
    {
 | 
			
		||||
        if (isset($this->routes[$id])) {
 | 
			
		||||
            throw new \Exception('Route already exists: ' . $id);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $pattern = array_merge($this->groupStack, $pattern === '' ? [] : explode('/', $pattern));
 | 
			
		||||
        $route = new Route($id, $pattern, $handler);
 | 
			
		||||
 | 
			
		||||
        $groupNumber = count($pattern);
 | 
			
		||||
 | 
			
		||||
        $this->searchTable[$method][$groupNumber][] = $route;
 | 
			
		||||
 | 
			
		||||
        while (preg_match('/^{\\w+\\?}$/', end($pattern)) === 1) {
 | 
			
		||||
            $groupNumber--;
 | 
			
		||||
            array_pop($pattern);
 | 
			
		||||
 | 
			
		||||
            $this->searchTable[$method][$groupNumber][] = $route;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->routes[$id] = $route;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,104 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Session;
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use MapGuesser\Database\Query\Modify;
 | 
			
		||||
use MapGuesser\Database\Query\Select;
 | 
			
		||||
use MapGuesser\Interfaces\Database\IResultSet;
 | 
			
		||||
use MapGuesser\Interfaces\Session\ISessionHandler;
 | 
			
		||||
 | 
			
		||||
class DatabaseSessionHandler implements ISessionHandler
 | 
			
		||||
{
 | 
			
		||||
    private bool $exists = false;
 | 
			
		||||
 | 
			
		||||
    private bool $written = false;
 | 
			
		||||
 | 
			
		||||
    public function open($savePath, $sessionName): bool
 | 
			
		||||
    {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function close(): bool
 | 
			
		||||
    {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function read($id): string
 | 
			
		||||
    {
 | 
			
		||||
        $select = new Select(\Container::$dbConnection, 'sessions');
 | 
			
		||||
        $select->columns(['data']);
 | 
			
		||||
        $select->whereId(substr($id, 0, 32));
 | 
			
		||||
 | 
			
		||||
        $result = $select->execute()->fetch(IResultSet::FETCH_ASSOC);
 | 
			
		||||
 | 
			
		||||
        if ($result === null) {
 | 
			
		||||
            return '';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->exists = true;
 | 
			
		||||
 | 
			
		||||
        return $result['data'];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function write($id, $data): bool
 | 
			
		||||
    {
 | 
			
		||||
        $modify = new Modify(\Container::$dbConnection, 'sessions');
 | 
			
		||||
 | 
			
		||||
        if ($this->exists) {
 | 
			
		||||
            $modify->setId(substr($id, 0, 32));
 | 
			
		||||
        } else {
 | 
			
		||||
            $modify->setExternalId(substr($id, 0, 32));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $modify->set('data', $data);
 | 
			
		||||
        $modify->set('updated', (new DateTime())->format('Y-m-d H:i:s'));
 | 
			
		||||
        $modify->save();
 | 
			
		||||
 | 
			
		||||
        $this->written = true;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function destroy($id): bool
 | 
			
		||||
    {
 | 
			
		||||
        $modify = new Modify(\Container::$dbConnection, 'sessions');
 | 
			
		||||
        $modify->setId(substr($id, 0, 32));
 | 
			
		||||
        $modify->delete();
 | 
			
		||||
 | 
			
		||||
        $this->exists = false;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function gc($maxlifetime): bool
 | 
			
		||||
    {
 | 
			
		||||
        // empty on purpose
 | 
			
		||||
        // old sessions are deleted by MaintainDatabaseCommand
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function create_sid(): string
 | 
			
		||||
    {
 | 
			
		||||
        return bin2hex(random_bytes(16));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function validateId($id): bool
 | 
			
		||||
    {
 | 
			
		||||
        return preg_match('/^[a-f0-9]{32}$/', $id) === 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function updateTimestamp($id, $data): bool
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->written) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $modify = new Modify(\Container::$dbConnection, 'sessions');
 | 
			
		||||
 | 
			
		||||
        $modify->setId(substr($id, 0, 32));
 | 
			
		||||
        $modify->set('updated', (new DateTime())->format('Y-m-d H:i:s'));
 | 
			
		||||
        $modify->save();
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,19 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Util;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Http\Request;
 | 
			
		||||
 | 
			
		||||
class CaptchaValidator
 | 
			
		||||
{
 | 
			
		||||
    public function validate(string $response)
 | 
			
		||||
    {
 | 
			
		||||
        $request = new Request('https://www.google.com/recaptcha/api/siteverify', Request::HTTP_GET);
 | 
			
		||||
        $request->setQuery([
 | 
			
		||||
            'secret' => $_ENV['RECAPTCHA_SECRET'],
 | 
			
		||||
            'response' => $response
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        $response = $request->send();
 | 
			
		||||
 | 
			
		||||
        return json_decode($response->getBody(), true);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,33 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Util;
 | 
			
		||||
 | 
			
		||||
class JwtParser
 | 
			
		||||
{
 | 
			
		||||
    private array $token;
 | 
			
		||||
 | 
			
		||||
    public function __construct(?string $token = null)
 | 
			
		||||
    {
 | 
			
		||||
        if ($token !== null) {
 | 
			
		||||
            $this->setToken($token);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setToken(string $token): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->token = explode('.', str_replace(['_', '-'], ['/', '+'], $token));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getHeader(): array
 | 
			
		||||
    {
 | 
			
		||||
        return json_decode(base64_decode($this->token[0]), true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getPayload(): array
 | 
			
		||||
    {
 | 
			
		||||
        return json_decode(base64_decode($this->token[1]), true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getSignature(): string
 | 
			
		||||
    {
 | 
			
		||||
        return base64_decode($this->token[2]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,152 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\View;
 | 
			
		||||
 | 
			
		||||
class Linker
 | 
			
		||||
{
 | 
			
		||||
    const INLINE_ASSET_LIMIT = 2000;
 | 
			
		||||
 | 
			
		||||
    private string $view;
 | 
			
		||||
 | 
			
		||||
    public function __construct(string $view)
 | 
			
		||||
    {
 | 
			
		||||
        $this->view = $view;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function generate(): void
 | 
			
		||||
    {
 | 
			
		||||
        $input = ROOT . '/views/' . $this->view . '.php';
 | 
			
		||||
 | 
			
		||||
        $temporaryFiles = [];
 | 
			
		||||
        $sections = ['externalCss' => '', 'inlineCss' => '', 'externalJs' => '', 'inlineJs' => ''];
 | 
			
		||||
        $extra = ['', ''];
 | 
			
		||||
 | 
			
		||||
        do {
 | 
			
		||||
            $parser = new Parser($input);
 | 
			
		||||
            $fragment = $parser->parse();
 | 
			
		||||
 | 
			
		||||
            $extends = $fragment->getExtends();
 | 
			
		||||
 | 
			
		||||
            $this->generateAssets($fragment, $sections);
 | 
			
		||||
 | 
			
		||||
            $sections = array_merge($sections, $fragment->getSections()); //TODO: detect if section defined multiple times
 | 
			
		||||
            $extra[0] = $fragment->getExtra()[0] . $extra[0];
 | 
			
		||||
            $extra[1] = $extra[1] . $fragment->getExtra()[1];
 | 
			
		||||
 | 
			
		||||
            if ($extends === null) {
 | 
			
		||||
                $this->writeFinal($extra, $input, ROOT . '/cache/views/' . $this->view . '.php');
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $tmpFile = tempnam(sys_get_temp_dir(), 'mapg-view-');
 | 
			
		||||
            $temporaryFiles[] = $tmpFile;
 | 
			
		||||
 | 
			
		||||
            $this->extendTemplate($sections, ROOT . '/views/' . $extends . '.php', $tmpFile);
 | 
			
		||||
 | 
			
		||||
            $input = $tmpFile;
 | 
			
		||||
        } while (true);
 | 
			
		||||
 | 
			
		||||
        foreach ($temporaryFiles as $tmpFile) {
 | 
			
		||||
            unlink($tmpFile);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function extendTemplate(array $sections, string $file, string $output): void
 | 
			
		||||
    {
 | 
			
		||||
        $inputFileHandle = fopen($file, 'r');
 | 
			
		||||
        if (!$inputFileHandle) {
 | 
			
		||||
            throw new \Exception('Cannot open file ' . $file);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $outputFileHandle = fopen($output, 'w');
 | 
			
		||||
        if (!$outputFileHandle) {
 | 
			
		||||
            throw new \Exception('Cannot open file ' . $output . 'for writing.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $lineNumber = 0;
 | 
			
		||||
        while (($line = fgets($inputFileHandle)) !== false) {
 | 
			
		||||
            ++$lineNumber;
 | 
			
		||||
 | 
			
		||||
            if (preg_match('/^\s*@yields\(\'([\w\/]+)\'\)\s*$/', $line, $matches) === 1) {
 | 
			
		||||
                if (isset($sections[$matches[1]])) {
 | 
			
		||||
                    fwrite($outputFileHandle, $sections[$matches[1]]);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                fwrite($outputFileHandle, $line);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fclose($inputFileHandle);
 | 
			
		||||
        fclose($outputFileHandle);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function writeFinal(array $extra, string $file, string $output): void
 | 
			
		||||
    {
 | 
			
		||||
        $dirname = pathinfo($output, PATHINFO_DIRNAME);
 | 
			
		||||
        if (!is_dir($dirname)) {
 | 
			
		||||
            mkdir($dirname, 0755, true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $inputFileHandle = fopen($file, 'r');
 | 
			
		||||
        if (!$inputFileHandle) {
 | 
			
		||||
            throw new \Exception('Cannot open file ' . $file);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $outputFileHandle = fopen($output, 'w');
 | 
			
		||||
        if (!$outputFileHandle) {
 | 
			
		||||
            throw new \Exception('Cannot open file ' . $output . 'for writing.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fwrite($outputFileHandle, $extra[0]);
 | 
			
		||||
        while (($line = fgets($inputFileHandle)) !== false) {
 | 
			
		||||
            fwrite($outputFileHandle, $line);
 | 
			
		||||
        }
 | 
			
		||||
        fwrite($outputFileHandle, $extra[1]);
 | 
			
		||||
 | 
			
		||||
        fclose($inputFileHandle);
 | 
			
		||||
        fclose($outputFileHandle);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function generateAssets(ParsedFragment $fragment, array &$sections): void
 | 
			
		||||
    {
 | 
			
		||||
        foreach ($fragment->getCss() as $cssFile) {
 | 
			
		||||
            $asset = $this->parseAsset($cssFile);
 | 
			
		||||
            if (isset($asset['code'])) {
 | 
			
		||||
                $sections['inlineCss'] .= '<style>' . PHP_EOL;
 | 
			
		||||
                $sections['inlineCss'] .= $asset['code'];
 | 
			
		||||
                $sections['inlineCss'] .= '</style>' . PHP_EOL;
 | 
			
		||||
            } elseif (isset($asset['file'])) {
 | 
			
		||||
                $sections['externalCss'] .= '<link href="' . $asset['file'] . '" rel="stylesheet">' . PHP_EOL;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach ($fragment->getJs() as $jsFile) {
 | 
			
		||||
            $asset = $this->parseAsset($jsFile);
 | 
			
		||||
            if (isset($asset['code'])) {
 | 
			
		||||
                $sections['inlineJs'] .= '<script>' . PHP_EOL;
 | 
			
		||||
                $sections['inlineJs'] .= $asset['code'];
 | 
			
		||||
                $sections['inlineJs'] .= '</script>' . PHP_EOL;
 | 
			
		||||
            } elseif (isset($asset['file'])) {
 | 
			
		||||
                $sections['externalJs'] .= '<script src="' . $asset['file'] . '"></script>' . PHP_EOL;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function parseAsset(string $asset): array
 | 
			
		||||
    {
 | 
			
		||||
        $output = [];
 | 
			
		||||
 | 
			
		||||
        if (preg_match('/^[\w\/\.]+$/', $asset) === 1) {
 | 
			
		||||
            if (
 | 
			
		||||
                empty($_ENV['DEV']) &&
 | 
			
		||||
                filesize(ROOT . '/public/static/' . $asset) < self::INLINE_ASSET_LIMIT
 | 
			
		||||
            ) {
 | 
			
		||||
                $output['code'] = file_get_contents(ROOT . '/public/static/' . $asset);
 | 
			
		||||
            } else {
 | 
			
		||||
                $output['file'] = '<?= $_ENV[\'STATIC_ROOT\'] ?>/' . $asset . '?rev=<?= REVISION ?>';
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            $output['file'] = $asset;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $output;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,48 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\View;
 | 
			
		||||
 | 
			
		||||
class ParsedFragment
 | 
			
		||||
{
 | 
			
		||||
    private ?string $extends;
 | 
			
		||||
 | 
			
		||||
    private array $css;
 | 
			
		||||
 | 
			
		||||
    private array $js;
 | 
			
		||||
 | 
			
		||||
    private array $sections;
 | 
			
		||||
 | 
			
		||||
    private array $extra;
 | 
			
		||||
 | 
			
		||||
    public function __construct(?string $extends, array $css, array $js, array $sections, array $extra)
 | 
			
		||||
    {
 | 
			
		||||
        $this->extends = $extends;
 | 
			
		||||
        $this->css = $css;
 | 
			
		||||
        $this->js = $js;
 | 
			
		||||
        $this->sections = $sections;
 | 
			
		||||
        $this->extra = $extra;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getExtends(): ?string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->extends;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getCss(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->css;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getJs(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->js;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getSections(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->sections;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getExtra(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->extra;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,159 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\View;
 | 
			
		||||
 | 
			
		||||
class Parser
 | 
			
		||||
{
 | 
			
		||||
    private string $file;
 | 
			
		||||
 | 
			
		||||
    public function __construct(string $file)
 | 
			
		||||
    {
 | 
			
		||||
        $this->file = $file;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function parse(): ParsedFragment
 | 
			
		||||
    {
 | 
			
		||||
        $sectionOpen = null;
 | 
			
		||||
        $extraOpen = false;
 | 
			
		||||
 | 
			
		||||
        $extends = null;
 | 
			
		||||
        $js = [];
 | 
			
		||||
        $css = [];
 | 
			
		||||
        $sections = [];
 | 
			
		||||
        $extra = ['', ''];
 | 
			
		||||
 | 
			
		||||
        $fileHandle = fopen($this->file, 'r');
 | 
			
		||||
        if (!$fileHandle) {
 | 
			
		||||
            throw new \Exception('Cannot open file ' . $this->file);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $lineNumber = 0;
 | 
			
		||||
        while (($line = fgets($fileHandle)) !== false) {
 | 
			
		||||
            ++$lineNumber;
 | 
			
		||||
 | 
			
		||||
            if (($cssMatched = $this->matchCss($line)) !== null) {
 | 
			
		||||
                $css[] = $cssMatched;
 | 
			
		||||
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (($jsMatched = $this->matchJs($line)) !== null) {
 | 
			
		||||
                $js[] = $jsMatched;
 | 
			
		||||
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (($extendsMatched = $this->matchExtends($line)) !== null) {
 | 
			
		||||
                if ($extends !== null) {
 | 
			
		||||
                    throw new \Exception('Error in file ' . $this->file . ' in line ' . $lineNumber . ' - There is already an \'@extends\' declared.');
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $extends = $extendsMatched;
 | 
			
		||||
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (($sectionMatched = $this->matchSection($line)) !== null) {
 | 
			
		||||
                if ($extends === null) {
 | 
			
		||||
                    throw new \Exception('Error in file ' . $this->file . ' in line ' . $lineNumber . ' - \'@section\' has no meaning if view extends nothing.');
 | 
			
		||||
                }
 | 
			
		||||
                if ($sectionOpen !== null) {
 | 
			
		||||
                    throw new \Exception('Parse error in file ' . $this->file . ' in line ' . $lineNumber . ' - A \'@section\' is already open (no \'@endsection\' found).');
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $sectionOpen = $sectionMatched;
 | 
			
		||||
                $sections[$sectionOpen] = '';
 | 
			
		||||
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($this->matchEndSection($line)) {
 | 
			
		||||
                if ($sectionOpen === null) {
 | 
			
		||||
                    throw new \Exception('Parse error in file ' . $this->file . ' in line ' . $lineNumber . ' - Cannot end section until no \'@section\' is open.');
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $sectionOpen = null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($this->matchExtra($line)) {
 | 
			
		||||
                if ($extraOpen) {
 | 
			
		||||
                    throw new \Exception('Parse error in file ' . $this->file . ' in line ' . $lineNumber . ' - An \'@extra\' is already open (no \'@endextra\' found).');
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $extraOpen = true;
 | 
			
		||||
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($this->matchEndExtra($line)) {
 | 
			
		||||
                if (!$extraOpen) {
 | 
			
		||||
                    throw new \Exception('Parse error in file ' . $this->file . ' in line ' . $lineNumber . ' - Cannot end extra until no \'@extra\' is open.');
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $extraOpen = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($sectionOpen !== null) {
 | 
			
		||||
                $sections[$sectionOpen] .= $line;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($extraOpen) {
 | 
			
		||||
                $offset = $extends === null ? 0 : 1;
 | 
			
		||||
                $extra[$offset] .= $line;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fclose($fileHandle);
 | 
			
		||||
 | 
			
		||||
        return new ParsedFragment($extends, $css, $js, $sections, $extra);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function matchCss(string $line): ?string
 | 
			
		||||
    {
 | 
			
		||||
        if (preg_match('/^\s*@css\((.*)\)\s*$/', $line, $matches) === 1) {
 | 
			
		||||
            return $matches[1];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function matchJs(string $line): ?string
 | 
			
		||||
    {
 | 
			
		||||
        if (preg_match('/^\s*@js\((.*)\)\s*$/', $line, $matches) === 1) {
 | 
			
		||||
            return $matches[1];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function matchExtends(string $line): ?string
 | 
			
		||||
    {
 | 
			
		||||
        if (preg_match('/^\s*@extends\(([\w\/]+)\)\s*$/', $line, $matches) === 1) {
 | 
			
		||||
            return $matches[1];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function matchSection(string $line): ?string
 | 
			
		||||
    {
 | 
			
		||||
        if (preg_match('/^\s*@section\((\w+)\)\s*$/', $line, $matches) === 1) {
 | 
			
		||||
            return $matches[1];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function matchEndSection(string $line): bool
 | 
			
		||||
    {
 | 
			
		||||
        return preg_match('/^\s*@endsection(?:\(\))?\s*$/', $line) === 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function matchExtra(string $line): bool
 | 
			
		||||
    {
 | 
			
		||||
        return preg_match('/^\s*@extra(?:\(\))?\s*$/', $line) === 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function matchEndExtra(string $line): bool
 | 
			
		||||
    {
 | 
			
		||||
        return preg_match('/^\s*@endextra(?:\(\))?\s*$/', $line) === 1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,86 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace MapGuesser\Tests\OAuth;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Interfaces\Http\IRequest;
 | 
			
		||||
use MapGuesser\Interfaces\Http\IResponse;
 | 
			
		||||
use MapGuesser\OAuth\GoogleOAuth;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
final class GoogleOAuthTest extends TestCase
 | 
			
		||||
{
 | 
			
		||||
    public function testCanCreateDialogUrl(): void
 | 
			
		||||
    {
 | 
			
		||||
        $_ENV['GOOGLE_OAUTH_CLIENT_ID'] = 'xyz';
 | 
			
		||||
        $state = 'random_state_string';
 | 
			
		||||
        $nonce = 'random_nonce_string';
 | 
			
		||||
        $redirectUrl = 'http://example.com/oauth';
 | 
			
		||||
 | 
			
		||||
        $requestMock = $this->getMockBuilder(IRequest::class)
 | 
			
		||||
            ->setMethods(['setUrl', 'setMethod', 'setQuery', 'setHeaders', 'send'])
 | 
			
		||||
            ->getMock();
 | 
			
		||||
        $googleOAuth = new GoogleOAuth($requestMock);
 | 
			
		||||
 | 
			
		||||
        $dialogUrl = $googleOAuth->getDialogUrl($state, $redirectUrl, $nonce);
 | 
			
		||||
        $dialogUrlParsed = explode('?', $dialogUrl);
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals('https://accounts.google.com/o/oauth2/v2/auth', $dialogUrlParsed[0]);
 | 
			
		||||
 | 
			
		||||
        parse_str($dialogUrlParsed[1], $dialogUrlQueryParams);
 | 
			
		||||
 | 
			
		||||
        $expectedQueryParams = [
 | 
			
		||||
            'response_type' => 'code',
 | 
			
		||||
            'client_id' => $_ENV['GOOGLE_OAUTH_CLIENT_ID'],
 | 
			
		||||
            'scope' => 'openid email',
 | 
			
		||||
            'redirect_uri' => $redirectUrl,
 | 
			
		||||
            'state' => $state,
 | 
			
		||||
            'nonce' => $nonce,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals($expectedQueryParams, $dialogUrlQueryParams);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testCanRequestToken(): void
 | 
			
		||||
    {
 | 
			
		||||
        $_ENV['GOOGLE_OAUTH_CLIENT_ID'] = 'abc';
 | 
			
		||||
        $_ENV['GOOGLE_OAUTH_CLIENT_SECRET'] = 'xxx';
 | 
			
		||||
        $code = 'code_from_google';
 | 
			
		||||
        $redirectUrl = 'http://example.com/oauth';
 | 
			
		||||
 | 
			
		||||
        $requestMock = $this->getMockBuilder(IRequest::class)
 | 
			
		||||
            ->setMethods(['setUrl', 'setMethod', 'setQuery', 'setHeaders', 'send'])
 | 
			
		||||
            ->getMock();
 | 
			
		||||
        $responseMock = $this->getMockBuilder(IResponse::class)
 | 
			
		||||
            ->setMethods(['getBody', 'getHeaders'])
 | 
			
		||||
            ->getMock();
 | 
			
		||||
        $googleOAuth = new GoogleOAuth($requestMock);
 | 
			
		||||
 | 
			
		||||
        $expectedQueryParams = [
 | 
			
		||||
            'code' => $code,
 | 
			
		||||
            'client_id' => $_ENV['GOOGLE_OAUTH_CLIENT_ID'],
 | 
			
		||||
            'client_secret' => $_ENV['GOOGLE_OAUTH_CLIENT_SECRET'],
 | 
			
		||||
            'redirect_uri' => $redirectUrl,
 | 
			
		||||
            'grant_type' => 'authorization_code',
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $requestMock->expects($this->once())
 | 
			
		||||
            ->method('setUrl')
 | 
			
		||||
            ->with($this->equalTo('https://oauth2.googleapis.com/token'));
 | 
			
		||||
        $requestMock->expects($this->once())
 | 
			
		||||
            ->method('setMethod')
 | 
			
		||||
            ->with($this->equalTo(IRequest::HTTP_POST));
 | 
			
		||||
        $requestMock->expects($this->once())
 | 
			
		||||
            ->method('setQuery')
 | 
			
		||||
            ->with($this->equalTo($expectedQueryParams));
 | 
			
		||||
        $requestMock->expects($this->once())
 | 
			
		||||
            ->method('send')
 | 
			
		||||
            ->will($this->returnValue($responseMock));
 | 
			
		||||
        $responseMock->expects($this->once())
 | 
			
		||||
            ->method('getBody')
 | 
			
		||||
            ->will($this->returnValue('{"test":"json"}'));
 | 
			
		||||
 | 
			
		||||
        $token = $googleOAuth->getToken($code, $redirectUrl);
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals(['test' => 'json'], $token);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,93 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Tests\Util;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\PersistentData\Model\Model;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
class OtherModel
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class DummyModel extends Model
 | 
			
		||||
{
 | 
			
		||||
    protected static string $table = 'test_table';
 | 
			
		||||
 | 
			
		||||
    protected static array $fields = ['name', 'valid'];
 | 
			
		||||
 | 
			
		||||
    protected static array $relations = ['other_model' => OtherModel::class];
 | 
			
		||||
 | 
			
		||||
    private string $name;
 | 
			
		||||
 | 
			
		||||
    private bool $valid;
 | 
			
		||||
 | 
			
		||||
    public function setName(string $name): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->name = $name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setValid(bool $valid): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->valid = $valid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getName(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getValid(): bool
 | 
			
		||||
    {
 | 
			
		||||
        return $this->valid;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
final class ModelTest extends TestCase
 | 
			
		||||
{
 | 
			
		||||
    public function testCanReturnTable(): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->assertEquals('test_table', DummyModel::getTable());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testCanReturnFields(): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->assertEquals(['id', 'name', 'valid'], DummyModel::getFields());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testCanReturnRelations(): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->assertEquals(['other_model' => OtherModel::class], DummyModel::getRelations());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testCanBeConvertedToArray(): void
 | 
			
		||||
    {
 | 
			
		||||
        $model = new DummyModel();
 | 
			
		||||
        $model->setId(123);
 | 
			
		||||
        $model->setName('John');
 | 
			
		||||
        $model->setValid(true);
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals([
 | 
			
		||||
            'id' => 123,
 | 
			
		||||
            'name' => 'John',
 | 
			
		||||
            'valid' => true
 | 
			
		||||
        ], $model->toArray());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testCanSaveAndResetSnapshot(): void
 | 
			
		||||
    {
 | 
			
		||||
        $model = new DummyModel();
 | 
			
		||||
        $model->setId(123);
 | 
			
		||||
        $model->setName('John');
 | 
			
		||||
        $model->setValid(true);
 | 
			
		||||
 | 
			
		||||
        $model->saveSnapshot();
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals([
 | 
			
		||||
            'id' => 123,
 | 
			
		||||
            'name' => 'John',
 | 
			
		||||
            'valid' => true
 | 
			
		||||
        ], $model->getSnapshot());
 | 
			
		||||
 | 
			
		||||
        $model->resetSnapshot();
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals([], $model->getSnapshot());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,51 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Tests\Util;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Util\JwtParser;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
final class JwtParserTest extends TestCase
 | 
			
		||||
{
 | 
			
		||||
    private JwtParser $jwtParser;
 | 
			
		||||
 | 
			
		||||
    protected function setUp(): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->jwtParser = new JwtParser(
 | 
			
		||||
            'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testSettingTokenIsTheSameAsCreatingWithToken(): void
 | 
			
		||||
    {
 | 
			
		||||
        $jwtParser2 = new JwtParser();
 | 
			
		||||
        $jwtParser2->setToken(
 | 
			
		||||
            'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals($this->jwtParser, $jwtParser2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testCanParseTokenHeader(): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->assertEquals([
 | 
			
		||||
            'alg' => 'HS256',
 | 
			
		||||
            'typ' => 'JWT'
 | 
			
		||||
        ], $this->jwtParser->getHeader());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testCanParseTokenPayload(): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->assertEquals([
 | 
			
		||||
            'sub' => '1234567890',
 | 
			
		||||
            'name' => 'John Doe',
 | 
			
		||||
            'iat' => 1516239022
 | 
			
		||||
        ], $this->jwtParser->getPayload());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testCanParseTokenSignature(): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->assertEquals(
 | 
			
		||||
            '49f94ac7044948c78a285d904f87f0a4c7897f7e8f3a4eb2255fda750b2cc397',
 | 
			
		||||
            bin2hex($this->jwtParser->getSignature())
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,144 +0,0 @@
 | 
			
		||||
<?php namespace MapGuesser\Tests\View\Parser;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\View\ParsedFragment;
 | 
			
		||||
use MapGuesser\View\Parser;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
final class ParserTest extends TestCase
 | 
			
		||||
{
 | 
			
		||||
    const TEST_VIEWS_PATH = __DIR__ . '/../../views/tests';
 | 
			
		||||
 | 
			
		||||
    public function testCanParseViewWithoutExtends(): void
 | 
			
		||||
    {
 | 
			
		||||
        $file = realpath(self::TEST_VIEWS_PATH . '/view_without_extends.php');
 | 
			
		||||
 | 
			
		||||
        $parser = new Parser($file);
 | 
			
		||||
        $fragment = $parser->parse();
 | 
			
		||||
 | 
			
		||||
        $expected = new ParsedFragment(
 | 
			
		||||
            null,
 | 
			
		||||
            [],
 | 
			
		||||
            [],
 | 
			
		||||
            [],
 | 
			
		||||
            ['', '']
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals($expected, $fragment);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testCanParseViewWithAssets(): void
 | 
			
		||||
    {
 | 
			
		||||
        $file = realpath(self::TEST_VIEWS_PATH . '/view_with_assets.php');
 | 
			
		||||
 | 
			
		||||
        $parser = new Parser($file);
 | 
			
		||||
        $fragment = $parser->parse();
 | 
			
		||||
 | 
			
		||||
        $expected = new ParsedFragment(
 | 
			
		||||
            null,
 | 
			
		||||
            [
 | 
			
		||||
                'test.css'
 | 
			
		||||
            ],
 | 
			
		||||
            [
 | 
			
		||||
                'test.js',
 | 
			
		||||
                'test_<?= $some[\'expression\'] ?>'
 | 
			
		||||
            ],
 | 
			
		||||
            [],
 | 
			
		||||
            ['', '']
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals($expected, $fragment);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testCanParseViewWithExtends(): void
 | 
			
		||||
    {
 | 
			
		||||
        $file = realpath(self::TEST_VIEWS_PATH . '/view_with_extends.php');
 | 
			
		||||
 | 
			
		||||
        $parser = new Parser($file);
 | 
			
		||||
        $fragment = $parser->parse();
 | 
			
		||||
 | 
			
		||||
        $expected = new ParsedFragment(
 | 
			
		||||
            'parent',
 | 
			
		||||
            [],
 | 
			
		||||
            [],
 | 
			
		||||
            [
 | 
			
		||||
                'section1' => '<div>Test HTML with @extends</div>' . "\n"
 | 
			
		||||
            ],
 | 
			
		||||
            ['', '']
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals($expected, $fragment);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testCanParseComplexView(): void
 | 
			
		||||
    {
 | 
			
		||||
        $file = realpath(self::TEST_VIEWS_PATH . '/view_complex.php');
 | 
			
		||||
 | 
			
		||||
        $parser = new Parser($file);
 | 
			
		||||
        $fragment = $parser->parse();
 | 
			
		||||
 | 
			
		||||
        $expected = new ParsedFragment(
 | 
			
		||||
            'parent',
 | 
			
		||||
            [
 | 
			
		||||
                'test1.css'
 | 
			
		||||
            ],
 | 
			
		||||
            [
 | 
			
		||||
                'test1.js',
 | 
			
		||||
                'test2_<?= $some[\'expression\'] ?>'
 | 
			
		||||
            ],
 | 
			
		||||
            [
 | 
			
		||||
                'section1' => '<div>Test HTML with @extends - section 1</div>' . "\n",
 | 
			
		||||
                'section2' => '<div>Test HTML with @extends - section 2</div>' . "\n"
 | 
			
		||||
            ],
 | 
			
		||||
            [
 | 
			
		||||
                '<?php phpinfo() ?>' . "\n",
 | 
			
		||||
                '<?php $a = \'test_string\' ?>' . "\n" . 'EXTRA' . "\n",
 | 
			
		||||
            ]
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals($expected, $fragment);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testFailsIfMultipleExtendsGiven(): void
 | 
			
		||||
    {
 | 
			
		||||
        $file = realpath(self::TEST_VIEWS_PATH . '/view_invalid_multiple_extends.php');
 | 
			
		||||
 | 
			
		||||
        $parser = new Parser($file);
 | 
			
		||||
 | 
			
		||||
        $this->expectExceptionMessage('Error in file ' . $file . ' in line 3 - There is already an \'@extends\' declared.');
 | 
			
		||||
 | 
			
		||||
        $parser->parse();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testFailsIfSectionWithoutExtendsGiven(): void
 | 
			
		||||
    {
 | 
			
		||||
        $file = realpath(self::TEST_VIEWS_PATH . '/view_invalid_section_without_extends.php');
 | 
			
		||||
 | 
			
		||||
        $parser = new Parser($file);
 | 
			
		||||
 | 
			
		||||
        $this->expectExceptionMessage('Error in file ' . $file . ' in line 1 - \'@section\' has no meaning if view extends nothing.');
 | 
			
		||||
 | 
			
		||||
        $parser->parse();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testFailsIfOpeningSectionBeforePreviousClosed(): void
 | 
			
		||||
    {
 | 
			
		||||
        $file = realpath(self::TEST_VIEWS_PATH . '/view_invalid_multiple_sections_open.php');
 | 
			
		||||
 | 
			
		||||
        $parser = new Parser($file);
 | 
			
		||||
 | 
			
		||||
        $this->expectExceptionMessage('Parse error in file ' . $file . ' in line 4 - A \'@section\' is already open (no \'@endsection\' found).');
 | 
			
		||||
 | 
			
		||||
        $parser->parse();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testFailsIfClosingSectionWhenNoSectionIsOpen(): void
 | 
			
		||||
    {
 | 
			
		||||
        $file = realpath(self::TEST_VIEWS_PATH . '/view_invalid_section_not_open.php');
 | 
			
		||||
 | 
			
		||||
        $parser = new Parser($file);
 | 
			
		||||
 | 
			
		||||
        $this->expectExceptionMessage('Parse error in file ' . $file . ' in line 4 - Cannot end section until no \'@section\' is open.');
 | 
			
		||||
 | 
			
		||||
        $parser->parse();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								web.php
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								web.php
									
									
									
									
									
								
							@ -10,17 +10,17 @@ if (!empty($_ENV['DEV'])) {
 | 
			
		||||
    ini_set('display_errors', '0');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Container::$routeCollection = new MapGuesser\Routing\RouteCollection();
 | 
			
		||||
Container::$routeCollection = new SokoWeb\Routing\RouteCollection();
 | 
			
		||||
 | 
			
		||||
Container::$routeCollection->get('index', '', [MapGuesser\Controller\MapsController::class, 'getMaps']);
 | 
			
		||||
Container::$routeCollection->get('startSession', 'startSession.json', [MapGuesser\Controller\HomeController::class, 'startSession']);
 | 
			
		||||
Container::$routeCollection->group('login', function (MapGuesser\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
Container::$routeCollection->group('login', function (SokoWeb\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
    $routeCollection->get('login', '', [MapGuesser\Controller\LoginController::class, 'getLoginForm']);
 | 
			
		||||
    $routeCollection->post('login-action', '', [MapGuesser\Controller\LoginController::class, 'login']);
 | 
			
		||||
    $routeCollection->get('login-google', 'google', [MapGuesser\Controller\LoginController::class, 'getGoogleLoginRedirect']);
 | 
			
		||||
    $routeCollection->get('login-google-action', 'google/code', [MapGuesser\Controller\LoginController::class, 'loginWithGoogle']);
 | 
			
		||||
});
 | 
			
		||||
Container::$routeCollection->group('signup', function (MapGuesser\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
Container::$routeCollection->group('signup', function (SokoWeb\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
    $routeCollection->get('signup', '', [MapGuesser\Controller\LoginController::class, 'getSignupForm']);
 | 
			
		||||
    $routeCollection->post('signup-action', '', [MapGuesser\Controller\LoginController::class, 'signup']);
 | 
			
		||||
    $routeCollection->get('signup-google', 'google', [MapGuesser\Controller\LoginController::class, 'getSignupWithGoogleForm']);
 | 
			
		||||
@ -31,7 +31,7 @@ Container::$routeCollection->group('signup', function (MapGuesser\Routing\RouteC
 | 
			
		||||
    $routeCollection->get('signup.activate', 'activate/{token}', [MapGuesser\Controller\LoginController::class, 'activate']);
 | 
			
		||||
    $routeCollection->get('signup.cancel', 'cancel/{token}', [MapGuesser\Controller\LoginController::class, 'cancel']);
 | 
			
		||||
});
 | 
			
		||||
Container::$routeCollection->group('password', function (MapGuesser\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
Container::$routeCollection->group('password', function (SokoWeb\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
    $routeCollection->get('password-requestReset', 'requestReset', [MapGuesser\Controller\LoginController::class, 'getRequestPasswordResetForm']);
 | 
			
		||||
    $routeCollection->post('password-requestReset-action', 'requestReset', [MapGuesser\Controller\LoginController::class, 'requestPasswordReset']);
 | 
			
		||||
    $routeCollection->get('password-requestReset.success', 'requestReset/success', [MapGuesser\Controller\LoginController::class, 'getRequestPasswordResetSuccess']);
 | 
			
		||||
@ -39,7 +39,7 @@ Container::$routeCollection->group('password', function (MapGuesser\Routing\Rout
 | 
			
		||||
    $routeCollection->post('password-reset.action', 'reset/{token}', [MapGuesser\Controller\LoginController::class, 'resetPassword']);
 | 
			
		||||
});
 | 
			
		||||
Container::$routeCollection->get('logout', 'logout', [MapGuesser\Controller\LoginController::class, 'logout']);
 | 
			
		||||
Container::$routeCollection->group('account', function (MapGuesser\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
Container::$routeCollection->group('account', function (SokoWeb\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
    $routeCollection->get('account', '', [MapGuesser\Controller\UserController::class, 'getAccount']);
 | 
			
		||||
    $routeCollection->post('account-action', '', [MapGuesser\Controller\UserController::class, 'saveAccount']);
 | 
			
		||||
    $routeCollection->get('account.delete', 'delete', [MapGuesser\Controller\UserController::class, 'getDeleteAccount']);
 | 
			
		||||
@ -48,13 +48,13 @@ Container::$routeCollection->group('account', function (MapGuesser\Routing\Route
 | 
			
		||||
    $routeCollection->get('account.googleAuthenticate-action', 'googleAuthenticate/code', [MapGuesser\Controller\UserController::class, 'authenticateWithGoogle']);
 | 
			
		||||
});
 | 
			
		||||
//Container::$routeCollection->get('maps', 'maps', [MapGuesser\Controller\MapsController::class, 'getMaps']);
 | 
			
		||||
Container::$routeCollection->group('game', function (MapGuesser\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
Container::$routeCollection->group('game', function (SokoWeb\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
    $routeCollection->get('game', '{mapId}', [MapGuesser\Controller\GameController::class, 'getGame']);
 | 
			
		||||
    $routeCollection->post('game.prepare-json', '{mapId}/prepare.json', [MapGuesser\Controller\GameController::class, 'prepareGame']);
 | 
			
		||||
    $routeCollection->post('game.initialData-json', '{mapId}/initialData.json', [MapGuesser\Controller\GameFlowController::class, 'initialData']);
 | 
			
		||||
    $routeCollection->post('game.guess-json', '{mapId}/guess.json', [MapGuesser\Controller\GameFlowController::class, 'guess']);
 | 
			
		||||
});
 | 
			
		||||
Container::$routeCollection->group('multiGame', function (MapGuesser\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
Container::$routeCollection->group('multiGame', function (SokoWeb\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
    $routeCollection->get('multiGame.new', 'new/{mapId}', [MapGuesser\Controller\GameController::class, 'getNewMultiGame']);
 | 
			
		||||
    $routeCollection->get('multiGame', '{roomId}', [MapGuesser\Controller\GameController::class, 'getMultiGame']);
 | 
			
		||||
    $routeCollection->post('multiGame.prepare-json', '{roomId}/prepare.json', [MapGuesser\Controller\GameController::class, 'prepareMultiGame']);
 | 
			
		||||
@ -62,14 +62,14 @@ Container::$routeCollection->group('multiGame', function (MapGuesser\Routing\Rou
 | 
			
		||||
    $routeCollection->post('multiGame.nextRound-json', '{roomId}/nextRound.json', [MapGuesser\Controller\GameFlowController::class, 'multiNextRound']);
 | 
			
		||||
    $routeCollection->post('multiGame.guess-json', '{roomId}/guess.json', [MapGuesser\Controller\GameFlowController::class, 'multiGuess']);
 | 
			
		||||
});
 | 
			
		||||
Container::$routeCollection->group('challenge', function (MapGuesser\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
Container::$routeCollection->group('challenge', function (SokoWeb\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
    $routeCollection->post('challenge.create', 'create.json', [\MapGuesser\Controller\GameController::class, 'createNewChallenge']);
 | 
			
		||||
    $routeCollection->get('challenge', '{challengeToken}', [MapGuesser\Controller\GameController::class, 'getChallenge']);
 | 
			
		||||
    $routeCollection->post('challenge.prepare-json', '{challengeToken}/prepare.json', [MapGuesser\Controller\GameController::class, 'prepareChallenge']);
 | 
			
		||||
    $routeCollection->post('challenge.initialData-json', '{challengeToken}/initialData.json', [MapGuesser\Controller\GameFlowController::class, 'challengeInitialData']);
 | 
			
		||||
    $routeCollection->post('challenge.guess-json', '{challengeToken}/guess.json', [MapGuesser\Controller\GameFlowController::class, 'challengeGuess']);
 | 
			
		||||
});
 | 
			
		||||
Container::$routeCollection->group('admin', function (MapGuesser\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
Container::$routeCollection->group('admin', function (SokoWeb\Routing\RouteCollection $routeCollection) {
 | 
			
		||||
    $routeCollection->get('admin.mapEditor', 'mapEditor/{mapId?}', [MapGuesser\Controller\MapAdminController::class, 'getMapEditor']);
 | 
			
		||||
    $routeCollection->get('admin.place', 'place.json/{placeId}', [MapGuesser\Controller\MapAdminController::class, 'getPlace']);
 | 
			
		||||
    $routeCollection->post('admin.saveMap', 'saveMap/{mapId}/json', [MapGuesser\Controller\MapAdminController::class, 'saveMap']);
 | 
			
		||||
@ -77,7 +77,7 @@ Container::$routeCollection->group('admin', function (MapGuesser\Routing\RouteCo
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
if (isset($_COOKIE['COOKIES_CONSENT'])) {
 | 
			
		||||
    Container::$sessionHandler = new MapGuesser\Session\DatabaseSessionHandler();
 | 
			
		||||
    Container::$sessionHandler = new SokoWeb\Session\DatabaseSessionHandler();
 | 
			
		||||
 | 
			
		||||
    session_set_save_handler(Container::$sessionHandler, true);
 | 
			
		||||
    session_start([
 | 
			
		||||
@ -106,7 +106,13 @@ if (isset($_COOKIE['COOKIES_CONSENT'])) {
 | 
			
		||||
    $_SESSION = [];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Container::$request = new MapGuesser\Request\Request($_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'], $_GET, $_POST, $_SESSION);
 | 
			
		||||
Container::$request = new SokoWeb\Request\Request(
 | 
			
		||||
    $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'],
 | 
			
		||||
    $_GET,
 | 
			
		||||
    $_POST,
 | 
			
		||||
    $_SESSION,
 | 
			
		||||
    new MapGuesser\Repository\UserRepository()
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
if (!Container::$request->session()->has('anti_csrf_token')) {
 | 
			
		||||
    Container::$request->session()->set('anti_csrf_token', bin2hex(random_bytes(16)));
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user