Merged in feature/MAPG-199-introduce-static-php-analyser (pull request #177)

Feature/MAPG-199 introduce static php analyser
This commit is contained in:
Bence Pőcze 2020-07-05 21:23:24 +00:00
commit 2ac9794e3f
47 changed files with 168 additions and 72 deletions

View File

@ -13,3 +13,12 @@ pipelines:
- curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
- composer install - composer install
- vendor/bin/phpunit --log-junit unit_test_results.xml --testdox tests - vendor/bin/phpunit --log-junit unit_test_results.xml --testdox tests
- step:
name: Static Code Analysis
caches:
- composer
script:
- apt-get update && apt-get install -y unzip
- curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
- composer install
- vendor/bin/phpstan analyse -c phpstan.neon

View File

@ -9,7 +9,8 @@
"phpmailer/phpmailer": "^6.1" "phpmailer/phpmailer": "^6.1"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^9" "phpunit/phpunit": "^9",
"phpstan/phpstan": "^0.12.32"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

58
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "fb37b0b958cdf02d82468cb4596613ad", "content-hash": "34563bfc619f47473b2e37a5639dd63e",
"packages": [ "packages": [
{ {
"name": "phpmailer/phpmailer", "name": "phpmailer/phpmailer",
@ -1379,6 +1379,62 @@
], ],
"time": "2020-03-05T15:02:03+00:00" "time": "2020-03-05T15:02:03+00:00"
}, },
{
"name": "phpstan/phpstan",
"version": "0.12.32",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "d03863f504c8432b3de4d1881f73f6acb8c0e67c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/d03863f504c8432b3de4d1881f73f6acb8c0e67c",
"reference": "d03863f504c8432b3de4d1881f73f6acb8c0e67c",
"shasum": ""
},
"require": {
"php": "^7.1"
},
"conflict": {
"phpstan/phpstan-shim": "*"
},
"bin": [
"phpstan",
"phpstan.phar"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.12-dev"
}
},
"autoload": {
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHPStan - PHP Static Analysis Tool",
"funding": [
{
"url": "https://github.com/ondrejmirtes",
"type": "github"
},
{
"url": "https://www.patreon.com/phpstan",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
"type": "tidelift"
}
],
"time": "2020-07-01T11:57:52+00:00"
},
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "8.0.2", "version": "8.0.2",

9
phpstan.neon Normal file
View File

@ -0,0 +1,9 @@
parameters:
level: 5
checkMissingIterableValueType: false
paths:
- main.php
- mapg
- web.php
- src
- tests

View File

