diff --git a/src/Cli/AddUserCommand.php b/src/Cli/AddUserCommand.php index 212649e..7ea8256 100644 --- a/src/Cli/AddUserCommand.php +++ b/src/Cli/AddUserCommand.php @@ -10,7 +10,7 @@ use Symfony\Component\Console\Output\OutputInterface; class AddUserCommand extends Command { - public function configure() + public function configure(): void { $this->setName('user:add') ->setDescription('Adding of user.') diff --git a/src/Cli/LinkViewCommand.php b/src/Cli/LinkViewCommand.php index cccd74e..0ac4a31 100644 --- a/src/Cli/LinkViewCommand.php +++ b/src/Cli/LinkViewCommand.php @@ -11,7 +11,7 @@ use Symfony\Component\Console\Output\OutputInterface; class LinkViewCommand extends Command { - public function configure() + public function configure(): void { $this->setName('view:link') ->setDescription('Linking of views.') diff --git a/src/Cli/MaintainDatabaseCommand.php b/src/Cli/MaintainDatabaseCommand.php index f2afc70..ccb5b59 100644 --- a/src/Cli/MaintainDatabaseCommand.php +++ b/src/Cli/MaintainDatabaseCommand.php @@ -14,7 +14,7 @@ use Symfony\Component\Console\Output\OutputInterface; class MaintainDatabaseCommand extends Command { - private PersistentDataManager $persistentDataManager; + private PersistentDataManager $pdm; private UserRepository $userRepository; @@ -26,13 +26,13 @@ class MaintainDatabaseCommand extends Command { parent::__construct(); - $this->persistentDataManager = new PersistentDataManager(); + $this->pdm = new PersistentDataManager(); $this->userRepository = new UserRepository(); $this->userConfirmationRepository = new UserConfirmationRepository(); $this->userPasswordResetterRepository = new UserPasswordResetterRepository(); } - public function configure() + public function configure(): void { $this->setName('db:maintain') ->setDescription('Maintain database.'); @@ -76,7 +76,7 @@ class MaintainDatabaseCommand extends Command $this->pdm->deleteFromDb($userPasswordResetter); } - $this->persistentDataManager->deleteFromDb($user); + $this->pdm->deleteFromDb($user); } \Container::$dbConnection->commit(); @@ -85,7 +85,7 @@ class MaintainDatabaseCommand extends Command private function deleteExpiredPasswordResetters(): void { foreach ($this->userPasswordResetterRepository->getAllExpired() as $passwordResetter) { - $this->persistentDataManager->deleteFromDb($passwordResetter); + $this->pdm->deleteFromDb($passwordResetter); } } diff --git a/src/Cli/MigrateDatabaseCommand.php b/src/Cli/MigrateDatabaseCommand.php index ac81743..da0943f 100644 --- a/src/Cli/MigrateDatabaseCommand.php +++ b/src/Cli/MigrateDatabaseCommand.php @@ -9,7 +9,7 @@ use Symfony\Component\Console\Output\OutputInterface; class MigrateDatabaseCommand extends Command { - public function configure() + public function configure(): void { $this->setName('db:migrate') ->setDescription('Migration of database changes.'); @@ -88,6 +88,10 @@ class MigrateDatabaseCommand extends Command $path = ROOT . '/database/migrations/' . $type; $dir = opendir($path); + if ($dir === false) { + throw new \Exception('Cannot open dir: ' . $path); + } + $files = []; while ($file = readdir($dir)) { $filePath = $path . '/' . $file; diff --git a/src/Controller/GameController.php b/src/Controller/GameController.php index 7bcc2dd..6d65b2a 100644 --- a/src/Controller/GameController.php +++ b/src/Controller/GameController.php @@ -32,7 +32,7 @@ class GameController return new JsonContent($this->prepareGame($mapId)); } - private function prepareGame(int $mapId) + private function prepareGame(int $mapId): array { $map = $this->mapRepository->getById($mapId); diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index c7cf95b..b414eb0 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -128,7 +128,7 @@ class GameFlowController ]); } - private function addNewRoundToState(&$state, Place $place, array $placesWithoutPano): void + private function addNewRoundToState(array &$state, Place $place, array $placesWithoutPano): void { $state['rounds'][] = [ 'placesWithoutPano' => $placesWithoutPano, diff --git a/src/Controller/MapAdminController.php b/src/Controller/MapAdminController.php index 6a46f28..add5232 100644 --- a/src/Controller/MapAdminController.php +++ b/src/Controller/MapAdminController.php @@ -161,7 +161,7 @@ class MapAdminController implements ISecured return new JsonContent(['mapId' => $map->getId(), 'added' => $addedIds]); } - public function deleteMap() + public function deleteMap(): IContent { $mapId = (int) $this->request->query('mapId'); diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 4b0e26f..805b752 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -210,7 +210,7 @@ class UserController implements ISecured return new JsonContent(['success' => true]); } - private function confirmUserIdentity(User $user, ?DateTime $authenticatedWithGoogleUntil, ?string $password, &$error): bool + private function confirmUserIdentity(User $user, ?DateTime $authenticatedWithGoogleUntil, ?string $password, string &$error): bool { if ($authenticatedWithGoogleUntil !== null && $authenticatedWithGoogleUntil > new DateTime()) { return true; diff --git a/src/Database/Mysql/Connection.php b/src/Database/Mysql/Connection.php index 42dcef6..da5022b 100644 --- a/src/Database/Mysql/Connection.php +++ b/src/Database/Mysql/Connection.php @@ -12,11 +12,11 @@ class Connection implements IConnection public function __construct(string $host, string $user, string $password, string $db, int $port = -1, string $socket = null) { if ($port < 0) { - $port = ini_get('mysqli.default_port'); + $port = (int) ini_get('mysqli.default_port'); } if ($socket === null) { - $socket = ini_get('mysqli.default_socket'); + $socket = (string) ini_get('mysqli.default_socket'); } $this->connection = new mysqli($host, $user, $password, $db, $port, $socket); diff --git a/src/Database/Query/Select.php b/src/Database/Query/Select.php index a2ffda6..08a1049 100644 --- a/src/Database/Query/Select.php +++ b/src/Database/Query/Select.php @@ -30,7 +30,7 @@ class Select private array $orders = []; - private array $limit; + private ?array $limit; public function __construct(IConnection $connection, ?string $table = null) { @@ -144,7 +144,7 @@ class Select $this->limit = null; } - public function paginate(int $page, int $itemsPerPage) + public function paginate(int $page, int $itemsPerPage): Select { $this->limit($itemsPerPage, ($page - 1) * $itemsPerPage); @@ -308,7 +308,7 @@ class Select return implode(' ', $joins); } - private function generateConditions(string $type): array + private function generateConditions(int $type): array { $conditions = ''; $params = []; @@ -376,9 +376,9 @@ class Select return [$conditionsString, $params]; } - private function generateComplexConditionFragment(string $type, Closure $conditionCallback): array + private function generateComplexConditionFragment(int $type, Closure $conditionCallback): array { - $instance = new static($this->connection, $this->table); + $instance = new self($this->connection, $this->table); $instance->tableAliases = $this->tableAliases; $conditionCallback($instance); diff --git a/src/Database/Utils.php b/src/Database/Utils.php index 3c4042e..be025c4 100644 --- a/src/Database/Utils.php +++ b/src/Database/Utils.php @@ -1,7 +1,8 @@ method = $method; } - public function setQuery($query) + public function setQuery($query): void { if (is_string($query)) { $this->query = $query; @@ -38,7 +38,7 @@ class Request implements IRequest } } - public function setHeaders(array $headers) + public function setHeaders(array $headers): void { $this->headers = array_merge($this->headers, $headers); } @@ -47,13 +47,13 @@ class Request implements IRequest { $ch = curl_init(); - if ($this->method === self::HTTP_GET) { - $url = $this->url . '?' . $this->query; - } elseif ($this->method === self::HTTP_POST) { - $url = $this->url; + 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); diff --git a/src/Http/Response.php b/src/Http/Response.php index 65958b0..a7354f3 100644 --- a/src/Http/Response.php +++ b/src/Http/Response.php @@ -14,12 +14,12 @@ class Response implements IResponse $this->headers = $headers; } - public function getBody() + public function getBody(): string { return $this->body; } - public function getHeaders() + public function getHeaders(): array { return $this->headers; } diff --git a/src/Interfaces/Http/IRequest.php b/src/Interfaces/Http/IRequest.php index 10436f7..0b791ea 100644 --- a/src/Interfaces/Http/IRequest.php +++ b/src/Interfaces/Http/IRequest.php @@ -10,9 +10,9 @@ interface IRequest public function setMethod(int $method): void; - public function setQuery($query); + public function setQuery($query): void; - public function setHeaders(array $headers); + public function setHeaders(array $headers): void; public function send(): IResponse; } diff --git a/src/Interfaces/Http/IResponse.php b/src/Interfaces/Http/IResponse.php index b840bac..1320fd2 100644 --- a/src/Interfaces/Http/IResponse.php +++ b/src/Interfaces/Http/IResponse.php @@ -2,7 +2,7 @@ interface IResponse { - public function getBody(); + public function getBody(): string; - public function getHeaders(); + public function getHeaders(): array; } diff --git a/src/Interfaces/Request/IRequest.php b/src/Interfaces/Request/IRequest.php index e4499e5..699daea 100644 --- a/src/Interfaces/Request/IRequest.php +++ b/src/Interfaces/Request/IRequest.php @@ -4,7 +4,7 @@ use MapGuesser\Interfaces\Authentication\IUser; interface IRequest { - public function setParsedRouteParams(array &$routeParams); + public function setParsedRouteParams(array &$routeParams): void; public function getBase(): string; diff --git a/src/Mailing/Mail.php b/src/Mailing/Mail.php index 58befb0..4f3600d 100644 --- a/src/Mailing/Mail.php +++ b/src/Mailing/Mail.php @@ -77,7 +77,7 @@ class Mail } } - private function sendMail(PHPMailer $mailer, array $recipient) + private function sendMail(PHPMailer $mailer, array $recipient): void { $mailer->clearAddresses(); $mailer->addAddress($recipient[0], $recipient[1]); diff --git a/src/OAuth/GoogleOAuth.php b/src/OAuth/GoogleOAuth.php index 1c9fbf0..cfd34f1 100644 --- a/src/OAuth/GoogleOAuth.php +++ b/src/OAuth/GoogleOAuth.php @@ -4,9 +4,9 @@ use MapGuesser\Interfaces\Http\IRequest; class GoogleOAuth { - private static $dialogUrlBase = 'https://accounts.google.com/o/oauth2/v2/auth'; + private static string $dialogUrlBase = 'https://accounts.google.com/o/oauth2/v2/auth'; - private static $tokenUrlBase = 'https://oauth2.googleapis.com/token'; + private static string $tokenUrlBase = 'https://oauth2.googleapis.com/token'; private IRequest $request; @@ -36,7 +36,7 @@ class GoogleOAuth return self::$dialogUrlBase . '?' . http_build_query($oauthParams); } - public function getToken(string $code, string $redirectUrl) + public function getToken(string $code, string $redirectUrl): array { $tokenParams = [ 'code' => $code, diff --git a/src/PersistentData/Model/User.php b/src/PersistentData/Model/User.php index a4ebf60..8081cac 100644 --- a/src/PersistentData/Model/User.php +++ b/src/PersistentData/Model/User.php @@ -45,9 +45,9 @@ class User extends Model implements IUser } } - public function setActive($active): void + public function setActive(bool $active): void { - $this->active = (bool) $active; + $this->active = $active; } public function setGoogleSub(?string $googleSub): void @@ -105,10 +105,10 @@ class User extends Model implements IUser switch ($permission) { case IUser::PERMISSION_NORMAL: return true; - break; case IUser::PERMISSION_ADMIN: return $this->type === 'admin'; - break; + default: + throw new \Exception('Permission does not exist: ' . $permission); } } @@ -124,6 +124,10 @@ class User extends Model implements IUser public function checkPassword(string $password): bool { + if ($this->password === null) { + return false; + } + return password_verify($password, $this->password); } } diff --git a/src/PersistentData/PersistentDataManager.php b/src/PersistentData/PersistentDataManager.php index 38c58db..126f695 100644 --- a/src/PersistentData/PersistentDataManager.php +++ b/src/PersistentData/PersistentDataManager.php @@ -8,7 +8,7 @@ use MapGuesser\PersistentData\Model\Model; class PersistentDataManager { - public function selectFromDb(Select $select, string $type, bool $withRelations = false): ?Model + public function selectFromDb(Select $select, string $type, bool $withRelations = false) { $select = $this->createSelect($select, $type, $withRelations); @@ -37,7 +37,7 @@ class PersistentDataManager } } - public function selectFromDbById($id, string $type, bool $withRelations = false): ?Model + public function selectFromDbById($id, string $type, bool $withRelations = false) { $select = new Select(\Container::$dbConnection); $select->whereId($id); diff --git a/src/Repository/PlaceRepository.php b/src/Repository/PlaceRepository.php index cccca7a..0a2a197 100644 --- a/src/Repository/PlaceRepository.php +++ b/src/Repository/PlaceRepository.php @@ -46,14 +46,20 @@ class PlaceRepository return $place; } - private function selectRandomFromDbForMap(int $mapId, array $exclude): ?Place + private function selectRandomFromDbForMap(int $mapId, array $exclude): Place { //TODO: omit table name here $select = new Select(\Container::$dbConnection, 'places'); $select->where('id', 'NOT IN', $exclude); $select->where('map_id', '=', $mapId); - $numberOfPlaces = $select->count(); // TODO: what if 0 + $numberOfPlaces = $select->count(); + + //TODO: prevent this + if ($numberOfPlaces === 0) { + throw new \Exception('There is no selectable place (count was 0).'); + } + $randomOffset = random_int(0, $numberOfPlaces - 1); $select->orderBy('id'); diff --git a/src/Request/Request.php b/src/Request/Request.php index 0b41fa7..cdbc02f 100644 --- a/src/Request/Request.php +++ b/src/Request/Request.php @@ -39,7 +39,7 @@ class Request implements IRequest } } - public function setParsedRouteParams(array &$routeParams) + public function setParsedRouteParams(array &$routeParams): void { $this->routeParams = &$routeParams; } diff --git a/src/Request/Session.php b/src/Request/Session.php index f1fedab..36d4f0f 100644 --- a/src/Request/Session.php +++ b/src/Request/Session.php @@ -11,12 +11,12 @@ class Session implements ISession $this->data = &$data; } - public function has($key): bool + public function has(string $key): bool { return isset($this->data[$key]); } - public function get($key) + public function get(string $key) { if (isset($this->data[$key])) { return $this->data[$key]; @@ -25,12 +25,12 @@ class Session implements ISession return null; } - public function set($key, $value): void + public function set(string $key, $value): void { $this->data[$key] = $value; } - public function delete($key): void + public function delete(string $key): void { unset($this->data[$key]); } diff --git a/src/Response/HtmlContent.php b/src/Response/HtmlContent.php index 377b32e..3dcc29a 100644 --- a/src/Response/HtmlContent.php +++ b/src/Response/HtmlContent.php @@ -23,6 +23,7 @@ class HtmlContent extends ContentBase require ROOT . '/cache/views/' . $this->view . '.php'; + // @phpstan-ignore-next-line - SCRIPT_STARTED is defined in main.php echo ''; } diff --git a/src/Response/JsonContent.php b/src/Response/JsonContent.php index 9632328..70f4769 100644 --- a/src/Response/JsonContent.php +++ b/src/Response/JsonContent.php @@ -9,6 +9,7 @@ class JsonContent extends ContentBase 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); diff --git a/src/Response/Redirect.php b/src/Response/Redirect.php index ed78183..4560155 100644 --- a/src/Response/Redirect.php +++ b/src/Response/Redirect.php @@ -16,7 +16,7 @@ class Redirect implements IRedirect public function getUrl(): string { - if (preg_match('/^http(s)?/', $this->target)) { + if (preg_match('/^http(s)?/', $this->target) === 1) { $link = $this->target; } else { $link = \Container::$request->getBase() . '/' . $this->target; diff --git a/src/Routing/Route.php b/src/Routing/Route.php index afe5491..5079fad 100644 --- a/src/Routing/Route.php +++ b/src/Routing/Route.php @@ -30,7 +30,7 @@ class Route $link = []; foreach ($this->pattern as $fragment) { - if (preg_match('/^{(\\w+)(\\?)?}$/', $fragment, $matches)) { + if (preg_match('/^{(\\w+)(\\?)?}$/', $fragment, $matches) === 1) { if (isset($parameters[$matches[1]])) { $link[] = $parameters[$matches[1]]; unset($parameters[$matches[1]]); @@ -61,7 +61,7 @@ class Route $parameters = []; foreach ($path as $i => $fragment) { - if (preg_match('/^{(\\w+)(?:\\?)?}$/', $this->pattern[$i], $matches)) { + if (preg_match('/^{(\\w+)(?:\\?)?}$/', $this->pattern[$i], $matches) === 1) { $parameters[$matches[1]] = $fragment; } elseif ($fragment != $this->pattern[$i]) { return null; diff --git a/src/Routing/RouteCollection.php b/src/Routing/RouteCollection.php index 1430002..0e65bb3 100644 --- a/src/Routing/RouteCollection.php +++ b/src/Routing/RouteCollection.php @@ -76,7 +76,7 @@ class RouteCollection $this->searchTable[$method][$groupNumber][] = $route; - while (preg_match('/^{\\w+\\?}$/', end($pattern))) { + while (preg_match('/^{\\w+\\?}$/', end($pattern)) === 1) { $groupNumber--; array_pop($pattern); diff --git a/src/Session/DatabaseSessionHandler.php b/src/Session/DatabaseSessionHandler.php index 3949625..64e7d37 100644 --- a/src/Session/DatabaseSessionHandler.php +++ b/src/Session/DatabaseSessionHandler.php @@ -69,7 +69,7 @@ class DatabaseSessionHandler implements ISessionHandler return true; } - public function gc($maxlifetime): int + public function gc($maxlifetime): bool { // empty on purpose // old sessions are deleted by MaintainDatabaseCommand @@ -84,7 +84,7 @@ class DatabaseSessionHandler implements ISessionHandler public function validateId($id): bool { - return preg_match('/^[a-f0-9]{32}$/', $id); + return preg_match('/^[a-f0-9]{32}$/', $id) === 1; } public function updateTimestamp($id, $data): bool diff --git a/src/Util/Geo/Bounds.php b/src/Util/Geo/Bounds.php index 99f1edb..2efef91 100644 --- a/src/Util/Geo/Bounds.php +++ b/src/Util/Geo/Bounds.php @@ -27,7 +27,7 @@ class Bounds public static function createDirectly(float $southLat, float $westLng, float $northLat, float $eastLng): Bounds { - $instance = new static(); + $instance = new self(); $instance->southLat = $southLat; $instance->westLng = $westLng; diff --git a/src/Util/JwtParser.php b/src/Util/JwtParser.php index b6e57a1..f9bc38e 100644 --- a/src/Util/JwtParser.php +++ b/src/Util/JwtParser.php @@ -11,7 +11,7 @@ class JwtParser } } - public function setToken(string $token) + public function setToken(string $token): void { $this->token = explode('.', str_replace(['_', '-'], ['/', '+'], $token)); } diff --git a/src/View/Linker.php b/src/View/Linker.php index b9e237b..ca67886 100644 --- a/src/View/Linker.php +++ b/src/View/Linker.php @@ -65,7 +65,7 @@ class Linker while (($line = fgets($inputFileHandle)) !== false) { ++$lineNumber; - if (preg_match('/^\s*@yields\(\'([\w\/]+)\'\)\s*$/', $line, $matches)) { + if (preg_match('/^\s*@yields\(\'([\w\/]+)\'\)\s*$/', $line, $matches) === 1) { if (isset($sections[$matches[1]])) { fwrite($outputFileHandle, $sections[$matches[1]]); } @@ -105,7 +105,7 @@ class Linker fclose($outputFileHandle); } - private function generateAssets(ParsedFragment $fragment, array &$sections) + private function generateAssets(ParsedFragment $fragment, array &$sections): void { foreach ($fragment->getCss() as $cssFile) { $asset = $this->parseAsset($cssFile); @@ -134,7 +134,7 @@ class Linker { $output = []; - if (preg_match('/^[\w\/\.]+$/', $asset)) { + if (preg_match('/^[\w\/\.]+$/', $asset) === 1) { if ( empty($_ENV['DEV']) && filesize(ROOT . '/public/static/' . $asset) < self::INLINE_ASSET_LIMIT diff --git a/src/View/Parser.php b/src/View/Parser.php index 7802a7a..29be6a6 100644 --- a/src/View/Parser.php +++ b/src/View/Parser.php @@ -108,7 +108,7 @@ class Parser private function matchCss(string $line): ?string { - if (preg_match('/^\s*@css\((.*)\)\s*$/', $line, $matches)) { + if (preg_match('/^\s*@css\((.*)\)\s*$/', $line, $matches) === 1) { return $matches[1]; } @@ -117,7 +117,7 @@ class Parser private function matchJs(string $line): ?string { - if (preg_match('/^\s*@js\((.*)\)\s*$/', $line, $matches)) { + if (preg_match('/^\s*@js\((.*)\)\s*$/', $line, $matches) === 1) { return $matches[1]; } @@ -126,7 +126,7 @@ class Parser private function matchExtends(string $line): ?string { - if (preg_match('/^\s*@extends\(([\w\/]+)\)\s*$/', $line, $matches)) { + if (preg_match('/^\s*@extends\(([\w\/]+)\)\s*$/', $line, $matches) === 1) { return $matches[1]; } @@ -135,7 +135,7 @@ class Parser private function matchSection(string $line): ?string { - if (preg_match('/^\s*@section\((\w+)\)\s*$/', $line, $matches)) { + if (preg_match('/^\s*@section\((\w+)\)\s*$/', $line, $matches) === 1) { return $matches[1]; } @@ -144,16 +144,16 @@ class Parser private function matchEndSection(string $line): bool { - return preg_match('/^\s*@endsection(?:\(\))?\s*$/', $line); + return preg_match('/^\s*@endsection(?:\(\))?\s*$/', $line) === 1; } private function matchExtra(string $line): bool { - return preg_match('/^\s*@extra(?:\(\))?\s*$/', $line); + return preg_match('/^\s*@extra(?:\(\))?\s*$/', $line) === 1; } private function matchEndExtra(string $line): bool { - return preg_match('/^\s*@endextra(?:\(\))?\s*$/', $line); + return preg_match('/^\s*@endextra(?:\(\))?\s*$/', $line) === 1; } } diff --git a/tests/PersistentData/Model/ModelTest.php b/tests/PersistentData/Model/ModelTest.php index 6e0b042..8cad912 100644 --- a/tests/PersistentData/Model/ModelTest.php +++ b/tests/PersistentData/Model/ModelTest.php @@ -3,6 +3,10 @@ use MapGuesser\PersistentData\Model\Model; use PHPUnit\Framework\TestCase; +class OtherModel +{ +} + class DummyModel extends Model { protected static string $table = 'test_table';