@ -10,7 +10,7 @@ use Symfony\Component\Console\Output\OutputInterface;
class AddUserCommand extends Command class AddUserCommand extends Command
{ {
public function configure() public function configure(): void
{ {
$this->setName('user:add') $this->setName('user:add')
->setDescription('Adding of user.') ->setDescription('Adding of user.')

View File

@ -11,7 +11,7 @@ use Symfony\Component\Console\Output\OutputInterface;
class LinkViewCommand extends Command class LinkViewCommand extends Command
{ {
public function configure() public function configure(): void
{ {
$this->setName('view:link') $this->setName('view:link')
->setDescription('Linking of views.') ->setDescription('Linking of views.')
@ -39,7 +39,7 @@ class LinkViewCommand extends Command
$view = substr($file->getPath(), $folderLength) . '/' . $file->getBasename('.php'); $view = substr($file->getPath(), $folderLength) . '/' . $file->getBasename('.php');
if (strpos($view, 'templates') === 0) { if (strpos($view, 'templates') === 0 || strpos($view, 'tests') === 0) {
continue; continue;
} }

View File

@ -14,7 +14,7 @@ use Symfony\Component\Console\Output\OutputInterface;
class MaintainDatabaseCommand extends Command class MaintainDatabaseCommand extends Command
{ {
private PersistentDataManager $persistentDataManager; private PersistentDataManager $pdm;
private UserRepository $userRepository; private UserRepository $userRepository;
@ -26,13 +26,13 @@ class MaintainDatabaseCommand extends Command
{ {
parent::__construct(); parent::__construct();
$this->persistentDataManager = new PersistentDataManager(); $this->pdm = new PersistentDataManager();
$this->userRepository = new UserRepository(); $this->userRepository = new UserRepository();
$this->userConfirmationRepository = new UserConfirmationRepository(); $this->userConfirmationRepository = new UserConfirmationRepository();
$this->userPasswordResetterRepository = new UserPasswordResetterRepository(); $this->userPasswordResetterRepository = new UserPasswordResetterRepository();
} }
public function configure() public function configure(): void
{ {
$this->setName('db:maintain') $this->setName('db:maintain')
->setDescription('Maintain database.'); ->setDescription('Maintain database.');
@ -76,7 +76,7 @@ class MaintainDatabaseCommand extends Command
$this->pdm->deleteFromDb($userPasswordResetter); $this->pdm->deleteFromDb($userPasswordResetter);
} }
$this->persistentDataManager->deleteFromDb($user); $this->pdm->deleteFromDb($user);
} }
\Container::$dbConnection->commit(); \Container::$dbConnection->commit();
@ -85,7 +85,7 @@ class MaintainDatabaseCommand extends Command
private function deleteExpiredPasswordResetters(): void private function deleteExpiredPasswordResetters(): void
{ {
foreach ($this->userPasswordResetterRepository->getAllExpired() as $passwordResetter) { foreach ($this->userPasswordResetterRepository->getAllExpired() as $passwordResetter) {
$this->persistentDataManager->deleteFromDb($passwordResetter); $this->pdm->deleteFromDb($passwordResetter);
} }
} }

View File

@ -9,7 +9,7 @@ use Symfony\Component\Console\Output\OutputInterface;
class MigrateDatabaseCommand extends Command class MigrateDatabaseCommand extends Command
{ {
public function configure() public function configure(): void
{ {
$this->setName('db:migrate') $this->setName('db:migrate')
->setDescription('Migration of database changes.'); ->setDescription('Migration of database changes.');
@ -88,6 +88,10 @@ class MigrateDatabaseCommand extends Command
$path = ROOT . '/database/migrations/' . $type; $path = ROOT . '/database/migrations/' . $type;
$dir = opendir($path); $dir = opendir($path);
if ($dir === false) {
throw new \Exception('Cannot open dir: ' . $path);
}
$files = []; $files = [];
while ($file = readdir($dir)) { while ($file = readdir($dir)) {
$filePath = $path . '/' . $file; $filePath = $path . '/' . $file;

View File

@ -32,7 +32,7 @@ class GameController
return new JsonContent($this->prepareGame($mapId)); return new JsonContent($this->prepareGame($mapId));
} }
private function prepareGame(int $mapId) private function prepareGame(int $mapId): array
{ {
$map = $this->mapRepository->getById($mapId); $map = $this->mapRepository->getById($mapId);

View File

@ -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'][] = [ $state['rounds'][] = [
'placesWithoutPano' => $placesWithoutPano, 'placesWithoutPano' => $placesWithoutPano,

View File

@ -161,7 +161,7 @@ class MapAdminController implements ISecured
return new JsonContent(['mapId' => $map->getId(), 'added' => $addedIds]); return new JsonContent(['mapId' => $map->getId(), 'added' => $addedIds]);
} }
public function deleteMap() public function deleteMap(): IContent
{ {
$mapId = (int) $this->request->query('mapId'); $mapId = (int) $this->request->query('mapId');

View File

@ -210,7 +210,7 @@ class UserController implements ISecured
return new JsonContent(['success' => true]); 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()) { if ($authenticatedWithGoogleUntil !== null && $authenticatedWithGoogleUntil > new DateTime()) {
return true; return true;

View File

@ -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) public function __construct(string $host, string $user, string $password, string $db, int $port = -1, string $socket = null)
{ {
if ($port < 0) { if ($port < 0) {
$port = ini_get('mysqli.default_port'); $port = (int) ini_get('mysqli.default_port');
} }
if ($socket === null) { 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); $this->connection = new mysqli($host, $user, $password, $db, $port, $socket);

View File

@ -30,7 +30,7 @@ class Select
private array $orders = []; private array $orders = [];
private array $limit; private ?array $limit;
public function __construct(IConnection $connection, ?string $table = null) public function __construct(IConnection $connection, ?string $table = null)
{ {
@ -144,7 +144,7 @@ class Select
$this->limit = null; $this->limit = null;
} }
public function paginate(int $page, int $itemsPerPage) public function paginate(int $page, int $itemsPerPage): Select
{ {
$this->limit($itemsPerPage, ($page - 1) * $itemsPerPage); $this->limit($itemsPerPage, ($page - 1) * $itemsPerPage);
@ -308,7 +308,7 @@ class Select
return implode(' ', $joins); return implode(' ', $joins);
} }
private function generateConditions(string $type): array private function generateConditions(int $type): array
{ {
$conditions = ''; $conditions = '';
$params = []; $params = [];
@ -376,9 +376,9 @@ class Select
return [$conditionsString, $params]; 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; $instance->tableAliases = $this->tableAliases;
$conditionCallback($instance); $conditionCallback($instance);

View File

@ -1,7 +1,8 @@
<?php namespace MapGuesser\Database; <?php namespace MapGuesser\Database;
class Utils { class Utils {
public static function backtick(string $name) { public static function backtick(string $name): string
{
return '`' . $name . '`'; return '`' . $name . '`';
} }
} }

View File

@ -29,7 +29,7 @@ class Request implements IRequest
$this->method = $method; $this->method = $method;
} }
public function setQuery($query) public function setQuery($query): void
{ {
if (is_string($query)) { if (is_string($query)) {
$this->query = $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); $this->headers = array_merge($this->headers, $headers);
} }
@ -47,13 +47,13 @@ class Request implements IRequest
{ {
$ch = curl_init(); $ch = curl_init();
if ($this->method === self::HTTP_GET) { if ($this->method === self::HTTP_POST) {
$url = $this->url . '?' . $this->query; $url = $this->url;
} elseif ($this->method === self::HTTP_POST) {
$url = $this->url;
curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $this->query); curl_setopt($ch, CURLOPT_POSTFIELDS, $this->query);
} else {
$url = $this->url . '?' . $this->query;
} }
curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_URL, $url);

View File

@ -14,12 +14,12 @@ class Response implements IResponse
$this->headers = $headers; $this->headers = $headers;
} }
public function getBody() public function getBody(): string
{ {
return $this->body; return $this->body;
} }
public function getHeaders() public function getHeaders(): array
{ {
return $this->headers; return $this->headers;
} }

View File

@ -10,9 +10,9 @@ interface IRequest
public function setMethod(int $method): void; 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; public function send(): IResponse;
} }

View File

@ -2,7 +2,7 @@
interface IResponse interface IResponse
{ {
public function getBody(); public function getBody(): string;
public function getHeaders(); public function getHeaders(): array;
} }

View File

@ -4,7 +4,7 @@ use MapGuesser\Interfaces\Authentication\IUser;
interface IRequest interface IRequest
{ {
public function setParsedRouteParams(array &$routeParams); public function setParsedRouteParams(array &$routeParams): void;
public function getBase(): string; public function getBase(): string;

View File

@ -77,7 +77,7 @@ class Mail
} }
} }
private function sendMail(PHPMailer $mailer, array $recipient) private function sendMail(PHPMailer $mailer, array $recipient): void
{ {
$mailer->clearAddresses(); $mailer->clearAddresses();
$mailer->addAddress($recipient[0], $recipient[1]); $mailer->addAddress($recipient[0], $recipient[1]);

View File

@ -4,9 +4,9 @@ use MapGuesser\Interfaces\Http\IRequest;
class GoogleOAuth 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; private IRequest $request;
@ -36,7 +36,7 @@ class GoogleOAuth
return self::$dialogUrlBase . '?' . http_build_query($oauthParams); return self::$dialogUrlBase . '?' . http_build_query($oauthParams);
} }
public function getToken(string $code, string $redirectUrl) public function getToken(string $code, string $redirectUrl): array
{ {
$tokenParams = [ $tokenParams = [
'code' => $code, 'code' => $code,

View File

@ -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 public function setGoogleSub(?string $googleSub): void
@ -105,10 +105,10 @@ class User extends Model implements IUser
switch ($permission) { switch ($permission) {
case IUser::PERMISSION_NORMAL: case IUser::PERMISSION_NORMAL:
return true; return true;
break;
case IUser::PERMISSION_ADMIN: case IUser::PERMISSION_ADMIN:
return $this->type === '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 public function checkPassword(string $password): bool
{ {
if ($this->password === null) {
return false;
}
return password_verify($password, $this->password); return password_verify($password, $this->password);
} }
} }

View File

@ -8,7 +8,7 @@ use MapGuesser\PersistentData\Model\Model;
class PersistentDataManager 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); $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 = new Select(\Container::$dbConnection);
$select->whereId($id); $select->whereId($id);

View File

@ -46,14 +46,20 @@ class PlaceRepository
return $place; return $place;
} }
private function selectRandomFromDbForMap(int $mapId, array $exclude): ?Place private function selectRandomFromDbForMap(int $mapId, array $exclude): Place
{ {
//TODO: omit table name here //TODO: omit table name here
$select = new Select(\Container::$dbConnection, 'places'); $select = new Select(\Container::$dbConnection, 'places');
$select->where('id', 'NOT IN', $exclude); $select->where('id', 'NOT IN', $exclude);
$select->where('map_id', '=', $mapId); $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); $randomOffset = random_int(0, $numberOfPlaces - 1);
$select->orderBy('id'); $select->orderBy('id');

View File

@ -39,7 +39,7 @@ class Request implements IRequest
} }
} }
public function setParsedRouteParams(array &$routeParams) public function setParsedRouteParams(array &$routeParams): void
{ {
$this->routeParams = &$routeParams; $this->routeParams = &$routeParams;
} }

View File

@ -11,12 +11,12 @@ class Session implements ISession
$this->data = &$data; $this->data = &$data;
} }
public function has($key): bool public function has(string $key): bool
{ {
return isset($this->data[$key]); return isset($this->data[$key]);
} }
public function get($key) public function get(string $key)
{ {
if (isset($this->data[$key])) { if (isset($this->data[$key])) {
return $this->data[$key]; return $this->data[$key];
@ -25,12 +25,12 @@ class Session implements ISession
return null; return null;
} }
public function set($key, $value): void public function set(string $key, $value): void
{ {
$this->data[$key] = $value; $this->data[$key] = $value;
} }
public function delete($key): void public function delete(string $key): void
{ {
unset($this->data[$key]); unset($this->data[$key]);
} }

View File

@ -23,6 +23,7 @@ class HtmlContent extends ContentBase
require ROOT . '/cache/views/' . $this->view . '.php'; 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) . ' -->'; echo '<!-- __debug__runtime: ' . round((hrtime(true) - SCRIPT_STARTED) / 1e+6, 1) . ' -->';
} }

View File

@ -9,6 +9,7 @@ class JsonContent extends ContentBase
public function render(): void 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); $this->data['__debug__runtime'] = round((hrtime(true) - SCRIPT_STARTED) / 1e+6, 1);
echo json_encode($this->data); echo json_encode($this->data);

View File

@ -16,7 +16,7 @@ class Redirect implements IRedirect
public function getUrl(): string public function getUrl(): string
{ {
if (preg_match('/^http(s)?/', $this->target)) { if (preg_match('/^http(s)?/', $this->target) === 1) {
$link = $this->target; $link = $this->target;
} else { } else {
$link = \Container::$request->getBase() . '/' . $this->target; $link = \Container::$request->getBase() . '/' . $this->target;

View File

@ -30,7 +30,7 @@ class Route
$link = []; $link = [];
foreach ($this->pattern as $fragment) { foreach ($this->pattern as $fragment) {
if (preg_match('/^{(\\w+)(\\?)?}$/', $fragment, $matches)) { if (preg_match('/^{(\\w+)(\\?)?}$/', $fragment, $matches) === 1) {
if (isset($parameters[$matches[1]])) { if (isset($parameters[$matches[1]])) {
$link[] = $parameters[$matches[1]]; $link[] = $parameters[$matches[1]];
unset($parameters[$matches[1]]); unset($parameters[$matches[1]]);
@ -61,7 +61,7 @@ class Route
$parameters = []; $parameters = [];
foreach ($path as $i => $fragment) { 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; $parameters[$matches[1]] = $fragment;
} elseif ($fragment != $this->pattern[$i]) { } elseif ($fragment != $this->pattern[$i]) {
return null; return null;

View File

@ -76,7 +76,7 @@ class RouteCollection
$this->searchTable[$method][$groupNumber][] = $route; $this->searchTable[$method][$groupNumber][] = $route;
while (preg_match('/^{\\w+\\?}$/', end($pattern))) { while (preg_match('/^{\\w+\\?}$/', end($pattern)) === 1) {
$groupNumber--; $groupNumber--;
array_pop($pattern); array_pop($pattern);

View File

@ -69,7 +69,7 @@ class DatabaseSessionHandler implements ISessionHandler
return true; return true;
} }
public function gc($maxlifetime): int public function gc($maxlifetime): bool
{ {
// empty on purpose // empty on purpose
// old sessions are deleted by MaintainDatabaseCommand // old sessions are deleted by MaintainDatabaseCommand
@ -84,7 +84,7 @@ class DatabaseSessionHandler implements ISessionHandler
public function validateId($id): bool 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 public function updateTimestamp($id, $data): bool

View File

@ -27,7 +27,7 @@ class Bounds
public static function createDirectly(float $southLat, float $westLng, float $northLat, float $eastLng): Bounds public static function createDirectly(float $southLat, float $westLng, float $northLat, float $eastLng): Bounds
{ {
$instance = new static(); $instance = new self();
$instance->southLat = $southLat; $instance->southLat = $southLat;
$instance->westLng = $westLng; $instance->westLng = $westLng;

View File

@ -11,7 +11,7 @@ class JwtParser
} }
} }
public function setToken(string $token) public function setToken(string $token): void
{ {
$this->token = explode('.', str_replace(['_', '-'], ['/', '+'], $token)); $this->token = explode('.', str_replace(['_', '-'], ['/', '+'], $token));
} }

View File

@ -65,7 +65,7 @@ class Linker
while (($line = fgets($inputFileHandle)) !== false) { while (($line = fgets($inputFileHandle)) !== false) {
++$lineNumber; ++$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]])) { if (isset($sections[$matches[1]])) {
fwrite($outputFileHandle, $sections[$matches[1]]); fwrite($outputFileHandle, $sections[$matches[1]]);
} }
@ -105,7 +105,7 @@ class Linker
fclose($outputFileHandle); fclose($outputFileHandle);
} }
private function generateAssets(ParsedFragment $fragment, array &$sections) private function generateAssets(ParsedFragment $fragment, array &$sections): void
{ {
foreach ($fragment->getCss() as $cssFile) { foreach ($fragment->getCss() as $cssFile) {
$asset = $this->parseAsset($cssFile); $asset = $this->parseAsset($cssFile);
@ -134,7 +134,7 @@ class Linker
{ {
$output = []; $output = [];
if (preg_match('/^[\w\/\.]+$/', $asset)) { if (preg_match('/^[\w\/\.]+$/', $asset) === 1) {
if ( if (
empty($_ENV['DEV']) && empty($_ENV['DEV']) &&
filesize(ROOT . '/public/static/' . $asset) < self::INLINE_ASSET_LIMIT filesize(ROOT . '/public/static/' . $asset) < self::INLINE_ASSET_LIMIT

View File

@ -108,7 +108,7 @@ class Parser
private function matchCss(string $line): ?string 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]; return $matches[1];
} }
@ -117,7 +117,7 @@ class Parser
private function matchJs(string $line): ?string 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]; return $matches[1];
} }
@ -126,7 +126,7 @@ class Parser
private function matchExtends(string $line): ?string 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]; return $matches[1];
} }
@ -135,7 +135,7 @@ class Parser
private function matchSection(string $line): ?string 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]; return $matches[1];
} }
@ -144,16 +144,16 @@ class Parser
private function matchEndSection(string $line): bool 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 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 private function matchEndExtra(string $line): bool
{ {
return preg_match('/^\s*@endextra(?:\(\))?\s*$/', $line); return preg_match('/^\s*@endextra(?:\(\))?\s*$/', $line) === 1;
} }
} }

View File

@ -3,6 +3,10 @@
use MapGuesser\PersistentData\Model\Model; use MapGuesser\PersistentData\Model\Model;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class OtherModel
{
}
class DummyModel extends Model class DummyModel extends Model
{ {
protected static string $table = 'test_table'; protected static string $table = 'test_table';

View File

@ -6,7 +6,7 @@ use PHPUnit\Framework\TestCase;
final class ParserTest extends TestCase final class ParserTest extends TestCase
{ {
const TEST_VIEWS_PATH = __DIR__ . '/../assets/views'; const TEST_VIEWS_PATH = __DIR__ . '/../../views/tests';
public function testCanParseViewWithoutExtends(): void public function testCanParseViewWithoutExtends(): void
{ {