From 53080ebeccc7551f6ecf50b221110d34ff740914 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 14:49:03 -0700 Subject: [PATCH 001/114] Give a more sensible message when someone tries to register with no password Before we were saying they didn't match which is not quite right. --- gatherling/Auth/Registration.php | 3 ++- gatherling/register.php | 2 ++ tests/Auth/RegistrationTest.php | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/gatherling/Auth/Registration.php b/gatherling/Auth/Registration.php index f8525fe88..537c31a9c 100644 --- a/gatherling/Auth/Registration.php +++ b/gatherling/Auth/Registration.php @@ -11,6 +11,7 @@ class Registration public const SUCCESS = 0; public const ERROR_PLAYER_EXISTS = -3; public const ERROR_PASSWORD_MISMATCH = -1; + public const ERROR_PASSWORD_EMPTY = -4; public static function register(string $username, string $pw1, string $pw2, string $email, int $emailStatus, float $timezone, ?string $discordId, ?string $discordName): int { @@ -22,7 +23,7 @@ public static function register(string $username, string $pw1, string $pw2, stri return self::ERROR_PASSWORD_MISMATCH; } if (empty($pw1) && !isset($discordId)) { - return self::ERROR_PASSWORD_MISMATCH; + return self::ERROR_PASSWORD_EMPTY; } $player->password = hash('sha256', $pw1); $player->super = Player::activeCount() == 0 ? 1 : 0; diff --git a/gatherling/register.php b/gatherling/register.php index 29dee65df..4a9bec2bc 100644 --- a/gatherling/register.php +++ b/gatherling/register.php @@ -33,6 +33,8 @@ function main(): void } elseif ($code == -3) { $message = 'A password has already been created for this account.'; (new LoginRedirect('player.php', $message, trim($username)))->send(); + } elseif ($code == -4) { + $message = 'Password cannot be empty.'; } } $showRegForm = !isset($_POST['pw1']); diff --git a/tests/Auth/RegistrationTest.php b/tests/Auth/RegistrationTest.php index 6ec7c8fc1..1fd6f13a2 100644 --- a/tests/Auth/RegistrationTest.php +++ b/tests/Auth/RegistrationTest.php @@ -24,7 +24,7 @@ public function testRegister(): void $this->assertEquals(Registration::ERROR_PASSWORD_MISMATCH, Registration::register('failuser1', 'password1', 'password2', 'failuser1@example.com', 0, 0, null, null)); // Test registration with empty password and no Discord ID - $this->assertEquals(Registration::ERROR_PASSWORD_MISMATCH, Registration::register('failuser2', '', '', 'failuser2@example.com', 0, 0, null, null)); + $this->assertEquals(Registration::ERROR_PASSWORD_EMPTY, Registration::register('failuser2', '', '', 'failuser2@example.com', 0, 0, null, null)); // Test registration with existing username $this->assertEquals(Registration::SUCCESS, Registration::register('existinguser', 'testpass', 'testpass', 'existinguser@example.com', 0, 0, null, null)); From 67a1f2ffbf1f6317a2ddfdf70bfe305d7ad2b567 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 15:02:08 -0700 Subject: [PATCH 002/114] Represent a maybe-not-present season as a nullable int, not as a string This is more precise as season is always an int or not present. --- gatherling/Views/Components/SeasonDropMenu.php | 2 +- gatherling/Views/Pages/EventList.php | 6 +++--- gatherling/Views/Pages/PlayerEventList.php | 2 +- gatherling/event.php | 2 +- gatherling/eventreport.php | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gatherling/Views/Components/SeasonDropMenu.php b/gatherling/Views/Components/SeasonDropMenu.php index d1767b1c8..c9c68db2b 100644 --- a/gatherling/Views/Components/SeasonDropMenu.php +++ b/gatherling/Views/Components/SeasonDropMenu.php @@ -8,7 +8,7 @@ class SeasonDropMenu extends NumDropMenu { - public function __construct(int|string|null $season, ?string $default = '- Season - ') + public function __construct(?int $season, ?string $default = '- Season - ') { $db = Database::getConnection(); $query = 'SELECT MAX(season) AS m FROM events'; diff --git a/gatherling/Views/Pages/EventList.php b/gatherling/Views/Pages/EventList.php index 73bdcf7eb..a62c81df6 100644 --- a/gatherling/Views/Pages/EventList.php +++ b/gatherling/Views/Pages/EventList.php @@ -27,7 +27,7 @@ class EventList extends Page public array $events = []; public bool $hasMore; - public function __construct(string $seriesName, string $format, string $season) + public function __construct(string $seriesName, string $format, ?int $season) { parent::__construct(); $player = Player::getSessionPlayer(); @@ -93,7 +93,7 @@ public function __construct(string $seriesName, string $format, string $season) * @param list $playerSeries * @return list */ -function queryEvents(Player $player, array $playerSeries, string $seriesName, string $format, string $season): array +function queryEvents(Player $player, array $playerSeries, string $seriesName, string $format, ?int $season): array { $sql = ' SELECT e.name, e.format, COUNT(DISTINCT n.player) AS players, e.host, e.start, @@ -110,7 +110,7 @@ function queryEvents(Player $player, array $playerSeries, string $seriesName, st $sql .= ' AND e.series = :series_name'; $params['series_name'] = $seriesName; } - if ($season) { + if ($season !== null) { $sql .= ' AND e.season = :season'; $params['season'] = $season; } diff --git a/gatherling/Views/Pages/PlayerEventList.php b/gatherling/Views/Pages/PlayerEventList.php index 5d288776d..045ecad43 100644 --- a/gatherling/Views/Pages/PlayerEventList.php +++ b/gatherling/Views/Pages/PlayerEventList.php @@ -19,7 +19,7 @@ class PlayerEventList extends Page public bool $hasMore; /** @param list $events */ - public function __construct(string $format, string $series, string $season, array $events) + public function __construct(string $format, string $series, ?int $season, array $events) { parent::__construct(); diff --git a/gatherling/event.php b/gatherling/event.php index c680b748b..9dd2ea41c 100644 --- a/gatherling/event.php +++ b/gatherling/event.php @@ -40,7 +40,7 @@ function main(): void } $getSeriesName = get()->string('series', ''); - $season = get()->string('season', ''); + $season = get()->optionalInt('season'); $requestEventName = request()->string('name', ''); $getEventName = get()->optionalString('name') ?? get()->optionalString('event'); $postEventName = post()->optionalString('name'); diff --git a/gatherling/eventreport.php b/gatherling/eventreport.php index 61bdf9af4..b3d4f52fa 100644 --- a/gatherling/eventreport.php +++ b/gatherling/eventreport.php @@ -24,7 +24,7 @@ function main(): void } else { $format = get()->string('format', ''); $series = get()->string('series', ''); - $season = get()->string('season', ''); + $season = get()->optionalInt('season'); $events = eventList($format, $series, $season); $page = new PlayerEventList($format, $series, $season, $events); } @@ -32,7 +32,7 @@ function main(): void } /** @return list */ -function eventList(string $format, string $series, string $season): array +function eventList(string $format, string $series, ?int $season): array { $sql = ' SELECT From bb3b838be0d37a1c2f3daa036ccb7dd2c4961d2a Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 15:08:42 -0700 Subject: [PATCH 003/114] Log slow queries --- gatherling/Data/Db.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/gatherling/Data/Db.php b/gatherling/Data/Db.php index 59f77628c..25efa9a69 100644 --- a/gatherling/Data/Db.php +++ b/gatherling/Data/Db.php @@ -20,6 +20,8 @@ // Do not access this directly, use Gatherling\Helpers\db() instead class Db { + private const SLOW_QUERY_THRESHOLD = 1.0; + private PDO $pdo; private bool $connected = false; /** @var list */ @@ -537,7 +539,13 @@ private function executeInternal(string $sql, array $params, callable $operation [$sql, $params] = $this->expandArrayParams($sql, $params); try { - return $operation($sql, $params); + $startTime = microtime(true); + $result = $operation($sql, $params); + $duration = microtime(true) - $startTime; + if ($duration > self::SLOW_QUERY_THRESHOLD) { + logger()->warning("[DB] Query took " . number_format($duration, 3) . "s: $sql", $context); + } + return $result; } catch (PDOException $e) { if ($e->getCode() === '3D000') { logger()->warning('Database connection lost, attempting to reconnect...'); From e3d811166e440fe882487d1d6cf12c88f6c801c9 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 15:12:21 -0700 Subject: [PATCH 004/114] Don't call a link to eventreport.php "eventLink" because that's event.php --- gatherling/Views/Components/Preregistration.php | 6 +++--- gatherling/templates/partials/preregistration.mustache | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gatherling/Views/Components/Preregistration.php b/gatherling/Views/Components/Preregistration.php index 16370869e..2e06eb780 100644 --- a/gatherling/Views/Components/Preregistration.php +++ b/gatherling/Views/Components/Preregistration.php @@ -14,7 +14,7 @@ class Preregistration extends Component public bool $hasUpcomingEvents = false; /** @var array */ public array $upcomingEvents = []; - /** @var array */ + /** @var array */ public array $availableEvents = []; public bool $promptToLinkMtgo = false; public bool $promptToLinkMtga = false; @@ -95,7 +95,7 @@ public function __construct(Player $player) } foreach ($availableEvents as $event) { - $eventLink = 'eventreport.php?event=' . rawurlencode($event->name ?? ''); + $eventReportLink = 'eventreport.php?event=' . rawurlencode($event->name ?? ''); $eventName = $event->name ?? ''; if (!$event->start || !strtotime($event->start)) { throw new NotFoundException("Event start time not found for event {$event->name}"); @@ -106,7 +106,7 @@ public function __construct(Player $player) $requiresMtga = $event->client == 2 && empty($player->mtga_username); $isOpen = !$isFull && !$requiresMtgo && !$requiresMtga; $this->availableEvents[] = [ - 'eventLink' => $eventLink, + 'eventReportLink' => $eventReportLink, 'eventName' => $eventName, 'startTime' => $startTime, 'isFull' => $isFull, diff --git a/gatherling/templates/partials/preregistration.mustache b/gatherling/templates/partials/preregistration.mustache index 9afa789e5..48160f27f 100644 --- a/gatherling/templates/partials/preregistration.mustache +++ b/gatherling/templates/partials/preregistration.mustache @@ -44,7 +44,7 @@ - {{eventName}} + {{eventName}}
{{#startTime}}{{> time}}{{/startTime}} From 9125b87b804e41c39d41d92d0e5441f61c9048ea Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 15:19:26 -0700 Subject: [PATCH 005/114] Remove unused import --- gatherling/Models/Deck.php | 1 - 1 file changed, 1 deletion(-) diff --git a/gatherling/Models/Deck.php b/gatherling/Models/Deck.php index 1c08f3084..3a25357b2 100644 --- a/gatherling/Models/Deck.php +++ b/gatherling/Models/Deck.php @@ -4,7 +4,6 @@ namespace Gatherling\Models; -use Exception; use Gatherling\Exceptions\NotFoundException; use InvalidArgumentException; use Gatherling\Views\Components\DeckLink; From 7b6f56b2e1d1ce42f2672e04c30b89ddfbd6aae8 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 15:19:35 -0700 Subject: [PATCH 006/114] Remove redundant func and simplify calling code in TrophyCell --- gatherling/Models/Event.php | 15 --------------- gatherling/Views/Components/TrophyCell.php | 9 ++++----- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/gatherling/Models/Event.php b/gatherling/Models/Event.php index 03d1643df..e72547a68 100644 --- a/gatherling/Models/Event.php +++ b/gatherling/Models/Event.php @@ -392,21 +392,6 @@ public function getPlaceDeck(string $placing = '1st'): ?Deck return new Deck($deckId); } - public function getPlacePlayer(string $placing = '1st'): ?string - { - $sql = ' - SELECT - n.player - FROM - entries n, events e - WHERE - n.event_id = e.id - AND n.medal = :medal - AND e.name = :name'; - $params = ['medal' => $placing, 'name' => $this->name]; - return db()->optionalString($sql, $params); - } - public function decklistsVisible(): bool { return ($this->finalized && !$this->active) || $this->private_decks == 0 || ($this->current_round > $this->mainrounds && !$this->private_finals); diff --git a/gatherling/Views/Components/TrophyCell.php b/gatherling/Views/Components/TrophyCell.php index 30cb57bda..fcd10004f 100644 --- a/gatherling/Views/Components/TrophyCell.php +++ b/gatherling/Views/Components/TrophyCell.php @@ -19,13 +19,12 @@ public function __construct(Event $event) $this->trophySrc = Event::trophySrc($event->name); } $deck = $event->getPlaceDeck('1st'); - $winnerName = $event->getPlacePlayer('1st'); - if ($winnerName) { - $winner = new Player($winnerName); + $winner = $deck?->getPlayer(); + if ($winner) { $this->winner = [ 'playerLink' => new PlayerLink($winner), - 'manaSrc' => $deck?->manaSrc() ?? '', - 'deckLink' => $deck ? new DeckLink($deck) : null, + 'manaSrc' => $deck->manaSrc(), + 'deckLink' => new DeckLink($deck), ]; } } From 7b7bdefe28d7c3984668c9b4cf89d61fb534ff7d Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 15:21:35 -0700 Subject: [PATCH 007/114] Remove some unused funcs --- gatherling/Models/Event.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/gatherling/Models/Event.php b/gatherling/Models/Event.php index e72547a68..e3da1039a 100644 --- a/gatherling/Models/Event.php +++ b/gatherling/Models/Event.php @@ -450,12 +450,6 @@ public function setFinalists(string $win, ?string $sec, ?array $t4 = null, ?arra db()->commit('set_finalists'); } - public function getTrophyImageLink(): string - { - return "id}\" class=\"borderless\">\n" - . self::trophyImageTag($this->name) . "\n\n"; - } - public function isHost(string $name): bool { $ishost = !is_null($this->host) && strcasecmp($name, $this->host) == 0; @@ -1098,11 +1092,6 @@ public function setSeasonPointAdjustment(string $player, int $points, string $re $stmt->close(); } - public static function trophyImageTag(string $eventname): string - { - return ""; - } - public static function trophySrc(string $eventname): string { return 'displayTrophy.php?event=' . rawurlencode($eventname); From 8d3e249685320b0e3d148b652885ad85797bb57e Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 15:33:41 -0700 Subject: [PATCH 008/114] Don't show a big "registered players" heading if we're not going to show any --- gatherling/Views/Components/FullMetagame.php | 2 ++ gatherling/templates/partials/fullMetagame.mustache | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/gatherling/Views/Components/FullMetagame.php b/gatherling/Views/Components/FullMetagame.php index 6cab03166..a32ea8c40 100644 --- a/gatherling/Views/Components/FullMetagame.php +++ b/gatherling/Views/Components/FullMetagame.php @@ -23,6 +23,7 @@ class FullMetagame extends Component public array $players; public bool $showTribes = false; public EventStandings $eventStandings; + public bool $showRegisteredPlayers; public function __construct(Event $event) { @@ -109,6 +110,7 @@ public function __construct(Event $event) $this->players[] = $info; } } + $this->showRegisteredPlayers = !$this->decklistsAreVisible && count($this->players) > 0; if ($event->active || $event->finalized) { $this->eventStandings = new EventStandings($event->name, session()->optionalString('username')); } diff --git a/gatherling/templates/partials/fullMetagame.mustache b/gatherling/templates/partials/fullMetagame.mustache index 21b50a569..ffc7f3d2a 100644 --- a/gatherling/templates/partials/fullMetagame.mustache +++ b/gatherling/templates/partials/fullMetagame.mustache @@ -24,15 +24,15 @@ {{/entries}} {{/meta}} {{/decklistsAreVisible}} - {{^decklistsAreVisible}} + {{#showRegisteredPlayers}}

Registered Players

-

Deck lists are not shown for privacy until event is finalized.

-
+

Deck lists are not shown for privacy until event is finalized.

+ {{#players}} @@ -43,6 +43,6 @@ {{/players}} - {{/decklistsAreVisible}} + {{/showRegisteredPlayers}} {{#eventStandings}}{{> eventStandings}}{{/eventStandings}} From cbd989a728798c17434ccbf3ce26809d39b8db50 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 16:39:41 -0700 Subject: [PATCH 009/114] Force consistent use of title by making it a required constructor arg You can still forget to call parent constructor but at least it's consistent now and I found a couple spots where we were not providing a title. --- gatherling/Views/Pages/AdminControlPanel.php | 3 +-- gatherling/Views/Pages/AuthDebug.php | 3 +-- gatherling/Views/Pages/AuthFailed.php | 5 ++++- gatherling/Views/Pages/BannedPlayer.php | 3 +-- gatherling/Views/Pages/CardsAdmin.php | 3 +-- gatherling/Views/Pages/Deck.php | 3 +-- gatherling/Views/Pages/DeckSearch.php | 3 +-- gatherling/Views/Pages/EventFrame.php | 3 +-- gatherling/Views/Pages/EventList.php | 3 +-- gatherling/Views/Pages/EventReport.php | 3 +-- gatherling/Views/Pages/Forgot.php | 3 +-- gatherling/Views/Pages/FormatAdmin.php | 3 +-- gatherling/Views/Pages/Home.php | 3 +-- gatherling/Views/Pages/InsertCardSet.php | 3 +-- gatherling/Views/Pages/InsufficientPermissions.php | 2 +- gatherling/Views/Pages/Login.php | 3 +-- gatherling/Views/Pages/Page.php | 3 +-- gatherling/Views/Pages/PlayerControlPanel.php | 3 +-- gatherling/Views/Pages/PlayerEventList.php | 2 +- gatherling/Views/Pages/Profile.php | 5 +---- gatherling/Views/Pages/PromptLinkAccount.php | 3 +-- gatherling/Views/Pages/Ratings.php | 3 +-- gatherling/Views/Pages/Register.php | 3 +-- gatherling/Views/Pages/Report.php | 3 +-- gatherling/Views/Pages/Series.php | 3 +-- gatherling/Views/Pages/SeriesControlPanel.php | 3 +-- gatherling/Views/Pages/SeriesReport.php | 3 +-- phpstan-baseline.neon | 5 ----- psalm-baseline.xml | 9 +++++---- 29 files changed, 35 insertions(+), 62 deletions(-) diff --git a/gatherling/Views/Pages/AdminControlPanel.php b/gatherling/Views/Pages/AdminControlPanel.php index ba60e155c..152566ef0 100644 --- a/gatherling/Views/Pages/AdminControlPanel.php +++ b/gatherling/Views/Pages/AdminControlPanel.php @@ -12,8 +12,7 @@ class AdminControlPanel extends Page public function __construct(public string $result, Component $viewComponent) { - parent::__construct(); - $this->title = 'Admin Control Panel'; + parent::__construct('Admin Control Panel'); $this->viewSafe = $viewComponent->render(); } } diff --git a/gatherling/Views/Pages/AuthDebug.php b/gatherling/Views/Pages/AuthDebug.php index 25c7b9a6d..717bb404d 100644 --- a/gatherling/Views/Pages/AuthDebug.php +++ b/gatherling/Views/Pages/AuthDebug.php @@ -13,8 +13,7 @@ class AuthDebug extends Page public function __construct(AccessToken $token) { - parent::__construct(); - $this->title = 'Auth Debug'; + parent::__construct('Auth Debug'); $this->authDebugInfo = new AuthDebugInfo($token); } } diff --git a/gatherling/Views/Pages/AuthFailed.php b/gatherling/Views/Pages/AuthFailed.php index a1e79bbc2..bee48652c 100644 --- a/gatherling/Views/Pages/AuthFailed.php +++ b/gatherling/Views/Pages/AuthFailed.php @@ -6,5 +6,8 @@ class AuthFailed extends Page { - public string $title = 'Event Host Control Panel'; + public function __construct() + { + parent::__construct('Event Host Control Panel'); + } } diff --git a/gatherling/Views/Pages/BannedPlayer.php b/gatherling/Views/Pages/BannedPlayer.php index 838867363..70fd98142 100644 --- a/gatherling/Views/Pages/BannedPlayer.php +++ b/gatherling/Views/Pages/BannedPlayer.php @@ -8,7 +8,6 @@ class BannedPlayer extends Page { public function __construct() { - parent::__construct(); - $this->title = 'You have been banned'; + parent::__construct('You have been banned'); } } diff --git a/gatherling/Views/Pages/CardsAdmin.php b/gatherling/Views/Pages/CardsAdmin.php index cba0d8052..53c3aefba 100644 --- a/gatherling/Views/Pages/CardsAdmin.php +++ b/gatherling/Views/Pages/CardsAdmin.php @@ -12,8 +12,7 @@ class CardsAdmin extends Page public function __construct(Component $viewComponent) { - parent::__construct(); - $this->title = 'Admin Control Panel'; + parent::__construct('Admin Control Panel'); $this->viewSafe = $viewComponent->render(); } } diff --git a/gatherling/Views/Pages/Deck.php b/gatherling/Views/Pages/Deck.php index 30caf9fc4..c679547cb 100644 --- a/gatherling/Views/Pages/Deck.php +++ b/gatherling/Views/Pages/Deck.php @@ -12,8 +12,7 @@ class Deck extends Page public function __construct(public string $title, Component $viewComponent) { - parent::__construct(); - $this->title = 'Deck Database'; + parent::__construct('Deck Database'); $this->viewSafe = $viewComponent->render(); } } diff --git a/gatherling/Views/Pages/DeckSearch.php b/gatherling/Views/Pages/DeckSearch.php index a9983e3ad..09a2088c0 100644 --- a/gatherling/Views/Pages/DeckSearch.php +++ b/gatherling/Views/Pages/DeckSearch.php @@ -21,8 +21,7 @@ class DeckSearch extends Page */ public function __construct(array $results, string $phpSelf, public array $errors, string $playerName, string $cardName, string $formatName, string $archetype, string $seriesName, string $medals, array $colors) { - parent::__construct(); - $this->title = 'Deck Search'; + parent::__construct('Deck Search'); $this->searchForm = new SearchForm(count($results), $phpSelf, $playerName, $cardName, $formatName, $archetype, $seriesName, $medals, $colors); if ($results) { $this->displayDecks = new DisplayDecks($results); diff --git a/gatherling/Views/Pages/EventFrame.php b/gatherling/Views/Pages/EventFrame.php index ac1768458..b9a7700ca 100644 --- a/gatherling/Views/Pages/EventFrame.php +++ b/gatherling/Views/Pages/EventFrame.php @@ -10,7 +10,6 @@ abstract class EventFrame extends Page { - public string $title = 'Event Host Control Panel'; /** @var array */ public array $event; /** @var list */ @@ -18,7 +17,7 @@ abstract class EventFrame extends Page public function __construct(Event $event) { - parent::__construct(); + parent::__construct('Event Host Control Panel'); $this->event = getObjectVarsCamelCase($event); $this->controlPanelLinks = $this->getControlPanelLinks($event); } diff --git a/gatherling/Views/Pages/EventList.php b/gatherling/Views/Pages/EventList.php index a62c81df6..2cbedb6d2 100644 --- a/gatherling/Views/Pages/EventList.php +++ b/gatherling/Views/Pages/EventList.php @@ -17,7 +17,6 @@ class EventList extends Page { - public string $title = 'Event Host Control Panel'; public ?HostActiveEvents $hostActiveEvents; public FormatDropMenu $formatDropMenu; public SeriesDropMenu $seriesDropMenu; @@ -29,7 +28,7 @@ class EventList extends Page public function __construct(string $seriesName, string $format, ?int $season) { - parent::__construct(); + parent::__construct('Event Host Control Panel'); $player = Player::getSessionPlayer(); $playerSeries = $player?->organizersSeries() ?? []; diff --git a/gatherling/Views/Pages/EventReport.php b/gatherling/Views/Pages/EventReport.php index 26e0e96a9..2def19aae 100644 --- a/gatherling/Views/Pages/EventReport.php +++ b/gatherling/Views/Pages/EventReport.php @@ -27,8 +27,7 @@ class EventReport extends Page public function __construct(Event $event, bool $canPrereg) { - parent::__construct(); - $this->title = 'Event Report'; + parent::__construct('Event Report'); $this->seriesLogoSrc = 'displaySeries.php?series=' . rawurlencode($event->series); $this->infoCell = new InfoCell($event); if ($event->finalized) { diff --git a/gatherling/Views/Pages/Forgot.php b/gatherling/Views/Pages/Forgot.php index e2472a89f..07ec1fccf 100644 --- a/gatherling/Views/Pages/Forgot.php +++ b/gatherling/Views/Pages/Forgot.php @@ -17,8 +17,7 @@ class Forgot extends Page public function __construct(public bool $hasResetPassword, public bool $passwordResetFailed, public bool $showForgotForm, public bool $showNewPasswordForm, public ?string $token, public ?string $email, public bool $sentLoginLink, public bool $cantSendLoginLink, public bool $cantFindPlayer) { - parent::__construct(); - $this->title = 'Login'; + parent::__construct('Login'); if ($this->showNewPasswordForm) { $this->newPasswordInput = new PasswordInput('New Password', 'password'); $this->resetPassword = new Submit('Reset Password'); diff --git a/gatherling/Views/Pages/FormatAdmin.php b/gatherling/Views/Pages/FormatAdmin.php index 902d3078d..740179bce 100644 --- a/gatherling/Views/Pages/FormatAdmin.php +++ b/gatherling/Views/Pages/FormatAdmin.php @@ -22,8 +22,7 @@ class FormatAdmin extends Page */ public function __construct(string $action, array $playerSeries, string $seriesName, Format $activeFormat, Component|array $actionResultComponent, ?Component $viewComponent = null) { - parent::__construct(); - $this->title = 'Format Control Panel'; + parent::__construct('Format Control Panel'); $this->organizerSelect = count($playerSeries) > 1 ? new OrganizerSelect($action, $playerSeries, $seriesName) : null; $actionResultComponents = is_array($actionResultComponent) ? $actionResultComponent : [$actionResultComponent]; diff --git a/gatherling/Views/Pages/Home.php b/gatherling/Views/Pages/Home.php index 0f8f192cf..e4310160d 100644 --- a/gatherling/Views/Pages/Home.php +++ b/gatherling/Views/Pages/Home.php @@ -34,8 +34,7 @@ class Home extends Page */ public function __construct(array $activeEvents, array $upcomingEvents, public array $stats, ?Player $player, ?Event $mostRecentHostedEvent, array $recentWinners) { - parent::__construct(); - $this->title = 'Home'; + parent::__construct('Home'); foreach ($activeEvents as $event) { $this->activeEvents[] = [ 'name' => $event->name, diff --git a/gatherling/Views/Pages/InsertCardSet.php b/gatherling/Views/Pages/InsertCardSet.php index b163917d7..922f229ba 100644 --- a/gatherling/Views/Pages/InsertCardSet.php +++ b/gatherling/Views/Pages/InsertCardSet.php @@ -9,7 +9,6 @@ class InsertCardSet extends Page /** @param list $messages */ public function __construct(public array $messages) { - parent::__construct(); - $this->title = 'Insert Card Set'; + parent::__construct('Insert Card Set'); } } diff --git a/gatherling/Views/Pages/InsufficientPermissions.php b/gatherling/Views/Pages/InsufficientPermissions.php index 44989e9be..340057979 100644 --- a/gatherling/Views/Pages/InsufficientPermissions.php +++ b/gatherling/Views/Pages/InsufficientPermissions.php @@ -9,7 +9,7 @@ class InsufficientPermissions extends Page public string $errorMsg; public function __construct(bool $isOrganizer) { - parent::__construct(); + parent::__construct('Forbidden'); http_response_code(403); if ($isOrganizer) { $this->errorMsg = "You're not authorized to edit this format! Access Restricted."; diff --git a/gatherling/Views/Pages/Login.php b/gatherling/Views/Pages/Login.php index 8e1537890..c06fcb111 100644 --- a/gatherling/Views/Pages/Login.php +++ b/gatherling/Views/Pages/Login.php @@ -14,7 +14,6 @@ public function __construct( public string $target, public string $discordId ) { - parent::__construct(); - $this->title = 'Login'; + parent::__construct('Login'); } } diff --git a/gatherling/Views/Pages/Page.php b/gatherling/Views/Pages/Page.php index 2acf5bb98..8d4642831 100644 --- a/gatherling/Views/Pages/Page.php +++ b/gatherling/Views/Pages/Page.php @@ -24,11 +24,10 @@ abstract class Page extends TemplateResponse public string $js; public ?Player $player; public string $siteName; - public string $title; public string $versionTagline; public string $jsLink; - public function __construct() + public function __construct(public string $title) { $this->siteName = config()->string('site_name', 'Gatherling'); $this->gitHash = git_hash(); diff --git a/gatherling/Views/Pages/PlayerControlPanel.php b/gatherling/Views/Pages/PlayerControlPanel.php index 09b63735e..c29b1ad04 100644 --- a/gatherling/Views/Pages/PlayerControlPanel.php +++ b/gatherling/Views/Pages/PlayerControlPanel.php @@ -13,8 +13,7 @@ class PlayerControlPanel extends Page public function __construct(string $result, Component $viewComponent) { - parent::__construct(); - $this->title = 'Player Control Panel'; + parent::__construct('Player Control Panel'); $this->result = $result; $this->viewSafe = $viewComponent->render(); } diff --git a/gatherling/Views/Pages/PlayerEventList.php b/gatherling/Views/Pages/PlayerEventList.php index 045ecad43..f66bc7d4f 100644 --- a/gatherling/Views/Pages/PlayerEventList.php +++ b/gatherling/Views/Pages/PlayerEventList.php @@ -21,7 +21,7 @@ class PlayerEventList extends Page /** @param list $events */ public function __construct(string $format, string $series, ?int $season, array $events) { - parent::__construct(); + parent::__construct('Event List'); $this->formatDropMenu = new FormatDropMenu($format, true); $this->seriesDropMenu = new SeriesDropMenu($series, 'All'); diff --git a/gatherling/Views/Pages/Profile.php b/gatherling/Views/Pages/Profile.php index f1a92bd4e..4a0655ea5 100644 --- a/gatherling/Views/Pages/Profile.php +++ b/gatherling/Views/Pages/Profile.php @@ -19,10 +19,7 @@ class Profile extends Page public function __construct(public string $playerName, ?Player $player, public int $profileEdit) { - parent::__construct(); - $this->title = 'Player Profile'; - - $this->playerSearchForm = new PlayerSearchForm($playerName); + parent::__construct('Player Profile'); if (rtrim($playerName) === '') { $this->isLoggedOut = true; diff --git a/gatherling/Views/Pages/PromptLinkAccount.php b/gatherling/Views/Pages/PromptLinkAccount.php index 318f00eed..7978b5be5 100644 --- a/gatherling/Views/Pages/PromptLinkAccount.php +++ b/gatherling/Views/Pages/PromptLinkAccount.php @@ -8,7 +8,6 @@ class PromptLinkAccount extends Page { public function __construct(public string $email) { - parent::__construct(); - $this->title = 'Login'; + parent::__construct('Login'); } } diff --git a/gatherling/Views/Pages/Ratings.php b/gatherling/Views/Pages/Ratings.php index 019072bdd..f261ab1f7 100644 --- a/gatherling/Views/Pages/Ratings.php +++ b/gatherling/Views/Pages/Ratings.php @@ -31,8 +31,7 @@ public function __construct( public array $ratingsData, Pagination $pagination, ) { - parent::__construct(); - $this->title = 'Ratings'; + parent::__construct('Ratings'); $this->formatDropMenuR = (new FormatDropMenuR($format)); $this->highestRatingDate = date('l, F j, Y', $highestRatingTimestamp); $this->lastTournamentDate = $lastTournamentDate->format('Y-m-d'); diff --git a/gatherling/Views/Pages/Register.php b/gatherling/Views/Pages/Register.php index 1e9986fa6..01f1f4c1e 100644 --- a/gatherling/Views/Pages/Register.php +++ b/gatherling/Views/Pages/Register.php @@ -14,8 +14,7 @@ class Register extends Page public function __construct(public bool $showRegForm, public string $message) { - parent::__construct(); - $this->title = 'Register'; + parent::__construct('Register'); $this->emailStatusDropMenu = new EmailStatusDropMenu(); $this->timeZoneDropMenu = new TimeZoneDropMenu(); } diff --git a/gatherling/Views/Pages/Report.php b/gatherling/Views/Pages/Report.php index 6586ffb7e..b9a4e4858 100644 --- a/gatherling/Views/Pages/Report.php +++ b/gatherling/Views/Pages/Report.php @@ -12,8 +12,7 @@ class Report extends Page public function __construct(public string $result, public Component $viewComponent) { - parent::__construct(); - $this->title = 'Player Control Panel'; + parent::__construct('Player Control Panel'); $this->viewSafe = $viewComponent->render(); } } diff --git a/gatherling/Views/Pages/Series.php b/gatherling/Views/Pages/Series.php index 9a30049ed..abcc9d855 100644 --- a/gatherling/Views/Pages/Series.php +++ b/gatherling/Views/Pages/Series.php @@ -16,8 +16,7 @@ class Series extends Page /** @param list $activeSeriesNames */ public function __construct(array $activeSeriesNames) { - parent::__construct(); - $this->title = 'Event Information'; + parent::__construct('Event Information'); $this->activeSeries = []; foreach ($activeSeriesNames as $seriesName) { $series = new SeriesModel($seriesName); diff --git a/gatherling/Views/Pages/SeriesControlPanel.php b/gatherling/Views/Pages/SeriesControlPanel.php index 179e4f23f..527b20815 100644 --- a/gatherling/Views/Pages/SeriesControlPanel.php +++ b/gatherling/Views/Pages/SeriesControlPanel.php @@ -16,8 +16,7 @@ class SeriesControlPanel extends Page public function __construct(?Series $activeSeries, ?Component $orientationComponent, public string $errorMsg, Component $viewComponent) { - parent::__construct(); - $this->title = 'Series Control Panel'; + parent::__construct('Series Control Panel'); $this->orientationSafe = $orientationComponent ? $orientationComponent->render() : ''; $this->seriesControlPanelMenu = $activeSeries ? new SeriesControlPanelMenu($activeSeries) : null; $this->viewSafe = $viewComponent->render(); diff --git a/gatherling/Views/Pages/SeriesReport.php b/gatherling/Views/Pages/SeriesReport.php index ba6ad158e..9b89886cc 100644 --- a/gatherling/Views/Pages/SeriesReport.php +++ b/gatherling/Views/Pages/SeriesReport.php @@ -16,8 +16,7 @@ class SeriesReport extends Page public function __construct(?string $seriesName, ?int $season) { - parent::__construct(); - $this->title = 'Season Report'; + parent::__construct('Season Report'); $this->seasonSelect = new SeasonSelect($seriesName, $season); if ($seriesName && $season) { $series = new Series($seriesName); diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index cc97dc13c..4e050eacc 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -285,11 +285,6 @@ parameters: count: 1 path: gatherling/Models/Event.php - - - message: "#^Parameter \\#1 \\$eventname of static method Gatherling\\\\Models\\\\Event\\:\\:trophyImageTag\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Models/Event.php - - message: "#^Parameter \\#1 \\$eventname of static method Gatherling\\\\Models\\\\Standings\\:\\:getEventStandings\\(\\) expects string, string\\|null given\\.$#" count: 1 diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 9143b2824..f10081bc4 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -16,9 +16,6 @@ - - - @@ -45,7 +42,6 @@ ]]> - @@ -170,6 +166,11 @@ + + + + + availableEvents]]> From 42e5071a8e47bb2e4a28f838d5a37383928512e2 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 16:48:06 -0700 Subject: [PATCH 010/114] Don't "self close" img tags, that hasn't been the recommended style since html5 --- gatherling/templates/eventReport.mustache | 2 +- gatherling/templates/medalList.mustache | 2 +- gatherling/templates/partials/allDecks.mustache | 2 +- gatherling/templates/partials/allRatings.mustache | 2 +- .../templates/partials/colorCheckboxMenu.mustache | 10 +++++----- gatherling/templates/partials/colorImages.mustache | 2 +- gatherling/templates/partials/finalists.mustache | 4 ++-- gatherling/templates/partials/logoForm.mustache | 2 +- gatherling/templates/partials/medal.mustache | 2 +- gatherling/templates/partials/metaStats.mustache | 10 +++++----- gatherling/templates/partials/statsTable.mustache | 2 +- gatherling/templates/partials/symbolTable.mustache | 2 +- gatherling/templates/partials/testIcon.mustache | 2 +- gatherling/templates/partials/trophy.mustache | 2 +- gatherling/templates/partials/trophyCell.mustache | 4 ++-- gatherling/templates/partials/trophyField.mustache | 2 +- gatherling/templates/playerList.mustache | 2 +- gatherling/templates/pointsAdjustmentForm.mustache | 2 +- gatherling/templates/series.mustache | 2 +- tests/Views/TemplateHelperTest.php | 4 ++-- 20 files changed, 31 insertions(+), 31 deletions(-) diff --git a/gatherling/templates/eventReport.mustache b/gatherling/templates/eventReport.mustache index e20b52f1b..9a089578c 100644 --- a/gatherling/templates/eventReport.mustache +++ b/gatherling/templates/eventReport.mustache @@ -7,7 +7,7 @@ diff --git a/gatherling/templates/medalList.mustache b/gatherling/templates/medalList.mustache index 49c68fe5d..0d21505eb 100644 --- a/gatherling/templates/medalList.mustache +++ b/gatherling/templates/medalList.mustache @@ -22,7 +22,7 @@ {{#finalists}} - {{medal}} + {{medal}} {{#playerDropMenu}} diff --git a/gatherling/templates/partials/allDecks.mustache b/gatherling/templates/partials/allDecks.mustache index f1890c689..8f4619388 100644 --- a/gatherling/templates/partials/allDecks.mustache +++ b/gatherling/templates/partials/allDecks.mustache @@ -8,7 +8,7 @@ {{#decks}} - {{#medalSrc}}Medal{{/medalSrc}} + {{#medalSrc}}Medal{{/medalSrc}} {{recordString}} {{#deckLink}}{{> deckLink}}{{/deckLink}}{{^isValid}}*{{/isValid}} {{eventName}} diff --git a/gatherling/templates/partials/allRatings.mustache b/gatherling/templates/partials/allRatings.mustache index f12a425a0..83f85a253 100644 --- a/gatherling/templates/partials/allRatings.mustache +++ b/gatherling/templates/partials/allRatings.mustache @@ -53,7 +53,7 @@ {{eventName}} {{#deckLink}}{{> deckLink}}{{/deckLink}} {{wl}} - {{#medalSrc}}Medal{{/medalSrc}} + {{#medalSrc}}Medal{{/medalSrc}} {{postEventRating}} {{/entries}} diff --git a/gatherling/templates/partials/colorCheckboxMenu.mustache b/gatherling/templates/partials/colorCheckboxMenu.mustache index 0670df05f..a88b98280 100644 --- a/gatherling/templates/partials/colorCheckboxMenu.mustache +++ b/gatherling/templates/partials/colorCheckboxMenu.mustache @@ -1,5 +1,5 @@ - - - - - + + + + + diff --git a/gatherling/templates/partials/colorImages.mustache b/gatherling/templates/partials/colorImages.mustache index 0761ee5b9..eb67ad713 100644 --- a/gatherling/templates/partials/colorImages.mustache +++ b/gatherling/templates/partials/colorImages.mustache @@ -1 +1 @@ -{{#colors}}{{color}}{{/colors}} +{{#colors}}{{color}}{{/colors}} diff --git a/gatherling/templates/partials/finalists.mustache b/gatherling/templates/partials/finalists.mustache index 3549fdb0b..9489ace63 100644 --- a/gatherling/templates/partials/finalists.mustache +++ b/gatherling/templates/partials/finalists.mustache @@ -5,11 +5,11 @@ {{#finalists}} - + {{medalText}} - {{#manaSrc}}Deck Colors{{/manaSrc}} + {{#manaSrc}}Deck Colors{{/manaSrc}} {{#deckLink}}{{> deckLink}}{{/deckLink}}{{^deckIsValid}}*{{/deckIsValid}} diff --git a/gatherling/templates/partials/logoForm.mustache b/gatherling/templates/partials/logoForm.mustache index 595f183c6..1e3086886 100644 --- a/gatherling/templates/partials/logoForm.mustache +++ b/gatherling/templates/partials/logoForm.mustache @@ -3,7 +3,7 @@ Current Logo - {{seriesName}} logo + {{seriesName}} logo Upload New Logo diff --git a/gatherling/templates/partials/medal.mustache b/gatherling/templates/partials/medal.mustache index 9753fd7c3..92d38e77c 100644 --- a/gatherling/templates/partials/medal.mustache +++ b/gatherling/templates/partials/medal.mustache @@ -1 +1 @@ - + diff --git a/gatherling/templates/partials/metaStats.mustache b/gatherling/templates/partials/metaStats.mustache index 7d31fa26b..3c1b923e1 100644 --- a/gatherling/templates/partials/metaStats.mustache +++ b/gatherling/templates/partials/metaStats.mustache @@ -12,11 +12,11 @@   - White mana symbol - Green mana symbol - Blue mana symbol - Red mana symbol - Black mana symbol + White mana symbol + Green mana symbol + Blue mana symbol + Red mana symbol + Black mana symbol {{#colors}} diff --git a/gatherling/templates/partials/statsTable.mustache b/gatherling/templates/partials/statsTable.mustache index 25ad89e40..62526fd8f 100644 --- a/gatherling/templates/partials/statsTable.mustache +++ b/gatherling/templates/partials/statsTable.mustache @@ -52,7 +52,7 @@ {{#mostRecentTrophySrc}} - Trophy + Trophy {{/mostRecentTrophySrc}} {{^mostRecentTrophySrc}} diff --git a/gatherling/templates/partials/symbolTable.mustache b/gatherling/templates/partials/symbolTable.mustache index 3faccc8b5..b1bf3ccd8 100644 --- a/gatherling/templates/partials/symbolTable.mustache +++ b/gatherling/templates/partials/symbolTable.mustache @@ -5,7 +5,7 @@ {{#colors}} - {{color}} + {{color}}   {{num}} diff --git a/gatherling/templates/partials/testIcon.mustache b/gatherling/templates/partials/testIcon.mustache index 1d9d76be0..75d6104dc 100644 --- a/gatherling/templates/partials/testIcon.mustache +++ b/gatherling/templates/partials/testIcon.mustache @@ -1 +1 @@ -{{name}} icon +{{name}} icon diff --git a/gatherling/templates/partials/trophy.mustache b/gatherling/templates/partials/trophy.mustache index c08dfff5e..fed8d4a0e 100644 --- a/gatherling/templates/partials/trophy.mustache +++ b/gatherling/templates/partials/trophy.mustache @@ -1 +1 @@ - + diff --git a/gatherling/templates/partials/trophyCell.mustache b/gatherling/templates/partials/trophyCell.mustache index bad2c3121..cee2dc9d7 100644 --- a/gatherling/templates/partials/trophyCell.mustache +++ b/gatherling/templates/partials/trophyCell.mustache @@ -1,12 +1,12 @@ {{#trophySrc}} - Trophy + Trophy {{/trophySrc}} {{^winner}}
No winner yet! {{/winner}} {{#winner}} {{#playerLink}}{{> playerLink}}{{/playerLink}} - Deck Colors + Deck Colors {{#deckLink}}{{> deckLink}}{{/deckLink}}
{{/winner}} diff --git a/gatherling/templates/partials/trophyField.mustache b/gatherling/templates/partials/trophyField.mustache index 1ec64ed96..8fc90adc9 100644 --- a/gatherling/templates/partials/trophyField.mustache +++ b/gatherling/templates/partials/trophyField.mustache @@ -4,7 +4,7 @@ - Trophy + Trophy {{/hasTrophy}} diff --git a/gatherling/templates/playerList.mustache b/gatherling/templates/playerList.mustache index eeaf68579..4c860c847 100644 --- a/gatherling/templates/playerList.mustache +++ b/gatherling/templates/playerList.mustache @@ -66,7 +66,7 @@ {{/canUndrop}} {{#medalSrc}} - Medal + Medal {{/medalSrc}} diff --git a/gatherling/templates/pointsAdjustmentForm.mustache b/gatherling/templates/pointsAdjustmentForm.mustache index 94521ac73..2a666fc89 100644 --- a/gatherling/templates/pointsAdjustmentForm.mustache +++ b/gatherling/templates/pointsAdjustmentForm.mustache @@ -17,7 +17,7 @@ {{player.name}} {{#medal}}medal{{/medal}} - {{#verifiedSrc}}Player posted deck{{/verifiedSrc}} + {{#verifiedSrc}}Player posted deck{{/verifiedSrc}} diff --git a/gatherling/templates/series.mustache b/gatherling/templates/series.mustache index d7bdd13b2..9484d6f2b 100644 --- a/gatherling/templates/series.mustache +++ b/gatherling/templates/series.mustache @@ -5,7 +5,7 @@

{{seriesName}}

diff --git a/tests/Views/TemplateHelperTest.php b/tests/Views/TemplateHelperTest.php index f97a58bf4..b761ba318 100644 --- a/tests/Views/TemplateHelperTest.php +++ b/tests/Views/TemplateHelperTest.php @@ -54,8 +54,8 @@ public function __construct(public string $name, public array $items) $expected = "

Fun & Games

" . "
    " - . '
  • icon1 icon Stick
  • ' - . '
  • icon2 icon Bat & Ball
  • ' + . '
  • icon1 icon Stick
  • ' + . '
  • icon2 icon Bat & Ball
  • ' . '
  • Crossbow
  • ' . "
"; $this->assertEquals($expected, str_replace("\n", '', $actual)); From 4fcadb28a061bc8e15a46ad9ab29a4c679fe92df Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 16:49:19 -0700 Subject: [PATCH 011/114] Don't self close br tags, use html5 preferred style --- gatherling/Views/Components/CommentsTable.php | 2 +- gatherling/action.php | 4 ++-- gatherling/templates/cardsAdmin.mustache | 2 +- gatherling/templates/home.mustache | 2 +- gatherling/templates/matchList.mustache | 10 +++++----- gatherling/templates/partials/allRatings.mustache | 2 +- gatherling/templates/partials/authDebugInfo.mustache | 2 +- gatherling/templates/partials/authFailed.mustache | 2 +- gatherling/templates/partials/deckNotAllowed.mustache | 2 +- gatherling/templates/partials/displayDecks.mustache | 6 +++--- .../templates/partials/mainPlayerControlPanel.mustache | 4 ++-- .../templates/partials/manualVerifyMtgoForm.mustache | 4 ++-- gatherling/templates/partials/mostPlayedDecks.mustache | 4 ++-- gatherling/templates/partials/noSeries.mustache | 2 +- gatherling/templates/partials/searchForm.mustache | 4 ++-- .../templates/partials/submitLeagueResultForm.mustache | 4 ++-- gatherling/templates/partials/verifyMtgoForm.mustache | 6 +++--- gatherling/templates/pointsAdjustmentForm.mustache | 2 +- gatherling/templates/promptLinkAccount.mustache | 2 +- 19 files changed, 33 insertions(+), 33 deletions(-) diff --git a/gatherling/Views/Components/CommentsTable.php b/gatherling/Views/Components/CommentsTable.php index ee0fac94c..d50c135a4 100644 --- a/gatherling/Views/Components/CommentsTable.php +++ b/gatherling/Views/Components/CommentsTable.php @@ -14,7 +14,7 @@ public function __construct(string $notes) { $notes = strip_tags($notes); $notes = htmlspecialchars($notes, ENT_QUOTES | ENT_HTML5, 'UTF-8'); - $notes = preg_replace("/\n/", '
', $notes) ?? $notes; + $notes = preg_replace("/\n/", '
', $notes) ?? $notes; $notes = preg_replace("/\[b\]/", '', $notes) ?? $notes; $notes = preg_replace("/\[\/b\]/", '', $notes) ?? $notes; $notes = preg_replace("/\[i\]/", '', $notes) ?? $notes; diff --git a/gatherling/action.php b/gatherling/action.php index 72ae3f5c1..9f4cd6a49 100644 --- a/gatherling/action.php +++ b/gatherling/action.php @@ -35,7 +35,7 @@ function main(): void $series = new Series($player_series); if ($series->active) { if (is_null($series->nextEvent())) { - $message = "Your series $player_series doesn't have an upcoming event.
"; + $message = "Your series $player_series doesn't have an upcoming event.
"; $mostRecentEvent = $series->mostRecentEvent(); $nameMostRecent = $mostRecentEvent ? $mostRecentEvent->name : null; if (is_null($nameMostRecent) || $nameMostRecent == '') { @@ -48,7 +48,7 @@ function main(): void } $recent = $series->mostRecentEvent(); if ($recent && !$recent->finalized && !$recent->active && !empty($recent->name)) { - $message = "Your event id}\">{$recent->name} is ready to start.
"; + $message = "Your event id}\">{$recent->name} is ready to start.
"; $reg = count($recent->getPlayers()); $valid = count($recent->getRegisteredPlayers()); $message .= "It has $reg entries, of whom $valid have valid decklists."; diff --git a/gatherling/templates/cardsAdmin.mustache b/gatherling/templates/cardsAdmin.mustache index 67dcccc4d..235b88560 100644 --- a/gatherling/templates/cardsAdmin.mustache +++ b/gatherling/templates/cardsAdmin.mustache @@ -3,7 +3,7 @@

Admin Control Panel

- Welcome to the Database Viewer!
+ Welcome to the Database Viewer!
diff --git a/gatherling/templates/home.mustache b/gatherling/templates/home.mustache index 44ab552d3..1449fcb2a 100644 --- a/gatherling/templates/home.mustache +++ b/gatherling/templates/home.mustache @@ -89,7 +89,7 @@

- Need to register?
+ Need to register?
Forgot your password?

{{/player}} diff --git a/gatherling/templates/matchList.mustache b/gatherling/templates/matchList.mustache index 0c4336c2f..d59d20cd3 100644 --- a/gatherling/templates/matchList.mustache +++ b/gatherling/templates/matchList.mustache @@ -3,8 +3,8 @@

Host Control Panel

{{> controlPanel}}

- Match List
- * denotes a playoff/finals match.
+ Match List
+ * denotes a playoff/finals match.
To drop a player while entering match results, select the check box next to the players name.

@@ -136,13 +136,13 @@ This event will be {{structureSummary}}

{{/isBeforeRoundTwo}} {{#lastRound}} - Pairings for Round {{round}} {{extraRoundTitle}}
+ Pairings for Round {{round}} {{extraRoundTitle}}
{{#matches}} {{#isActiveUnverified}} {{#gameNameA}}{{> gameName}}{{/gameNameA}} vs. {{#gameNameB}}{{> gameName}}{{/gameNameB}}
{{/isActiveUnverified}} {{#isBye}} - {{#gameNameA}}{{> gameName}}{{/gameNameA}} has the BYE
+ {{#gameNameA}}{{> gameName}}{{/gameNameA}} has the BYE
{{/isBye}} {{#isDraw}} {{#gameNameA}}{{> gameName}}{{/gameNameA}} {{playerAWins}}-{{playerBWins}} {{#gameNameB}}{{> gameName}}{{/gameNameB}}
@@ -152,7 +152,7 @@ {{/hasResult}} {{/matches}} {{/lastRound}} - Good luck everyone!
+ Good luck everyone!

{{/event.finalized}} diff --git a/gatherling/templates/partials/allRatings.mustache b/gatherling/templates/partials/allRatings.mustache index 83f85a253..47180fc60 100644 --- a/gatherling/templates/partials/allRatings.mustache +++ b/gatherling/templates/partials/allRatings.mustache @@ -29,7 +29,7 @@
Show history for {{#formatDropMenu}}{{> dropMenu}}{{/formatDropMenu}} -

+

diff --git a/gatherling/templates/partials/authDebugInfo.mustache b/gatherling/templates/partials/authDebugInfo.mustache index 190236e33..dcdf9e0d2 100644 --- a/gatherling/templates/partials/authDebugInfo.mustache +++ b/gatherling/templates/partials/authDebugInfo.mustache @@ -4,7 +4,7 @@ Refresh token: {{refreshToken}}
Expires: {{expires}} - {{^hasExpired}}not{{/hasExpired}} expired
Values:
{{#values}} - {{key}}={{value}}
+ {{key}}={{value}}
{{/values}} {{#username}} diff --git a/gatherling/templates/partials/authFailed.mustache b/gatherling/templates/partials/authFailed.mustache index 1015dc961..f0d5fb7fb 100644 --- a/gatherling/templates/partials/authFailed.mustache +++ b/gatherling/templates/partials/authFailed.mustache @@ -1,4 +1,4 @@ -You are not permitted to make that change. Reasons why you cannot make changes to a deck are:
+You are not permitted to make that change. Reasons why you cannot make changes to a deck are:
  • You are not the player who played/created the deck for this event.
  • The event has already started and become active.
  • diff --git a/gatherling/templates/partials/deckNotAllowed.mustache b/gatherling/templates/partials/deckNotAllowed.mustache index 2a2f9f683..f0f46b8c8 100644 --- a/gatherling/templates/partials/deckNotAllowed.mustache +++ b/gatherling/templates/partials/deckNotAllowed.mustache @@ -1,5 +1,5 @@
    You do not have permission to view this deck. Decks are anonymous for privacy until event is finalized.
    -

    +

    People who can see decks while events are active:
    • Gatherling Admins
    • diff --git a/gatherling/templates/partials/displayDecks.mustache b/gatherling/templates/partials/displayDecks.mustache index 9577adc2c..3e2714a9a 100644 --- a/gatherling/templates/partials/displayDecks.mustache +++ b/gatherling/templates/partials/displayDecks.mustache @@ -22,7 +22,7 @@
-
+
{{{paginationSafe}}} -
-
+
+
diff --git a/gatherling/templates/partials/mainPlayerControlPanel.mustache b/gatherling/templates/partials/mainPlayerControlPanel.mustache index b4af01340..a6c84f015 100644 --- a/gatherling/templates/partials/mainPlayerControlPanel.mustache +++ b/gatherling/templates/partials/mainPlayerControlPanel.mustache @@ -51,7 +51,7 @@
- ACTIONS
+ ACTIONS
- CONNECTIONS
+ CONNECTIONS
    {{^mtgoUsername}}
  • Add a Magic Online account
  • diff --git a/gatherling/templates/partials/manualVerifyMtgoForm.mustache b/gatherling/templates/partials/manualVerifyMtgoForm.mustache index 544b5d139..04cf81e6f 100644 --- a/gatherling/templates/partials/manualVerifyMtgoForm.mustache +++ b/gatherling/templates/partials/manualVerifyMtgoForm.mustache @@ -4,6 +4,6 @@ {{/isVerified}} {{^isVerified}}

    Verifying your MTGO account

    - Verify your MTGO account in one simple step:
    - 1. Contact an admin via the Discord server.
    + Verify your MTGO account in one simple step:
    + 1. Contact an admin via the Discord server.
    {{/isVerified}} diff --git a/gatherling/templates/partials/mostPlayedDecks.mustache b/gatherling/templates/partials/mostPlayedDecks.mustache index 1e0efaaf0..5e34659f8 100644 --- a/gatherling/templates/partials/mostPlayedDecks.mustache +++ b/gatherling/templates/partials/mostPlayedDecks.mustache @@ -1,4 +1,4 @@ -
    +

    Most Played Decks

    @@ -24,4 +24,4 @@
    -
    +
    diff --git a/gatherling/templates/partials/noSeries.mustache b/gatherling/templates/partials/noSeries.mustache index b49412bec..ab5fd5c1a 100644 --- a/gatherling/templates/partials/noSeries.mustache +++ b/gatherling/templates/partials/noSeries.mustache @@ -1,4 +1,4 @@
    - You're not a organizer of any series, so you can't use this page.
    + You're not a organizer of any series, so you can't use this page.
    Back to the Player Control Panel
    diff --git a/gatherling/templates/partials/searchForm.mustache b/gatherling/templates/partials/searchForm.mustache index 9210f86b6..4fab5b2d7 100644 --- a/gatherling/templates/partials/searchForm.mustache +++ b/gatherling/templates/partials/searchForm.mustache @@ -1,4 +1,4 @@ -
    +
    @@ -20,7 +20,7 @@
    -
    +
    diff --git a/gatherling/templates/partials/submitLeagueResultForm.mustache b/gatherling/templates/partials/submitLeagueResultForm.mustache index 1ce3e605b..38685db31 100644 --- a/gatherling/templates/partials/submitLeagueResultForm.mustache +++ b/gatherling/templates/partials/submitLeagueResultForm.mustache @@ -18,7 +18,7 @@ - I won the match 2-0
    + I won the match 2-0
    @@ -29,7 +29,7 @@ - + diff --git a/gatherling/templates/partials/submitResultForm.mustache b/gatherling/templates/partials/submitResultForm.mustache index dedd474fa..f2f812998 100644 --- a/gatherling/templates/partials/submitResultForm.mustache +++ b/gatherling/templates/partials/submitResultForm.mustache @@ -4,26 +4,26 @@ - - - - + + + + - + - + - + - + {{#allowsPlayerReportedDraws}} - + {{/allowsPlayerReportedDraws}} @@ -37,7 +37,7 @@
    I won the match 2-0 I won the match 2-0
    I won the match 2-1I won the match 2-1
    I lost the match 0-2 I lost the match 0-2
    I lost the match 1-2I lost the match 1-2
    The match was a drawThe match was a draw
    - +
    diff --git a/gatherling/templates/partials/tribalBAndR.mustache b/gatherling/templates/partials/tribalBAndR.mustache index 37ea82afb..eb14e3e0e 100644 --- a/gatherling/templates/partials/tribalBAndR.mustache +++ b/gatherling/templates/partials/tribalBAndR.mustache @@ -1,8 +1,8 @@

    Restricted To Tribe List: {{cardCount}} Cards

    - - - + + + @@ -15,7 +15,7 @@ {{^cardLink}}{{> cardName}}
    {{/cardLink}} {{/restrictedToTribe}} @@ -34,10 +34,10 @@
    Card Name - +
    - + - +
    @@ -45,9 +45,9 @@

    Tribe Banlist

    - - - + + + @@ -57,7 +57,7 @@ {{/tribesBanned}} @@ -76,11 +76,11 @@ {{#tribeBanDropMenu}}{{> dropMenu}}{{/tribeBanDropMenu}}
    Tribe Name
    {{.}} - +
    - - + + - +
    @@ -88,9 +88,9 @@

    Subtype Banlist

    - - - + + + @@ -100,7 +100,7 @@ {{/subTypesBanned}} @@ -117,8 +117,8 @@ {{#subTypeBanDropMenu}}{{> dropMenu}}{{/subTypeBanDropMenu}}
    Tribe Name
    {{.}} - +
    - - + +
    diff --git a/gatherling/templates/partials/verifyMtgoForm.mustache b/gatherling/templates/partials/verifyMtgoForm.mustache index d2947a444..396fd9760 100644 --- a/gatherling/templates/partials/verifyMtgoForm.mustache +++ b/gatherling/templates/partials/verifyMtgoForm.mustache @@ -10,17 +10,17 @@ Verify your MTGO account by following these simple steps:
    {{/isVerified}} {{^isVerified}} - - - + + + - +
    Verification Code
    - +
    diff --git a/gatherling/templates/partials/verifyResultForm.mustache b/gatherling/templates/partials/verifyResultForm.mustache index 015bc6ffa..cefabddb0 100644 --- a/gatherling/templates/partials/verifyResultForm.mustache +++ b/gatherling/templates/partials/verifyResultForm.mustache @@ -20,15 +20,15 @@ {{#isDrop}} - + {{/isDrop}} - - - - - - - + + + + + + + @@ -36,10 +36,10 @@ where there is no match_id, but it currently does not so don't show it. --> {{#matchId}}
    - - - - + + + +
    {{/matchId}} diff --git a/gatherling/templates/pointsAdjustmentForm.mustache b/gatherling/templates/pointsAdjustmentForm.mustache index 48d23df63..b0239c231 100644 --- a/gatherling/templates/pointsAdjustmentForm.mustache +++ b/gatherling/templates/pointsAdjustmentForm.mustache @@ -3,7 +3,7 @@

    Host Control Panel

    {{> controlPanel}}
    - + @@ -19,16 +19,16 @@ {{/entries}}
    {{#medal}}medal{{/medal}} {{#verifiedSrc}}Player posted deck{{/verifiedSrc}} - + - +
    - +
    diff --git a/gatherling/templates/ratings.mustache b/gatherling/templates/ratings.mustache index 0035fefc7..7a77e8f6a 100644 --- a/gatherling/templates/ratings.mustache +++ b/gatherling/templates/ratings.mustache @@ -8,7 +8,7 @@ Select a rating to display: {{#formatDropMenuR}}{{> dropMenu}}{{/formatDropMenuR}}   - + From fd480df07312b28a769b709896148e6ea9b4fed4 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 17:02:42 -0700 Subject: [PATCH 013/114] Clean up the last of the self closing html elements, we now use html5 style --- gatherling/templates/partials/cardsetDropMenu.mustache | 2 +- .../templates/partials/createNewSeriesForm.mustache | 8 ++++---- gatherling/templates/partials/deckForm.mustache | 2 +- gatherling/templates/partials/playerBanForm.mustache | 2 +- gatherling/templates/partials/pointsRule.mustache | 2 +- .../templates/partials/seriesOrganizersForm.mustache | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/gatherling/templates/partials/cardsetDropMenu.mustache b/gatherling/templates/partials/cardsetDropMenu.mustache index 0526f05fe..e9d6c85ed 100644 --- a/gatherling/templates/partials/cardsetDropMenu.mustache +++ b/gatherling/templates/partials/cardsetDropMenu.mustache @@ -4,5 +4,5 @@ {{/options}} {{#hasMany}} - + {{/hasMany}} diff --git a/gatherling/templates/partials/createNewSeriesForm.mustache b/gatherling/templates/partials/createNewSeriesForm.mustache index d9c00928e..a07562eb1 100644 --- a/gatherling/templates/partials/createNewSeriesForm.mustache +++ b/gatherling/templates/partials/createNewSeriesForm.mustache @@ -1,10 +1,10 @@

    Create New Series

    - + @@ -35,13 +35,13 @@
    - New Series Name: + New Series Name:
    Pre-Registration Default - +
    - +
    diff --git a/gatherling/templates/partials/deckForm.mustache b/gatherling/templates/partials/deckForm.mustache index 0a7a8b5ae..2773308b0 100644 --- a/gatherling/templates/partials/deckForm.mustache +++ b/gatherling/templates/partials/deckForm.mustache @@ -43,7 +43,7 @@ {{/fileInput}} -
    +
    {{#nameTextInput}}{{> textInput}}{{/nameTextInput}} {{^create}} diff --git a/gatherling/templates/partials/playerBanForm.mustache b/gatherling/templates/partials/playerBanForm.mustache index 6eb01161d..888f6c612 100644 --- a/gatherling/templates/partials/playerBanForm.mustache +++ b/gatherling/templates/partials/playerBanForm.mustache @@ -22,7 +22,7 @@ + > {{/bannedPlayers}} diff --git a/gatherling/templates/partials/pointsRule.mustache b/gatherling/templates/partials/pointsRule.mustache index 70d40e349..47ac54fea 100644 --- a/gatherling/templates/partials/pointsRule.mustache +++ b/gatherling/templates/partials/pointsRule.mustache @@ -7,7 +7,7 @@ {{#isCheckbox}} + > {{/isCheckbox}} {{#isDropMenu}} {{#formatDropMenu}}{{> dropMenu}}{{/formatDropMenu}} diff --git a/gatherling/templates/partials/seriesOrganizersForm.mustache b/gatherling/templates/partials/seriesOrganizersForm.mustache index c439e9ab3..b463b0719 100644 --- a/gatherling/templates/partials/seriesOrganizersForm.mustache +++ b/gatherling/templates/partials/seriesOrganizersForm.mustache @@ -14,7 +14,7 @@ + > {{/organizers}} From a256d0fb66b1fc731b7acba0aec36020d6fafd96 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 17:13:40 -0700 Subject: [PATCH 014/114] Fix bug in decksearch error display We can do better here but return it to working order for now. --- gatherling/templates/deckSearch.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gatherling/templates/deckSearch.mustache b/gatherling/templates/deckSearch.mustache index 587ed6bb3..857a701f3 100644 --- a/gatherling/templates/deckSearch.mustache +++ b/gatherling/templates/deckSearch.mustache @@ -6,7 +6,7 @@ {{> searchForm}} {{/searchForm}} {{#errors}} - {{> errors}} + {{{.}}} {{/errors}} {{#mostPlayedDecks}} {{> mostPlayedDecks}} From a27a1ab5df4b7ad8bd15aed472b1e565b17ee868 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 17:17:40 -0700 Subject: [PATCH 015/114] Remove HTML from DeckSearch and put it in the template where it belongs We lose the highlighting of the fault search term but that doesn't seem super important. --- gatherling/Models/Decksearch.php | 18 +++++++++--------- gatherling/templates/deckSearch.mustache | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/gatherling/Models/Decksearch.php b/gatherling/Models/Decksearch.php index 669a76cde..e031625da 100644 --- a/gatherling/Models/Decksearch.php +++ b/gatherling/Models/Decksearch.php @@ -73,7 +73,7 @@ public function getFinalResults(): array|false $tmp_results = array_intersect($tmp_results, $this->results[$key]); } if (count($tmp_results) == 0) { - $this->errors[] = '

    Your search query did not have any matches'; + $this->errors[] = 'Your search query did not have any matches'; return false; } // Filter out decks in events that haven't been finalized (and should remain secret for now) @@ -106,7 +106,7 @@ public function searchByFormat(string $format): void if (count($results) > 0) { $this->results['format'] = $results; } else { - $this->errors[] = "

    No decks match the format: $format"; + $this->errors[] = "No decks match the format: $format"; } } @@ -123,7 +123,7 @@ public function searchByPlayer(string $player): void if (count($results) > 0) { $this->results['player'] = $results; } else { - $this->errors[] = "

    No decks by the player like: $player
    "; + $this->errors[] = "No decks by the player like: $player"; } } @@ -147,7 +147,7 @@ public function searchByMedals(string $medal): void if (count($results) > 0) { $this->results['medal'] = $results; } else { - $this->errors[] = "

    No decks found with the medal: $medal
    "; + $this->errors[] = "No decks found with the medal: $medal"; } } @@ -174,7 +174,7 @@ public function searchByColor(array $color_str_input): void if (count($results) > 0) { $this->results['color'] = $results; } else { - $this->errors[] = "

    No decks found matching the colors: $final_color_str
    "; + $this->errors[] = "No decks found matching the colors: $final_color_str"; } } @@ -191,7 +191,7 @@ public function searchByArchetype(string $archetype): void if (count($results) > 0) { $this->results['archetype'] = $results; } else { - $this->errors[] = "

    No decks found matching archetype: $archetype
    "; + $this->errors[] = "No decks found matching archetype: $archetype"; } } @@ -213,7 +213,7 @@ public function searchBySeries(string $series): void if (count($results) > 0) { $this->results['series'] = $results; } else { - $this->errors[] = "

    No decks found matching series: $series
    "; + $this->errors[] = "No decks found matching series: $series"; } } @@ -225,7 +225,7 @@ public function searchBySeries(string $series): void public function searchByCardName(string $cardname): void { if (strlen($cardname) < 3) { - $this->errors[] = '

    String length is too short must be 3 characters or greater
    '; + $this->errors[] = 'String length is too short must be 3 characters or greater'; return; } $sql = ' @@ -238,7 +238,7 @@ public function searchByCardName(string $cardname): void if (count($results) > 0) { $this->results['cardname'] = $results; } else { - $this->errors[] = "

    No decks found with the card name like: $cardname
    "; + $this->errors[] = "No decks found with the card name like: $cardname"; } } diff --git a/gatherling/templates/deckSearch.mustache b/gatherling/templates/deckSearch.mustache index 857a701f3..418623333 100644 --- a/gatherling/templates/deckSearch.mustache +++ b/gatherling/templates/deckSearch.mustache @@ -6,7 +6,7 @@ {{> searchForm}} {{/searchForm}} {{#errors}} - {{{.}}} +

    {{.}}
    {{/errors}} {{#mostPlayedDecks}} {{> mostPlayedDecks}} From 012ab1c045422da01fe09235bb89737d09344605 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 17:22:57 -0700 Subject: [PATCH 016/114] Remove some
    tags that don't contribute a lot Getting rid of them throughout the app is going to require changing how table.form works, though, I think. --- .../templates/adminControlPanel.mustache | 28 ++++++++++--------- gatherling/templates/cardsAdmin.mustache | 22 +++++++-------- gatherling/templates/deckSearch.mustache | 2 +- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/gatherling/templates/adminControlPanel.mustache b/gatherling/templates/adminControlPanel.mustache index 7dee58df4..86f40e8b4 100644 --- a/gatherling/templates/adminControlPanel.mustache +++ b/gatherling/templates/adminControlPanel.mustache @@ -1,22 +1,24 @@

    Admin Control Panel

    -
    -
    - Change Player Password - | Manually Verify Player - | Create New Series - | Maintenance Tasks - | Add New Cardset -
    + + + + +
    + Change Player Password + | Manually Verify Player + | Create New Series + | Maintenance Tasks + | Add New Cardset +
    - {{#result}} -
    {{result}}
    - {{/result}} + {{#result}} +
    {{result}}
    + {{/result}} - {{{viewSafe}}} + {{{viewSafe}}} -
    diff --git a/gatherling/templates/cardsAdmin.mustache b/gatherling/templates/cardsAdmin.mustache index 235b88560..1589321d7 100644 --- a/gatherling/templates/cardsAdmin.mustache +++ b/gatherling/templates/cardsAdmin.mustache @@ -1,22 +1,20 @@

    Admin Control Panel

    -
    - Welcome to the Database Viewer!
    +

    Welcome to the Database Viewer!

    - - - - -
    - List Card Sets - | Back to AdminCP -
    + + + + +
    + List Card Sets + | Back to AdminCP +
    - {{{viewSafe}}} + {{{viewSafe}}} -
    diff --git a/gatherling/templates/deckSearch.mustache b/gatherling/templates/deckSearch.mustache index 418623333..f2edc76e8 100644 --- a/gatherling/templates/deckSearch.mustache +++ b/gatherling/templates/deckSearch.mustache @@ -6,7 +6,7 @@ {{> searchForm}} {{/searchForm}} {{#errors}} -

    {{.}}
    +

    {{.}}

    {{/errors}} {{#mostPlayedDecks}} {{> mostPlayedDecks}} From 51e726ccb9c905827994557e89cf77efb4bb2821 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 17:31:18 -0700 Subject: [PATCH 017/114] Shush phpstan with an explicit cast in case json_encode returns false --- tests/EventsTest.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/EventsTest.php b/tests/EventsTest.php index 6f86672e0..3e352277d 100644 --- a/tests/EventsTest.php +++ b/tests/EventsTest.php @@ -93,21 +93,21 @@ public function testRegistration(Event $event): Event $this->assertEquals(0, count($event->getRegisteredEntries(false, true))); $deck = insertDeck('testplayer0', $event, '60 Plains', ''); - $this->assertEmpty($deck->errors, json_encode($deck->errors)); + $this->assertEmpty($deck->errors, (string) json_encode($deck->errors)); $deck = insertDeck('testplayer1', $event, '60 Island', ''); - $this->assertEmpty($deck->errors, json_encode($deck->errors)); + $this->assertEmpty($deck->errors, (string) json_encode($deck->errors)); $deck = insertDeck('testplayer2', $event, '40 Swamp', ''); $this->assertNotEmpty($deck->errors, 'No errors for a 40 card deck.'); $deck = insertDeck('testplayer3', $event, "60 Swamp\n100 Relentless Rats", '15 Swamp'); - $this->assertEmpty($deck->errors, json_encode($deck->errors)); + $this->assertEmpty($deck->errors, (string) json_encode($deck->errors)); $deck = insertDeck('testplayer4', $event, "20 Mountain\n20 Forest\n\n\n\n\n\n\n\n\n\n\n\n4 Plains\n4 Plains\n4 Plains\n4 Plains\n4 Plains\n\n\n", ''); - $this->assertEmpty($deck->errors, json_encode($deck->errors)); + $this->assertEmpty($deck->errors, (string) json_encode($deck->errors)); $deck = insertDeck('testplayer5', $event, "54 Mountain\n6 Seven Dwarves", '1 Seven Dwarves'); - $this->assertEmpty($deck->errors, json_encode($deck->errors)); + $this->assertEmpty($deck->errors, (string) json_encode($deck->errors)); $deck = insertDeck('testplayer6', $event, "50 Mountain\n10 Seven Dwarves", ''); - $this->assertNotEmpty($deck->errors, json_encode($deck->errors)); + $this->assertNotEmpty($deck->errors, (string) json_encode($deck->errors)); $deck = insertDeck('testplayer7', $event, "55 Mountain\n5 Seven Dwarves", '5 Seven Dwarves'); - $this->assertNotEmpty($deck->errors, json_encode($deck->errors)); + $this->assertNotEmpty($deck->errors, (string) json_encode($deck->errors)); // None of this changes entry status. $this->assertEquals(10, count($event->getEntries())); // 5 Valid decks (0, 1, 3, 4, 5), 3 invalid decks (2, 6, 7), and 2 not submitted decks (8, 9). From cff402f1d76b573f80fa1615fdffd508acf97b5e Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 17:47:22 -0700 Subject: [PATCH 018/114] Clean up masterplayerfile at least enough to satisfy phpstan level 7 --- gatherling/masterplayerfile.php | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/gatherling/masterplayerfile.php b/gatherling/masterplayerfile.php index 0b49d925b..298847f57 100644 --- a/gatherling/masterplayerfile.php +++ b/gatherling/masterplayerfile.php @@ -2,17 +2,23 @@ declare(strict_types=1); -use Gatherling\Models\Database; +use function Gatherling\Helpers\db; +use function Gatherling\Helpers\server; -header('Content-type: text/plain'); require_once 'lib.php'; -$db = Database::getConnection(); -$result = $db->query('SELECT name FROM players ORDER BY name'); -$n = 10000001; -while ($row = $result->fetch_assoc()) { - if (rtrim($row['name']) != '') { - printf("%08d\tx\t%s\tUS\n", $n, $row['name']); + +function main(): void +{ + header('Content-type: text/plain'); + $sql = 'SELECT name FROM players ORDER BY name'; + $names = db()->strings($sql); + $n = 10000001; + foreach ($names as $name) { + printf("%08d\tx\t%s\tUS\n", $n, $name); $n++; } } -$result->close(); + +if (basename(__FILE__) == basename(server()->string('PHP_SELF'))) { + main(); +} From 50e577eeb9fb4472257d10d66e2d086bbc33e53d Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 17:47:50 -0700 Subject: [PATCH 019/114] Some phpstan level 7 fixes --- gatherling/seriescp.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/gatherling/seriescp.php b/gatherling/seriescp.php index a69bf3b68..3ce0a720b 100644 --- a/gatherling/seriescp.php +++ b/gatherling/seriescp.php @@ -47,10 +47,12 @@ function main(): void } $activeSeriesName = get()->string('series'); + $playerName = Player::loginName(); + if (isset($_POST['series'])) { $seriesname = post()->string('series'); $series = new Series($seriesname); - if ($series->authCheck(Player::loginName())) { + if ($playerName !== false && $series->authCheck($playerName)) { if ($_POST['action'] == 'Update Series') { $newactive = post()->int('isactive', 0); $newtime = $_POST['hour']; @@ -60,14 +62,12 @@ function main(): void $prereg = post()->int('preregdefault', 0); $series = new Series($seriesname); - if ($series->authCheck(Player::loginName())) { - $series->active = $newactive; - $series->start_time = $newtime . ':00'; - $series->start_day = $newday; - $series->prereg_default = $prereg; - $series->mtgo_room = $room; - $series->save(); - } + $series->active = $newactive; + $series->start_time = $newtime . ':00'; + $series->start_day = $newday; + $series->prereg_default = $prereg; + $series->mtgo_room = $room; + $series->save(); } elseif ($_POST['action'] == 'Change Logo') { $file = files()->file('logo'); if ($file->size > 0) { @@ -99,7 +99,7 @@ function main(): void } $activeSeries = new Series($activeSeriesName); - if (!$activeSeries->authCheck(Player::loginName())) { + if ($playerName === false || !$activeSeries->authCheck($playerName)) { $viewComponent = new NoSeries(); } else { switch ($view) { From bba0e9b0565497b29d2d9686e52c7563d736d47f Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 17:47:55 -0700 Subject: [PATCH 020/114] Use json_encode's throw on error to get typesafe result --- gatherling/util/email.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gatherling/util/email.php b/gatherling/util/email.php index 2744b6322..e077a0414 100644 --- a/gatherling/util/email.php +++ b/gatherling/util/email.php @@ -20,7 +20,7 @@ function sendEmail(string $to, string $subj, string $msg): bool curl_setopt($ch, CURLOPT_URL, 'https://api.brevo.com/v3/smtp/email'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body)); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body, JSON_THROW_ON_ERROR)); $headers = []; $headers[] = 'Accept: application/json'; From 4d0099aab2dfbfcb6b67eeac29b9297f4531b80f Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 18:06:12 -0700 Subject: [PATCH 021/114] Type fixes working towards phpstan level 7 --- gatherling/Views/Pages/StandingsList.php | 2 +- gatherling/api.php | 36 +++----- gatherling/api_lib.php | 13 +-- gatherling/bootstrap.php | 6 +- gatherling/deckXmlParser.php | 3 + gatherling/event.php | 12 +-- gatherling/lib.php | 16 +++- phpstan-baseline.neon | 110 ----------------------- psalm-baseline.xml | 14 --- 9 files changed, 45 insertions(+), 167 deletions(-) diff --git a/gatherling/Views/Pages/StandingsList.php b/gatherling/Views/Pages/StandingsList.php index 87b3e4b74..8fa1b205c 100644 --- a/gatherling/Views/Pages/StandingsList.php +++ b/gatherling/Views/Pages/StandingsList.php @@ -11,7 +11,7 @@ class StandingsList extends EventFrame { public EventStandings $eventStandings; - public function __construct(Event $event, string|bool $playerLoginName) + public function __construct(Event $event, ?string $playerLoginName) { parent::__construct($event); $this->eventStandings = new EventStandings($event->name, $playerLoginName); diff --git a/gatherling/api.php b/gatherling/api.php index 73dd46eef..21d233e55 100644 --- a/gatherling/api.php +++ b/gatherling/api.php @@ -8,6 +8,7 @@ use Gatherling\Models\Player; use Gatherling\Models\Series; +use function Gatherling\Helpers\db; use function Gatherling\Helpers\get; use function Gatherling\Helpers\request; @@ -99,33 +100,24 @@ break; case 'recent_events': - $events = []; - $db = Database::getConnection(); - $query = $db->query('SELECT e.name as name FROM events e - WHERE e.finalized AND e.start < NOW() - ORDER BY e.start DESC LIMIT 10'); - while ($row = $query->fetch_assoc()) { - $events[] = $row['name']; - } - $query->close(); - foreach ($events as $eventname) { - $event = new Event($eventname); + $sql = ' + SELECT e.name + FROM events e + WHERE e.finalized AND e.start < NOW() + ORDER BY e.start DESC + LIMIT 10'; + $eventNames = db()->strings($sql); + foreach ($eventNames as $eventName) { + $event = new Event($eventName); $result[$event->name] = repr_json_event($event); } break; case 'upcoming_events': - $events = []; - $db = Database::getConnection(); - $query = $db->query('SELECT e.name as name FROM events e - WHERE e.start > NOW() - ORDER BY e.start ASC'); - while ($row = $query->fetch_assoc()) { - $events[] = $row['name']; - } - $query->close(); - foreach ($events as $eventname) { - $event = new Event($eventname); + $sql = 'SELECT e.name FROM events e WHERE e.start > NOW() ORDER BY e.start ASC'; + $eventNames = db()->strings($sql); + foreach ($eventNames as $eventName) { + $event = new Event($eventName); $result[$event->name] = repr_json_event($event); } break; diff --git a/gatherling/api_lib.php b/gatherling/api_lib.php index cb0710cba..0e4cd1c6e 100644 --- a/gatherling/api_lib.php +++ b/gatherling/api_lib.php @@ -436,17 +436,8 @@ function create_pairing(Event $event, int $round, ?string $a, ?string $b, ?strin /** @return list */ function card_catalog(): array { - $result = []; - $db = Database::getConnection(); - $query = $db->query('SELECT c.name as name FROM cards c'); - while ($row = $query->fetch_assoc()) { - if (!in_array($row['name'], $result)) { - $result[] = $row['name']; - } - } - $query->close(); - - return $result; + $sql = 'SELECT DISTINCT name FROM cards'; + return db()->strings($sql); } function cardname_from_id(string $id): string diff --git a/gatherling/bootstrap.php b/gatherling/bootstrap.php index 41b672f9c..625ad1f18 100644 --- a/gatherling/bootstrap.php +++ b/gatherling/bootstrap.php @@ -19,7 +19,11 @@ $CONFIG['GIT_HASH'] = null; if (file_exists('../.git/HEAD')) { - $branch = trim(substr(file_get_contents('../.git/HEAD'), 5)); + $s = file_get_contents('../.git/HEAD'); + if ($s === false) { + throw new \RuntimeException('Failed to read .git/HEAD'); + } + $branch = trim(substr($s, 5)); if ($hash = file_get_contents(sprintf('../.git/%s', $branch))) { $CONFIG['GIT_HASH'] = $hash; } diff --git a/gatherling/deckXmlParser.php b/gatherling/deckXmlParser.php index 28a7f06ac..a32310d0a 100644 --- a/gatherling/deckXmlParser.php +++ b/gatherling/deckXmlParser.php @@ -11,6 +11,9 @@ function main(): void { $data = filter_input(INPUT_POST, 'data'); + if ($data === false || $data === null) { + throw new \RuntimeException('No data provided'); + } $xml = simplexml_load_string($data) or exit('Error: Cannot create object'); $quantities = ['main' => [], 'side' => []]; $deck = ['main' => [], 'side' => []]; diff --git a/gatherling/event.php b/gatherling/event.php index 9dd2ea41c..5f21e0ba6 100644 --- a/gatherling/event.php +++ b/gatherling/event.php @@ -76,10 +76,10 @@ function mode_is(string $str): bool function createNewEvent(): Event|bool { $series = new Series(post()->optionalString('series')); - if ($series->authCheck(Player::loginName()) && isset($_POST['insert'])) { + $playerName = Player::loginName(); + if ($playerName !== false && $series->authCheck($playerName) && isset($_POST['insert'])) { return insertEvent(); } - return false; } @@ -125,7 +125,8 @@ function newEventFromEventName(string $eventName, bool $newSeason = false): Even function getEvent(string $eventName, ?string $action, ?string $eventId, ?string $player): Page { $event = new Event($eventName); - if (!$event->authCheck(Player::loginName())) { + $playerName = Player::loginName(); + if ($playerName !== false && !$event->authCheck($playerName)) { return new AuthFailed(); } if ($action && strcmp($action, 'undrop') == 0) { @@ -144,7 +145,8 @@ function postEvent(string $eventName): Page { $event = new Event($eventName); - if (!$event->authCheck(Player::loginName())) { + $playerName = Player::loginName(); + if ($playerName !== false && !$event->authCheck($playerName)) { return new AuthFailed(); } @@ -214,7 +216,7 @@ function eventFrame(Event $event = null, bool $forceNew = false): EventFrame } return new MatchList($event, post()->optionalString('newmatchround')); } elseif (strcmp($view, 'standings') == 0) { - return new StandingsList($event, Player::loginName()); + return new StandingsList($event, Player::loginName() ?: null); } elseif (strcmp($view, 'medal') == 0) { return new MedalList($event); } elseif (strcmp($view, 'points_adj') == 0) { diff --git a/gatherling/lib.php b/gatherling/lib.php index f96ebe27b..9308ceb69 100644 --- a/gatherling/lib.php +++ b/gatherling/lib.php @@ -131,7 +131,7 @@ function parseCardsWithQuantity(string|array $cards): array foreach ($cards as $line) { $chopped = rtrim($line); if (preg_match("/^[ \t]*([0-9]+)x?[ \t]+(.*?)( \(\w+\) \d+)?$/i", $chopped, $m)) { - $qty = $m[1]; + $qty = (int) $m[1]; $card = rtrim($m[2]); if (isset($cardarr[$card])) { $cardarr[$card] += $qty; @@ -153,7 +153,11 @@ function parseCardsWithQuantity(string|array $cards): array function getObjectVarsCamelCase(object $obj): array { $vars = get_object_vars($obj); - return arrayMapRecursive(fn($key) => is_string($key) ? toCamel($key) : $key, $vars); + // Force phpstan to understand that an object never has a property that could be coerced to int + // when used as an array key. + /** @var array */ + $result = arrayMapRecursive(fn(string $key) => toCamel($key), $vars); + return $result; } // https://stackoverflow.com/a/45440841/375262 @@ -161,8 +165,14 @@ function toCamel(string $string): string { // Convert to ASCII, remove apostrophes, and split into words $string = iconv('UTF-8', 'ASCII//TRANSLIT', $string); + if ($string === false) { + throw new \RuntimeException("Failed to convert string to ASCII: $string"); + } $string = str_replace("'", "", $string); $words = preg_split('/[^a-zA-Z0-9]+/', $string); + if ($words === false) { + throw new \RuntimeException("Failed to split string into words: $string"); + } // Convert each word to camel case $camelCase = array_map(function ($word) { @@ -187,7 +197,7 @@ function toCamel(string $string): string /** * @param array $arr - * @return array + * @return array */ function arrayMapRecursive(callable $func, array $arr): array { diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 4e050eacc..68f60105b 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1280,11 +1280,6 @@ parameters: count: 1 path: gatherling/Views/Pages/StandingsList.php - - - message: "#^Parameter \\#2 \\$playerName of class Gatherling\\\\Views\\\\Components\\\\EventStandings constructor expects string\\|null, bool\\|string given\\.$#" - count: 1 - path: gatherling/Views/Pages/StandingsList.php - - message: "#^Cannot access property \\$deck on Gatherling\\\\Models\\\\Entry\\|null\\.$#" count: 1 @@ -1350,46 +1345,16 @@ parameters: count: 1 path: gatherling/api.php - - - message: "#^Cannot call method close\\(\\) on bool\\|mysqli_result\\.$#" - count: 2 - path: gatherling/api.php - - - - message: "#^Cannot call method fetch_assoc\\(\\) on bool\\|mysqli_result\\.$#" - count: 2 - path: gatherling/api.php - - message: "#^Cannot call method setApiKey\\(\\) on Gatherling\\\\Models\\\\Player\\|null\\.$#" count: 1 path: gatherling/api.php - - - message: "#^Parameter \\#1 \\$name of class Gatherling\\\\Models\\\\Event constructor expects int\\|string, float\\|int\\|string\\|null given\\.$#" - count: 2 - path: gatherling/api.php - - message: "#^Part \\$action \\(mixed\\) of encapsed string cannot be cast to string\\.$#" count: 1 path: gatherling/api.php - - - message: "#^Cannot call method close\\(\\) on bool\\|mysqli_result\\.$#" - count: 1 - path: gatherling/api_lib.php - - - - message: "#^Cannot call method fetch_assoc\\(\\) on bool\\|mysqli_result\\.$#" - count: 1 - path: gatherling/api_lib.php - - - - message: "#^Function card_catalog\\(\\) should return list\\ but returns list\\\\.$#" - count: 1 - path: gatherling/api_lib.php - - message: "#^Parameter \\#1 \\$eventname of class Gatherling\\\\Models\\\\Standings constructor expects string, string\\|null given\\.$#" count: 2 @@ -1440,11 +1405,6 @@ parameters: count: 1 path: gatherling/auth.php - - - message: "#^Parameter \\#1 \\$string of function substr expects string, string\\|false given\\.$#" - count: 1 - path: gatherling/bootstrap.php - - message: "#^Cannot access property \\$name on Gatherling\\\\Models\\\\Event\\|null\\.$#" count: 1 @@ -1480,11 +1440,6 @@ parameters: count: 2 path: gatherling/deck.php - - - message: "#^Parameter \\#1 \\$data of function simplexml_load_string expects string, string\\|false\\|null given\\.$#" - count: 1 - path: gatherling/deckXmlParser.php - - message: "#^Cannot access offset int\\<0, max\\> on mixed\\.$#" count: 2 @@ -1515,21 +1470,11 @@ parameters: count: 2 path: gatherling/event.php - - - message: "#^Parameter \\#1 \\$playername of method Gatherling\\\\Models\\\\Event\\:\\:authCheck\\(\\) expects string\\|null, string\\|false given\\.$#" - count: 2 - path: gatherling/event.php - - message: "#^Parameter \\#1 \\$playername of method Gatherling\\\\Models\\\\Event\\:\\:undropPlayer\\(\\) expects string, string\\|null given\\.$#" count: 1 path: gatherling/event.php - - - message: "#^Parameter \\#1 \\$playername of method Gatherling\\\\Models\\\\Series\\:\\:authCheck\\(\\) expects string, string\\|false given\\.$#" - count: 1 - path: gatherling/event.php - - message: "#^Parameter \\#2 \\$playername of class Gatherling\\\\Models\\\\Entry constructor expects string, string\\|null given\\.$#" count: 1 @@ -1645,16 +1590,6 @@ parameters: count: 1 path: gatherling/index.php - - - message: "#^Function arrayMapRecursive\\(\\) should return array\\ but returns array\\\\.$#" - count: 1 - path: gatherling/lib.php - - - - message: "#^Function parseCardsWithQuantity\\(\\) should return array\\ but returns array\\\\.$#" - count: 1 - path: gatherling/lib.php - - message: "#^Parameter \\#1 \\$string of function strtolower expects string, string\\|null given\\.$#" count: 1 @@ -1670,11 +1605,6 @@ parameters: count: 1 path: gatherling/lib.php - - - message: "#^Parameter \\#2 \\$array of function array_map expects array, array\\\\|false given\\.$#" - count: 1 - path: gatherling/lib.php - - message: "#^Parameter \\#2 \\$string of function explode expects string, string\\|null given\\.$#" count: 1 @@ -1685,26 +1615,6 @@ parameters: count: 3 path: gatherling/lib.php - - - message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, string\\|false given\\.$#" - count: 1 - path: gatherling/lib.php - - - - message: "#^Cannot call method close\\(\\) on bool\\|mysqli_result\\.$#" - count: 1 - path: gatherling/masterplayerfile.php - - - - message: "#^Cannot call method fetch_assoc\\(\\) on bool\\|mysqli_result\\.$#" - count: 1 - path: gatherling/masterplayerfile.php - - - - message: "#^Parameter \\#1 \\$string of function rtrim expects string, float\\|int\\|string\\|null given\\.$#" - count: 1 - path: gatherling/masterplayerfile.php - - message: "#^Parameter \\#1 \\$username of static method Gatherling\\\\Models\\\\Player\\:\\:checkPassword\\(\\) expects string, string\\|null given\\.$#" count: 1 @@ -1765,11 +1675,6 @@ parameters: count: 1 path: gatherling/seriescp.php - - - message: "#^Parameter \\#1 \\$playername of method Gatherling\\\\Models\\\\Series\\:\\:authCheck\\(\\) expects string, string\\|false given\\.$#" - count: 3 - path: gatherling/seriescp.php - - message: "#^Property Gatherling\\\\Models\\\\Series\\:\\:\\$mtgo_room \\(string\\|null\\) does not accept mixed\\.$#" count: 1 @@ -1780,26 +1685,11 @@ parameters: count: 1 path: gatherling/seriescp.php - - - message: "#^Parameter \\#3 \\$value of function curl_setopt expects array\\|string, string\\|false given\\.$#" - count: 1 - path: gatherling/util/email.php - - message: "#^Parameter \\#1 \\$name of class Gatherling\\\\Models\\\\Event constructor expects int\\|string, string\\|null given\\.$#" count: 1 path: tests/EventsTest.php - - - message: "#^Parameter \\#2 \\$message of method PHPUnit\\\\Framework\\\\Assert\\:\\:assertEmpty\\(\\) expects string, string\\|false given\\.$#" - count: 5 - path: tests/EventsTest.php - - - - message: "#^Parameter \\#2 \\$message of method PHPUnit\\\\Framework\\\\Assert\\:\\:assertNotEmpty\\(\\) expects string, string\\|false given\\.$#" - count: 2 - path: tests/EventsTest.php - - message: "#^Parameter \\#1 \\$name of class Gatherling\\\\Models\\\\Event constructor expects int\\|string, string\\|null given\\.$#" count: 1 diff --git a/psalm-baseline.xml b/psalm-baseline.xml index f10081bc4..eef5eed0b 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -240,12 +240,6 @@ - - - - - ]]> - @@ -273,14 +267,6 @@ - - - - - - ]]> - - From 1075bd9adc8427d6ce350a213bbd7856b7815597 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 18:31:22 -0700 Subject: [PATCH 022/114] Move test to ComponentTest where it more correctly belongs --- .../{TemplateHelperTest.php => Components/ComponentTest.php} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/Views/{TemplateHelperTest.php => Components/ComponentTest.php} (98%) diff --git a/tests/Views/TemplateHelperTest.php b/tests/Views/Components/ComponentTest.php similarity index 98% rename from tests/Views/TemplateHelperTest.php rename to tests/Views/Components/ComponentTest.php index b761ba318..5ba987262 100644 --- a/tests/Views/TemplateHelperTest.php +++ b/tests/Views/Components/ComponentTest.php @@ -7,7 +7,7 @@ use Gatherling\Views\Components\Component; use PHPUnit\Framework\TestCase; -class TemplateHelperTest extends TestCase +class ComponentTest extends TestCase { public function testRenderComponent(): void { From 4a148e0e3c03a57cfeb922dba136e552daf9c9e7 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 18:31:38 -0700 Subject: [PATCH 023/114] Undo recent change to getObjectVarsCamelCase It's more complicated than I thought - we also spider over lists that DO have integer keys. --- gatherling/lib.php | 8 ++------ phpstan-baseline.neon | 5 +++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/gatherling/lib.php b/gatherling/lib.php index 9308ceb69..fbc0fdec8 100644 --- a/gatherling/lib.php +++ b/gatherling/lib.php @@ -153,11 +153,7 @@ function parseCardsWithQuantity(string|array $cards): array function getObjectVarsCamelCase(object $obj): array { $vars = get_object_vars($obj); - // Force phpstan to understand that an object never has a property that could be coerced to int - // when used as an array key. - /** @var array */ - $result = arrayMapRecursive(fn(string $key) => toCamel($key), $vars); - return $result; + return arrayMapRecursive(fn($key) => is_string($key) ? toCamel($key) : $key, $vars); } // https://stackoverflow.com/a/45440841/375262 @@ -197,7 +193,7 @@ function toCamel(string $string): string /** * @param array $arr - * @return array + * @return array */ function arrayMapRecursive(callable $func, array $arr): array { diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 68f60105b..08307bca5 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1590,6 +1590,11 @@ parameters: count: 1 path: gatherling/index.php + - + message: "#^Function arrayMapRecursive\\(\\) should return array\\ but returns array\\\\.$#" + count: 1 + path: gatherling/lib.php + - message: "#^Parameter \\#1 \\$string of function strtolower expects string, string\\|null given\\.$#" count: 1 From ab2aa551a3d66ab606fdcc9ac2b1fde9b7df756c Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 18:35:26 -0700 Subject: [PATCH 024/114] Remove a hardcoded file_put_contents that hasn't working in 100 million years --- gatherling/Models/Player.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gatherling/Models/Player.php b/gatherling/Models/Player.php index 3e5889d04..b640e36ec 100644 --- a/gatherling/Models/Player.php +++ b/gatherling/Models/Player.php @@ -11,6 +11,7 @@ use Gatherling\Views\Components\PlayerLink; use function Gatherling\Helpers\db; +use function Gatherling\Helpers\logger; use function Gatherling\Helpers\session; class Player @@ -1275,8 +1276,7 @@ public function checkChallenge(string $challenge): bool return true; } else { $error_log = "Player = '{$this->name}' Challenge = '{$challenge}' Verify = '{$verifyplayer}' DBChallenge = '{$db_challenge}'\n"; - file_put_contents('/var/www/pdcmagic.com/gatherling/challenge.log', $error_log, FILE_APPEND); - + logger()->error("Challenge check failed: $error_log"); return false; } } From 2de75f538dc804799f1167ed9f3added73ebdaff Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 19:42:18 -0700 Subject: [PATCH 025/114] Remove unused funcs --- gatherling/Models/Standings.php | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/gatherling/Models/Standings.php b/gatherling/Models/Standings.php index c37cf9c25..3a74a0dfc 100644 --- a/gatherling/Models/Standings.php +++ b/gatherling/Models/Standings.php @@ -327,19 +327,6 @@ public static function startEvent(array $entries, string $event_name): void } } - public static function addPlayerToEvent(string $event_name, string $entry): void - { - $standing = new self($event_name, $entry); - $standing->save(); - } - - public static function dropPlayer(string $eventname, string $playername): void - { - $standing = new self($eventname, $playername); - $standing->active = 0; - $standing->save(); - } - public static function playerActive(string $eventname, string $playername): bool { $standing = new self($eventname, $playername); From ae5568a857078379a59ffdde22c382af604e6a1f Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 21:17:56 -0700 Subject: [PATCH 026/114] Always call a link to eventreport.php eventReportLink not reportLink reportLink is when you report a match result. --- .../{ReportLink.php => EventReportLink.php} | 6 +++--- gatherling/Views/Components/InfoCell.php | 4 ++-- gatherling/Views/Components/SeasonStandings.php | 6 +++--- gatherling/Views/Pages/Home.php | 12 ++++++------ gatherling/Views/Pages/Series.php | 4 ++-- gatherling/templates/home.mustache | 6 +++--- .../templates/partials/eventReportLink.mustache | 1 + gatherling/templates/partials/infoCell.mustache | 6 +++--- gatherling/templates/partials/reportLink.mustache | 1 - .../templates/partials/seasonStandings.mustache | 2 +- gatherling/templates/series.mustache | 2 +- phpstan-baseline.neon | 4 ++-- psalm-baseline.xml | 4 ---- 13 files changed, 27 insertions(+), 31 deletions(-) rename gatherling/Views/Components/{ReportLink.php => EventReportLink.php} (54%) create mode 100644 gatherling/templates/partials/eventReportLink.mustache delete mode 100644 gatherling/templates/partials/reportLink.mustache diff --git a/gatherling/Views/Components/ReportLink.php b/gatherling/Views/Components/EventReportLink.php similarity index 54% rename from gatherling/Views/Components/ReportLink.php rename to gatherling/Views/Components/EventReportLink.php index 314b9d70f..e89287ee1 100644 --- a/gatherling/Views/Components/ReportLink.php +++ b/gatherling/Views/Components/EventReportLink.php @@ -4,14 +4,14 @@ namespace Gatherling\Views\Components; -class ReportLink extends Component +class EventReportLink extends Component { - public string $reportLink; + public string $eventReportLink; public string $text; public function __construct(string $name) { - $this->reportLink = 'eventreport.php?event=' . rawurlencode($name); + $this->eventReportLink = 'eventreport.php?event=' . rawurlencode($name); $this->text = $name; } } diff --git a/gatherling/Views/Components/InfoCell.php b/gatherling/Views/Components/InfoCell.php index 35f8809b2..ad6da780a 100644 --- a/gatherling/Views/Components/InfoCell.php +++ b/gatherling/Views/Components/InfoCell.php @@ -20,7 +20,7 @@ class InfoCell extends Component /** @var list */ public array $subevents; public PlayerLink $hostLink; - public string $reportLink; + public string $eventReportLink; public string $seasonLeaderboardLink; public function __construct(Event $event) @@ -48,7 +48,7 @@ public function __construct(Event $event) $host = new Player($event->host); $this->hostLink = new PlayerLink($host); } - $this->reportLink = $event->reporturl ?? ''; + $this->eventReportLink = $event->reporturl ?? ''; $this->seasonLeaderboardLink = 'seriesreport.php?series=' . rawurlencode($event->series) . '&season=' . rawurlencode((string) $event->season); } } diff --git a/gatherling/Views/Components/SeasonStandings.php b/gatherling/Views/Components/SeasonStandings.php index d2835bb99..36f11ac40 100644 --- a/gatherling/Views/Components/SeasonStandings.php +++ b/gatherling/Views/Components/SeasonStandings.php @@ -12,7 +12,7 @@ class SeasonStandings extends Component { public string $seriesName; public int $season; - /** @var list */ + /** @var list */ public array $seasonEvents; /** @var list}> */ public array $players; @@ -27,10 +27,10 @@ public function __construct(Series $series, int $season) $seasonEvents = []; foreach ($seasonEventNames as $eventName) { $shortName = preg_replace("/^{$series->name} /", '', $eventName) ?? ''; - $reportLink = 'eventreport.php?event=' . rawurlencode($eventName); + $eventReportLink = 'eventreport.php?event=' . rawurlencode($eventName); $seasonEvents[] = [ 'shortName' => $shortName, - 'reportLink' => $reportLink, + 'eventReportLink' => $eventReportLink, ]; } diff --git a/gatherling/Views/Pages/Home.php b/gatherling/Views/Pages/Home.php index e4310160d..30deb5837 100644 --- a/gatherling/Views/Pages/Home.php +++ b/gatherling/Views/Pages/Home.php @@ -12,17 +12,17 @@ class Home extends Page { - /** @var list */ + /** @var list */ public array $activeEvents = []; public bool $hasActiveEvents; - /** @var list */ + /** @var list */ public array $upcomingEvents = []; public bool $hasUpcomingEvents; /** @var ?array{name: string, link: string} */ public ?array $playerInfo = null; /** @var ?array{name: string, link: string} */ public ?array $mostRecentHostedEvent = null; - /** @var list */ + /** @var list */ public array $recentWinners; public bool $hasRecentWinners; @@ -40,7 +40,7 @@ public function __construct(array $activeEvents, array $upcomingEvents, public a 'name' => $event->name, 'format' => $event->format, 'currentRound' => $event->current_round, - 'reportLink' => 'eventreport.php?event=' . rawurlencode($event->name), + 'eventReportLink' => 'eventreport.php?event=' . rawurlencode($event->name), ]; } $this->hasActiveEvents = count($this->activeEvents) > 0; @@ -48,7 +48,7 @@ public function __construct(array $activeEvents, array $upcomingEvents, public a $this->upcomingEvents[] = [ 'name' => $event->name, 'format' => $event->format, - 'reportLink' => 'eventreport.php?event=' . rawurlencode($event->name), + 'eventReportLink' => 'eventreport.php?event=' . rawurlencode($event->name), 'time' => new Time($event->d, time()), ]; } @@ -68,7 +68,7 @@ public function __construct(array $activeEvents, array $upcomingEvents, public a foreach ($recentWinners as $winner) { $this->recentWinners[] = [ 'eventName' => $winner['event'], - 'reportLink' => 'eventreport.php?event=' . rawurlencode($winner['event']), + 'eventReportLink' => 'eventreport.php?event=' . rawurlencode($winner['event']), 'playerLink' => 'profile.php?player=' . rawurlencode($winner['player']), 'deckLink' => 'deck.php?mode=view&event=' . rawurlencode($winner['event']), 'playerName' => $winner['player'], diff --git a/gatherling/Views/Pages/Series.php b/gatherling/Views/Pages/Series.php index abcc9d855..5aa2bb081 100644 --- a/gatherling/Views/Pages/Series.php +++ b/gatherling/Views/Pages/Series.php @@ -5,7 +5,7 @@ namespace Gatherling\Views\Pages; use Gatherling\Views\Components\Time; -use Gatherling\Views\Components\ReportLink; +use Gatherling\Views\Components\EventReportLink; use Gatherling\Models\Series as SeriesModel; class Series extends Page @@ -44,7 +44,7 @@ public function __construct(array $activeSeriesNames) 'regularTime' => $regularTime, 'masterDocumentLink' => $masterDocumentLink, 'season' => $season, - 'reportLink' => $mostRecentEvent ? new ReportLink($mostRecentEvent->name) : null, + 'eventReportLink' => $mostRecentEvent ? new EventReportLink($mostRecentEvent->name) : null, 'nextEventStart' => $nextEventStart, ]; } diff --git a/gatherling/templates/home.mustache b/gatherling/templates/home.mustache index be47a55ec..a615b1eba 100644 --- a/gatherling/templates/home.mustache +++ b/gatherling/templates/home.mustache @@ -13,7 +13,7 @@ {{#activeEvents}} - {{name}}
    + {{name}}
    {{format}} Round {{currentRound}} @@ -29,7 +29,7 @@ {{#upcomingEvents}} - {{name}}
    + {{name}}
    {{format}} @@ -104,7 +104,7 @@ - + {{#colorImages}}{{> colorImages}}{{/colorImages}} diff --git a/gatherling/templates/partials/eventReportLink.mustache b/gatherling/templates/partials/eventReportLink.mustache new file mode 100644 index 000000000..7d0aa557a --- /dev/null +++ b/gatherling/templates/partials/eventReportLink.mustache @@ -0,0 +1 @@ +{{text}} diff --git a/gatherling/templates/partials/infoCell.mustache b/gatherling/templates/partials/infoCell.mustache index 4a858a5ed..b060bfd4e 100644 --- a/gatherling/templates/partials/infoCell.mustache +++ b/gatherling/templates/partials/infoCell.mustache @@ -17,7 +17,7 @@ {{/subevents}} Hosted by {{#hostLink}}{{> playerLink}}{{/hostLink}}
    -{{#reportLink}} - Event Report
    -{{/reportLink}} +{{#eventReportLink}} + Event Report
    +{{/eventReportLink}} Season Leaderboard diff --git a/gatherling/templates/partials/reportLink.mustache b/gatherling/templates/partials/reportLink.mustache deleted file mode 100644 index 48dc453ec..000000000 --- a/gatherling/templates/partials/reportLink.mustache +++ /dev/null @@ -1 +0,0 @@ -{{text}} diff --git a/gatherling/templates/partials/seasonStandings.mustache b/gatherling/templates/partials/seasonStandings.mustache index a39fa3500..be4de3d86 100644 --- a/gatherling/templates/partials/seasonStandings.mustache +++ b/gatherling/templates/partials/seasonStandings.mustache @@ -6,7 +6,7 @@ Total {{#seasonEvents}} - {{shortName}} + {{shortName}} {{/seasonEvents}} diff --git a/gatherling/templates/series.mustache b/gatherling/templates/series.mustache index 9484d6f2b..016ab4ef8 100644 --- a/gatherling/templates/series.mustache +++ b/gatherling/templates/series.mustache @@ -29,7 +29,7 @@ Most Recent Event - {{#reportLink}}{{> reportLink}}{{/reportLink}} + {{#eventReportLink}}{{> eventReportLink}}{{/eventReportLink}} Next Event diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 08307bca5..154ad8270 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1131,7 +1131,7 @@ parameters: path: gatherling/Views/Pages/Home.php - - message: "#^Property Gatherling\\\\Views\\\\Pages\\\\Home\\:\\:\\$activeEvents \\(list\\\\) does not accept non\\-empty\\-list\\\\.$#" + message: "#^Property Gatherling\\\\Views\\\\Pages\\\\Home\\:\\:\\$activeEvents \\(list\\\\) does not accept non\\-empty\\-list\\\\.$#" count: 1 path: gatherling/Views/Pages/Home.php @@ -1256,7 +1256,7 @@ parameters: path: gatherling/Views/Pages/Series.php - - message: "#^Parameter \\#1 \\$name of class Gatherling\\\\Views\\\\Components\\\\ReportLink constructor expects string, string\\|null given\\.$#" + message: "#^Parameter \\#1 \\$name of class Gatherling\\\\Views\\\\Components\\\\EventReportLink constructor expects string, string\\|null given\\.$#" count: 1 path: gatherling/Views/Pages/Series.php diff --git a/psalm-baseline.xml b/psalm-baseline.xml index eef5eed0b..c783102c6 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -135,10 +135,6 @@
    - - - - From 63c339901eba8731f216f1194eaca8b3233b81c7 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 21:27:18 -0700 Subject: [PATCH 027/114] Log interpolated queries, they are just much easier to debug --- gatherling/Data/Db.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gatherling/Data/Db.php b/gatherling/Data/Db.php index 25efa9a69..f85b85351 100644 --- a/gatherling/Data/Db.php +++ b/gatherling/Data/Db.php @@ -556,7 +556,7 @@ private function executeInternal(string $sql, array $params, callable $operation throw new DatabaseException("Failed to reconnect and execute query: $sql with params " . json_encode($params), 0, $e); } } - $msg = "Failed to execute query: $sql with params " . json_encode($params); + $msg = "Failed to execute query: " . $this->interpolateQuery($sql, $params); logger()->error($msg, $context); throw new DatabaseException($msg, 0, $e); From 6fa8489051e78cbf420b1f9ce455359ee491cbce Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 21:27:33 -0700 Subject: [PATCH 028/114] Don't allow event name to be null, we don't allow events without names --- gatherling/Models/Event.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gatherling/Models/Event.php b/gatherling/Models/Event.php index e3da1039a..80b4e0c1f 100644 --- a/gatherling/Models/Event.php +++ b/gatherling/Models/Event.php @@ -12,7 +12,7 @@ class Event { - public ?string $name; + public string $name; public ?int $id; public ?int $season; From fc5307030d043c6dcd6588333651b4217ca367b7 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 21:28:19 -0700 Subject: [PATCH 029/114] Update psalm baseline now that we use interpolateQuery for debugging logging --- psalm-baseline.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index c783102c6..ae2de196f 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,10 +1,5 @@ - - - - - From 51cbb1fbabbaccc205f57f6bfaf73cee1ac9a21b Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 21:29:04 -0700 Subject: [PATCH 030/114] Event->name is no longer nullable which clears up a lot of type errors It was never really nullable truly but the code is able to express it now in a way that phpstan can understand. --- phpstan-baseline.neon | 142 +----------------------------------------- 1 file changed, 1 insertion(+), 141 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 154ad8270..ee5bad524 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -150,11 +150,6 @@ parameters: count: 1 path: gatherling/Models/Entry.php - - - message: "#^Parameter \\#1 \\$eventname of method Gatherling\\\\Models\\\\Player\\:\\:getMatchesEvent\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Models/Entry.php - - message: "#^Parameter \\#2 \\$string2 of function strcasecmp expects string, string\\|null given\\.$#" count: 1 @@ -250,11 +245,6 @@ parameters: count: 1 path: gatherling/Models/Event.php - - - message: "#^Parameter \\#1 \\$event of method Gatherling\\\\Models\\\\Ratings\\:\\:calcFinalizedEventRatings\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Models/Event.php - - message: "#^Parameter \\#1 \\$event_id of class Gatherling\\\\Models\\\\Entry constructor expects int, int\\|null given\\.$#" count: 5 @@ -270,51 +260,11 @@ parameters: count: 1 path: gatherling/Models/Event.php - - - message: "#^Parameter \\#1 \\$eventname of class Gatherling\\\\Models\\\\Standings constructor expects string, string\\|null given\\.$#" - count: 9 - path: gatherling/Models/Event.php - - - - message: "#^Parameter \\#1 \\$eventname of method Gatherling\\\\Models\\\\Standings\\:\\:getEventStandings\\(\\) expects string, string\\|null given\\.$#" - count: 6 - path: gatherling/Models/Event.php - - - - message: "#^Parameter \\#1 \\$eventname of method Gatherling\\\\Models\\\\Standings\\:\\:getOpponents\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Models/Event.php - - - - message: "#^Parameter \\#1 \\$eventname of static method Gatherling\\\\Models\\\\Standings\\:\\:getEventStandings\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Models/Event.php - - - - message: "#^Parameter \\#1 \\$eventname of static method Gatherling\\\\Models\\\\Standings\\:\\:resetMatched\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Models/Event.php - - - - message: "#^Parameter \\#1 \\$eventname of static method Gatherling\\\\Models\\\\Standings\\:\\:updateStandings\\(\\) expects string, string\\|null given\\.$#" - count: 3 - path: gatherling/Models/Event.php - - - - message: "#^Parameter \\#1 \\$eventname of static method Gatherling\\\\Models\\\\Standings\\:\\:writeSeed\\(\\) expects string, string\\|null given\\.$#" - count: 17 - path: gatherling/Models/Event.php - - message: "#^Parameter \\#1 \\$roundnum of method Gatherling\\\\Models\\\\Event\\:\\:getRoundMatches\\(\\) expects int\\|string, int\\|null given\\.$#" count: 1 path: gatherling/Models/Event.php - - - message: "#^Parameter \\#1 \\$string of function rawurlencode expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Models/Event.php - - message: "#^Parameter \\#1 \\$subevent_id of method Gatherling\\\\Models\\\\Event\\:\\:swissPairingBlossom\\(\\) expects int, int\\|null given\\.$#" count: 1 @@ -325,11 +275,6 @@ parameters: count: 2 path: gatherling/Models/Event.php - - - message: "#^Parameter \\#2 \\$event_name of static method Gatherling\\\\Models\\\\Standings\\:\\:startEvent\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Models/Event.php - - message: "#^Parameter \\#2 \\$format of method Gatherling\\\\Models\\\\Ratings\\:\\:calcFinalizedEventRatings\\(\\) expects string, string\\|null given\\.$#" count: 1 @@ -575,11 +520,6 @@ parameters: count: 1 path: gatherling/Models/Matchup.php - - - message: "#^Parameter \\#1 \\$eventname of class Gatherling\\\\Models\\\\Standings constructor expects string, string\\|null given\\.$#" - count: 4 - path: gatherling/Models/Matchup.php - - message: "#^Parameter \\#1 \\$name of class Gatherling\\\\Models\\\\Event constructor expects int\\|string, string\\|null given\\.$#" count: 1 @@ -970,11 +910,6 @@ parameters: count: 1 path: gatherling/Views/Components/FormatCPMenu.php - - - message: "#^Parameter \\#1 \\$eventName of class Gatherling\\\\Views\\\\Components\\\\EventStandings constructor expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Components/FullMetagame.php - - message: "#^Parameter \\#1 \\$event_id of class Gatherling\\\\Models\\\\Entry constructor expects int, int\\|null given\\.$#" count: 1 @@ -1025,11 +960,6 @@ parameters: count: 1 path: gatherling/Views/Components/Prereg.php - - - message: "#^Parameter \\#1 \\$string of function rawurlencode expects string, string\\|null given\\.$#" - count: 2 - path: gatherling/Views/Components/Prereg.php - - message: "#^Binary operation \"\\+\" between float\\|int\\|string\\|null and 1 results in an error\\.$#" count: 1 @@ -1060,11 +990,6 @@ parameters: count: 1 path: gatherling/Views/Components/TextInput.php - - - message: "#^Parameter \\#1 \\$eventname of static method Gatherling\\\\Models\\\\Event\\:\\:trophySrc\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Components/TrophyCell.php - - message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, string\\|null given\\.$#" count: 1 @@ -1095,11 +1020,6 @@ parameters: count: 1 path: gatherling/Views/Pages/EventForm.php - - - message: "#^Parameter \\#1 \\$string of function rawurlencode expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Pages/EventForm.php - - message: "#^Parameter \\#1 \\$string1 of function strcmp expects string, int\\|string given\\.$#" count: 1 @@ -1127,16 +1047,11 @@ parameters: - message: "#^Parameter \\#1 \\$string of function rawurlencode expects string, string\\|null given\\.$#" - count: 3 - path: gatherling/Views/Pages/Home.php - - - - message: "#^Property Gatherling\\\\Views\\\\Pages\\\\Home\\:\\:\\$activeEvents \\(list\\\\) does not accept non\\-empty\\-list\\\\.$#" count: 1 path: gatherling/Views/Pages/Home.php - - message: "#^Property Gatherling\\\\Views\\\\Pages\\\\Home\\:\\:\\$mostRecentHostedEvent \\(array\\{name\\: string, link\\: string\\}\\|null\\) does not accept array\\{name\\: string\\|null, link\\: non\\-falsy\\-string\\}\\.$#" + message: "#^Property Gatherling\\\\Views\\\\Pages\\\\Home\\:\\:\\$activeEvents \\(list\\\\) does not accept non\\-empty\\-list\\\\.$#" count: 1 path: gatherling/Views/Pages/Home.php @@ -1165,21 +1080,11 @@ parameters: count: 3 path: gatherling/Views/Pages/MatchList.php - - - message: "#^Parameter \\#1 \\$string of function rawurlencode expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Pages/MatchList.php - - message: "#^Parameter \\#1 \\$string1 of function strcasecmp expects string, string\\|null given\\.$#" count: 1 path: gatherling/Views/Pages/MatchList.php - - - message: "#^Parameter \\#1 \\$eventname of static method Gatherling\\\\Models\\\\Standings\\:\\:playerActive\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Pages/PlayerList.php - - message: "#^Parameter \\#1 \\$name of class Gatherling\\\\Models\\\\Format constructor expects string, string\\|null given\\.$#" count: 1 @@ -1255,11 +1160,6 @@ parameters: count: 1 path: gatherling/Views/Pages/Series.php - - - message: "#^Parameter \\#1 \\$name of class Gatherling\\\\Views\\\\Components\\\\EventReportLink constructor expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Pages/Series.php - - message: "#^Parameter \\#1 \\$time of class Gatherling\\\\Views\\\\Components\\\\Time constructor expects int, int\\|false given\\.$#" count: 1 @@ -1275,11 +1175,6 @@ parameters: count: 1 path: gatherling/Views/Pages/Series.php - - - message: "#^Parameter \\#1 \\$eventName of class Gatherling\\\\Views\\\\Components\\\\EventStandings constructor expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Pages/StandingsList.php - - message: "#^Cannot access property \\$deck on Gatherling\\\\Models\\\\Entry\\|null\\.$#" count: 1 @@ -1355,16 +1250,6 @@ parameters: count: 1 path: gatherling/api.php - - - message: "#^Parameter \\#1 \\$eventname of class Gatherling\\\\Models\\\\Standings constructor expects string, string\\|null given\\.$#" - count: 2 - path: gatherling/api_lib.php - - - - message: "#^Parameter \\#1 \\$eventname of static method Gatherling\\\\Models\\\\Standings\\:\\:getEventStandings\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/api_lib.php - - message: "#^Parameter \\#1 \\$name of class Gatherling\\\\Models\\\\Player constructor expects string, string\\|null given\\.$#" count: 2 @@ -1460,16 +1345,6 @@ parameters: count: 2 path: gatherling/event.php - - - message: "#^Parameter \\#1 \\$eventname of class Gatherling\\\\Models\\\\Standings constructor expects string, string\\|null given\\.$#" - count: 3 - path: gatherling/event.php - - - - message: "#^Parameter \\#1 \\$eventname of static method Gatherling\\\\Models\\\\Standings\\:\\:updateStandings\\(\\) expects string, string\\|null given\\.$#" - count: 2 - path: gatherling/event.php - - message: "#^Parameter \\#1 \\$playername of method Gatherling\\\\Models\\\\Event\\:\\:undropPlayer\\(\\) expects string, string\\|null given\\.$#" count: 1 @@ -1650,11 +1525,6 @@ parameters: count: 1 path: gatherling/profile.php - - - message: "#^Parameter \\#1 \\$eventname of class Gatherling\\\\Models\\\\Standings constructor expects string, string\\|null given\\.$#" - count: 2 - path: gatherling/report.php - - message: "#^Parameter \\#1 \\$playername of method Gatherling\\\\Models\\\\Event\\:\\:dropPlayer\\(\\) expects string, string\\|null given\\.$#" count: 2 @@ -1690,16 +1560,6 @@ parameters: count: 1 path: gatherling/seriescp.php - - - message: "#^Parameter \\#1 \\$name of class Gatherling\\\\Models\\\\Event constructor expects int\\|string, string\\|null given\\.$#" - count: 1 - path: tests/EventsTest.php - - - - message: "#^Parameter \\#1 \\$name of class Gatherling\\\\Models\\\\Event constructor expects int\\|string, string\\|null given\\.$#" - count: 1 - path: tests/Models/DeckTest.php - - message: "#^Parameter \\#1 \\$playername of method Gatherling\\\\Models\\\\Event\\:\\:addPlayer\\(\\) expects string, string\\|null given\\.$#" count: 1 From 8303badf4435e6de26bce9a9b9945a36439a7241 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 21:36:25 -0700 Subject: [PATCH 031/114] Set Event->current_round to not be nullable, like the database --- gatherling/Models/Event.php | 2 +- phpstan-baseline.neon | 28 ++++------------------------ 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/gatherling/Models/Event.php b/gatherling/Models/Event.php index 80b4e0c1f..eb564d009 100644 --- a/gatherling/Models/Event.php +++ b/gatherling/Models/Event.php @@ -46,7 +46,7 @@ class Event public ?int $finalid; // Has one final subevent // Pairing/event related - public ?int $current_round; + public int $current_round; public Standings $standing; public ?int $player_reportable; public ?int $player_reported_draws; diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ee5bad524..2c111421a 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -167,12 +167,7 @@ parameters: - message: "#^Binary operation \"\\-\" between int and int\\|string results in an error\\.$#" - count: 2 - path: gatherling/Models/Event.php - - - - message: "#^Binary operation \"\\-\" between int\\|null and int\\|string results in an error\\.$#" - count: 4 + count: 6 path: gatherling/Models/Event.php - @@ -186,12 +181,12 @@ parameters: path: gatherling/Models/Event.php - - message: "#^Binary operation \"\\-\" between int\\|string and int\\|null results in an error\\.$#" + message: "#^Binary operation \"\\-\" between int\\|string and int\\\\|int\\<1, max\\> results in an error\\.$#" count: 1 path: gatherling/Models/Event.php - - message: "#^Binary operation \"\\-\\=\" between int\\|null and int\\|string results in an error\\.$#" + message: "#^Binary operation \"\\-\\=\" between int and int\\|string results in an error\\.$#" count: 1 path: gatherling/Models/Event.php @@ -260,11 +255,6 @@ parameters: count: 1 path: gatherling/Models/Event.php - - - message: "#^Parameter \\#1 \\$roundnum of method Gatherling\\\\Models\\\\Event\\:\\:getRoundMatches\\(\\) expects int\\|string, int\\|null given\\.$#" - count: 1 - path: gatherling/Models/Event.php - - message: "#^Parameter \\#1 \\$subevent_id of method Gatherling\\\\Models\\\\Event\\:\\:swissPairingBlossom\\(\\) expects int, int\\|null given\\.$#" count: 1 @@ -565,11 +555,6 @@ parameters: count: 1 path: gatherling/Models/Matchup.php - - - message: "#^Parameter \\#2 \\$current_round of method Gatherling\\\\Models\\\\Event\\:\\:resolveRound\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: gatherling/Models/Matchup.php - - message: "#^Parameter \\#2 \\$playername of class Gatherling\\\\Models\\\\Standings constructor expects string, string\\|null given\\.$#" count: 4 @@ -1051,7 +1036,7 @@ parameters: path: gatherling/Views/Pages/Home.php - - message: "#^Property Gatherling\\\\Views\\\\Pages\\\\Home\\:\\:\\$activeEvents \\(list\\\\) does not accept non\\-empty\\-list\\\\.$#" + message: "#^Property Gatherling\\\\Views\\\\Pages\\\\Home\\:\\:\\$activeEvents \\(list\\\\) does not accept non\\-empty\\-list\\\\.$#" count: 1 path: gatherling/Views/Pages/Home.php @@ -1530,11 +1515,6 @@ parameters: count: 2 path: gatherling/report.php - - - message: "#^Parameter \\#3 \\$round of method Gatherling\\\\Models\\\\Event\\:\\:addPairing\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: gatherling/report.php - - message: "#^Binary operation \"\\.\" between mixed and '\\:00' results in an error\\.$#" count: 1 From 3c8079bb3fc9479c7eb8bfcf607be24433d48dfd Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 21:40:06 -0700 Subject: [PATCH 032/114] Mark a lot more properties of Event that are not-nullable in the db as not null --- gatherling/Models/Event.php | 34 +++++++++++++++++----------------- gatherling/event.php | 12 ++++++------ phpstan-baseline.neon | 20 -------------------- 3 files changed, 23 insertions(+), 43 deletions(-) diff --git a/gatherling/Models/Event.php b/gatherling/Models/Event.php index eb564d009..4be8c5b3a 100644 --- a/gatherling/Models/Event.php +++ b/gatherling/Models/Event.php @@ -21,16 +21,16 @@ class Event public ?string $start; public ?int $kvalue = null; - public ?int $active; - public ?int $finalized; - public ?int $prereg_allowed; - public ?string $threadurl; - public ?string $reporturl; - public ?string $metaurl; + public int $active; + public int $finalized; + public int $prereg_allowed; + public string $threadurl; + public string $reporturl; + public string $metaurl; public ?int $private; - public ?int $client; + public int $client; - public ?int $player_editdecks; + public int $player_editdecks; // Class associations public ?string $series = null; // belongs to Series @@ -48,13 +48,13 @@ class Event // Pairing/event related public int $current_round; public Standings $standing; - public ?int $player_reportable; - public ?int $player_reported_draws; - public ?int $prereg_cap; // Cap on player initiated registration - public ?int $late_entry_limit; // How many rounds we let people perform late entries + public int $player_reportable; + public int $player_reported_draws; + public int $prereg_cap; // Cap on player initiated registration + public int $late_entry_limit; // How many rounds we let people perform late entries - public ?int $private_decks; // Toggle to disable deck privacy for active events. Allows the metagame page to display during an active event and lets deck lists be viewed if disabled. - public ?int $private_finals; // As above, but for finals + public int $private_decks; // Toggle to disable deck privacy for active events. Allows the metagame page to display during an active event and lets deck lists be viewed if disabled. + public int $private_finals; // As above, but for finals public ?int $hastrophy; private ?bool $new = null; @@ -70,9 +70,9 @@ public function __construct(int|string $name = '') $this->finalstruct = ''; $this->host = null; $this->cohost = null; - $this->threadurl = null; - $this->reporturl = null; - $this->metaurl = null; + $this->threadurl = ''; + $this->reporturl = ''; + $this->metaurl = ''; $this->start = null; $this->finalized = 0; $this->prereg_allowed = 0; diff --git a/gatherling/event.php b/gatherling/event.php index 5f21e0ba6..81dcc412d 100644 --- a/gatherling/event.php +++ b/gatherling/event.php @@ -337,14 +337,14 @@ function updateEvent(): Event $event->series = $_POST['series']; $event->season = (int) $_POST['season']; $event->number = (int) $_POST['number']; - $event->threadurl = $_POST['threadurl']; - $event->metaurl = $_POST['metaurl']; - $event->reporturl = $_POST['reporturl']; + $event->threadurl = post()->string('threadurl'); + $event->metaurl = post()->string('metaurl'); + $event->reporturl = post()->string('reporturl'); - if ($_POST['mainrounds'] == '') { - $_POST['mainrounds'] = 3; + if (post()->string('mainrounds') == '') { + post()->string('mainrounds', '3'); } - if ($_POST['mainstruct'] == '') { + if (post()->string('mainstruct') == '') { $_POST['mainstruct'] = 'Swiss'; } if ($_POST['mainrounds'] >= $event->current_round) { diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 2c111421a..cb7cea621 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1010,11 +1010,6 @@ parameters: count: 1 path: gatherling/Views/Pages/EventForm.php - - - message: "#^Parameter \\#2 \\$def of function Gatherling\\\\Views\\\\Pages\\\\clientDropMenuArgs expects int, int\\|null given\\.$#" - count: 1 - path: gatherling/Views/Pages/EventForm.php - - message: "#^Parameter \\#3 \\$limitTo of class Gatherling\\\\Views\\\\Components\\\\SeriesDropMenu constructor expects list\\, array\\, string\\> given\\.$#" count: 1 @@ -1400,26 +1395,11 @@ parameters: count: 1 path: gatherling/event.php - - - message: "#^Property Gatherling\\\\Models\\\\Event\\:\\:\\$metaurl \\(string\\|null\\) does not accept mixed\\.$#" - count: 1 - path: gatherling/event.php - - - - message: "#^Property Gatherling\\\\Models\\\\Event\\:\\:\\$reporturl \\(string\\|null\\) does not accept mixed\\.$#" - count: 1 - path: gatherling/event.php - - message: "#^Property Gatherling\\\\Models\\\\Event\\:\\:\\$series \\(string\\|null\\) does not accept mixed\\.$#" count: 1 path: gatherling/event.php - - - message: "#^Property Gatherling\\\\Models\\\\Event\\:\\:\\$threadurl \\(string\\|null\\) does not accept mixed\\.$#" - count: 1 - path: gatherling/event.php - - message: "#^Parameter \\#1 \\$name of function generateSecureResetLink expects string, string\\|null given\\.$#" count: 1 From a1165b312c93362dd0970dea383d072740cdd34a Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 21:45:12 -0700 Subject: [PATCH 033/114] Bring model into line with database and make Player->name not nullable --- gatherling/Models/Matchup.php | 2 +- gatherling/Models/Player.php | 2 +- .../Components/MainPlayerControlPanel.php | 1 - gatherling/seriescp.php | 1 - phpstan-baseline.neon | 189 +----------------- 5 files changed, 4 insertions(+), 191 deletions(-) diff --git a/gatherling/Models/Matchup.php b/gatherling/Models/Matchup.php index 8e94e1bbe..7ef513f6b 100644 --- a/gatherling/Models/Matchup.php +++ b/gatherling/Models/Matchup.php @@ -101,7 +101,7 @@ private function playerB(string $name): bool return strcasecmp($this->playerb, $name) == 0; } - private function toName(string|Player $player_or_name): ?string + private function toName(string|Player $player_or_name): string { if (is_object($player_or_name)) { return $player_or_name->name; diff --git a/gatherling/Models/Player.php b/gatherling/Models/Player.php index b640e36ec..ca6bd6891 100644 --- a/gatherling/Models/Player.php +++ b/gatherling/Models/Player.php @@ -16,7 +16,7 @@ class Player { - public ?string $name; + public string $name; public ?string $password; public ?int $host = 0; public ?int $super; diff --git a/gatherling/Views/Components/MainPlayerControlPanel.php b/gatherling/Views/Components/MainPlayerControlPanel.php index c3c572550..7077ecdec 100644 --- a/gatherling/Views/Components/MainPlayerControlPanel.php +++ b/gatherling/Views/Components/MainPlayerControlPanel.php @@ -98,7 +98,6 @@ private function activeEventsAndMatches(Player $player): array $dropLink = $joinLink = $joinLinkText = ''; $createDeckLink = null; if ($playerActive) { - assert($player->name !== null); // You can't be active if you don't exist $entry = new Entry($event->id, $player->name); if (is_null($entry->deck) || !$entry->deck->isValid()) { $createDeckLink = new CreateDeckLink($entry); diff --git a/gatherling/seriescp.php b/gatherling/seriescp.php index 3ce0a720b..696893652 100644 --- a/gatherling/seriescp.php +++ b/gatherling/seriescp.php @@ -169,7 +169,6 @@ function updateBannedPlayers(Series $series, array $removeBannedPlayers, string if ($addplayer == null) { return "Can't add {$addition} to Banned Players, they don't exist!"; } - assert($addplayer->name !== null); // Else we would not have found them $series->addBannedPlayer($addplayer->name, $reason); return ''; } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index cb7cea621..7f493cf53 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -150,11 +150,6 @@ parameters: count: 1 path: gatherling/Models/Entry.php - - - message: "#^Parameter \\#2 \\$string2 of function strcasecmp expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Models/Entry.php - - message: "#^Binary operation \"\\+\" between int\\|string and int\\<1, max\\>\\|string results in an error\\.$#" count: 3 @@ -270,11 +265,6 @@ parameters: count: 1 path: gatherling/Models/Event.php - - - message: "#^Parameter \\#2 \\$playername of class Gatherling\\\\Models\\\\Standings constructor expects string, string\\|null given\\.$#" - count: 2 - path: gatherling/Models/Event.php - - message: "#^Parameter \\#2 \\$playername of static method Gatherling\\\\Models\\\\Standings\\:\\:writeSeed\\(\\) expects string, string\\|null given\\.$#" count: 17 @@ -515,16 +505,6 @@ parameters: count: 1 path: gatherling/Models/Matchup.php - - - message: "#^Parameter \\#1 \\$name of method Gatherling\\\\Models\\\\Matchup\\:\\:playerA\\(\\) expects string, string\\|null given\\.$#" - count: 7 - path: gatherling/Models/Matchup.php - - - - message: "#^Parameter \\#1 \\$name of method Gatherling\\\\Models\\\\Matchup\\:\\:playerB\\(\\) expects string, string\\|null given\\.$#" - count: 7 - path: gatherling/Models/Matchup.php - - message: "#^Parameter \\#1 \\$player of method Gatherling\\\\Models\\\\Matchup\\:\\:playerLost\\(\\) expects Gatherling\\\\Models\\\\Player\\|string, string\\|null given\\.$#" count: 2 @@ -620,41 +600,11 @@ parameters: count: 1 path: gatherling/Models/Player.php - - - message: "#^Parameter \\#1 \\$oneplayer of method Gatherling\\\\Models\\\\Matchup\\:\\:otherPlayer\\(\\) expects string, string\\|null given\\.$#" - count: 2 - path: gatherling/Models/Player.php - - - - message: "#^Parameter \\#1 \\$player of method Gatherling\\\\Models\\\\Matchup\\:\\:playerBye\\(\\) expects Gatherling\\\\Models\\\\Player\\|string, string\\|null given\\.$#" - count: 1 - path: gatherling/Models/Player.php - - - - message: "#^Parameter \\#1 \\$player of method Gatherling\\\\Models\\\\Matchup\\:\\:playerLost\\(\\) expects Gatherling\\\\Models\\\\Player\\|string, string\\|null given\\.$#" - count: 3 - path: gatherling/Models/Player.php - - - - message: "#^Parameter \\#1 \\$player of method Gatherling\\\\Models\\\\Matchup\\:\\:playerMatchInProgress\\(\\) expects Gatherling\\\\Models\\\\Player\\|string, string\\|null given\\.$#" - count: 1 - path: gatherling/Models/Player.php - - - - message: "#^Parameter \\#1 \\$player of method Gatherling\\\\Models\\\\Matchup\\:\\:playerWon\\(\\) expects Gatherling\\\\Models\\\\Player\\|string, string\\|null given\\.$#" - count: 3 - path: gatherling/Models/Player.php - - message: "#^Parameter \\#1 \\$string1 of function strcasecmp expects string, string\\|null given\\.$#" count: 4 path: gatherling/Models/Player.php - - - message: "#^Parameter \\#2 \\$string2 of function strcasecmp expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Models/Player.php - - message: "#^Cannot access property \\$error on mysqli_stmt\\|false\\.$#" count: 1 @@ -790,11 +740,6 @@ parameters: count: 1 path: gatherling/Models/Series.php - - - message: "#^Parameter \\#1 \\$name of method Gatherling\\\\Models\\\\Series\\:\\:isOrganizer\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Models/Series.php - - message: "#^Parameter \\#1 \\$string of function substr expects string, string\\|null given\\.$#" count: 1 @@ -845,11 +790,6 @@ parameters: count: 2 path: gatherling/Models/Standings.php - - - message: "#^Parameter \\#2 \\$playername of class Gatherling\\\\Models\\\\Standings constructor expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Models/Standings.php - - message: "#^Parameter \\#3 \\$initial_seed of class Gatherling\\\\Models\\\\Standings constructor expects int, int\\|null given\\.$#" count: 1 @@ -860,26 +800,6 @@ parameters: count: 1 path: gatherling/Views/Components/BAndR.php - - - message: "#^Parameter \\#1 \\$player of method Gatherling\\\\Models\\\\Matchup\\:\\:playerBye\\(\\) expects Gatherling\\\\Models\\\\Player\\|string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Components/BestDecksTable.php - - - - message: "#^Parameter \\#1 \\$player of method Gatherling\\\\Models\\\\Matchup\\:\\:playerLost\\(\\) expects Gatherling\\\\Models\\\\Player\\|string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Components/BestDecksTable.php - - - - message: "#^Parameter \\#1 \\$player of method Gatherling\\\\Models\\\\Matchup\\:\\:playerMatchInProgress\\(\\) expects Gatherling\\\\Models\\\\Player\\|string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Components/BestDecksTable.php - - - - message: "#^Parameter \\#1 \\$player of method Gatherling\\\\Models\\\\Matchup\\:\\:playerWon\\(\\) expects Gatherling\\\\Models\\\\Player\\|string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Components/BestDecksTable.php - - message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, string\\|null given\\.$#" count: 1 @@ -905,11 +825,6 @@ parameters: count: 1 path: gatherling/Views/Components/FullMetagame.php - - - message: "#^Parameter \\#2 \\$playername of class Gatherling\\\\Models\\\\Entry constructor expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Components/FullMetagame.php - - message: "#^Parameter \\#1 \\$string of function rawurlencode expects string, string\\|null given\\.$#" count: 1 @@ -930,21 +845,11 @@ parameters: count: 1 path: gatherling/Views/Components/InfoTable.php - - - message: "#^Parameter \\#1 \\$string of function strtoupper expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Components/InfoTable.php - - message: "#^Parameter \\#1 \\$time of class Gatherling\\\\Views\\\\Components\\\\Time constructor expects int, int\\|false given\\.$#" count: 1 path: gatherling/Views/Components/InfoTable.php - - - message: "#^Parameter \\#1 \\$playername of method Gatherling\\\\Models\\\\Event\\:\\:hasRegistrant\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Components/Prereg.php - - message: "#^Binary operation \"\\+\" between float\\|int\\|string\\|null and 1 results in an error\\.$#" count: 1 @@ -1025,39 +930,24 @@ parameters: count: 1 path: gatherling/Views/Pages/EventReport.php - - - message: "#^Parameter \\#1 \\$string of function rawurlencode expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Pages/Home.php - - message: "#^Property Gatherling\\\\Views\\\\Pages\\\\Home\\:\\:\\$activeEvents \\(list\\\\) does not accept non\\-empty\\-list\\\\.$#" count: 1 path: gatherling/Views/Pages/Home.php - - - message: "#^Property Gatherling\\\\Views\\\\Pages\\\\Home\\:\\:\\$playerInfo \\(array\\{name\\: string, link\\: string\\}\\|null\\) does not accept array\\{name\\: string\\|null, link\\: non\\-falsy\\-string\\}\\.$#" - count: 1 - path: gatherling/Views/Pages/Home.php - - message: "#^Parameter \\#1 \\$name of class Gatherling\\\\Models\\\\Player constructor expects string, string\\|null given\\.$#" count: 2 path: gatherling/Views/Pages/MatchList.php - - - message: "#^Parameter \\#1 \\$player of method Gatherling\\\\Models\\\\Matchup\\:\\:getPlayerLosses\\(\\) expects Gatherling\\\\Models\\\\Player\\|string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Pages/MatchList.php - - message: "#^Parameter \\#1 \\$player of method Gatherling\\\\Models\\\\Matchup\\:\\:getPlayerWins\\(\\) expects Gatherling\\\\Models\\\\Player\\|string, string\\|null given\\.$#" - count: 5 + count: 4 path: gatherling/Views/Pages/MatchList.php - message: "#^Parameter \\#1 \\$player of method Gatherling\\\\Models\\\\Matchup\\:\\:playerDropped\\(\\) expects string, string\\|null given\\.$#" - count: 3 + count: 2 path: gatherling/Views/Pages/MatchList.php - @@ -1070,21 +960,6 @@ parameters: count: 1 path: gatherling/Views/Pages/PlayerList.php - - - message: "#^Parameter \\#2 \\$playerName of class Gatherling\\\\Views\\\\Components\\\\InitialByesDropMenu constructor expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Pages/PlayerList.php - - - - message: "#^Parameter \\#2 \\$playerName of class Gatherling\\\\Views\\\\Components\\\\InitialSeedDropMenu constructor expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Pages/PlayerList.php - - - - message: "#^Parameter \\#2 \\$playername of static method Gatherling\\\\Models\\\\Standings\\:\\:playerActive\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Pages/PlayerList.php - - message: "#^Parameter \\#2 \\$string2 of function strcmp expects string, string\\|null given\\.$#" count: 1 @@ -1100,11 +975,6 @@ parameters: count: 1 path: gatherling/Views/Pages/PlayerList.php - - - message: "#^Parameter \\#1 \\$player of method Gatherling\\\\Models\\\\Event\\:\\:getSeasonPointAdjustment\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Pages/PointsAdjustmentForm.php - - message: "#^Parameter \\#1 \\$timezone of class Gatherling\\\\Views\\\\Components\\\\ProfileEditForm constructor expects float, float\\|null given\\.$#" count: 1 @@ -1175,16 +1045,6 @@ parameters: count: 2 path: gatherling/action.php - - - message: "#^Parameter \\#1 \\$string1 of function strcasecmp expects string, string\\|null given\\.$#" - count: 2 - path: gatherling/action.php - - - - message: "#^Parameter \\#2 \\$playername of static method Gatherling\\\\Models\\\\Entry\\:\\:findByEventAndPlayer\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/action.php - - message: "#^Parameter \\#2 \\$string2 of function strcasecmp expects string, string\\|null given\\.$#" count: 2 @@ -1400,11 +1260,6 @@ parameters: count: 1 path: gatherling/event.php - - - message: "#^Parameter \\#1 \\$name of function generateSecureResetLink expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/forgot.php - - message: "#^Parameter \\#1 \\$to of function sendEmail expects string, string\\|null given\\.$#" count: 1 @@ -1425,11 +1280,6 @@ parameters: count: 1 path: gatherling/formatcp.php - - - message: "#^Parameter \\#1 \\$host_name of static method Gatherling\\\\Models\\\\Event\\:\\:findMostRecentByHost\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/index.php - - message: "#^Function arrayMapRecursive\\(\\) should return array\\ but returns array\\\\.$#" count: 1 @@ -1460,26 +1310,6 @@ parameters: count: 3 path: gatherling/lib.php - - - message: "#^Parameter \\#1 \\$username of static method Gatherling\\\\Models\\\\Player\\:\\:checkPassword\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/player.php - - - - message: "#^Parameter \\#1 \\$name of method Gatherling\\\\Models\\\\Series\\:\\:isPlayerBanned\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/prereg.php - - - - message: "#^Parameter \\#1 \\$playername of method Gatherling\\\\Models\\\\Event\\:\\:addPlayer\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/prereg.php - - - - message: "#^Parameter \\#1 \\$playername of method Gatherling\\\\Models\\\\Event\\:\\:removeEntry\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/prereg.php - - message: "#^Cannot cast mixed to float\\.$#" count: 1 @@ -1490,11 +1320,6 @@ parameters: count: 1 path: gatherling/profile.php - - - message: "#^Parameter \\#1 \\$playername of method Gatherling\\\\Models\\\\Event\\:\\:dropPlayer\\(\\) expects string, string\\|null given\\.$#" - count: 2 - path: gatherling/report.php - - message: "#^Binary operation \"\\.\" between mixed and '\\:00' results in an error\\.$#" count: 1 @@ -1505,11 +1330,6 @@ parameters: count: 1 path: gatherling/seriescp.php - - - message: "#^Parameter \\#1 \\$name of method Gatherling\\\\Models\\\\Series\\:\\:addOrganizer\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/seriescp.php - - message: "#^Property Gatherling\\\\Models\\\\Series\\:\\:\\$mtgo_room \\(string\\|null\\) does not accept mixed\\.$#" count: 1 @@ -1519,8 +1339,3 @@ parameters: message: "#^Property Gatherling\\\\Models\\\\Series\\:\\:\\$start_day \\(string\\|null\\) does not accept mixed\\.$#" count: 1 path: gatherling/seriescp.php - - - - message: "#^Parameter \\#1 \\$playername of method Gatherling\\\\Models\\\\Event\\:\\:addPlayer\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: tests/Models/DeckTest.php From 850d155db301d46cf962752fcc96b8fd2e5009d4 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 21:46:55 -0700 Subject: [PATCH 034/114] Declare in line with db that some properties of Player are not nullable --- gatherling/Models/Player.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gatherling/Models/Player.php b/gatherling/Models/Player.php index ca6bd6891..3c291c498 100644 --- a/gatherling/Models/Player.php +++ b/gatherling/Models/Player.php @@ -18,14 +18,14 @@ class Player { public string $name; public ?string $password; - public ?int $host = 0; - public ?int $super; - public ?int $rememberMe; // if selected will record IP address. Gatherling will automatically log players in of known IP addresses. + public int $host = 0; + public int $super; + public int $rememberMe; // if selected will record IP address. Gatherling will automatically log players in of known IP addresses. public ?string $ipAddress; public ?string $emailAddress = null; public ?int $emailPrivacy = 0; public ?float $timezone = -5.0; - public ?int $verified; + public int $verified; public ?string $theme = null; // DEPRECATED. Always null. public ?string $discord_id = null; public ?string $discord_handle = null; From 2e59420bf24d26fa571e3623b932c1845e4d450b Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 21:48:33 -0700 Subject: [PATCH 035/114] Move Entry's constructor to top of file That's where you expect to find the constructor. --- gatherling/Models/Entry.php | 64 ++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/gatherling/Models/Entry.php b/gatherling/Models/Entry.php index 74ce6c2cc..80595666d 100644 --- a/gatherling/Models/Entry.php +++ b/gatherling/Models/Entry.php @@ -21,6 +21,38 @@ class Entry public ?int $initial_seed; public ?int $ignored; + // TODO: remove ignore functionality + public function __construct(int $event_id, string $playername) + { + $this->ignored = 0; + $sql = ' + SELECT + deck AS deck_id, medal, ignored, drop_round, initial_byes, initial_seed + FROM + entries + WHERE + event_id = :event_id AND player = :player'; + $params = ['event_id' => $event_id, 'player' => $playername]; + $entry = db()->selectOnlyOrNull($sql, EntryDto::class, $params); + if ($entry == null) { + throw new NotFoundException('Entry for ' . $playername . ' in ' . $event_id . ' not found'); + } + $this->medal = $entry->medal; + $this->ignored = $entry->ignored; + $this->drop_round = $entry->drop_round; + $this->initial_byes = $entry->initial_byes; + $this->initial_seed = $entry->initial_seed; + + if ($entry->deck_id != null) { + $this->deck = new Deck($entry->deck_id); + } else { + $this->deck = null; + } + + $this->event = new Event($event_id); + $this->player = new Player($playername); + } + public static function findByEventAndPlayer(int $event_id, string $playername): ?self { $sql = 'SELECT deck FROM entries WHERE event_id = :event_id AND player = :player'; @@ -73,38 +105,6 @@ public static function playerRegistered(int $eventid, string $playername): bool return db()->optionalString($sql, $params) !== null; } - // TODO: remove ignore functionality - public function __construct(int $event_id, string $playername) - { - $this->ignored = 0; - $sql = ' - SELECT - deck AS deck_id, medal, ignored, drop_round, initial_byes, initial_seed - FROM - entries - WHERE - event_id = :event_id AND player = :player'; - $params = ['event_id' => $event_id, 'player' => $playername]; - $entry = db()->selectOnlyOrNull($sql, EntryDto::class, $params); - if ($entry == null) { - throw new NotFoundException('Entry for ' . $playername . ' in ' . $event_id . ' not found'); - } - $this->medal = $entry->medal; - $this->ignored = $entry->ignored; - $this->drop_round = $entry->drop_round; - $this->initial_byes = $entry->initial_byes; - $this->initial_seed = $entry->initial_seed; - - if ($entry->deck_id != null) { - $this->deck = new Deck($entry->deck_id); - } else { - $this->deck = null; - } - - $this->event = new Event($event_id); - $this->player = new Player($playername); - } - public function recordString(): string { $matches = $this->getMatches(); From 6ef5de13858f67b08675bc548921d803174c6a34 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 21:50:28 -0700 Subject: [PATCH 036/114] Bit overzealous marking things as not nullable This field is derived and maybe needs to be in an undecided state at times. --- gatherling/Models/Player.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gatherling/Models/Player.php b/gatherling/Models/Player.php index 3c291c498..6c9c40923 100644 --- a/gatherling/Models/Player.php +++ b/gatherling/Models/Player.php @@ -25,7 +25,7 @@ class Player public ?string $emailAddress = null; public ?int $emailPrivacy = 0; public ?float $timezone = -5.0; - public int $verified; + public ?int $verified; public ?string $theme = null; // DEPRECATED. Always null. public ?string $discord_id = null; public ?string $discord_handle = null; From 640a2ecf572a133f1dbff493bfc42d5af8205dc8 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 21:52:59 -0700 Subject: [PATCH 037/114] Remove "ignore" functionality I'm not even completely sure what this is/was but this column is always false in the db and the code is peppered with some very old TODOs saying to remove it. --- gatherling/Data/sql/migrations/73.sql | 1 + gatherling/Models/Entry.php | 18 +----------------- gatherling/Models/EntryDto.php | 1 - phpstan-baseline.neon | 15 --------------- psalm-baseline.xml | 1 - 5 files changed, 2 insertions(+), 34 deletions(-) create mode 100644 gatherling/Data/sql/migrations/73.sql diff --git a/gatherling/Data/sql/migrations/73.sql b/gatherling/Data/sql/migrations/73.sql new file mode 100644 index 000000000..95ae10949 --- /dev/null +++ b/gatherling/Data/sql/migrations/73.sql @@ -0,0 +1 @@ +ALTER TABLE entries DROP COLUMN ignored; diff --git a/gatherling/Models/Entry.php b/gatherling/Models/Entry.php index 80595666d..7fa5c9894 100644 --- a/gatherling/Models/Entry.php +++ b/gatherling/Models/Entry.php @@ -19,15 +19,12 @@ class Entry public ?int $drop_round; public ?int $initial_byes; public ?int $initial_seed; - public ?int $ignored; - // TODO: remove ignore functionality public function __construct(int $event_id, string $playername) { - $this->ignored = 0; $sql = ' SELECT - deck AS deck_id, medal, ignored, drop_round, initial_byes, initial_seed + deck AS deck_id, medal, drop_round, initial_byes, initial_seed FROM entries WHERE @@ -38,7 +35,6 @@ public function __construct(int $event_id, string $playername) throw new NotFoundException('Entry for ' . $playername . ' in ' . $event_id . ' not found'); } $this->medal = $entry->medal; - $this->ignored = $entry->ignored; $this->drop_round = $entry->drop_round; $this->initial_byes = $entry->initial_byes; $this->initial_seed = $entry->initial_seed; @@ -165,18 +161,6 @@ public function canCreateDeck(string $username): bool return false; } - // TODO: Remove ignore functionality - public function setIgnored(int $new_ignored): void - { - $db = Database::getConnection(); - $stmt = $db->prepare('UPDATE entries SET ignored = ? WHERE player = ? and event_id = ?'); - $playername = $this->player->name; - $event_id = $this->event->id; - $stmt->bind_param('isd', $new_ignored, $playername, $event_id); - $stmt->execute(); - $stmt->close(); - } - public function removeEntry(): bool { db()->begin('remove_entry'); diff --git a/gatherling/Models/EntryDto.php b/gatherling/Models/EntryDto.php index 6fb46a567..0368442b6 100644 --- a/gatherling/Models/EntryDto.php +++ b/gatherling/Models/EntryDto.php @@ -8,7 +8,6 @@ class EntryDto extends Dto { public ?int $deck_id; public string $medal; - public ?int $ignored; public int $drop_round; public int $initial_byes; public ?int $initial_seed; diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 7f493cf53..a11892e5c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -135,21 +135,6 @@ parameters: count: 1 path: gatherling/Models/Decksearch.php - - - message: "#^Cannot call method bind_param\\(\\) on mysqli_stmt\\|false\\.$#" - count: 1 - path: gatherling/Models/Entry.php - - - - message: "#^Cannot call method close\\(\\) on mysqli_stmt\\|false\\.$#" - count: 1 - path: gatherling/Models/Entry.php - - - - message: "#^Cannot call method execute\\(\\) on mysqli_stmt\\|false\\.$#" - count: 1 - path: gatherling/Models/Entry.php - - message: "#^Binary operation \"\\+\" between int\\|string and int\\<1, max\\>\\|string results in an error\\.$#" count: 3 diff --git a/psalm-baseline.xml b/psalm-baseline.xml index ae2de196f..ec6259d58 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -23,7 +23,6 @@ - From c6f1b9e8e4182381d15a6e4e1128aee918d5f630 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 21:57:09 -0700 Subject: [PATCH 038/114] River-style SQL over more spaced out version I think the river style is just about as clear and takes up less vertical space by a lot. --- gatherling/Models/Entry.php | 62 ++++++++++++------------------------- 1 file changed, 20 insertions(+), 42 deletions(-) diff --git a/gatherling/Models/Entry.php b/gatherling/Models/Entry.php index 7fa5c9894..833ec2c12 100644 --- a/gatherling/Models/Entry.php +++ b/gatherling/Models/Entry.php @@ -23,12 +23,9 @@ class Entry public function __construct(int $event_id, string $playername) { $sql = ' - SELECT - deck AS deck_id, medal, drop_round, initial_byes, initial_seed - FROM - entries - WHERE - event_id = :event_id AND player = :player'; + SELECT deck AS deck_id, medal, drop_round, initial_byes, initial_seed + FROM entries + WHERE event_id = :event_id AND player = :player'; $params = ['event_id' => $event_id, 'player' => $playername]; $entry = db()->selectOnlyOrNull($sql, EntryDto::class, $params); if ($entry == null) { @@ -66,20 +63,12 @@ public static function findByEventAndPlayer(int $event_id, string $playername): public static function getActivePlayers(int $eventid): array { $sql = ' - SELECT - e.player - FROM - entries e - JOIN - events ev ON e.event_id = ev.id - JOIN - standings s ON ev.name = s.event - WHERE - e.event_id = :eventid - AND - s.active = 1 - GROUP BY - player'; + SELECT e.player + FROM entries e + JOIN events ev ON e.event_id = ev.id + JOIN standings s ON ev.name = s.event + WHERE e.event_id = :eventid AND s.active = 1 + GROUP BY player'; $playernames = db()->strings($sql, ['eventid' => $eventid]); return array_map(fn (string $name) => new Entry($eventid, $name), $playernames); } @@ -87,16 +76,11 @@ public static function getActivePlayers(int $eventid): array public static function playerRegistered(int $eventid, string $playername): bool { $sql = ' - SELECT - n.player - FROM - entries n - JOIN - events e ON n.event_id = e.id - WHERE - n.event_id = :event_id AND n.player = :player - GROUP BY - player'; + SELECT n.player + FROM entries n + JOIN events e ON n.event_id = e.id + WHERE n.event_id = :event_id AND n.player = :player + GROUP BY player'; $params = ['event_id' => $eventid, 'player' => $playername]; return db()->optionalString($sql, $params) !== null; } @@ -192,12 +176,9 @@ public function removeEntry(): bool public function setInitialByes(int $byeqty): void { $sql = ' - UPDATE - entries - SET - initial_byes = :initial_byes - WHERE - player = :player AND event_id = :event_id'; + UPDATE entries + SET initial_byes = :initial_byes + WHERE player = :player AND event_id = :event_id'; $params = [ 'initial_byes' => $byeqty, 'player' => $this->player->name, @@ -209,12 +190,9 @@ public function setInitialByes(int $byeqty): void public function setInitialSeed(int $byeqty): void { $sql = ' - UPDATE - entries - SET - initial_seed = :initial_seed - WHERE - player = :player AND event_id = :event_id'; + UPDATE entries + SET initial_seed = :initial_seed + WHERE player = :player AND event_id = :event_id'; $params = [ 'initial_seed' => $byeqty, 'player' => $this->player->name, From fdfefba2cea35ee8cf2edf42a1f71415e980d618 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 21:58:40 -0700 Subject: [PATCH 039/114] Bring Entry and EntryDto in line with nullability of props in database --- gatherling/Models/Entry.php | 8 ++++---- gatherling/Models/EntryDto.php | 2 +- phpstan-baseline.neon | 20 -------------------- 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/gatherling/Models/Entry.php b/gatherling/Models/Entry.php index 833ec2c12..5eca9615d 100644 --- a/gatherling/Models/Entry.php +++ b/gatherling/Models/Entry.php @@ -15,10 +15,10 @@ class Entry public Event $event; public Player $player; public ?Deck $deck; - public ?string $medal; - public ?int $drop_round; - public ?int $initial_byes; - public ?int $initial_seed; + public string $medal; + public int $drop_round; + public int $initial_byes; + public int $initial_seed; public function __construct(int $event_id, string $playername) { diff --git a/gatherling/Models/EntryDto.php b/gatherling/Models/EntryDto.php index 0368442b6..ec289a0d5 100644 --- a/gatherling/Models/EntryDto.php +++ b/gatherling/Models/EntryDto.php @@ -10,5 +10,5 @@ class EntryDto extends Dto public string $medal; public int $drop_round; public int $initial_byes; - public ?int $initial_seed; + public int $initial_seed; } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index a11892e5c..549454e51 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -775,11 +775,6 @@ parameters: count: 2 path: gatherling/Models/Standings.php - - - message: "#^Parameter \\#3 \\$initial_seed of class Gatherling\\\\Models\\\\Standings constructor expects int, int\\|null given\\.$#" - count: 1 - path: gatherling/Models/Standings.php - - message: "#^Property Gatherling\\\\Views\\\\Components\\\\BAndR\\:\\:\\$activeFormatName \\(string\\) does not accept string\\|null\\.$#" count: 1 @@ -945,21 +940,6 @@ parameters: count: 1 path: gatherling/Views/Pages/PlayerList.php - - - message: "#^Parameter \\#2 \\$string2 of function strcmp expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Pages/PlayerList.php - - - - message: "#^Parameter \\#3 \\$currentByes of class Gatherling\\\\Views\\\\Components\\\\InitialByesDropMenu constructor expects int, int\\|null given\\.$#" - count: 1 - path: gatherling/Views/Pages/PlayerList.php - - - - message: "#^Parameter \\#3 \\$currentSeed of class Gatherling\\\\Views\\\\Components\\\\InitialSeedDropMenu constructor expects int, int\\|null given\\.$#" - count: 1 - path: gatherling/Views/Pages/PlayerList.php - - message: "#^Parameter \\#1 \\$timezone of class Gatherling\\\\Views\\\\Components\\\\ProfileEditForm constructor expects float, float\\|null given\\.$#" count: 1 From 6ae30c037827b1e875ccc9311e8c561173c816b5 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 22:00:28 -0700 Subject: [PATCH 040/114] Remove PHPDoc tags we aren't supporting --- gatherling/Models/Decksearch.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/gatherling/Models/Decksearch.php b/gatherling/Models/Decksearch.php index e031625da..72d56b6f8 100644 --- a/gatherling/Models/Decksearch.php +++ b/gatherling/Models/Decksearch.php @@ -44,12 +44,6 @@ class Decksearch * * * will return an array of deck id's matching the set search terms - * - * - * - * @version 1.0 - * - * @category Deck */ public function __construct() { From 3611273c622a69a6b2d1626dca1999c1533e955b Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 22:21:22 -0700 Subject: [PATCH 041/114] Bring models and database yet further in sync, removing lots of nullable props --- gatherling/Data/sql/migrations/74.sql | 4 + gatherling/Models/Format.php | 138 +++++++++--------- gatherling/Models/FormatDto.php | 26 ++++ .../Views/Components/FormatDropMenu.php | 10 +- phpstan-baseline.neon | 20 --- 5 files changed, 102 insertions(+), 96 deletions(-) create mode 100644 gatherling/Data/sql/migrations/74.sql diff --git a/gatherling/Data/sql/migrations/74.sql b/gatherling/Data/sql/migrations/74.sql new file mode 100644 index 000000000..c3bf30ebb --- /dev/null +++ b/gatherling/Data/sql/migrations/74.sql @@ -0,0 +1,4 @@ +UPDATE formats SET description = '' WHERE description IS NULL; +ALTER TABLE formats MODIFY description MEDIUMTEXT NOT NULL; +UPDATE formats SET limitless = 0 WHERE limitless IS NULL; +ALTER TABLE formats MODIFY limitless TINYINT(3) NOT NULL; diff --git a/gatherling/Models/Format.php b/gatherling/Models/Format.php index 05b26a588..56f649098 100644 --- a/gatherling/Models/Format.php +++ b/gatherling/Models/Format.php @@ -11,11 +11,11 @@ class Format { - public ?string $name; - public ?string $description; - public ?string $type; // who has access to filter: public, private, system - public ?string $series_name; // filter owner - public ?int $priority; + public string $name; + public string $description; + public string $type; // who has access to filter: public, private, system + public string $series_name; // filter owner + public int $priority; public ?bool $new = null; // card set construction @@ -27,36 +27,36 @@ class Format public array $card_legallist = []; /** @var list */ public array $legal_sets = []; - public ?int $eternal; - public ?int $modern; - public ?int $standard; + public int $eternal; + public int $modern; + public int $standard; // deck construction switches - public ?int $singleton; - public ?int $commander; - public ?int $planechase; - public ?int $vanguard; - public ?int $prismatic; - public ?int $tribal; - public ?int $pure; - public ?int $underdog; - public ?int $limitless; + public int $singleton; + public int $commander; + public int $planechase; + public int $vanguard; + public int $prismatic; + public int $tribal; + public int $pure; + public int $underdog; + public int $limitless; // rarities allowed switches - public ?int $allow_commons; - public ?int $allow_uncommons; - public ?int $allow_rares; - public ?int $allow_mythics; - public ?int $allow_timeshifted; + public int $allow_commons; + public int $allow_uncommons; + public int $allow_rares; + public int $allow_mythics; + public int $allow_timeshifted; // deck limits - public ?int $min_main_cards_allowed; - public ?int $max_main_cards_allowed; - public ?int $min_side_cards_allowed; - public ?int $max_side_cards_allowed; + public int $min_main_cards_allowed; + public int $max_main_cards_allowed; + public int $min_side_cards_allowed; + public int $max_side_cards_allowed; // Meta Formats - public ?int $is_meta_format; + public int $is_meta_format; /** @var list */ private array $error = []; @@ -84,6 +84,7 @@ public function __construct(string $name) $this->tribal = 0; $this->pure = 0; $this->underdog = 0; + $this->limitless = 0; $this->allow_commons = 1; $this->allow_uncommons = 1; $this->allow_rares = 1; @@ -105,49 +106,44 @@ public function __construct(string $name) $this->insertNewFormat(); return; } else { - $db = Database::getConnection(); - $stmt = $db->prepare('SELECT name, description, type, series_name, singleton, commander, planechase, vanguard, - prismatic, tribal, pure, underdog, limitless, allow_commons, allow_uncommons, allow_rares, allow_mythics, - allow_timeshifted, priority, min_main_cards_allowed, max_main_cards_allowed, - min_side_cards_allowed, max_side_cards_allowed, eternal, modern, `standard`, is_meta_format - FROM formats - WHERE name = ?'); - $stmt or exit($db->error); - $stmt->bind_param('s', $name); - $stmt->execute(); - $stmt->bind_result( - $this->name, - $this->description, - $this->type, - $this->series_name, - $this->singleton, - $this->commander, - $this->planechase, - $this->vanguard, - $this->prismatic, - $this->tribal, - $this->pure, - $this->underdog, - $this->limitless, - $this->allow_commons, - $this->allow_uncommons, - $this->allow_rares, - $this->allow_mythics, - $this->allow_timeshifted, - $this->priority, - $this->min_main_cards_allowed, - $this->max_main_cards_allowed, - $this->min_side_cards_allowed, - $this->max_side_cards_allowed, - $this->eternal, - $this->modern, - $this->standard, - $this->is_meta_format - ); - if ($stmt->fetch() == null) { - throw new Exception('Format ' . $name . ' not found in DB'); - } - $stmt->close(); + $sql = ' + SELECT name, description, type, series_name, singleton, commander, planechase, vanguard, + prismatic, tribal, pure, underdog, limitless, allow_commons, allow_uncommons, + allow_rares, allow_mythics, allow_timeshifted, priority, min_main_cards_allowed, + max_main_cards_allowed, min_side_cards_allowed, max_side_cards_allowed, eternal, + modern, `standard`, is_meta_format + FROM formats + WHERE name = :name'; + $result = db()->selectOnly($sql, FormatDto::class, ['name' => $name]); + + $this->name = $result->name; + $this->description = $result->description; + $this->type = $result->type; + $this->series_name = $result->series_name; + $this->singleton = $result->singleton; + $this->commander = $result->commander; + $this->planechase = $result->planechase; + $this->vanguard = $result->vanguard; + $this->prismatic = $result->prismatic; + $this->tribal = $result->tribal; + $this->pure = $result->pure; + $this->underdog = $result->underdog; + $this->limitless = $result->limitless; + $this->allow_commons = $result->allow_commons; + $this->allow_uncommons = $result->allow_uncommons; + $this->allow_rares = $result->allow_rares; + $this->allow_mythics = $result->allow_mythics; + $this->allow_timeshifted = $result->allow_timeshifted; + $this->priority = $result->priority; + $this->min_main_cards_allowed = $result->min_main_cards_allowed; + $this->max_main_cards_allowed = $result->max_main_cards_allowed; + $this->min_side_cards_allowed = $result->min_side_cards_allowed; + $this->max_side_cards_allowed = $result->max_side_cards_allowed; + $this->eternal = $result->eternal; + $this->modern = $result->modern; + $this->standard = $result->standard; + $this->is_meta_format = $result->is_meta_format; + $this->card_banlist = $this->getBanList(); $this->card_legallist = $this->getLegalList(); $this->card_restrictedlist = $this->getRestrictedList(); @@ -458,7 +454,7 @@ public function delete(): bool public function noFormatLoaded(): bool { - return ($this->name == '') || is_null($this->name); + return $this->name == ''; } /** @return list */ diff --git a/gatherling/Models/FormatDto.php b/gatherling/Models/FormatDto.php index a27872921..b7a65ba08 100644 --- a/gatherling/Models/FormatDto.php +++ b/gatherling/Models/FormatDto.php @@ -7,4 +7,30 @@ class FormatDto extends Dto { public string $name; + public string $description; + public string $type; + public string $series_name; + public int $singleton; + public int $commander; + public int $planechase; + public int $vanguard; + public int $prismatic; + public int $tribal; + public int $pure; + public int $underdog; + public int $limitless; + public int $allow_commons; + public int $allow_uncommons; + public int $allow_rares; + public int $allow_mythics; + public int $allow_timeshifted; + public int $priority; + public int $min_main_cards_allowed; + public int $max_main_cards_allowed; + public int $min_side_cards_allowed; + public int $max_side_cards_allowed; + public int $eternal; + public int $modern; + public int $standard; + public int $is_meta_format; } diff --git a/gatherling/Views/Components/FormatDropMenu.php b/gatherling/Views/Components/FormatDropMenu.php index 229f2131f..a75736a4d 100644 --- a/gatherling/Views/Components/FormatDropMenu.php +++ b/gatherling/Views/Components/FormatDropMenu.php @@ -13,14 +13,14 @@ class FormatDropMenu extends DropMenu public function __construct(?string $format, bool $useAll = false, string $formName = 'format') { $sql = 'SELECT name FROM formats ORDER BY priority desc, name'; - $formats = db()->select($sql, FormatDto::class); + $formats = db()->strings($sql); $options = []; - foreach ($formats as $f) { + foreach ($formats as $formatName) { $options[] = [ - 'text' => $f->name, - 'value' => $f->name, - 'isSelected' => $f->name === $format, + 'text' => $formatName, + 'value' => $formatName, + 'isSelected' => $formatName === $format, ]; } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 549454e51..2e6d62d3a 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -410,11 +410,6 @@ parameters: count: 1 path: gatherling/Models/Format.php - - - message: "#^Parameter \\#1 \\$seriesName of method Gatherling\\\\Models\\\\Player\\:\\:isOrganizer\\(\\) expects string, string\\|null given\\.$#" - count: 3 - path: gatherling/Models/Format.php - - message: "#^Parameter \\#1 \\$string1 of function strcasecmp expects string, mixed given\\.$#" count: 1 @@ -775,11 +770,6 @@ parameters: count: 2 path: gatherling/Models/Standings.php - - - message: "#^Property Gatherling\\\\Views\\\\Components\\\\BAndR\\:\\:\\$activeFormatName \\(string\\) does not accept string\\|null\\.$#" - count: 1 - path: gatherling/Views/Components/BAndR.php - - message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, string\\|null given\\.$#" count: 1 @@ -790,11 +780,6 @@ parameters: count: 1 path: gatherling/Views/Components/EventStandings.php - - - message: "#^Parameter \\#1 \\$string of function rawurlencode expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Components/FormatCPMenu.php - - message: "#^Parameter \\#1 \\$event_id of class Gatherling\\\\Models\\\\Entry constructor expects int, int\\|null given\\.$#" count: 1 @@ -1230,11 +1215,6 @@ parameters: count: 1 path: gatherling/forgot.php - - - message: "#^Parameter \\#1 \\$oldFormatName of class Gatherling\\\\Views\\\\Components\\\\FormatSaveAsForm constructor expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/formatcp.php - - message: "#^Parameter \\#1 \\$values of function updateFormat expects array\\, array given\\.$#" count: 1 From a61e918f70f251e54296d790b1265d7dc0c954d4 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 22:23:50 -0700 Subject: [PATCH 042/114] Bring some properties of Matchup in line with database --- gatherling/Models/Matchup.php | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/gatherling/Models/Matchup.php b/gatherling/Models/Matchup.php index 7ef513f6b..9175d9838 100644 --- a/gatherling/Models/Matchup.php +++ b/gatherling/Models/Matchup.php @@ -19,12 +19,12 @@ class Matchup public ?string $playerb; public ?string $result; // We keep both players wins and losses, so that they can independently report their scores. - public ?int $playera_wins; - public ?int $playera_losses; - public ?int $playera_draws; - public ?int $playerb_wins; - public ?int $playerb_losses; - public ?int $playerb_draws; + public int $playera_wins = 0; + public int $playera_losses = 0; + public int $playera_draws = 0; + public int $playerb_wins = 0; + public int $playerb_losses = 0; + public int $playerb_draws = 0; // Inherited from subevent @@ -53,14 +53,11 @@ public static function destroy(int $matchid): void public function __construct(int $id) { $sql = ' - SELECT - m.subevent, m.round, m.playera, m.playerb, m.result, m.playera_wins, m.playera_losses, - m.playera_draws, m.playerb_wins, m.playerb_losses, m.playerb_draws, s.timing, s.type, - s.rounds, e.format, e.series, e.season, m.verification, e.name AS eventname, e.id AS event_id - FROM - matches m, subevents s, events e - WHERE - m.id = :id AND m.subevent = s.id AND e.name = s.parent'; + SELECT m.subevent, m.round, m.playera, m.playerb, m.result, m.playera_wins, m.playera_losses, + m.playera_draws, m.playerb_wins, m.playerb_losses, m.playerb_draws, s.timing, s.type, + s.rounds, e.format, e.series, e.season, m.verification, e.name AS eventname, e.id AS event_id + FROM matches m, subevents s, events e + WHERE m.id = :id AND m.subevent = s.id AND e.name = s.parent'; $row = db()->selectOnlyOrNull($sql, MatchupDto::class, ['id' => $id]); if ($row === null) { return; From 5157f08eebb642ef1ecf16a88951ed258254bff9 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 22:28:38 -0700 Subject: [PATCH 043/114] Update copy to more correctly describe what's going to happen --- gatherling/templates/home.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gatherling/templates/home.mustache b/gatherling/templates/home.mustache index a615b1eba..770ecbdb9 100644 --- a/gatherling/templates/home.mustache +++ b/gatherling/templates/home.mustache @@ -58,7 +58,7 @@

    Welcome back {{name}}

    • Check out your profile
    • -
    • Enter your own decklists
    • +
    • See your decklists
    • {{#mostRecentHostedEvent}}
    • Manage {{name}}
    • {{/mostRecentHostedEvent}} From fafbcfe7fc435607c602f923bb79a42519c69f22 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 22:31:51 -0700 Subject: [PATCH 044/114] Use our superglobal helper to get typesafe values and fix static checker errors --- gatherling/event.php | 56 +++++++++++++++++++++---------------------- phpstan-baseline.neon | 42 +------------------------------- 2 files changed, 29 insertions(+), 69 deletions(-) diff --git a/gatherling/event.php b/gatherling/event.php index 81dcc412d..2b3fbfe47 100644 --- a/gatherling/event.php +++ b/gatherling/event.php @@ -315,28 +315,28 @@ function updateEvent(): Event $event = new Event(post()->string('name')); $event->start = "{$_POST['year']}-{$_POST['month']}-{$_POST['day']} {$_POST['hour']}:00"; - $event->finalized = (int) $_POST['finalized']; - $event->active = (int) $_POST['active']; - $event->current_round = (int) $_POST['newmatchround']; - $event->prereg_allowed = (int) $_POST['prereg_allowed']; - $event->player_reportable = (int) $_POST['player_reportable']; - $event->prereg_cap = (int) $_POST['prereg_cap']; - $event->private_decks = (int) $_POST['private_decks']; - $event->private_finals = (int) $_POST['private_finals']; - $event->player_reported_draws = (int) $_POST['player_reported_draws']; - $event->late_entry_limit = (int) $_POST['late_entry_limit']; - - if ($event->format != $_POST['format']) { - $event->format = $_POST['format']; + $event->finalized = post()->int('finalized'); + $event->active = post()->int('active'); + $event->current_round = post()->int('newmatchround'); + $event->prereg_allowed = post()->int('prereg_allowed'); + $event->player_reportable = post()->int('player_reportable'); + $event->prereg_cap = post()->int('prereg_cap'); + $event->private_decks = post()->int('private_decks'); + $event->private_finals = post()->int('private_finals'); + $event->player_reported_draws = post()->int('player_reported_draws'); + $event->late_entry_limit = post()->int('late_entry_limit'); + + if ($event->format != post()->string('format')) { + $event->format = post()->string('format'); $event->updateDecksFormat(post()->string('format')); } - $event->host = $_POST['host']; - $event->cohost = $_POST['cohost']; - $event->kvalue = (int) $_POST['kvalue']; - $event->series = $_POST['series']; - $event->season = (int) $_POST['season']; - $event->number = (int) $_POST['number']; + $event->host = post()->string('host'); + $event->cohost = post()->string('cohost'); + $event->kvalue = post()->int('kvalue'); + $event->series = post()->string('series'); + $event->season = post()->int('season'); + $event->number = post()->int('number'); $event->threadurl = post()->string('threadurl'); $event->metaurl = post()->string('metaurl'); $event->reporturl = post()->string('reporturl'); @@ -347,21 +347,21 @@ function updateEvent(): Event if (post()->string('mainstruct') == '') { $_POST['mainstruct'] = 'Swiss'; } - if ($_POST['mainrounds'] >= $event->current_round) { - $event->mainrounds = $_POST['mainrounds']; - $event->mainstruct = $_POST['mainstruct']; + if (post()->int('mainrounds') >= $event->current_round) { + $event->mainrounds = post()->int('mainrounds'); + $event->mainstruct = post()->string('mainstruct'); } - if ($_POST['finalrounds'] == '') { + if (post()->string('finalrounds') == '') { $_POST['finalrounds'] = 0; } - if ($_POST['finalstruct'] == '') { + if (post()->string('finalstruct') == '') { $_POST['finalstruct'] = 'Single Elimination'; } - $event->finalrounds = $_POST['finalrounds']; - $event->finalstruct = $_POST['finalstruct']; - $event->private = (int) $_POST['private']; - $event->client = (int) $_POST['client']; + $event->finalrounds = post()->int('finalrounds'); + $event->finalstruct = post()->string('finalstruct'); + $event->private = post()->int('private'); + $event->client = post()->int('client'); $event->save(); diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 2e6d62d3a..dc4becd71 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1122,7 +1122,7 @@ parameters: - message: "#^Cannot cast mixed to int\\.$#" - count: 16 + count: 1 path: gatherling/event.php - @@ -1170,46 +1170,6 @@ parameters: count: 1 path: gatherling/event.php - - - message: "#^Property Gatherling\\\\Models\\\\Event\\:\\:\\$cohost \\(string\\|null\\) does not accept mixed\\.$#" - count: 1 - path: gatherling/event.php - - - - message: "#^Property Gatherling\\\\Models\\\\Event\\:\\:\\$finalrounds \\(int\\|string\\) does not accept mixed\\.$#" - count: 1 - path: gatherling/event.php - - - - message: "#^Property Gatherling\\\\Models\\\\Event\\:\\:\\$finalstruct \\(string\\) does not accept mixed\\.$#" - count: 1 - path: gatherling/event.php - - - - message: "#^Property Gatherling\\\\Models\\\\Event\\:\\:\\$format \\(string\\|null\\) does not accept mixed\\.$#" - count: 1 - path: gatherling/event.php - - - - message: "#^Property Gatherling\\\\Models\\\\Event\\:\\:\\$host \\(string\\|null\\) does not accept mixed\\.$#" - count: 1 - path: gatherling/event.php - - - - message: "#^Property Gatherling\\\\Models\\\\Event\\:\\:\\$mainrounds \\(int\\|string\\) does not accept mixed\\.$#" - count: 1 - path: gatherling/event.php - - - - message: "#^Property Gatherling\\\\Models\\\\Event\\:\\:\\$mainstruct \\(string\\) does not accept mixed\\.$#" - count: 1 - path: gatherling/event.php - - - - message: "#^Property Gatherling\\\\Models\\\\Event\\:\\:\\$series \\(string\\|null\\) does not accept mixed\\.$#" - count: 1 - path: gatherling/event.php - - message: "#^Parameter \\#1 \\$to of function sendEmail expects string, string\\|null given\\.$#" count: 1 From 89c6b73691444959fcb4b508395855095fbfca88 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 22:39:33 -0700 Subject: [PATCH 045/114] Declare war on pointless use of strcmp --- gatherling/deck.php | 14 +++++++------- gatherling/decksearch.php | 2 +- gatherling/event.php | 3 +-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/gatherling/deck.php b/gatherling/deck.php index 283495c69..5058e0707 100644 --- a/gatherling/deck.php +++ b/gatherling/deck.php @@ -44,7 +44,7 @@ function main(): void $postMode = post()->string('mode', ''); $viewComponent = new NullComponent(); - if (strcmp($requestMode, 'view') == 0) { + if ($requestMode === 'view') { $deck = null; if (isset($_GET['event'])) { $deck = $event->getPlaceDeck('1st'); @@ -73,11 +73,11 @@ function main(): void $playerName = isset($_POST['player']) ? post()->string('player') : get()->optionalString('player'); $eventName = isset($_POST['player']) ? request()->optionalString('event') : get()->optionalString('event'); // part of the reg-decklist feature. both "register" and "addregdeck" switches - if (strcmp($requestMode, 'register') == 0) { + if ($requestMode === 'register') { $playerName = isset($_POST['player']) ? post()->string('player') : get()->string('player'); $eventName = isset($_POST['player']) ? post()->string('event') : get()->string('event'); $viewComponent = new DeckRegisterForm($playerName, $eventName); - } elseif (strcmp($requestMode, 'addregdeck') == 0) { + } elseif ($requestMode === 'addregdeck') { $deck = insertDeck($event, post()->string('name'), post()->string('archetype'), post()->string('notes'), post()->string('player'), post()->string('contents', ''), post()->string('sideboard', '')); $viewComponent = new DeckProfile($deck); } elseif (is_null($deck) && $event->name == '') { @@ -85,14 +85,14 @@ function main(): void } elseif ($deck_player === false || !Player::isLoggedIn()) { $viewComponent = new LoginRequired(); } elseif (checkDeckAuth($event, $deck_player, $deck)) { - if (strcmp($postMode, 'Create Deck') == 0) { + if ($postMode === 'Create Deck') { $deck = insertDeck($event, post()->string('name'), post()->string('archetype'), post()->string('notes'), post()->string('player'), post()->string('contents', ''), post()->string('sideboard', '')); if ($deck->isValid()) { $viewComponent = deckProfile($deck); } else { $viewComponent = deckForm($deck, $playerName, $eventName); } - } elseif (strcmp($postMode, 'Update Deck') == 0) { + } elseif ($postMode === 'Update Deck') { $deck = updateDeck($deck, post()->string('archetype'), post()->string('name'), post()->string('notes'), post()->string('contents', ''), post()->string('sideboard', '')); if ($deck->id === null) { throw new NotFoundException('Trying to update a deck with null id, which is not possible'); @@ -103,9 +103,9 @@ function main(): void } else { $viewComponent = deckForm($deck, $playerName, $eventName); } - } elseif (strcmp($postMode, 'Edit Deck') == 0) { + } elseif ($postMode === 'Edit Deck') { $viewComponent = deckForm($deck, $playerName, $eventName); - } elseif (strcmp($requestMode, 'create') == 0) { + } elseif ($requestMode === 'create') { $viewComponent = deckForm(null, $playerName, $eventName); } } else { diff --git a/gatherling/decksearch.php b/gatherling/decksearch.php index e463cf5a1..305fa8ff2 100644 --- a/gatherling/decksearch.php +++ b/gatherling/decksearch.php @@ -26,7 +26,7 @@ function main(): void } $results = $errors = []; - if (isset($_GET['mode']) && strcmp(get()->string('mode'), 'search') == 0 && !isset($_GET['page'])) { + if (isset($_GET['mode']) && get()->string('mode') === 'search' && !isset($_GET['page'])) { if (!empty($_POST['format'])) { $decksearch->searchByFormat(post()->string('format')); $_SESSION['format'] = $_POST['format']; diff --git a/gatherling/event.php b/gatherling/event.php index 2b3fbfe47..428191218 100644 --- a/gatherling/event.php +++ b/gatherling/event.php @@ -69,8 +69,7 @@ function main(): void function mode_is(string $str): bool { - $mode = request()->string('mode', ''); - return strcmp($mode, $str) == 0; + return request()->string('mode', '') === $str; } function createNewEvent(): Event|bool From d170d72dd773085b0af3647cf1be32d74af5ea6f Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 22:52:18 -0700 Subject: [PATCH 046/114] Eradicate strcmp from the codebase I'm not against strcmp exactly but it's for stuff like lexicographical sort not direct string comparison. Now everything is strongly typed and we know these are strings '===' comparison is more appropriate, easier to read, and has the same result. --- gatherling/Auth/Registration.php | 2 +- gatherling/Models/Event.php | 4 ++-- gatherling/Models/Format.php | 8 +++---- gatherling/Models/Matchup.php | 4 ++-- gatherling/Models/Player.php | 2 +- gatherling/Models/Ratings.php | 4 ++-- .../Views/Components/ArchetypeDropMenu.php | 2 +- .../Views/Components/FormatDropMenuDS.php | 2 +- .../Views/Components/PlayerDropMenu.php | 4 ++-- .../Views/Components/SeriesDropMenu.php | 2 +- .../Views/Components/SeriesDropMenuDS.php | 2 +- gatherling/Views/Components/TimeDropMenu.php | 2 +- gatherling/Views/Pages/EventForm.php | 4 ++-- gatherling/Views/Pages/EventList.php | 2 +- gatherling/Views/Pages/PlayerList.php | 2 +- gatherling/admin/infobot.php | 6 +++--- gatherling/event.php | 21 ++++++++----------- phpstan-baseline.neon | 20 ------------------ 18 files changed, 35 insertions(+), 58 deletions(-) diff --git a/gatherling/Auth/Registration.php b/gatherling/Auth/Registration.php index 537c31a9c..de0141681 100644 --- a/gatherling/Auth/Registration.php +++ b/gatherling/Auth/Registration.php @@ -19,7 +19,7 @@ public static function register(string $username, string $pw1, string $pw2, stri if (!is_null($player->password)) { return self::ERROR_PLAYER_EXISTS; } - if (strcmp($pw1, $pw2) != 0) { + if ($pw1 !== $pw2) { return self::ERROR_PASSWORD_MISMATCH; } if (empty($pw1) && !isset($discordId)) { diff --git a/gatherling/Models/Event.php b/gatherling/Models/Event.php index 4be8c5b3a..8239247ab 100644 --- a/gatherling/Models/Event.php +++ b/gatherling/Models/Event.php @@ -195,7 +195,7 @@ public static function createEvent( $number = $mostRecentEvent ? $mostRecentEvent->number + 1 : 1; } - if (strcmp($naming, 'auto') == 0) { + if ($naming === 'auto') { $event->name = sprintf('%s %d.%02d', $series, $season, $number); } else { $event->name = $name; @@ -643,7 +643,7 @@ public function removeEntry(string $playername): bool public function addPlayer(string $playername): bool { $playername = trim($playername); - if (strcmp($playername, '') == 0) { + if ($playername === '') { return false; } $series = new Series($this->series); diff --git a/gatherling/Models/Format.php b/gatherling/Models/Format.php index 56f649098..fab461246 100644 --- a/gatherling/Models/Format.php +++ b/gatherling/Models/Format.php @@ -528,7 +528,7 @@ public function getCoreCardsets(): array $legalCoreSets = []; foreach ($legalSets as $legalSet) { $setType = Database::singleResultSingleParam('SELECT type FROM cardsets WHERE name = ?', 's', $legalSet); - if (strcmp($setType, 'Core') == 0) { + if ($setType === 'Core') { $legalCoreSets[] = $legalSet; } } @@ -544,7 +544,7 @@ public function getBlockCardsets(): array $legalBlockSets = []; foreach ($legalSets as $legalSet) { $setType = Database::singleResultSingleParam('SELECT type FROM cardsets WHERE name = ?', 's', $legalSet); - if (strcmp($setType, 'Block') == 0) { + if ($setType === 'Block') { $legalBlockSets[] = $legalSet; } } @@ -560,7 +560,7 @@ public function getExtraCardsets(): array $legalExtraSets = []; foreach ($legalSets as $legalSet) { $setType = Database::singleResultSingleParam('SELECT type FROM cardsets WHERE name = ?', 's', $legalSet); - if (strcmp($setType, 'Extra') == 0) { + if ($setType === 'Extra') { $legalExtraSets[] = $legalSet; } } @@ -770,7 +770,7 @@ public function isCardSetLegal(string $setName): bool } $legal = $this->getLegalCardsets(); foreach ($legal as $legalsetName) { - if (strcmp($setName, $legalsetName) == 0) { + if ($setName === $legalsetName) { return true; } } diff --git a/gatherling/Models/Matchup.php b/gatherling/Models/Matchup.php index 9175d9838..e2bd6892f 100644 --- a/gatherling/Models/Matchup.php +++ b/gatherling/Models/Matchup.php @@ -433,7 +433,7 @@ public function updateScores(string $structure): void $this->result = 'B'; } } - if (strcmp($playera_standing->player, $playerb_standing->player) == 0) { + if ($playera_standing->player === $playerb_standing->player) { // Moved to above } else { if ($structure !== 'Single Elimination') { @@ -510,7 +510,7 @@ public function fixScores(string $structure): void $this->result = 'B'; } } - if (strcmp($playera_standing->player, $playerb_standing->player) == 0) { + if ($playera_standing->player === $playerb_standing->player) { //Might need this later if I want to rebuild bye score with standings $playera_standing->byes++; } else { $playera_standing->matches_played++; diff --git a/gatherling/Models/Player.php b/gatherling/Models/Player.php index 6c9c40923..fc679b755 100644 --- a/gatherling/Models/Player.php +++ b/gatherling/Models/Player.php @@ -109,7 +109,7 @@ public static function checkPassword(string $username, string $password): bool return false; } $hashpwd = hash('sha256', $password); - return strcmp($srvpass, $hashpwd) == 0; + return $srvpass === $hashpwd; } public static function getClientIPAddress(): string diff --git a/gatherling/Models/Ratings.php b/gatherling/Models/Ratings.php index 2bd5646ca..69ab1f152 100644 --- a/gatherling/Models/Ratings.php +++ b/gatherling/Models/Ratings.php @@ -156,12 +156,12 @@ public function calcPostEventRatings(string $event, string $format): array for ($ndx = 0; $ndx < count($matches); $ndx++) { $aPts = 0.5; $bPts = 0.5; - if (strcmp($matches[$ndx]['result'], 'A') == 0) { + if ($matches[$ndx]['result'] === 'A') { $aPts = 1.0; $bPts = 0.0; $players[$matches[$ndx]['playera']]['wins']++; $players[$matches[$ndx]['playerb']]['losses']++; - } elseif (strcmp($matches[$ndx]['result'], 'B') == 0) { + } elseif ($matches[$ndx]['result'] === 'B') { $aPts = 0.0; $bPts = 1.0; $players[$matches[$ndx]['playerb']]['wins']++; diff --git a/gatherling/Views/Components/ArchetypeDropMenu.php b/gatherling/Views/Components/ArchetypeDropMenu.php index 6a9f5031c..a75e6bfed 100644 --- a/gatherling/Views/Components/ArchetypeDropMenu.php +++ b/gatherling/Views/Components/ArchetypeDropMenu.php @@ -18,7 +18,7 @@ public function __construct(public string $archetypeName, int $useAll = 0, strin $options[] = [ 'value' => $archetype, 'text' => $archetype, - 'isSelected' => strcmp($archetype, $archetypeName) == 0, + 'isSelected' => $archetype === $archetypeName, ]; } parent::__construct($formName, $options, $title, 'ds_select'); diff --git a/gatherling/Views/Components/FormatDropMenuDS.php b/gatherling/Views/Components/FormatDropMenuDS.php index dba7c8e1a..be804ee51 100644 --- a/gatherling/Views/Components/FormatDropMenuDS.php +++ b/gatherling/Views/Components/FormatDropMenuDS.php @@ -18,7 +18,7 @@ public function __construct(public string $formatName, int $useAll = 0, string $ $options[] = [ 'value' => $name, 'text' => $name, - 'isSelected' => strcmp($name, $formatName) == 0, + 'isSelected' => $name === $formatName, ]; } parent::__construct($formName, $options, $title, 'ds_select'); diff --git a/gatherling/Views/Components/PlayerDropMenu.php b/gatherling/Views/Components/PlayerDropMenu.php index 9bda78102..57bf77bd7 100644 --- a/gatherling/Views/Components/PlayerDropMenu.php +++ b/gatherling/Views/Components/PlayerDropMenu.php @@ -15,11 +15,11 @@ public function __construct(Event $event, string|int $letter, string $def = "\n" $playerNames = $event->getRegisteredPlayers((bool) $event->active); sort($playerNames, SORT_STRING | SORT_NATURAL | SORT_FLAG_CASE); - $default = strcmp("\n", $def) == 0 ? "- Player $letter -" : '- None -'; + $default = $def === "\n" ? "- Player $letter -" : '- None -'; $options = []; foreach ($playerNames as $player) { $options[] = [ - 'isSelected' => strcmp($player, $def) == 0, + 'isSelected' => $player === $def, 'value' => $player, 'text' => $player, ]; diff --git a/gatherling/Views/Components/SeriesDropMenu.php b/gatherling/Views/Components/SeriesDropMenu.php index be17cb945..165e118ff 100644 --- a/gatherling/Views/Components/SeriesDropMenu.php +++ b/gatherling/Views/Components/SeriesDropMenu.php @@ -17,7 +17,7 @@ public function __construct(?string $seriesName, ?string $default = '- Series -' $options[] = [ 'text' => $name, 'value' => $name, - 'isSelected' => $seriesName && strcmp($seriesName, $name) == 0, + 'isSelected' => $seriesName === $name, ]; } parent::__construct('series', $options, $default); diff --git a/gatherling/Views/Components/SeriesDropMenuDS.php b/gatherling/Views/Components/SeriesDropMenuDS.php index 3f73818cd..cac2da3ad 100644 --- a/gatherling/Views/Components/SeriesDropMenuDS.php +++ b/gatherling/Views/Components/SeriesDropMenuDS.php @@ -19,7 +19,7 @@ public function __construct(?string $seriesName = null, int $useAll = 0, string $options[] = [ 'text' => $name, 'value' => $name, - 'isSelected' => $seriesName && strcmp($name, $seriesName) == 0, + 'isSelected' => $name === $seriesName, ]; } parent::__construct($formName, $options, $title, 'ds_select'); diff --git a/gatherling/Views/Components/TimeDropMenu.php b/gatherling/Views/Components/TimeDropMenu.php index f82fe50b4..7f696db6d 100644 --- a/gatherling/Views/Components/TimeDropMenu.php +++ b/gatherling/Views/Components/TimeDropMenu.php @@ -8,7 +8,7 @@ class TimeDropMenu extends DropMenu { public function __construct(string $name, int|string $hour, int|string $minutes = 0) { - if (strcmp((string) $hour, '') == 0) { + if ((string) $hour === '') { $hour = -1; } $options = []; diff --git a/gatherling/Views/Pages/EventForm.php b/gatherling/Views/Pages/EventForm.php index bf40ef00a..930a54428 100644 --- a/gatherling/Views/Pages/EventForm.php +++ b/gatherling/Views/Pages/EventForm.php @@ -221,7 +221,7 @@ function kValueSelectInput(int $kvalue): SelectInput /** @return array{name: string, default: string, options: array} */ function monthDropMenuArgs(string|int $month): array { - if (strcmp($month, '') == 0) { + if ($month === '') { $month = -1; } $names = [ @@ -259,7 +259,7 @@ function structDropMenuArgs(string $field, string $def): array $options[] = [ 'value' => $name, 'text' => $name, - 'isSelected' => strcmp($def, $name) == 0, + 'isSelected' => $def === $name, ]; } diff --git a/gatherling/Views/Pages/EventList.php b/gatherling/Views/Pages/EventList.php index 2cbedb6d2..a463a9c20 100644 --- a/gatherling/Views/Pages/EventList.php +++ b/gatherling/Views/Pages/EventList.php @@ -105,7 +105,7 @@ function queryEvents(Player $player, array $playerSeries, string $seriesName, st $sql .= ' AND e.format = :format'; $params['format'] = $format; } - if (strcmp($seriesName, '') != 0) { + if ($seriesName !== '') { $sql .= ' AND e.series = :series_name'; $params['series_name'] = $seriesName; } diff --git a/gatherling/Views/Pages/PlayerList.php b/gatherling/Views/Pages/PlayerList.php index b982b001e..126cc489b 100644 --- a/gatherling/Views/Pages/PlayerList.php +++ b/gatherling/Views/Pages/PlayerList.php @@ -103,7 +103,7 @@ function entryListArgs(Entry $entry, int $numEntries, bool $isTribal): array ]; $entryInfo['undropLink'] = 'event.php?' . http_build_query($undropParams, '', '&', PHP_QUERY_RFC3986); } - if ($entry->event->isFinished() && strcmp('', $entry->medal) != 0) { + if ($entry->event->isFinished() && $entry->medal !== '') { $entryInfo['medalSrc'] = "styles/images/{$entry->medal}.png"; } $entryInfo['gameName'] = new GameName($entry->player, $entry->event->client); diff --git a/gatherling/admin/infobot.php b/gatherling/admin/infobot.php index 52090c3b7..13d28ebce 100644 --- a/gatherling/admin/infobot.php +++ b/gatherling/admin/infobot.php @@ -35,14 +35,14 @@ function main(): void (new InfobotReply("You're not registered on {$siteName}!"))->send(); } - if (strcmp(request()->string('mode', ''), 'verify') == 0) { + $mode = request()->string('mode', ''); + if ($mode === 'verify') { $player->setChallenge($challenge); (new InfobotReply("Your verification code for {$siteName} is $challenge"))->send(); - } elseif (strcmp(request()->string('mode', ''), 'reset') == 0) { + } elseif ($mode === 'reset') { $player->setPassword($challenge); (new InfobotReply("Your temporary password for {$siteName} is $challenge"))->send(); } else { - $mode = request()->string('mode', ''); (new InfobotError("Unknown Action {$mode}"))->send(); } } diff --git a/gatherling/event.php b/gatherling/event.php index 428191218..90980eacd 100644 --- a/gatherling/event.php +++ b/gatherling/event.php @@ -128,7 +128,7 @@ function getEvent(string $eventName, ?string $action, ?string $eventId, ?string if ($playerName !== false && !$event->authCheck($playerName)) { return new AuthFailed(); } - if ($action && strcmp($action, 'undrop') == 0) { + if ($action === 'undrop') { $entry = new Entry((int) $eventId, $player); if ($entry->deck && $entry->deck->isValid()) { $event->undropPlayer($player); @@ -206,21 +206,21 @@ function eventFrame(Event $event = null, bool $forceNew = false): EventFrame $view = 'edit'; } - if (strcmp($view, 'reg') == 0) { + if ($view === 'reg') { return new PlayerList($event); - } elseif (strcmp($view, 'match') == 0) { + } elseif ($view === 'match') { // Prevent warnings in php output. TODO: make this not needed. if (!isset($_POST['newmatchround'])) { $_POST['newmatchround'] = ''; } return new MatchList($event, post()->optionalString('newmatchround')); - } elseif (strcmp($view, 'standings') == 0) { + } elseif ($view === 'standings') { return new StandingsList($event, Player::loginName() ?: null); - } elseif (strcmp($view, 'medal') == 0) { + } elseif ($view === 'medal') { return new MedalList($event); - } elseif (strcmp($view, 'points_adj') == 0) { + } elseif ($view === 'points_adj') { return new PointsAdjustmentForm($event); - } elseif (strcmp($view, 'reports') == 0) { + } elseif ($view === 'reports') { return new ReportsForm($event); } @@ -525,10 +525,7 @@ function updateMatches(): void } $rnd = post()->int('newmatchround'); - if ( - strcmp($pA, '') != 0 && strcmp($pB, '') != 0 - && strcmp($res, '') != 0 && $rnd - ) { + if ($rnd) { $playerA = new Standings($event->name, $pA); $playerB = new Standings($event->name, $pB); if ($res == 'P') { @@ -538,7 +535,7 @@ function updateMatches(): void } } - if (strcmp(post()->string('newbyeplayer', ''), '') != 0) { + if (post()->string('newbyeplayer', '') !== '') { $playerBye = new Standings($event->name, post()->string('newbyeplayer')); $event->addMatch($playerBye, $playerBye, $rnd, 'BYE'); } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index dc4becd71..10b7201fc 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -415,11 +415,6 @@ parameters: count: 1 path: gatherling/Models/Format.php - - - message: "#^Parameter \\#1 \\$string1 of function strcmp expects string, mixed given\\.$#" - count: 3 - path: gatherling/Models/Format.php - - message: "#^Parameter \\#1 \\$typeString of static method Gatherling\\\\Models\\\\Format\\:\\:removeTypeCrap\\(\\) expects string, mixed given\\.$#" count: 1 @@ -500,11 +495,6 @@ parameters: count: 2 path: gatherling/Models/Matchup.php - - - message: "#^Parameter \\#1 \\$string1 of function strcmp expects string, string\\|null given\\.$#" - count: 2 - path: gatherling/Models/Matchup.php - - message: "#^Parameter \\#1 \\$subevent of static method Gatherling\\\\Models\\\\Event\\:\\:getEventBySubevent\\(\\) expects int, int\\|null given\\.$#" count: 2 @@ -525,11 +515,6 @@ parameters: count: 2 path: gatherling/Models/Matchup.php - - - message: "#^Parameter \\#2 \\$string2 of function strcmp expects string, string\\|null given\\.$#" - count: 2 - path: gatherling/Models/Matchup.php - - message: "#^Parameter \\#2 \\$player1 of method Gatherling\\\\Models\\\\Pairings\\:\\:weight\\(\\) expects array\\{score\\: int, player\\: string, opponents\\: array\\\\}, array\\\\|int\\|string\\> given\\.$#" count: 1 @@ -875,11 +860,6 @@ parameters: count: 1 path: gatherling/Views/Pages/EventForm.php - - - message: "#^Parameter \\#1 \\$string1 of function strcmp expects string, int\\|string given\\.$#" - count: 1 - path: gatherling/Views/Pages/EventForm.php - - message: "#^Parameter \\#3 \\$limitTo of class Gatherling\\\\Views\\\\Components\\\\SeriesDropMenu constructor expects list\\, array\\, string\\> given\\.$#" count: 1 From a66c7f0e4d8bbfbde5563403213493576bfe6bd3 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 22:57:17 -0700 Subject: [PATCH 047/114] Get timezone in the approved way --- gatherling/profile.php | 2 +- phpstan-baseline.neon | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/gatherling/profile.php b/gatherling/profile.php index 9ccad0c62..22162b8ee 100644 --- a/gatherling/profile.php +++ b/gatherling/profile.php @@ -22,7 +22,7 @@ function main(): void if ($player && $profileEdit == 2) { $player->emailAddress = $_GET['email']; $player->emailPrivacy = get()->int('email_public'); - $player->timezone = (float) $_GET['timezone']; + $player->timezone = get()->float('timezone'); $player->save(); } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 10b7201fc..83b14010a 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1195,11 +1195,6 @@ parameters: count: 3 path: gatherling/lib.php - - - message: "#^Cannot cast mixed to float\\.$#" - count: 1 - path: gatherling/profile.php - - message: "#^Property Gatherling\\\\Models\\\\Player\\:\\:\\$emailAddress \\(string\\|null\\) does not accept mixed\\.$#" count: 1 From 5c600ee090f74971cbd1aa4bb39db84eb9efbc60 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 22:57:43 -0700 Subject: [PATCH 048/114] Swtich to river-style SQL to save space/for clarity --- gatherling/ratings.php | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/gatherling/ratings.php b/gatherling/ratings.php index a074781b3..ac7d08d30 100644 --- a/gatherling/ratings.php +++ b/gatherling/ratings.php @@ -79,21 +79,15 @@ function ratingsData(string $format, int $minMatches): array function bestEver(string $format): array { $sql = ' - SELECT - p.name AS player, r.rating, UNIX_TIMESTAMP(r.updated) AS t - FROM - ratings AS r, - players AS p, - ( - SELECT - MAX(qr.rating) AS qmax - FROM - ratings AS qr - WHERE - qr.format = :format - ) AS q - WHERE - format = :format AND p.name = r.player AND q.qmax = r.rating'; + SELECT p.name AS player, r.rating, UNIX_TIMESTAMP(r.updated) AS t + FROM ratings AS r, + players AS p, + ( + SELECT MAX(qr.rating) AS qmax + FROM ratings AS qr + WHERE qr.format = :format + ) AS q + WHERE format = :format AND p.name = r.player AND q.qmax = r.rating'; $bestEver = db()->select($sql, BestEverDto::class, ['format' => $format])[0]; return [ 'player' => $bestEver->player, From 176016f0d880f3a01fa85cd37e04f0d044f5050a Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 23:06:36 -0700 Subject: [PATCH 049/114] Give main the more correct return type of never - we output and then exit This is the standard behavior of Page and other Responses. Update edge casey controllers to also do the same so all mains have return type of never. --- gatherling/action.php | 13 +++++++------ gatherling/admin/db-upgrade.php | 3 ++- gatherling/admin/infobot.php | 2 +- gatherling/admincp.php | 2 +- gatherling/auth.php | 4 ++-- gatherling/authdebug.php | 2 +- gatherling/bannedplayer.php | 2 +- gatherling/calendar.php | 2 +- gatherling/cardscp.php | 2 +- gatherling/deck.php | 2 +- gatherling/deckXmlParser.php | 2 +- gatherling/deckdl.php | 2 +- gatherling/decksearch.php | 2 +- gatherling/displaySeries.php | 2 +- gatherling/displayTrophy.php | 2 +- gatherling/event.php | 2 +- gatherling/eventreport.php | 2 +- gatherling/forgot.php | 2 +- gatherling/formatcp.php | 2 +- gatherling/index.php | 2 +- gatherling/insertcardset.php | 4 ++-- gatherling/login.php | 2 +- gatherling/masterplayerfile.php | 3 ++- gatherling/player.php | 2 +- gatherling/prereg.php | 2 +- gatherling/profile.php | 2 +- gatherling/ratings.php | 2 +- gatherling/register.php | 2 +- gatherling/report.php | 2 +- gatherling/series.php | 2 +- gatherling/seriescp.php | 2 +- gatherling/seriesreport.php | 2 +- gatherling/util/updateDefaultFormats.php | 2 +- 33 files changed, 43 insertions(+), 40 deletions(-) diff --git a/gatherling/action.php b/gatherling/action.php index 9f4cd6a49..c78ebaa95 100644 --- a/gatherling/action.php +++ b/gatherling/action.php @@ -15,13 +15,13 @@ require_once 'lib.php'; -function main(): void +function main(): never { $player = Player::getSessionPlayer(); if (is_null($player)) { - return; + exit; } - $message = null; + $message = ''; if ($player->emailAddress == '') { $message = 'Add an Email Address to your account.'; } @@ -114,10 +114,11 @@ function main(): void } } - if (!is_null($message)) { - $response = new WireResponse(''); - $response->send(); + if ($message !== '') { + $message = ''; } + $response = new WireResponse($message); + $response->send(); } if (basename(__FILE__) == basename(server()->string('PHP_SELF'))) { diff --git a/gatherling/admin/db-upgrade.php b/gatherling/admin/db-upgrade.php index 2aea3a862..89d43fdfe 100644 --- a/gatherling/admin/db-upgrade.php +++ b/gatherling/admin/db-upgrade.php @@ -12,10 +12,11 @@ require_once __DIR__ . '/../lib.php'; -function main(): void +function main(): never { Setup::setupDatabase(); echo 'done'; + exit; } if (basename(__FILE__) == basename(server()->string('PHP_SELF'))) { diff --git a/gatherling/admin/infobot.php b/gatherling/admin/infobot.php index 13d28ebce..97a60a2b9 100644 --- a/gatherling/admin/infobot.php +++ b/gatherling/admin/infobot.php @@ -13,7 +13,7 @@ require_once __DIR__ . '/../lib.php'; -function main(): void +function main(): never { if (strncmp(server()->string('HTTP_USER_AGENT', ''), 'infobot', 7) != 0) { (new InfobotError("You're not infobot!"))->send(); diff --git a/gatherling/admincp.php b/gatherling/admincp.php index 24bdc5c49..b24da92a2 100644 --- a/gatherling/admincp.php +++ b/gatherling/admincp.php @@ -22,7 +22,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { if (!(Player::getSessionPlayer()?->isSuper() ?? false)) { (new Redirect('index.php'))->send(); diff --git a/gatherling/auth.php b/gatherling/auth.php index 37cad19ff..dc52de103 100644 --- a/gatherling/auth.php +++ b/gatherling/auth.php @@ -17,7 +17,7 @@ require_once __DIR__ . '/lib.php'; require __DIR__ . '/authlib.php'; -function main(): void +function main(): never { global $provider; @@ -93,7 +93,7 @@ function sendToDiscord(mixed $scope = null): never (new Redirect($authUrl))->send(); } -function doLogin(AccessTokenInterface $token): void +function doLogin(AccessTokenInterface $token): never { global $provider; diff --git a/gatherling/authdebug.php b/gatherling/authdebug.php index 7f2ddac91..2c08add70 100644 --- a/gatherling/authdebug.php +++ b/gatherling/authdebug.php @@ -16,7 +16,7 @@ require_once __DIR__ . '/lib.php'; require __DIR__ . '/authlib.php'; -function main(): void +function main(): never { global $provider; diff --git a/gatherling/bannedplayer.php b/gatherling/bannedplayer.php index fe6481e4e..90da91276 100644 --- a/gatherling/bannedplayer.php +++ b/gatherling/bannedplayer.php @@ -10,7 +10,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $page = new BannedPlayer(); $page->send(); diff --git a/gatherling/calendar.php b/gatherling/calendar.php index a1ddd444e..c6b5290b4 100644 --- a/gatherling/calendar.php +++ b/gatherling/calendar.php @@ -10,7 +10,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $name = 'Gatherling Tournament Schedule'; $description = 'Magic Player Run Events on Magic: The Gathering Online'; diff --git a/gatherling/cardscp.php b/gatherling/cardscp.php index 755e5541c..f5254ba26 100644 --- a/gatherling/cardscp.php +++ b/gatherling/cardscp.php @@ -22,7 +22,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { if (!(Player::getSessionPlayer()?->isSuper() ?? false)) { (new Redirect('index.php'))->send(); diff --git a/gatherling/deck.php b/gatherling/deck.php index 5058e0707..273da9bc4 100644 --- a/gatherling/deck.php +++ b/gatherling/deck.php @@ -26,7 +26,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $event = null; diff --git a/gatherling/deckXmlParser.php b/gatherling/deckXmlParser.php index a32310d0a..3e40c51e2 100644 --- a/gatherling/deckXmlParser.php +++ b/gatherling/deckXmlParser.php @@ -8,7 +8,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $data = filter_input(INPUT_POST, 'data'); if ($data === false || $data === null) { diff --git a/gatherling/deckdl.php b/gatherling/deckdl.php index 5ab26829b..5c2587844 100644 --- a/gatherling/deckdl.php +++ b/gatherling/deckdl.php @@ -13,7 +13,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $id = get()->optionalInt('id') ?? post()->optionalInt('id') ?? null; if (!$id) { diff --git a/gatherling/decksearch.php b/gatherling/decksearch.php index 305fa8ff2..c20dc238c 100644 --- a/gatherling/decksearch.php +++ b/gatherling/decksearch.php @@ -14,7 +14,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { if (count($_POST) > 0) { unset($_SESSION['search_results']); diff --git a/gatherling/displaySeries.php b/gatherling/displaySeries.php index d1ef8822d..cfc3dc357 100644 --- a/gatherling/displaySeries.php +++ b/gatherling/displaySeries.php @@ -11,7 +11,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $sql = 'SELECT logo AS image, imgtype AS type, imgsize AS size FROM series WHERE name = :series'; $args = ['series' => $_GET['series']]; diff --git a/gatherling/displayTrophy.php b/gatherling/displayTrophy.php index 936cec388..fa6ad6be8 100644 --- a/gatherling/displayTrophy.php +++ b/gatherling/displayTrophy.php @@ -11,7 +11,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $sql = 'SELECT image, type, size FROM trophies WHERE event = :event'; $args = ['event' => $_GET['event']]; diff --git a/gatherling/event.php b/gatherling/event.php index 90980eacd..622504781 100644 --- a/gatherling/event.php +++ b/gatherling/event.php @@ -33,7 +33,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { if (!Player::isLoggedIn()) { (new LoginRedirect())->send(); diff --git a/gatherling/eventreport.php b/gatherling/eventreport.php index b3d4f52fa..0b8b67503 100644 --- a/gatherling/eventreport.php +++ b/gatherling/eventreport.php @@ -13,7 +13,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $eventName = get()->optionalString('event') ?? get()->optionalString('name'); if ($eventName && Event::exists($eventName)) { diff --git a/gatherling/forgot.php b/gatherling/forgot.php index 121c47d29..1e1a936ba 100644 --- a/gatherling/forgot.php +++ b/gatherling/forgot.php @@ -15,7 +15,7 @@ include 'util/email.php'; require_once 'lib.php'; -function main(): void +function main(): never { $hasResetPassword = $passwordResetFailed = $showForgotForm = $showNewPasswordForm = $sentLoginLink = $cantSendLoginLink = $cantFindPlayer = false; $token = $email = null; diff --git a/gatherling/formatcp.php b/gatherling/formatcp.php index 0526fce14..c139009de 100644 --- a/gatherling/formatcp.php +++ b/gatherling/formatcp.php @@ -29,7 +29,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $player = Player::getSessionPlayer(); if (!$player) { diff --git a/gatherling/index.php b/gatherling/index.php index 582c9f444..7af1f9847 100644 --- a/gatherling/index.php +++ b/gatherling/index.php @@ -16,7 +16,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $activeEvents = Event::getActiveEvents(false); $upcomingEvents = getUpcomingEvents(); diff --git a/gatherling/insertcardset.php b/gatherling/insertcardset.php index 670d4cc43..7dce3dbc8 100644 --- a/gatherling/insertcardset.php +++ b/gatherling/insertcardset.php @@ -16,7 +16,7 @@ require_once __DIR__ . '/lib.php'; -function main(): void +function main(): never { global $argv; if (PHP_SAPI == 'cli') { @@ -31,7 +31,7 @@ function main(): void foreach ($messages as $message) { print("$message\n"); } - return; + exit; } if (!(Player::getSessionPlayer()?->isSuper() ?? false)) { diff --git a/gatherling/login.php b/gatherling/login.php index 30cd7a1d1..c9828df6a 100644 --- a/gatherling/login.php +++ b/gatherling/login.php @@ -14,7 +14,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $mode = $_REQUEST['mode'] ?? null; diff --git a/gatherling/masterplayerfile.php b/gatherling/masterplayerfile.php index 298847f57..d9e084a8b 100644 --- a/gatherling/masterplayerfile.php +++ b/gatherling/masterplayerfile.php @@ -7,7 +7,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { header('Content-type: text/plain'); $sql = 'SELECT name FROM players ORDER BY name'; @@ -17,6 +17,7 @@ function main(): void printf("%08d\tx\t%s\tUS\n", $n, $name); $n++; } + exit; } if (basename(__FILE__) == basename(server()->string('PHP_SELF'))) { diff --git a/gatherling/player.php b/gatherling/player.php index 8d6432c9d..9ac689f1f 100644 --- a/gatherling/player.php +++ b/gatherling/player.php @@ -25,7 +25,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $player = Player::getSessionPlayer(); if ($player == null) { diff --git a/gatherling/prereg.php b/gatherling/prereg.php index 7cce810eb..d805da58d 100644 --- a/gatherling/prereg.php +++ b/gatherling/prereg.php @@ -13,7 +13,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $prevent_registration = 0; $player = Player::getSessionPlayer(); diff --git a/gatherling/profile.php b/gatherling/profile.php index 22162b8ee..aa10c6378 100644 --- a/gatherling/profile.php +++ b/gatherling/profile.php @@ -13,7 +13,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $playerName = post()->optionalString('player') ?? get()->optionalString('player') ?? session()->optionalString('username') ?? ''; $profileEdit = request()->int('profile_edit', 0); diff --git a/gatherling/ratings.php b/gatherling/ratings.php index ac7d08d30..0f8534ee2 100644 --- a/gatherling/ratings.php +++ b/gatherling/ratings.php @@ -14,7 +14,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $format = post()->string('format', 'Composite'); ['date' => $lastTournamentDate, 'name' => $lastTournamentName] = currentThrough($format); diff --git a/gatherling/register.php b/gatherling/register.php index 4a9bec2bc..ab0f4b90c 100644 --- a/gatherling/register.php +++ b/gatherling/register.php @@ -13,7 +13,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $message = ''; if (isset($_POST['pw1'])) { diff --git a/gatherling/report.php b/gatherling/report.php index 280da861d..10adfc093 100644 --- a/gatherling/report.php +++ b/gatherling/report.php @@ -22,7 +22,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $player = Player::getSessionPlayer(); if (!$player) { diff --git a/gatherling/series.php b/gatherling/series.php index dbbfda73b..cf953ef98 100644 --- a/gatherling/series.php +++ b/gatherling/series.php @@ -9,7 +9,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $activeSeriesNames = SeriesModel::activeNames(); $page = new Series($activeSeriesNames); diff --git a/gatherling/seriescp.php b/gatherling/seriescp.php index 696893652..c6c7f9149 100644 --- a/gatherling/seriescp.php +++ b/gatherling/seriescp.php @@ -26,7 +26,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { if (!Player::isLoggedIn()) { (new LoginRedirect())->send(); diff --git a/gatherling/seriesreport.php b/gatherling/seriesreport.php index 798643aec..a9ff909c9 100644 --- a/gatherling/seriesreport.php +++ b/gatherling/seriesreport.php @@ -14,7 +14,7 @@ require_once 'lib.php'; -function main(): void +function main(): never { $seriesName = get()->optionalString('series'); $season = get()->int('season', 0); diff --git a/gatherling/util/updateDefaultFormats.php b/gatherling/util/updateDefaultFormats.php index 43e7287e6..de5f031ab 100644 --- a/gatherling/util/updateDefaultFormats.php +++ b/gatherling/util/updateDefaultFormats.php @@ -17,7 +17,7 @@ require_once __DIR__ . '/../lib.php'; -function main(): void +function main(): never { if (PHP_SAPI != 'cli' && $_SERVER['REQUEST_METHOD'] == 'GET') { // unauthorized POST is okay if (!(Player::getSessionPlayer()?->isSuper() ?? false)) { From fb193cb0811220bc95c27f1eedb8eab645f833a5 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 23:11:58 -0700 Subject: [PATCH 050/114] Redirect to player.php rather than show a blank screen on report.php --- gatherling/report.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gatherling/report.php b/gatherling/report.php index 10adfc093..4981756a8 100644 --- a/gatherling/report.php +++ b/gatherling/report.php @@ -136,6 +136,8 @@ function main(): never $viewComponent = new SubmitResultForm($match->id, true); } break; + default: + (new Redirect('player.php'))->send(); } $page = new Report($result, $viewComponent); $page->send(); From 51db47f82ca2503a069049332c4933422b34b8b2 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 23:26:19 -0700 Subject: [PATCH 051/114] Copy update for error messages about sideboard --- gatherling/Models/Deck.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gatherling/Models/Deck.php b/gatherling/Models/Deck.php index 3a25357b2..2945039f7 100644 --- a/gatherling/Models/Deck.php +++ b/gatherling/Models/Deck.php @@ -577,12 +577,12 @@ public function save(): void if ($format->singleton) { if (!$format->isCardSingletonLegal($card, $amt)) { $this->errors[] = "Singleton formats allow only one of any card, except basic lands. - You entered {$amt} {$card} on your sideboard."; + You entered {$amt} {$card} in your sideboard."; } foreach ($this->maindeck_cards as $singletonCard => $mainamt) { if ($singletonCard == $card) { $this->errors[] = "Singleton formats allow only one of any card, except basic lands. - You entered {$amt} {$card} on your sideboard + You entered {$amt} {$card} in your sideboard and {$mainamt} {$card} in your mainboard."; break; } @@ -590,12 +590,12 @@ public function save(): void } else { if (!$format->isQuantityLegal($card, $amt)) { $this->errors[] = "No more than four of any card is allowed in this format, except basic lands. - You entered {$amt} {$card} on your sideboard."; + You entered {$amt} {$card} in your sideboard."; } else { foreach ($this->maindeck_cards as $quantityCard => $mainamt) { if (!$format->isQuantityLegalAgainstMain($card, $amt, $quantityCard, $mainamt)) { $this->errors[] = "No more than four of any card is allowed in this format, except basic lands. - You entered {$amt} {$card} on your sideboard + You entered {$amt} {$card} in your sideboard and {$mainamt} {$card} in your mainboard."; break; } From 141799429f813eb923c52970d7c23c030b09317e Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 23:26:34 -0700 Subject: [PATCH 052/114] River-style SQL formatting --- gatherling/Models/Deck.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/gatherling/Models/Deck.php b/gatherling/Models/Deck.php index 2945039f7..9f1cc0590 100644 --- a/gatherling/Models/Deck.php +++ b/gatherling/Models/Deck.php @@ -722,14 +722,10 @@ public function findIdenticalDecks(): array private function findIdenticalDecksInternal(): array { $sql = ' - SELECT - d.id - FROM - decks d, entries n, events e - WHERE - deck_hash = :deck_hash AND d.id != :id AND n.deck = d.id AND e.id = n.event_id AND e.finalized = 1 - ORDER BY - e.start DESC'; + SELECT d.id + FROM decks d, entries n, events e + WHERE deck_hash = :deck_hash AND d.id != :id AND n.deck = d.id AND e.id = n.event_id AND e.finalized = 1 + ORDER BY e.start DESC'; $params = ['deck_hash' => $this->deck_hash, 'id' => $this->id]; $deckIds = db()->ints($sql, $params); $decks = []; From 0cace203d201617b8849fb062779f6f0e883bf61 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 23:26:45 -0700 Subject: [PATCH 053/114] Don't use strings-as-funcs anywhere, prefer fn and real lambdas --- gatherling/Models/Decksearch.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gatherling/Models/Decksearch.php b/gatherling/Models/Decksearch.php index 72d56b6f8..eb48a0e02 100644 --- a/gatherling/Models/Decksearch.php +++ b/gatherling/Models/Decksearch.php @@ -245,7 +245,7 @@ public function idsToSortedInfo(array $id_arr): array $db = Database::getConnection(); //sanitize the id_arr to protect against sql injection. - $id_arr = array_filter(array_map('intval', $id_arr)); + $id_arr = array_filter(array_map(fn($id) => intval($id), $id_arr)); $query = 'SELECT id, archetype, name, playername, format, created_date from decks WHERE id IN (' . implode(',', $id_arr) . ') ORDER BY DATE(`created_date`) DESC'; $stmt = $db->prepare($query); From 709a3b7b7a636faf9ab4c77e419f67d0a3431292 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 23:33:43 -0700 Subject: [PATCH 054/114] Get rid of unused column on players --- gatherling/Data/sql/migrations/75.sql | 1 + gatherling/Models/Player.php | 4 +--- gatherling/Models/PlayerDto.php | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) create mode 100644 gatherling/Data/sql/migrations/75.sql diff --git a/gatherling/Data/sql/migrations/75.sql b/gatherling/Data/sql/migrations/75.sql new file mode 100644 index 000000000..529f7ff5b --- /dev/null +++ b/gatherling/Data/sql/migrations/75.sql @@ -0,0 +1 @@ +ALTER TABLE players DROP COLUMN theme; diff --git a/gatherling/Models/Player.php b/gatherling/Models/Player.php index fc679b755..5e9672a91 100644 --- a/gatherling/Models/Player.php +++ b/gatherling/Models/Player.php @@ -26,7 +26,6 @@ class Player public ?int $emailPrivacy = 0; public ?float $timezone = -5.0; public ?int $verified; - public ?string $theme = null; // DEPRECATED. Always null. public ?string $discord_id = null; public ?string $discord_handle = null; public ?string $api_key; @@ -41,14 +40,13 @@ public function __construct(string $name) $this->super = 0; $this->rememberMe = 0; $this->verified = 0; - $this->theme = null; return; } $sql = ' SELECT name, password, rememberme AS rememberMe, INET_NTOA(ipaddress) AS ipAddress, host, super, mtgo_confirmed AS verified, email AS emailAddress, email_privacy as emailPrivacy, timezone, - theme, discord_id, discord_handle, api_key, mtga_username, mtgo_username + discord_id, discord_handle, api_key, mtga_username, mtgo_username FROM players WHERE diff --git a/gatherling/Models/PlayerDto.php b/gatherling/Models/PlayerDto.php index 0ec519cf4..e58e54f51 100644 --- a/gatherling/Models/PlayerDto.php +++ b/gatherling/Models/PlayerDto.php @@ -16,7 +16,6 @@ class PlayerDto extends Dto public ?string $emailAddress; public int $emailPrivacy; public float $timezone; - public ?string $theme; public ?string $discord_id; public ?string $discord_handle; public ?string $api_key; From de443383851adef8dba3ffc4f37192f22c40f479 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Wed, 30 Oct 2024 23:36:00 -0700 Subject: [PATCH 055/114] Site name is no longer configurable Now that theme is locked this makes less sense than it ever did so just remove. --- gatherling/Views/Pages/Page.php | 2 -- gatherling/admin/infobot.php | 8 +++----- gatherling/bootstrap.php | 2 +- gatherling/config.php.docker | 3 --- gatherling/config.php.example | 3 --- gatherling/config.php.github | 1 - gatherling/templates/partials/header.mustache | 2 +- 7 files changed, 5 insertions(+), 16 deletions(-) diff --git a/gatherling/Views/Pages/Page.php b/gatherling/Views/Pages/Page.php index 8d4642831..615771732 100644 --- a/gatherling/Views/Pages/Page.php +++ b/gatherling/Views/Pages/Page.php @@ -23,13 +23,11 @@ abstract class Page extends TemplateResponse public bool $isSuper; public string $js; public ?Player $player; - public string $siteName; public string $versionTagline; public string $jsLink; public function __construct(public string $title) { - $this->siteName = config()->string('site_name', 'Gatherling'); $this->gitHash = git_hash(); $this->cssLink = 'styles/css/stylesheet.css?v=' . rawurlencode($this->gitHash); $this->headerLogoSrc = 'styles/images/header_logo.png'; diff --git a/gatherling/admin/infobot.php b/gatherling/admin/infobot.php index 97a60a2b9..ccfc1f4c3 100644 --- a/gatherling/admin/infobot.php +++ b/gatherling/admin/infobot.php @@ -24,24 +24,22 @@ function main(): never (new InfobotError('Wrong passkey'))->send(); } - $siteName = config()->string('site_name'); - // generate a user passkey for verification $random_num = mt_rand(); $key = sha1((string) $random_num); $challenge = substr($key, 0, 5); $player = Player::findByName(get()->optionalString('username') ?? ''); if (!$player) { - (new InfobotReply("You're not registered on {$siteName}!"))->send(); + (new InfobotReply("You're not registered on Gatherling!"))->send(); } $mode = request()->string('mode', ''); if ($mode === 'verify') { $player->setChallenge($challenge); - (new InfobotReply("Your verification code for {$siteName} is $challenge"))->send(); + (new InfobotReply("Your verification code for Gatherling is $challenge"))->send(); } elseif ($mode === 'reset') { $player->setPassword($challenge); - (new InfobotReply("Your temporary password for {$siteName} is $challenge"))->send(); + (new InfobotReply("Your temporary password for Gatherling is $challenge"))->send(); } else { (new InfobotError("Unknown Action {$mode}"))->send(); } diff --git a/gatherling/bootstrap.php b/gatherling/bootstrap.php index 625ad1f18..c52cfcf09 100644 --- a/gatherling/bootstrap.php +++ b/gatherling/bootstrap.php @@ -31,6 +31,6 @@ Sentry\init([ 'dsn' => 'https://ed7243cbdd9e47c8bc2205d4ac36b764@sentry.redpoint.games/16', - 'environment' => $CONFIG['site_name'], + 'environment' => 'Gatherling', 'release' => $CONFIG['GIT_HASH'], ]); diff --git a/gatherling/config.php.docker b/gatherling/config.php.docker index 0ce05c32f..9592bd05a 100644 --- a/gatherling/config.php.docker +++ b/gatherling/config.php.docker @@ -15,9 +15,6 @@ $CONFIG['db_test_database'] = 'gatherling_test'; # The base url where gatherling is installed. This should be an absolute URL, with HTTPS. $CONFIG['base_url'] = "http://localhost/"; -# The name of your site. This is used in a lot of places. -$CONFIG['site_name'] = "Gatherling"; - # A description for the ical calendar which is accessible at calendar.php $CONFIG['calendar_description'] = "a description for the events calendar"; diff --git a/gatherling/config.php.example b/gatherling/config.php.example index 93bc6a1b3..d2bdea9b6 100644 --- a/gatherling/config.php.example +++ b/gatherling/config.php.example @@ -20,9 +20,6 @@ $CONFIG['db_test_database'] = 'gatherling_test'; // $CONFIG['base_url'] = "https://gatherling.com"; $CONFIG['base_url'] = "http://localhost:8080/gatherling/"; -# The name of your site. This is used in a lot of places. -$CONFIG['site_name'] = "Gatherling"; - # A description for the ical calendar which is accessible at calendar.php $CONFIG['calendar_description'] = "a description for the events calendar"; diff --git a/gatherling/config.php.github b/gatherling/config.php.github index 3d164d693..6212211df 100644 --- a/gatherling/config.php.github +++ b/gatherling/config.php.github @@ -12,7 +12,6 @@ $CONFIG['db_database'] = 'gatherling'; $CONFIG['db_test_database'] = 'gatherling_test'; $CONFIG['base_url'] = "http://localhost/gatherling/"; -$CONFIG['site_name'] = "Gatherling on Github Actions"; $CONFIG['calendar_description'] = "Gatherling Calendar"; $CONFIG['infobot_passkey'] = ""; diff --git a/gatherling/templates/partials/header.mustache b/gatherling/templates/partials/header.mustache index 8bc4f8e13..6aaeefb5f 100644 --- a/gatherling/templates/partials/header.mustache +++ b/gatherling/templates/partials/header.mustache @@ -3,7 +3,7 @@ - {{ siteName }} | {{ title }} + Gatherling | {{ title }} From 8cd38f3822444ec71d52a7b5e9e0bc0c2ba041c4 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 06:28:23 -0700 Subject: [PATCH 056/114] Don't error if there are two or more events with the most recent date --- gatherling/ratings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gatherling/ratings.php b/gatherling/ratings.php index 0f8534ee2..50d40b068 100644 --- a/gatherling/ratings.php +++ b/gatherling/ratings.php @@ -100,7 +100,7 @@ function bestEver(string $format): array function currentThrough(string $format): array { $start = db()->string('SELECT MAX(updated) FROM ratings WHERE format = :format', ['format' => $format]); - $name = db()->string('SELECT name FROM events WHERE start = :start', ['start' => $start]); + $name = db()->string('SELECT name FROM events WHERE start = :start ORDER BY name LIMIT 1', ['start' => $start]); return ['date' => new DateTime($start), 'name' => $name]; } From 0ea5b5f14f6f7bb702c02ee37f5db8f797783e7f Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 06:34:05 -0700 Subject: [PATCH 057/114] Don't center player name on ratings table, it looks wonky --- gatherling/templates/partials/ratingsTable.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gatherling/templates/partials/ratingsTable.mustache b/gatherling/templates/partials/ratingsTable.mustache index 6d95c0f28..041a95831 100644 --- a/gatherling/templates/partials/ratingsTable.mustache +++ b/gatherling/templates/partials/ratingsTable.mustache @@ -1,4 +1,4 @@ - +
      Only players with {{minMatches}} or more matches are included. From 88bd30bbc350fdd9f1f03766abbdbdca584dbad5 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 06:54:34 -0700 Subject: [PATCH 058/114] Don't need to check for null event name now that it's not nullable --- gatherling/deck.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gatherling/deck.php b/gatherling/deck.php index 273da9bc4..110f691de 100644 --- a/gatherling/deck.php +++ b/gatherling/deck.php @@ -36,7 +36,7 @@ function main(): never unset($_GET['event']); } else { $event = new Event(get()->string('event')); - $title = $event->name ?? 'Deck Database'; + $title = $event->name; } } From 385c7fbe6307614df622f4ecc6e0cd1c45607fc5 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 07:54:32 -0700 Subject: [PATCH 059/114] Don't show 'Round 0' for unstarted events, show 'Not Yet Started' We should differentiate between "ready to start" and coming soon, though. --- .../templates/partials/hostActiveEvents.mustache | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/gatherling/templates/partials/hostActiveEvents.mustache b/gatherling/templates/partials/hostActiveEvents.mustache index f08a184f6..39040b8a9 100644 --- a/gatherling/templates/partials/hostActiveEvents.mustache +++ b/gatherling/templates/partials/hostActiveEvents.mustache @@ -21,11 +21,16 @@ From 4dfae20a38d09e29daf18b1d62b95f9b6e6574bb Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 08:04:38 -0700 Subject: [PATCH 060/114] Always show headings in silver not link color in Current Events section --- gatherling/styles/css/stylesheet.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gatherling/styles/css/stylesheet.css b/gatherling/styles/css/stylesheet.css index 4aba69308..31874b2e7 100644 --- a/gatherling/styles/css/stylesheet.css +++ b/gatherling/styles/css/stylesheet.css @@ -766,7 +766,7 @@ a:hover { padding: 0px; border-radius: 5px; } -a:has(h1), a:has(h2), a:has(h3), a:has(h4), a:has(h5), a:has(h6), +a:has(h1):link, a:has(h2):link, a:has(h3):link, a:has(h4):link, a:has(h5):link, a:has(h6):link, a:has(h1):visited, a:has(h2):visited, a:has(h3):visited, a:has(h4):visited, a:has(h5):visited, a:has(h6):visited { color: var(--main-text-color); padding: inherit; From 055518c6e6d4eb582af9f697c9503279d179f3df Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 08:07:19 -0700 Subject: [PATCH 061/114] Buttons are now larger, this looks silly for the not allowed icon Not only that but it's not clickable, so why is it a button in the first place? This is way beter. --- gatherling/templates/partials/notAllowed.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gatherling/templates/partials/notAllowed.mustache b/gatherling/templates/partials/notAllowed.mustache index dc4497828..fc28ca819 100644 --- a/gatherling/templates/partials/notAllowed.mustache +++ b/gatherling/templates/partials/notAllowed.mustache @@ -1 +1 @@ - + From c4e1668482c53e610418cb06fc52e3e61f123dd8 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 08:12:52 -0700 Subject: [PATCH 062/114] Whoops we clobbered the player search form on profile.php at some point --- gatherling/Views/Pages/Profile.php | 1 + gatherling/templates/profile.mustache | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/gatherling/Views/Pages/Profile.php b/gatherling/Views/Pages/Profile.php index 4a0655ea5..b3511b855 100644 --- a/gatherling/Views/Pages/Profile.php +++ b/gatherling/Views/Pages/Profile.php @@ -21,6 +21,7 @@ public function __construct(public string $playerName, ?Player $player, public i { parent::__construct('Player Profile'); + $this->playerSearchForm = new PlayerSearchForm($playerName); if (rtrim($playerName) === '') { $this->isLoggedOut = true; return; diff --git a/gatherling/templates/profile.mustache b/gatherling/templates/profile.mustache index d3a9d56ad..738fe24be 100644 --- a/gatherling/templates/profile.mustache +++ b/gatherling/templates/profile.mustache @@ -8,7 +8,7 @@

      Please log in to see your profile. You may also use the search above without logging in.

      {{/isLoggedOut}} {{#notFound}} -

      {{playername}} could not be found in the database. Please check your spelling and try again.

      +

      {{playerName}} could not be found in the database. Please check your spelling and try again.

      {{/notFound}} {{#profileEditForm}} {{>profileEditForm}} From f326786426ced58461ea8ac1ff1175bd92aa48cf Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 08:14:10 -0700 Subject: [PATCH 063/114] Don't squish text on buttons of formatcp It's maybe a nice idea to have them the same size but if we do something like that we'll do it wholesale across the site not just for one page. --- gatherling/templates/partials/formatCPMenu.mustache | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gatherling/templates/partials/formatCPMenu.mustache b/gatherling/templates/partials/formatCPMenu.mustache index 1201d93ca..50314b9bb 100644 --- a/gatherling/templates/partials/formatCPMenu.mustache +++ b/gatherling/templates/partials/formatCPMenu.mustache @@ -9,11 +9,11 @@
      - - - - - + + + + +
      From ae70ba505595334f9bcabf38517b6530126bf812 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 08:17:52 -0700 Subject: [PATCH 064/114] Bump semver to celebrate our first real visual changes --- gatherling/lib.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gatherling/lib.php b/gatherling/lib.php index fbc0fdec8..82cdebcd9 100644 --- a/gatherling/lib.php +++ b/gatherling/lib.php @@ -43,7 +43,8 @@ function git_hash(): string function version_tagline(): string { - return 'Gatherling version 6.0.2 ("Nixon was normalizing relations with China. I figured that if he could normalize relations, then so could I.")'; + return 'Gatherling version 6.0.3 ("Nothing is so painful to the human mind as a great and sudden change.")'; + // return 'Gatherling version 6.0.2 ("Nixon was normalizing relations with China. I figured that if he could normalize relations, then so could I.")'; // return 'Gatherling version 6.0.1 ("A guilty system recognizes no innocents.")'; // return 'Gatherling version 6.0.0 ("A foolish consistency is the hobgoblin of little minds")'; // return 'Gatherling version 5.2.0 ("I mustache you a question...")'; From d6632f68aaa9d7a491f88a2e59e498e046810323 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 08:36:54 -0700 Subject: [PATCH 065/114] Update psalm baseline now we are using the unused PlayerSearchForm --- psalm-baseline.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index ec6259d58..ba515e966 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -156,11 +156,6 @@ - - - - - availableEvents]]> From f1be06fe5ca03e834fbac9056b1c080d4c89ddd8 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 09:00:44 -0700 Subject: [PATCH 066/114] Remove unused import --- gatherling/Helpers/helpers.php | 1 - 1 file changed, 1 deletion(-) diff --git a/gatherling/Helpers/helpers.php b/gatherling/Helpers/helpers.php index 519ce0820..f789748a4 100644 --- a/gatherling/Helpers/helpers.php +++ b/gatherling/Helpers/helpers.php @@ -5,7 +5,6 @@ namespace Gatherling\Helpers; use Gatherling\Logger; -use Psr\Log\LoggerInterface; function request(): Request { From a632f28c4f3a106a5e0ae5f2675a5efd6ab639ee Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 09:00:54 -0700 Subject: [PATCH 067/114] Suppress unused method warning from psalm This method is provided for completeness of the API and while we may not use it currently it's easy to imagine using it in future. --- gatherling/Helpers/Request.php | 1 + 1 file changed, 1 insertion(+) diff --git a/gatherling/Helpers/Request.php b/gatherling/Helpers/Request.php index e19813a22..32f1b3533 100644 --- a/gatherling/Helpers/Request.php +++ b/gatherling/Helpers/Request.php @@ -36,6 +36,7 @@ public function float(string $key, float|false $default = false): float return marshal($this->vars[$key] ?? null)->float($default); } + /** @psalm-suppress PossiblyUnusedMethod */ public function optionalFloat(string $key): ?float { return marshal($this->vars[$key] ?? null)->optionalFloat(); From aa06afdba405dacfc75cb929f45c98acde0c6969 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 09:04:27 -0700 Subject: [PATCH 068/114] Move test to the correct namespace --- tests/Views/Components/ComponentTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Views/Components/ComponentTest.php b/tests/Views/Components/ComponentTest.php index 5ba987262..ba31d2a11 100644 --- a/tests/Views/Components/ComponentTest.php +++ b/tests/Views/Components/ComponentTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Gatherling\Tests\Views; +namespace Gatherling\Tests\Views\Components; use Gatherling\Views\Components\Component; use PHPUnit\Framework\TestCase; From 97fc55bce2b971e4311d0c88c36076817c41d7f3 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 09:05:20 -0700 Subject: [PATCH 069/114] Start to move the last of the "global" funcs out of lib.php These are used in enough places that I'm going to let them remain semiglobal in helpers but at least this way they will be namespaced, explicitly imported, and visible to psalm when static checking. --- composer.json | 5 +- gatherling/Helpers/cards.php | 61 +++++++++++++++++++++++++ gatherling/Models/CardSet.php | 2 +- gatherling/Models/Format.php | 1 + gatherling/Models/Formats.php | 2 +- gatherling/Views/Pages/DeckDownload.php | 2 + gatherling/api_lib.php | 2 +- gatherling/deck.php | 1 + gatherling/formatcp.php | 1 + gatherling/lib.php | 58 ----------------------- tests/EventsTest.php | 2 + tests/Models/DeckTest.php | 2 + tests/NamesTest.php | 2 + 13 files changed, 78 insertions(+), 63 deletions(-) create mode 100644 gatherling/Helpers/cards.php diff --git a/composer.json b/composer.json index 32bcd2b81..e3a6fded5 100644 --- a/composer.json +++ b/composer.json @@ -44,8 +44,9 @@ "Gatherling\\Tests\\": "tests/" }, "files": [ - "gatherling/Helpers/helpers.php", - "gatherling/Helpers/database.php" + "gatherling/Helpers/cards.php", + "gatherling/Helpers/database.php", + "gatherling/Helpers/helpers.php" ] } } diff --git a/gatherling/Helpers/cards.php b/gatherling/Helpers/cards.php new file mode 100644 index 000000000..578d154b6 --- /dev/null +++ b/gatherling/Helpers/cards.php @@ -0,0 +1,61 @@ + $cards + * @return list + */ +function parseCards(string|array $cards): array +{ + $cardarr = []; + if (!is_array($cards)) { + $cards = explode("\n", $cards); + } + foreach ($cards as $card) { + // AE Litigation + $card = normaliseCardName($card); + if ($card != '') { + $cardarr[] = $card; + } + } + return $cardarr; +} + +function normaliseCardName(string $card, bool $toLower = false): string +{ + $pattern = ['/é/', '/è/', '/ë/', '/ê/', '/É/', '/È/', '/Ë/', '/Ê/', '/á/', '/à/', '/ä/', '/â/', '/å/', '/Á/', '/À/', '/Ä/', '/Â/', '/Å/', '/ó/', '/ò/', '/ö/', '/ô/', '/Ó/', '/Ò/', '/Ö/', '/Ô/', '/í/', '/ì/', '/ï/', '/î/', '/Í/', '/Ì/', '/Ï/', '/Î/', '/ú/', '/ù/', '/ü/', '/û/', '/Ú/', '/Ù/', '/Ü/', '/Û/', '/ý/', '/ÿ/', '/Ý/', '/ø/', '/Ø/', '/œ/', '/Œ/', '/Æ/', '/AE/', '/ç/', '/Ç/', '/—/', '/−/', '/—/', '/’/', '/½/']; + $replace = ['e', 'e', 'e', 'e', 'E', 'E', 'E', 'E', 'a', 'a', 'a', 'a', 'a', 'A', 'A', 'A', 'A', 'A', 'o', 'o', 'o', 'o', 'O', 'O', 'O', 'O', 'i', 'i', 'i', 'I', 'I', 'I', 'I', 'I', 'u', 'u', 'u', 'u', 'U', 'U', 'U', 'U', 'y', 'y', 'Y', 'o', 'O', 'ae', 'ae', 'Ae', 'Ae', 'c', 'C', '-', '-', '-', "'", '{1/2}']; + $card = preg_replace($pattern, $replace, $card); + $card = preg_replace("/\306/", 'AE', $card); + $card = preg_replace("/ \/\/\/? /", '/', $card); + if ($toLower) { + $card = strtolower($card); + } + return trim($card); +} + +/** + * @param string|array $cards + * @return array + */ +function parseCardsWithQuantity(string|array $cards): array +{ + $cards = parseCards($cards); + $cardarr = []; + foreach ($cards as $line) { + $chopped = rtrim($line); + if (preg_match("/^[ \t]*([0-9]+)x?[ \t]+(.*?)( \(\w+\) \d+)?$/i", $chopped, $m)) { + $qty = (int) $m[1]; + $card = rtrim($m[2]); + if (isset($cardarr[$card])) { + $cardarr[$card] += $qty; + } else { + $cardarr[$card] = $qty; + } + } + } + return $cardarr; +} diff --git a/gatherling/Models/CardSet.php b/gatherling/Models/CardSet.php index 112c67f4a..3b6ce2c09 100644 --- a/gatherling/Models/CardSet.php +++ b/gatherling/Models/CardSet.php @@ -6,10 +6,10 @@ use stdClass; use mysqli_stmt; -use Gatherling\Exceptions\DatabaseException; use Gatherling\Exceptions\FileNotFoundException; use function Gatherling\Helpers\db; +use function Gatherling\Helpers\normaliseCardName; class CardSet { diff --git a/gatherling/Models/Format.php b/gatherling/Models/Format.php index fab461246..04008f846 100644 --- a/gatherling/Models/Format.php +++ b/gatherling/Models/Format.php @@ -8,6 +8,7 @@ use function Gatherling\Helpers\db; use function Gatherling\Helpers\logger; +use function Gatherling\Helpers\normaliseCardName; class Format { diff --git a/gatherling/Models/Formats.php b/gatherling/Models/Formats.php index f8dbb4cd5..787a81638 100644 --- a/gatherling/Models/Formats.php +++ b/gatherling/Models/Formats.php @@ -8,7 +8,7 @@ use function Gatherling\Helpers\db; use function Gatherling\Helpers\logger; - +use function Gatherling\Helpers\parseCards; class Formats { public static function updateDefaultFormats(): void diff --git a/gatherling/Views/Pages/DeckDownload.php b/gatherling/Views/Pages/DeckDownload.php index 5dc5724df..1617700b1 100644 --- a/gatherling/Views/Pages/DeckDownload.php +++ b/gatherling/Views/Pages/DeckDownload.php @@ -7,6 +7,8 @@ use Gatherling\Models\Deck; use Gatherling\Views\TextFileDownload; +use function Gatherling\Helpers\normaliseCardName; + class DeckDownload extends TextFileDownload { /** @var list */ diff --git a/gatherling/api_lib.php b/gatherling/api_lib.php index 0e4cd1c6e..29148a3fc 100644 --- a/gatherling/api_lib.php +++ b/gatherling/api_lib.php @@ -10,11 +10,11 @@ use Gatherling\Models\Event; use Gatherling\Models\Player; use Gatherling\Models\Series; -use Gatherling\Models\Database; use Gatherling\Models\Standings; use function Gatherling\Helpers\db; use function Gatherling\Helpers\config; +use function Gatherling\Helpers\parseCardsWithQuantity; use function Gatherling\Helpers\server; use function Gatherling\Helpers\request; use function Gatherling\Helpers\session; diff --git a/gatherling/deck.php b/gatherling/deck.php index 110f691de..7767d90fe 100644 --- a/gatherling/deck.php +++ b/gatherling/deck.php @@ -20,6 +20,7 @@ use Gatherling\Views\Pages\Deck as DeckPage; use function Gatherling\Helpers\get; +use function Gatherling\Helpers\parseCardsWithQuantity; use function Gatherling\Helpers\post; use function Gatherling\Helpers\server; use function Gatherling\Helpers\request; diff --git a/gatherling/formatcp.php b/gatherling/formatcp.php index c139009de..bd3f27119 100644 --- a/gatherling/formatcp.php +++ b/gatherling/formatcp.php @@ -23,6 +23,7 @@ use Gatherling\Views\Pages\FormatAdmin; use Gatherling\Views\Pages\InsufficientPermissions; +use function Gatherling\Helpers\parseCards; use function Gatherling\Helpers\post; use function Gatherling\Helpers\server; use function Gatherling\Helpers\request; diff --git a/gatherling/lib.php b/gatherling/lib.php index 82cdebcd9..215a19bc2 100644 --- a/gatherling/lib.php +++ b/gatherling/lib.php @@ -87,64 +87,6 @@ function version_tagline(): string // "Gatherling version 1.9 (\"It's funny 'cause the squirrel got dead\")"; } -/** - * @param string|array $cards - * @return list - */ -function parseCards(string|array $cards): array -{ - $cardarr = []; - if (!is_array($cards)) { - $cards = explode("\n", $cards); - } - foreach ($cards as $card) { - // AE Litigation - $card = normaliseCardName($card); - if ($card != '') { - $cardarr[] = $card; - } - } - return $cardarr; -} - -function normaliseCardName(string $card, bool $toLower = false): string -{ - $pattern = ['/é/', '/è/', '/ë/', '/ê/', '/É/', '/È/', '/Ë/', '/Ê/', '/á/', '/à/', '/ä/', '/â/', '/å/', '/Á/', '/À/', '/Ä/', '/Â/', '/Å/', '/ó/', '/ò/', '/ö/', '/ô/', '/Ó/', '/Ò/', '/Ö/', '/Ô/', '/í/', '/ì/', '/ï/', '/î/', '/Í/', '/Ì/', '/Ï/', '/Î/', '/ú/', '/ù/', '/ü/', '/û/', '/Ú/', '/Ù/', '/Ü/', '/Û/', '/ý/', '/ÿ/', '/Ý/', '/ø/', '/Ø/', '/œ/', '/Œ/', '/Æ/', '/AE/', '/ç/', '/Ç/', '/—/', '/−/', '/—/', '/’/', '/½/']; - $replace = ['e', 'e', 'e', 'e', 'E', 'E', 'E', 'E', 'a', 'a', 'a', 'a', 'a', 'A', 'A', 'A', 'A', 'A', 'o', 'o', 'o', 'o', 'O', 'O', 'O', 'O', 'i', 'i', 'i', 'I', 'I', 'I', 'I', 'I', 'u', 'u', 'u', 'u', 'U', 'U', 'U', 'U', 'y', 'y', 'Y', 'o', 'O', 'ae', 'ae', 'Ae', 'Ae', 'c', 'C', '-', '-', '-', "'", '{1/2}']; - $card = preg_replace($pattern, $replace, $card); - $card = preg_replace("/\306/", 'AE', $card); - $card = preg_replace("/ \/\/\/? /", '/', $card); - if ($toLower) { - $card = strtolower($card); - } - - return trim($card); -} - -/** - * @param string|array $cards - * @return array - */ -function parseCardsWithQuantity(string|array $cards): array -{ - $cards = parseCards($cards); - $cardarr = []; - foreach ($cards as $line) { - $chopped = rtrim($line); - if (preg_match("/^[ \t]*([0-9]+)x?[ \t]+(.*?)( \(\w+\) \d+)?$/i", $chopped, $m)) { - $qty = (int) $m[1]; - $card = rtrim($m[2]); - if (isset($cardarr[$card])) { - $cardarr[$card] += $qty; - } else { - $cardarr[$card] = $qty; - } - } - } - - return $cardarr; -} - // Our standard template variable naming is camelCase. // Some of our objects have properties named in snake_case. // So when we grab the values from an object to pass into diff --git a/tests/EventsTest.php b/tests/EventsTest.php index 3e352277d..2dde08c2c 100644 --- a/tests/EventsTest.php +++ b/tests/EventsTest.php @@ -13,6 +13,8 @@ use Gatherling\Models\Matchup; use Gatherling\Tests\Support\TestCases\DatabaseCase; +use function Gatherling\Helpers\parseCardsWithQuantity; + final class EventsTest extends DatabaseCase { public function testSeriesCreation(): Series diff --git a/tests/Models/DeckTest.php b/tests/Models/DeckTest.php index d884848d5..01a0f6e2d 100644 --- a/tests/Models/DeckTest.php +++ b/tests/Models/DeckTest.php @@ -10,6 +10,8 @@ use Gatherling\Models\Series; use Gatherling\Tests\Support\TestCases\DatabaseCase; +use function Gatherling\Helpers\parseCardsWithQuantity; + class DeckTest extends DatabaseCase { protected Event $event; diff --git a/tests/NamesTest.php b/tests/NamesTest.php index a585055ea..da943855a 100644 --- a/tests/NamesTest.php +++ b/tests/NamesTest.php @@ -8,6 +8,8 @@ use PHPUnit\Framework\TestCase; +use function Gatherling\Helpers\normaliseCardName; + final class NamesTest extends TestCase { public function testNames(): void From 1f12161f8f7b3dc757ab7186c0eeaad680d7b030 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 10:20:15 -0700 Subject: [PATCH 070/114] Upgrade to PHP 8.2, switch to using "Safe" versions of built in funcs It's annoying to have to check that preg_replace did not return null and that ob_start did not return false. In these cases there's a catastrophic failure not a normal run of events and we'd much prefer an exception be thrown and our code be kept concise for normal operations. Someone solved this for PHP7 with a library called "safe": - https://github.com/thecodingmachine/safe The library rewrites builtin PHP funcs to throw exceptions instead of return false or null or other funky behavior you have to check for in the edge cases. Unfortunately they abandoned the library before PHP8 came out. Luckily someone forked it and updated it for PHP8: - https://github.com/shish/safe Unfortunately they only support PHP8.2+. But I have already written a bunch of pointless code to workaround this problem, and there are tons of exceptions in our static checker baselines to accommodate all this silliness. So I upgraded us to PHP8.2 and updated all the code and removed some of the pointless checking we were doing. --- composer.json | 8 +- composer.lock | 209 +++++++++++++++++- gatherling/Auth/Session.php | 2 + gatherling/Data/Db.php | 15 +- gatherling/Data/Setup.php | 12 +- gatherling/Helpers/cards.php | 3 + gatherling/Models/CardSet.php | 8 +- gatherling/Models/Database.php | 1 + gatherling/Models/Formats.php | 4 + gatherling/Models/Image.php | 2 + gatherling/Models/Player.php | 1 + gatherling/Models/Series.php | 3 +- gatherling/Models/SetScraper.php | 2 + gatherling/Views/Components/CardLink.php | 2 + gatherling/Views/Components/CommentsTable.php | 12 +- gatherling/Views/Components/DisplayDecks.php | 8 +- gatherling/Views/Components/InfoCell.php | 2 + gatherling/Views/Components/InfoTable.php | 4 +- .../Views/Components/MissingTrophies.php | 5 +- .../Views/Components/MostPlayedDecks.php | 4 +- gatherling/Views/Components/Placing.php | 2 + gatherling/Views/Components/Prereg.php | 7 +- .../Views/Components/Preregistration.php | 2 + .../Views/Components/RecentEventsTable.php | 2 + .../Views/Components/SeasonStandings.php | 4 +- gatherling/Views/JsonResponse.php | 8 +- gatherling/Views/Pages/DeckDownload.php | 3 +- gatherling/Views/Pages/EventForm.php | 2 + gatherling/Views/Pages/EventList.php | 1 + gatherling/Views/Pages/Ratings.php | 3 + gatherling/Views/Pages/Series.php | 2 + gatherling/admin/db-upgrade.php | 2 +- gatherling/api.php | 1 + gatherling/api_lib.php | 1 + gatherling/bootstrap.php | 8 +- gatherling/deckXmlParser.php | 1 + gatherling/event.php | 4 + gatherling/insertcardset.php | 1 + gatherling/lib.php | 12 +- gatherling/player.php | 1 + gatherling/ratings.php | 1 + gatherling/seriesreport.php | 2 + gatherling/util/email.php | 8 +- gatherling/util/updateDefaultFormats.php | 1 + phpstan-baseline.neon | 99 ++------- phpstan.neon | 1 + tests/EventsTest.php | 15 +- tests/Util/TimeTest.php | 2 + tests/Views/Components/TimeTest.php | 6 +- 49 files changed, 347 insertions(+), 162 deletions(-) diff --git a/composer.json b/composer.json index e3a6fded5..7b90e204f 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "jstests": "bun test" }, "require": { - "php": "8.1.*", + "php": "^8.2", "ext-curl": "*", "ext-mysqli": "*", "ext-pdo": "*", @@ -27,7 +27,8 @@ "symfony/css-selector": "^6.4", "bakert/blossom-php": "^1.0", "stefangabos/zebra_pagination": "^2.4", - "yassinedoghri/php-icons": "^1.1" + "yassinedoghri/php-icons": "^1.1", + "shish/safe": "^2.6" }, "require-dev": { "squizlabs/php_codesniffer": "3.*", @@ -35,7 +36,8 @@ "phpcompatibility/php-compatibility": "^9.3", "phpstan/phpstan": "^1.12", "vimeo/psalm": "^5.26", - "psalm/plugin-phpunit": "^0.19.0" + "psalm/plugin-phpunit": "^0.19.0", + "shish/phpstan-safe-rule": "^1.3" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index aa847cac0..66ae57d1d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "46cd7a33d0ba03d19195fad1b5584314", + "content-hash": "116ba52bb170df518a6a2912be512aa4", "packages": [ { "name": "adhocore/cli", @@ -1300,6 +1300,152 @@ ], "time": "2024-08-08T14:40:50+00:00" }, + { + "name": "shish/safe", + "version": "v2.6.3", + "source": { + "type": "git", + "url": "https://github.com/shish/safe.git", + "reference": "88c2cf506c6b497cd2a961c5e8116a18c5c272c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/shish/safe/zipball/88c2cf506c6b497cd2a961c5e8116a18c5c272c0", + "reference": "88c2cf506c6b497cd2a961c5e8116a18c5c272c0", + "shasum": "" + }, + "require": { + "php": ">= 8.2" + }, + "replace": { + "thecodingmachine/safe": "2.5.0" + }, + "require-dev": { + "phpstan/phpstan": "^1", + "phpunit/phpunit": "^10.0 || ^11.0", + "squizlabs/php_codesniffer": "^3", + "thecodingmachine/phpstan-strict-rules": "^1.0" + }, + "type": "library", + "autoload": { + "files": [ + "deprecated/apc.php", + "deprecated/array.php", + "deprecated/datetime.php", + "deprecated/libevent.php", + "deprecated/misc.php", + "deprecated/password.php", + "deprecated/mssql.php", + "deprecated/stats.php", + "deprecated/strings.php", + "lib/special_cases.php", + "deprecated/mysqli.php", + "generated/apache.php", + "generated/apcu.php", + "generated/bzip2.php", + "generated/calendar.php", + "generated/classobj.php", + "generated/com.php", + "generated/cubrid.php", + "generated/curl.php", + "generated/datetime.php", + "generated/dir.php", + "generated/eio.php", + "generated/errorfunc.php", + "generated/exec.php", + "generated/fileinfo.php", + "generated/filesystem.php", + "generated/filter.php", + "generated/fpm.php", + "generated/ftp.php", + "generated/funchand.php", + "generated/gettext.php", + "generated/gnupg.php", + "generated/hash.php", + "generated/ibase.php", + "generated/ibmDb2.php", + "generated/iconv.php", + "generated/image.php", + "generated/imap.php", + "generated/info.php", + "generated/inotify.php", + "generated/json.php", + "generated/ldap.php", + "generated/libxml.php", + "generated/lzf.php", + "generated/mailparse.php", + "generated/mbstring.php", + "generated/misc.php", + "generated/mysql.php", + "generated/network.php", + "generated/oci8.php", + "generated/opcache.php", + "generated/openssl.php", + "generated/outcontrol.php", + "generated/pcntl.php", + "generated/pcre.php", + "generated/pgsql.php", + "generated/posix.php", + "generated/ps.php", + "generated/pspell.php", + "generated/readline.php", + "generated/rnp.php", + "generated/rpminfo.php", + "generated/rrd.php", + "generated/sem.php", + "generated/session.php", + "generated/shmop.php", + "generated/sockets.php", + "generated/sodium.php", + "generated/solr.php", + "generated/spl.php", + "generated/sqlsrv.php", + "generated/ssdeep.php", + "generated/ssh2.php", + "generated/stream.php", + "generated/strings.php", + "generated/swoole.php", + "generated/uodbc.php", + "generated/uopz.php", + "generated/url.php", + "generated/var.php", + "generated/xdiff.php", + "generated/xml.php", + "generated/xmlrpc.php", + "generated/yaml.php", + "generated/yaz.php", + "generated/zip.php", + "generated/zlib.php" + ], + "classmap": [ + "lib/DateTime.php", + "lib/DateTimeImmutable.php", + "lib/Exceptions/", + "deprecated/Exceptions/", + "generated/Exceptions/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHP core functions that throw exceptions instead of returning FALSE on error (a less-abandoned fork of thecodingmachine/safe)", + "support": { + "issues": "https://github.com/shish/safe/issues", + "source": "https://github.com/shish/safe/tree/v2.6.3" + }, + "funding": [ + { + "url": "https://github.com/shish", + "type": "github" + }, + { + "url": "https://ko-fi.com/shish2k", + "type": "ko_fi" + } + ], + "time": "2024-10-08T20:21:12+00:00" + }, { "name": "stefangabos/zebra_pagination", "version": "2.4.8", @@ -4748,6 +4894,61 @@ ], "time": "2023-02-07T11:34:05+00:00" }, + { + "name": "shish/phpstan-safe-rule", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/shish/phpstan-safe-rule.git", + "reference": "a5041fd41543fc49a7d036a563304b8aba8760f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/shish/phpstan-safe-rule/zipball/a5041fd41543fc49a7d036a563304b8aba8760f5", + "reference": "a5041fd41543fc49a7d036a563304b8aba8760f5", + "shasum": "" + }, + "require": { + "php": "^8.2", + "phpstan/phpstan": "^1.0", + "shish/safe": "^2.0" + }, + "replace": { + "thecodingmachine/phpstan-safe-rule": "1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.0", + "squizlabs/php_codesniffer": "^3.4" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "phpstan-safe-rule.neon" + ] + } + }, + "autoload": { + "psr-4": { + "TheCodingMachine\\Safe\\PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "David Négrier", + "email": "d.negrier@thecodingmachine.com" + } + ], + "description": "A PHPStan rule to detect safety issues. Must be used in conjunction with shish/safe", + "support": { + "source": "https://github.com/shish/phpstan-safe-rule/tree/v1.3.0" + }, + "time": "2024-10-08T18:43:14+00:00" + }, { "name": "spatie/array-to-xml", "version": "3.3.0", @@ -5605,17 +5806,17 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "8.1.*", + "php": "^8.2", "ext-curl": "*", "ext-mysqli": "*", "ext-pdo": "*", "ext-pdo_mysql": "*", "ext-simplexml": "*" }, - "platform-dev": [], + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/gatherling/Auth/Session.php b/gatherling/Auth/Session.php index f8ff2c2ef..45925eea6 100644 --- a/gatherling/Auth/Session.php +++ b/gatherling/Auth/Session.php @@ -5,6 +5,8 @@ namespace Gatherling\Auth; use function Gatherling\Helpers\db; +use function Safe\json_decode; +use function Safe\json_encode; class Session { diff --git a/gatherling/Data/Db.php b/gatherling/Data/Db.php index f85b85351..136a059a9 100644 --- a/gatherling/Data/Db.php +++ b/gatherling/Data/Db.php @@ -16,6 +16,9 @@ use function Gatherling\Helpers\config; use function Gatherling\Helpers\logger; use function Gatherling\Helpers\marshal; +use function Safe\json_encode; +use function Safe\preg_match; +use function Safe\preg_replace; // Do not access this directly, use Gatherling\Helpers\db() instead class Db @@ -450,7 +453,7 @@ public function interpolateQuery(string $query, array $params): string if (is_null($value)) { $values[$key] = 'NULL'; } elseif (is_int($value) || is_float($value)) { - $values[$key] = $value; + $values[$key] = (string) $value; } elseif (is_bool($value)) { $values[$key] = $value ? 'true' : 'false'; } elseif (is_string($value)) { @@ -464,15 +467,8 @@ public function interpolateQuery(string $query, array $params): string // Surround placehodlers with escape sequence, so we don't accidentally match // "?" or ":foo" inside any of the values. $query = preg_replace(['/\?/', '/(:[a-zA-Z0-9_]+)/'], ["$s?$e", "$s$1$e"], $query); - if ($query === null) { - throw new DatabaseException("Failed to interpolate query: $query"); - } // Replace placeholders with actual values $query = preg_replace($keys, $values, $query, -1, $count); - if ($query === null) { - throw new DatabaseException("Failed to interpolate query: $query"); - } - return $query; } @@ -566,9 +562,6 @@ private function executeInternal(string $sql, array $params, callable $operation private function safeName(string $name): string { $safeName = preg_replace('/[^a-zA-Z0-9_]/', '_', $name); - if ($safeName === null) { - throw new DatabaseException("Failed to safely name $name"); - } $safeName = trim($safeName, '_'); if (empty($safeName) || is_numeric($safeName[0])) { $safeName = 'sp_' . $safeName; diff --git a/gatherling/Data/Setup.php b/gatherling/Data/Setup.php index 69884d21d..578a213f1 100644 --- a/gatherling/Data/Setup.php +++ b/gatherling/Data/Setup.php @@ -11,6 +11,9 @@ use function Gatherling\Helpers\config; use function Gatherling\Helpers\db; use function Gatherling\Helpers\logger; +use function Safe\file_get_contents; +use function Safe\preg_match; +use function Safe\scandir; require_once __DIR__ . '/../lib.php'; @@ -107,9 +110,6 @@ private static function restoreDump(string $path): void throw new DatabaseException('Refusing to restore dump in production environment'); } $s = file_get_contents($path); - if (!$s) { - throw new FileNotFoundException("Dump file not found: $path"); - } $commands = explode(';', $s); foreach ($commands as $sql) { db()->execute($sql); @@ -125,9 +125,6 @@ private static function findMigrations(int $version): array $migrationDirectory = __DIR__ . '/sql/migrations'; $migrations = []; $dir = scandir($migrationDirectory); - if ($dir === false) { - throw new FileNotFoundException("Failed to read migration directory: $migrationDirectory"); - } foreach ($dir as $file) { if (!preg_match('/^[1-9]\d*\.sql$/', $file)) { continue; @@ -140,9 +137,6 @@ private static function findMigrations(int $version): array $path = $migrationDirectory . DIRECTORY_SEPARATOR . $file; logger()->debug("Loading migration $fileVersion from $path"); $sql = file_get_contents($path); - if (!$sql) { - throw new FileNotFoundException("Failed to read migration file: $path"); - } $migrations[] = new Migration($fileVersion, $sql); } } diff --git a/gatherling/Helpers/cards.php b/gatherling/Helpers/cards.php index 578d154b6..ef1f361f8 100644 --- a/gatherling/Helpers/cards.php +++ b/gatherling/Helpers/cards.php @@ -4,6 +4,9 @@ namespace Gatherling\Helpers; +use function Safe\preg_match; +use function Safe\preg_replace; + /** * @param string|array $cards * @return list diff --git a/gatherling/Models/CardSet.php b/gatherling/Models/CardSet.php index 3b6ce2c09..af6758cd8 100644 --- a/gatherling/Models/CardSet.php +++ b/gatherling/Models/CardSet.php @@ -10,6 +10,9 @@ use function Gatherling\Helpers\db; use function Gatherling\Helpers\normaliseCardName; +use function Safe\file_get_contents; +use function Safe\preg_match; +use function Safe\json_decode; class CardSet { @@ -26,11 +29,6 @@ public static function insertFromLocation(string $filename): array { $messages = []; $file = file_get_contents($filename); - - if (!$file) { - throw new FileNotFoundException("Can't open the file you requested: {$filename}"); - } - $data = json_decode($file); $data = $data->data; $set = $data->name; diff --git a/gatherling/Models/Database.php b/gatherling/Models/Database.php index 3de6cc8bc..60ed3d5ce 100644 --- a/gatherling/Models/Database.php +++ b/gatherling/Models/Database.php @@ -9,6 +9,7 @@ use PDO; use function Gatherling\Helpers\config; +use function Safe\define; // Use PHP7 default error reporting to avoid a complex refactor mysqli_report(MYSQLI_REPORT_OFF); diff --git a/gatherling/Models/Formats.php b/gatherling/Models/Formats.php index 787a81638..b1dc9babd 100644 --- a/gatherling/Models/Formats.php +++ b/gatherling/Models/Formats.php @@ -9,6 +9,10 @@ use function Gatherling\Helpers\db; use function Gatherling\Helpers\logger; use function Gatherling\Helpers\parseCards; +use function Safe\file_get_contents; +use function Safe\json_decode; +use function Safe\strtotime; + class Formats { public static function updateDefaultFormats(): void diff --git a/gatherling/Models/Image.php b/gatherling/Models/Image.php index d2603e233..fdc635282 100644 --- a/gatherling/Models/Image.php +++ b/gatherling/Models/Image.php @@ -4,6 +4,8 @@ namespace Gatherling\Models; +use function Safe\base64_decode; + class Image { public function __construct(public string $content, public string $type, public int $size) diff --git a/gatherling/Models/Player.php b/gatherling/Models/Player.php index 5e9672a91..1f8bd4aaa 100644 --- a/gatherling/Models/Player.php +++ b/gatherling/Models/Player.php @@ -13,6 +13,7 @@ use function Gatherling\Helpers\db; use function Gatherling\Helpers\logger; use function Gatherling\Helpers\session; +use function Safe\session_destroy; class Player { diff --git a/gatherling/Models/Series.php b/gatherling/Models/Series.php index 094dd26b7..64b37db75 100644 --- a/gatherling/Models/Series.php +++ b/gatherling/Models/Series.php @@ -5,10 +5,11 @@ namespace Gatherling\Models; use PDO; -use Exception; use InvalidArgumentException; use function Gatherling\Helpers\db; +use function Safe\fclose; +use function Safe\fopen; class Series { diff --git a/gatherling/Models/SetScraper.php b/gatherling/Models/SetScraper.php index 4f7e8f537..3c6c2fa46 100644 --- a/gatherling/Models/SetScraper.php +++ b/gatherling/Models/SetScraper.php @@ -5,6 +5,8 @@ namespace Gatherling\Models; use function Gatherling\Helpers\db; +use function Safe\file_get_contents; +use function Safe\json_decode; class SetScraper { diff --git a/gatherling/Views/Components/CardLink.php b/gatherling/Views/Components/CardLink.php index fc9ec2ffb..a72e2a600 100644 --- a/gatherling/Views/Components/CardLink.php +++ b/gatherling/Views/Components/CardLink.php @@ -4,6 +4,8 @@ namespace Gatherling\Views\Components; +use function Safe\preg_replace; + class CardLink extends Component { public string $gathererLink; diff --git a/gatherling/Views/Components/CommentsTable.php b/gatherling/Views/Components/CommentsTable.php index d50c135a4..fc88df16f 100644 --- a/gatherling/Views/Components/CommentsTable.php +++ b/gatherling/Views/Components/CommentsTable.php @@ -4,7 +4,7 @@ namespace Gatherling\Views\Components; -use Gatherling\Models\Deck; +use function Safe\preg_replace; class CommentsTable extends Component { @@ -14,11 +14,11 @@ public function __construct(string $notes) { $notes = strip_tags($notes); $notes = htmlspecialchars($notes, ENT_QUOTES | ENT_HTML5, 'UTF-8'); - $notes = preg_replace("/\n/", '
      ', $notes) ?? $notes; - $notes = preg_replace("/\[b\]/", '', $notes) ?? $notes; - $notes = preg_replace("/\[\/b\]/", '', $notes) ?? $notes; - $notes = preg_replace("/\[i\]/", '', $notes) ?? $notes; - $notes = preg_replace("/\[\/i\]/", '', $notes) ?? $notes; + $notes = preg_replace("/\n/", '
      ', $notes); + $notes = preg_replace("/\[b\]/", '', $notes); + $notes = preg_replace("/\[\/b\]/", '', $notes); + $notes = preg_replace("/\[i\]/", '', $notes); + $notes = preg_replace("/\[\/i\]/", '', $notes); $this->notesSafe = $notes; } } diff --git a/gatherling/Views/Components/DisplayDecks.php b/gatherling/Views/Components/DisplayDecks.php index 7845d84b1..547f63a9d 100644 --- a/gatherling/Views/Components/DisplayDecks.php +++ b/gatherling/Views/Components/DisplayDecks.php @@ -7,6 +7,11 @@ use Gatherling\Models\Decksearch; use Zebra_Pagination as Pagination; +use function Safe\ob_get_clean; +use function Safe\ob_start; +use function Safe\preg_replace; +use function Safe\strtotime; + class DisplayDecks extends Component { /** @var list */ @@ -34,8 +39,7 @@ public function __construct(public array $deckIds) if (strlen($deckinfo['name']) > 23) { $deckinfo['name'] = preg_replace('/\s+?(\S+)?$/', '', substr($deckinfo['name'], 0, 22)) . '...'; } - $created = $deckinfo['created_date'] ? strtotime($deckinfo['created_date']) : null; - $createdTime = $created ? new Time($created, $now) : null; + $createdTime = $deckinfo['created_date'] ? new Time(strtotime($deckinfo['created_date']), $now) : null; $this->decks[] = [ 'isEven' => $index % 2 === 0, 'playerLink' => 'profile.php?player=' . rawurlencode($deckinfo['playername']) . '&mode=Lookup+Profile', diff --git a/gatherling/Views/Components/InfoCell.php b/gatherling/Views/Components/InfoCell.php index ad6da780a..c9956231d 100644 --- a/gatherling/Views/Components/InfoCell.php +++ b/gatherling/Views/Components/InfoCell.php @@ -7,6 +7,8 @@ use Gatherling\Models\Event; use Gatherling\Models\Player; +use function Safe\strtotime; + class InfoCell extends Component { public string $threadLink; diff --git a/gatherling/Views/Components/InfoTable.php b/gatherling/Views/Components/InfoTable.php index 515e15da0..63387c5c9 100644 --- a/gatherling/Views/Components/InfoTable.php +++ b/gatherling/Views/Components/InfoTable.php @@ -6,6 +6,8 @@ use Gatherling\Models\Player; +use function Safe\strtotime; + class InfoTable extends Component { public string $line1; @@ -75,7 +77,7 @@ public function __construct(Player $player) $this->pcgS = $pcgS; $this->hosted = $player->getHostedEventsCount(); if ($lastEvent) { - $this->lastEventTime = new Time(strtotime($lastEvent->start), time(), true); + $this->lastEventTime = $lastEvent->start ? new Time(strtotime($lastEvent->start), time(), true) : null; $this->lastEventName = $lastEvent->name; } $this->numMatches = count($matches); diff --git a/gatherling/Views/Components/MissingTrophies.php b/gatherling/Views/Components/MissingTrophies.php index abf6c612d..799901a9a 100644 --- a/gatherling/Views/Components/MissingTrophies.php +++ b/gatherling/Views/Components/MissingTrophies.php @@ -7,6 +7,8 @@ use Gatherling\Models\Deck; use Gatherling\Models\Series; +use function Safe\strtotime; + class MissingTrophies extends Component { public bool $noRecentEvents; @@ -34,8 +36,7 @@ public function __construct(Series $series) $hasWinner = true; } } - $eventStartTime = $event->start ? strtotime($event->start) : null; - $startTime = $eventStartTime ? new Time($eventStartTime, $now) : null; + $startTime = $event->start ? new Time(strtotime($event->start), $now) : null; $this->eventsMissingTrophies[] = [ 'hasWinner' => $hasWinner, 'eventName' => $event->name ?? '', diff --git a/gatherling/Views/Components/MostPlayedDecks.php b/gatherling/Views/Components/MostPlayedDecks.php index 9611446b3..a360e83cd 100644 --- a/gatherling/Views/Components/MostPlayedDecks.php +++ b/gatherling/Views/Components/MostPlayedDecks.php @@ -8,6 +8,7 @@ use Gatherling\Models\MostPlayedDeckDto; use function Gatherling\Helpers\db; +use function Safe\strtotime; class MostPlayedDecks extends Component { @@ -48,8 +49,7 @@ public function __construct() $decks = db()->select($sql, MostPlayedDeckDto::class); foreach ($decks as $deck) { - $created = $deck->created_date ? strtotime($deck->created_date) : null; - $createdTime = $created ? new Time($created, time()) : null; + $createdTime = $deck->created_date ? new Time(strtotime($deck->created_date), time()) : null; $this->decks[] = [ 'count' => $deck->cnt, 'playerLink' => 'profile.php?player=' . rawurlencode($deck->playername) . '&mode=Lookup+Profile', diff --git a/gatherling/Views/Components/Placing.php b/gatherling/Views/Components/Placing.php index ca8017b89..f30ad195d 100644 --- a/gatherling/Views/Components/Placing.php +++ b/gatherling/Views/Components/Placing.php @@ -9,6 +9,8 @@ use Gatherling\Models\Player; use InvalidArgumentException; +use function Safe\strtotime; + class Placing extends Component { public Medal $medal; diff --git a/gatherling/Views/Components/Prereg.php b/gatherling/Views/Components/Prereg.php index 14a1b1075..b77580414 100644 --- a/gatherling/Views/Components/Prereg.php +++ b/gatherling/Views/Components/Prereg.php @@ -7,6 +7,8 @@ use Gatherling\Models\Event; use Gatherling\Models\Player; +use function Safe\strtotime; + class Prereg extends Component { public ?string $eventName; @@ -34,9 +36,6 @@ public function __construct(public Event $event) $this->showRegister = true; $this->registerLink = 'prereg.php?action=reg&event=' . rawurlencode($event->name); } - if ($event->start) { - $start = strtotime($event->start); - $this->start = $start ? new Time($start, time(), true) : null; - } + $this->start = $event->start ? new Time(strtotime($event->start), time(), true): null; } } diff --git a/gatherling/Views/Components/Preregistration.php b/gatherling/Views/Components/Preregistration.php index 2e06eb780..fc2540f18 100644 --- a/gatherling/Views/Components/Preregistration.php +++ b/gatherling/Views/Components/Preregistration.php @@ -9,6 +9,8 @@ use Gatherling\Models\Event; use Gatherling\Models\Player; +use function Safe\strtotime; + class Preregistration extends Component { public bool $hasUpcomingEvents = false; diff --git a/gatherling/Views/Components/RecentEventsTable.php b/gatherling/Views/Components/RecentEventsTable.php index 3b40e7710..31c8a5abb 100644 --- a/gatherling/Views/Components/RecentEventsTable.php +++ b/gatherling/Views/Components/RecentEventsTable.php @@ -6,6 +6,8 @@ use Gatherling\Models\Series; +use function Safe\strtotime; + class RecentEventsTable extends Component { /** @var array */ diff --git a/gatherling/Views/Components/SeasonStandings.php b/gatherling/Views/Components/SeasonStandings.php index 36f11ac40..c61eb7f7b 100644 --- a/gatherling/Views/Components/SeasonStandings.php +++ b/gatherling/Views/Components/SeasonStandings.php @@ -8,6 +8,8 @@ use Gatherling\Models\Series; use Gatherling\Views\Components\Component; +use function Safe\preg_replace; + class SeasonStandings extends Component { public string $seriesName; @@ -26,7 +28,7 @@ public function __construct(Series $series, int $season) $seasonEvents = []; foreach ($seasonEventNames as $eventName) { - $shortName = preg_replace("/^{$series->name} /", '', $eventName) ?? ''; + $shortName = preg_replace("/^{$series->name} /", '', $eventName); $eventReportLink = 'eventreport.php?event=' . rawurlencode($eventName); $seasonEvents[] = [ 'shortName' => $shortName, diff --git a/gatherling/Views/JsonResponse.php b/gatherling/Views/JsonResponse.php index a1e9b4e8b..203a3af44 100644 --- a/gatherling/Views/JsonResponse.php +++ b/gatherling/Views/JsonResponse.php @@ -6,6 +6,8 @@ use InvalidArgumentException; +use function Safe\json_encode; + class JsonResponse extends Response { /** @param array $data */ @@ -16,10 +18,6 @@ public function __construct(private array $data) public function body(): string { - $result = json_encode($this->data); - if ($result === false) { - throw new InvalidArgumentException('Failed to encode data to JSON'); - } - return $result; + return json_encode($this->data); } } diff --git a/gatherling/Views/Pages/DeckDownload.php b/gatherling/Views/Pages/DeckDownload.php index 1617700b1..b99538573 100644 --- a/gatherling/Views/Pages/DeckDownload.php +++ b/gatherling/Views/Pages/DeckDownload.php @@ -8,6 +8,7 @@ use Gatherling\Views\TextFileDownload; use function Gatherling\Helpers\normaliseCardName; +use function Safe\preg_replace; class DeckDownload extends TextFileDownload { @@ -18,7 +19,7 @@ class DeckDownload extends TextFileDownload public function __construct(Deck $deck) { - $filename = preg_replace('/ /', '_', $deck->name) . '.txt'; + $filename = preg_replace('/ /', '_', $deck->name ?? 'deck') . '.txt'; parent::__construct($filename); $this->maindeckCards = $this->prepare($deck->maindeck_cards); $this->sideboardCards = $this->prepare($deck->sideboard_cards); diff --git a/gatherling/Views/Pages/EventForm.php b/gatherling/Views/Pages/EventForm.php index 930a54428..05ec53603 100644 --- a/gatherling/Views/Pages/EventForm.php +++ b/gatherling/Views/Pages/EventForm.php @@ -17,6 +17,8 @@ use Gatherling\Views\Components\SeriesDropMenu; use Gatherling\Views\Components\TimeDropMenu; +use function Safe\preg_match; + class EventForm extends EventFrame { public bool $currentlyEditing; diff --git a/gatherling/Views/Pages/EventList.php b/gatherling/Views/Pages/EventList.php index a463a9c20..366a57d45 100644 --- a/gatherling/Views/Pages/EventList.php +++ b/gatherling/Views/Pages/EventList.php @@ -14,6 +14,7 @@ use function Gatherling\Helpers\db; use function Gatherling\Helpers\get; +use function Safe\strtotime; class EventList extends Page { diff --git a/gatherling/Views/Pages/Ratings.php b/gatherling/Views/Pages/Ratings.php index f261ab1f7..16bc72e72 100644 --- a/gatherling/Views/Pages/Ratings.php +++ b/gatherling/Views/Pages/Ratings.php @@ -11,6 +11,9 @@ use Gatherling\Views\Components\FormatDropMenuR; use Zebra_Pagination as Pagination; +use function Safe\ob_start; +use function Safe\ob_get_clean; + class Ratings extends Page { public FormatDropMenuR $formatDropMenuR; diff --git a/gatherling/Views/Pages/Series.php b/gatherling/Views/Pages/Series.php index 5aa2bb081..b9385c0fb 100644 --- a/gatherling/Views/Pages/Series.php +++ b/gatherling/Views/Pages/Series.php @@ -8,6 +8,8 @@ use Gatherling\Views\Components\EventReportLink; use Gatherling\Models\Series as SeriesModel; +use function Safe\strtotime; + class Series extends Page { /** @var array */ diff --git a/gatherling/admin/db-upgrade.php b/gatherling/admin/db-upgrade.php index 89d43fdfe..9b8d71b9c 100644 --- a/gatherling/admin/db-upgrade.php +++ b/gatherling/admin/db-upgrade.php @@ -7,7 +7,7 @@ use Gatherling\Data\Setup; use function Gatherling\Helpers\server; - +use function Safe\set_time_limit; set_time_limit(0); require_once __DIR__ . '/../lib.php'; diff --git a/gatherling/api.php b/gatherling/api.php index 21d233e55..08937524e 100644 --- a/gatherling/api.php +++ b/gatherling/api.php @@ -11,6 +11,7 @@ use function Gatherling\Helpers\db; use function Gatherling\Helpers\get; use function Gatherling\Helpers\request; +use function Safe\json_encode; require_once 'lib.php'; require_once 'api_lib.php'; diff --git a/gatherling/api_lib.php b/gatherling/api_lib.php index 29148a3fc..883d6984e 100644 --- a/gatherling/api_lib.php +++ b/gatherling/api_lib.php @@ -18,6 +18,7 @@ use function Gatherling\Helpers\server; use function Gatherling\Helpers\request; use function Gatherling\Helpers\session; +use function Safe\json_encode; /** * @param array $array diff --git a/gatherling/bootstrap.php b/gatherling/bootstrap.php index c52cfcf09..dd063bdcd 100644 --- a/gatherling/bootstrap.php +++ b/gatherling/bootstrap.php @@ -2,6 +2,8 @@ declare(strict_types=1); +use function Safe\file_get_contents; + if (file_exists('/var/www/vendor/autoload.php')) { // Docker environment /** @phpstan-ignore-next-line */ @@ -19,11 +21,7 @@ $CONFIG['GIT_HASH'] = null; if (file_exists('../.git/HEAD')) { - $s = file_get_contents('../.git/HEAD'); - if ($s === false) { - throw new \RuntimeException('Failed to read .git/HEAD'); - } - $branch = trim(substr($s, 5)); + $branch = trim(substr(file_get_contents('../.git/HEAD'), 5)); if ($hash = file_get_contents(sprintf('../.git/%s', $branch))) { $CONFIG['GIT_HASH'] = $hash; } diff --git a/gatherling/deckXmlParser.php b/gatherling/deckXmlParser.php index 3e40c51e2..69b097ef4 100644 --- a/gatherling/deckXmlParser.php +++ b/gatherling/deckXmlParser.php @@ -5,6 +5,7 @@ use Gatherling\Views\JsonResponse; use function Gatherling\Helpers\server; +use function Safe\simplexml_load_string; require_once 'lib.php'; diff --git a/gatherling/event.php b/gatherling/event.php index 622504781..630e3ae4e 100644 --- a/gatherling/event.php +++ b/gatherling/event.php @@ -30,6 +30,10 @@ use function Gatherling\Helpers\post; use function Gatherling\Helpers\request; use function Gatherling\Helpers\server; +use function Safe\fclose; +use function Safe\fopen; +use function Safe\preg_replace; +use function Safe\strtotime; require_once 'lib.php'; diff --git a/gatherling/insertcardset.php b/gatherling/insertcardset.php index 7dce3dbc8..d44719ff0 100644 --- a/gatherling/insertcardset.php +++ b/gatherling/insertcardset.php @@ -10,6 +10,7 @@ use function Gatherling\Helpers\files; use function Gatherling\Helpers\request; use function Gatherling\Helpers\server; +use function Safe\set_time_limit; set_time_limit(0); mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); diff --git a/gatherling/lib.php b/gatherling/lib.php index 215a19bc2..50721a74d 100644 --- a/gatherling/lib.php +++ b/gatherling/lib.php @@ -6,6 +6,11 @@ use Gatherling\Models\Player; use function Gatherling\Helpers\config; +use function Safe\iconv; +use function Safe\ob_start; +use function Safe\php_sapi_name; +use function Safe\preg_replace; +use function Safe\preg_split; require_once 'bootstrap.php'; @@ -104,19 +109,14 @@ function toCamel(string $string): string { // Convert to ASCII, remove apostrophes, and split into words $string = iconv('UTF-8', 'ASCII//TRANSLIT', $string); - if ($string === false) { - throw new \RuntimeException("Failed to convert string to ASCII: $string"); - } $string = str_replace("'", "", $string); $words = preg_split('/[^a-zA-Z0-9]+/', $string); - if ($words === false) { - throw new \RuntimeException("Failed to split string into words: $string"); - } // Convert each word to camel case $camelCase = array_map(function ($word) { // Split words that are already in camel case $word = preg_replace('/(?<=\p{Ll})(?=\p{Lu})/u', ' ', $word); + /** @var string $word */ $word = preg_replace('/(?<=\p{Lu})(?=\p{Lu}\p{Ll})/u', ' ', $word); $subWords = explode(' ', $word); diff --git a/gatherling/player.php b/gatherling/player.php index 9ac689f1f..05206d298 100644 --- a/gatherling/player.php +++ b/gatherling/player.php @@ -22,6 +22,7 @@ use function Gatherling\Helpers\post; use function Gatherling\Helpers\request; use function Gatherling\Helpers\server; +use function Safe\preg_match; require_once 'lib.php'; diff --git a/gatherling/ratings.php b/gatherling/ratings.php index 50d40b068..a35f45ba6 100644 --- a/gatherling/ratings.php +++ b/gatherling/ratings.php @@ -6,6 +6,7 @@ use Gatherling\Models\BestEverDto; use Gatherling\Models\PlayerRatingDto; use Gatherling\Views\Pages\Ratings; +use Safe\DateTime; use Zebra_Pagination as Pagination; use function Gatherling\Helpers\db; diff --git a/gatherling/seriesreport.php b/gatherling/seriesreport.php index a9ff909c9..b8589641c 100644 --- a/gatherling/seriesreport.php +++ b/gatherling/seriesreport.php @@ -2,6 +2,8 @@ declare(strict_types=1); +use function Safe\ini_set; + // Some of the Pauper series seasons had 1000+ entrants and 256 tourneys. // We should find a better way to display that but for now make them work // without hitting the PHP memory limit. diff --git a/gatherling/util/email.php b/gatherling/util/email.php index e077a0414..d5fb57f36 100644 --- a/gatherling/util/email.php +++ b/gatherling/util/email.php @@ -4,6 +4,11 @@ use function Gatherling\Helpers\config; use function Gatherling\Helpers\logger; +use function Safe\curl_exec; +use function Safe\curl_getinfo; +use function Safe\curl_init; +use function Safe\curl_setopt; +use function Safe\json_encode; // Use Brevo to send an email from us to a single recipient. Brevo supports multiple recipients, attachments, etc. but we don't need that yet. function sendEmail(string $to, string $subj, string $msg): bool @@ -20,7 +25,7 @@ function sendEmail(string $to, string $subj, string $msg): bool curl_setopt($ch, CURLOPT_URL, 'https://api.brevo.com/v3/smtp/email'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body, JSON_THROW_ON_ERROR)); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body)); $headers = []; $headers[] = 'Accept: application/json'; @@ -33,6 +38,7 @@ function sendEmail(string $to, string $subj, string $msg): bool logger()->error('Error sending email: ' . curl_error($ch)); return false; } + /** @var int $response_code */ $response_code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); if ($response_code >= 300) { logger()->error('Error sending email: ' . $response_code . ' response code'); diff --git a/gatherling/util/updateDefaultFormats.php b/gatherling/util/updateDefaultFormats.php index de5f031ab..245e93c26 100644 --- a/gatherling/util/updateDefaultFormats.php +++ b/gatherling/util/updateDefaultFormats.php @@ -12,6 +12,7 @@ use function Gatherling\Helpers\logger; use function Gatherling\Helpers\server; +use function Safe\set_time_limit; set_time_limit(0); diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 83b14010a..1f053eb50 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -10,6 +10,16 @@ parameters: count: 1 path: gatherling/Auth/Session.php + - + message: "#^Cannot access offset 1 on iterable\\<\\(int\\|string\\), string\\>\\|null\\.$#" + count: 1 + path: gatherling/Helpers/cards.php + + - + message: "#^Cannot access offset 2 on iterable\\<\\(int\\|string\\), string\\>\\|null\\.$#" + count: 1 + path: gatherling/Helpers/cards.php + - message: "#^Cannot access property \\$data on mixed\\.$#" count: 1 @@ -455,16 +465,6 @@ parameters: count: 2 path: gatherling/Models/Formats.php - - - message: "#^Parameter \\#1 \\$cards of function parseCards expects array\\\\|string, string\\|false given\\.$#" - count: 1 - path: gatherling/Models/Formats.php - - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#" - count: 2 - path: gatherling/Models/Formats.php - - message: "#^Binary operation \"\\+\\=\" between int\\|null and int\\|string results in an error\\.$#" count: 4 @@ -725,11 +725,6 @@ parameters: count: 1 path: gatherling/Models/SetScraper.php - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#" - count: 1 - path: gatherling/Models/SetScraper.php - - message: "#^Cannot call method bind_param\\(\\) on mysqli_stmt\\|false\\.$#" count: 2 @@ -755,11 +750,6 @@ parameters: count: 2 path: gatherling/Models/Standings.php - - - message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Components/CardLink.php - - message: "#^Parameter \\#1 \\$name of class Gatherling\\\\Models\\\\Player constructor expects string, string\\|null given\\.$#" count: 1 @@ -785,21 +775,6 @@ parameters: count: 1 path: gatherling/Views/Components/InfoCell.php - - - message: "#^Parameter \\#2 \\$timestamp of function date expects int\\|null, int\\|false given\\.$#" - count: 1 - path: gatherling/Views/Components/InfoCell.php - - - - message: "#^Parameter \\#1 \\$datetime of function strtotime expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Components/InfoTable.php - - - - message: "#^Parameter \\#1 \\$time of class Gatherling\\\\Views\\\\Components\\\\Time constructor expects int, int\\|false given\\.$#" - count: 1 - path: gatherling/Views/Components/InfoTable.php - - message: "#^Binary operation \"\\+\" between float\\|int\\|string\\|null and 1 results in an error\\.$#" count: 1 @@ -831,32 +806,27 @@ parameters: path: gatherling/Views/Components/TextInput.php - - message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Pages/DeckDownload.php - - - - message: "#^Offset 1 might not exist on array\\{0\\?\\: string, 1\\?\\: numeric\\-string, 2\\?\\: numeric\\-string, 3\\?\\: numeric\\-string, 4\\?\\: numeric\\-string, 5\\?\\: numeric\\-string\\}\\.$#" + message: "#^Cannot access offset 1 on iterable\\<\\(int\\|string\\), string\\>\\|null\\.$#" count: 1 path: gatherling/Views/Pages/EventForm.php - - message: "#^Offset 2 might not exist on array\\{0\\?\\: string, 1\\?\\: numeric\\-string, 2\\?\\: numeric\\-string, 3\\?\\: numeric\\-string, 4\\?\\: numeric\\-string, 5\\?\\: numeric\\-string\\}\\.$#" + message: "#^Cannot access offset 2 on iterable\\<\\(int\\|string\\), string\\>\\|null\\.$#" count: 1 path: gatherling/Views/Pages/EventForm.php - - message: "#^Offset 3 might not exist on array\\{0\\?\\: string, 1\\?\\: numeric\\-string, 2\\?\\: numeric\\-string, 3\\?\\: numeric\\-string, 4\\?\\: numeric\\-string, 5\\?\\: numeric\\-string\\}\\.$#" + message: "#^Cannot access offset 3 on iterable\\<\\(int\\|string\\), string\\>\\|null\\.$#" count: 1 path: gatherling/Views/Pages/EventForm.php - - message: "#^Offset 4 might not exist on array\\{0\\?\\: string, 1\\?\\: numeric\\-string, 2\\?\\: numeric\\-string, 3\\?\\: numeric\\-string, 4\\?\\: numeric\\-string, 5\\?\\: numeric\\-string\\}\\.$#" + message: "#^Cannot access offset 4 on iterable\\<\\(int\\|string\\), string\\>\\|null\\.$#" count: 1 path: gatherling/Views/Pages/EventForm.php - - message: "#^Offset 5 might not exist on array\\{0\\?\\: string, 1\\?\\: numeric\\-string, 2\\?\\: numeric\\-string, 3\\?\\: numeric\\-string, 4\\?\\: numeric\\-string, 5\\?\\: numeric\\-string\\}\\.$#" + message: "#^Cannot access offset 5 on iterable\\<\\(int\\|string\\), string\\>\\|null\\.$#" count: 1 path: gatherling/Views/Pages/EventForm.php @@ -920,11 +890,6 @@ parameters: count: 1 path: gatherling/Views/Pages/Profile.php - - - message: "#^Property Gatherling\\\\Views\\\\Pages\\\\Ratings\\:\\:\\$paginationSafe \\(string\\) does not accept string\\|false\\.$#" - count: 1 - path: gatherling/Views/Pages/Ratings.php - - message: "#^Property Gatherling\\\\Views\\\\Pages\\\\ReportsForm\\:\\:\\$registrants \\(list\\\\>\\) does not accept list\\, entryName\\: mixed, emailAd\\: string\\|null\\}\\>\\.$#" count: 1 @@ -936,17 +901,7 @@ parameters: path: gatherling/Views/Pages/ReportsForm.php - - message: "#^Parameter \\#1 \\$datetime of function strtotime expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/Views/Pages/Series.php - - - - message: "#^Parameter \\#1 \\$time of class Gatherling\\\\Views\\\\Components\\\\Time constructor expects int, int\\|false given\\.$#" - count: 1 - path: gatherling/Views/Pages/Series.php - - - - message: "#^Parameter \\#2 \\$timestamp of function date expects int\\|null, int\\|false given\\.$#" + message: "#^Parameter \\#1 \\$datetime of function Safe\\\\strtotime expects string, string\\|null given\\.$#" count: 1 path: gatherling/Views/Pages/Series.php @@ -1106,7 +1061,7 @@ parameters: path: gatherling/event.php - - message: "#^Parameter \\#1 \\$datetime of function strtotime expects string, string\\|null given\\.$#" + message: "#^Parameter \\#1 \\$datetime of function Safe\\\\strtotime expects string, string\\|null given\\.$#" count: 1 path: gatherling/event.php @@ -1170,31 +1125,11 @@ parameters: count: 1 path: gatherling/lib.php - - - message: "#^Parameter \\#1 \\$string of function strtolower expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/lib.php - - - - message: "#^Parameter \\#1 \\$string of function trim expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/lib.php - - message: "#^Parameter \\#2 \\$arr of function arrayMapRecursive expects array\\, array given\\.$#" count: 1 path: gatherling/lib.php - - - message: "#^Parameter \\#2 \\$string of function explode expects string, string\\|null given\\.$#" - count: 1 - path: gatherling/lib.php - - - - message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, string\\|null given\\.$#" - count: 3 - path: gatherling/lib.php - - message: "#^Property Gatherling\\\\Models\\\\Player\\:\\:\\$emailAddress \\(string\\|null\\) does not accept mixed\\.$#" count: 1 diff --git a/phpstan.neon b/phpstan.neon index 279031028..566398545 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,6 @@ includes: - phar://phpstan.phar/conf/bleedingEdge.neon + - vendor/shish/phpstan-safe-rule/phpstan-safe-rule.neon - phpstan-baseline.neon parameters: level: 9 diff --git a/tests/EventsTest.php b/tests/EventsTest.php index 2dde08c2c..a1d7f715e 100644 --- a/tests/EventsTest.php +++ b/tests/EventsTest.php @@ -14,6 +14,7 @@ use Gatherling\Tests\Support\TestCases\DatabaseCase; use function Gatherling\Helpers\parseCardsWithQuantity; +use function Safe\json_encode; final class EventsTest extends DatabaseCase { @@ -95,21 +96,21 @@ public function testRegistration(Event $event): Event $this->assertEquals(0, count($event->getRegisteredEntries(false, true))); $deck = insertDeck('testplayer0', $event, '60 Plains', ''); - $this->assertEmpty($deck->errors, (string) json_encode($deck->errors)); + $this->assertEmpty($deck->errors, json_encode($deck->errors)); $deck = insertDeck('testplayer1', $event, '60 Island', ''); - $this->assertEmpty($deck->errors, (string) json_encode($deck->errors)); + $this->assertEmpty($deck->errors, json_encode($deck->errors)); $deck = insertDeck('testplayer2', $event, '40 Swamp', ''); $this->assertNotEmpty($deck->errors, 'No errors for a 40 card deck.'); $deck = insertDeck('testplayer3', $event, "60 Swamp\n100 Relentless Rats", '15 Swamp'); - $this->assertEmpty($deck->errors, (string) json_encode($deck->errors)); + $this->assertEmpty($deck->errors, json_encode($deck->errors)); $deck = insertDeck('testplayer4', $event, "20 Mountain\n20 Forest\n\n\n\n\n\n\n\n\n\n\n\n4 Plains\n4 Plains\n4 Plains\n4 Plains\n4 Plains\n\n\n", ''); - $this->assertEmpty($deck->errors, (string) json_encode($deck->errors)); + $this->assertEmpty($deck->errors, json_encode($deck->errors)); $deck = insertDeck('testplayer5', $event, "54 Mountain\n6 Seven Dwarves", '1 Seven Dwarves'); - $this->assertEmpty($deck->errors, (string) json_encode($deck->errors)); + $this->assertEmpty($deck->errors, json_encode($deck->errors)); $deck = insertDeck('testplayer6', $event, "50 Mountain\n10 Seven Dwarves", ''); - $this->assertNotEmpty($deck->errors, (string) json_encode($deck->errors)); + $this->assertNotEmpty($deck->errors, json_encode($deck->errors)); $deck = insertDeck('testplayer7', $event, "55 Mountain\n5 Seven Dwarves", '5 Seven Dwarves'); - $this->assertNotEmpty($deck->errors, (string) json_encode($deck->errors)); + $this->assertNotEmpty($deck->errors, json_encode($deck->errors)); // None of this changes entry status. $this->assertEquals(10, count($event->getEntries())); // 5 Valid decks (0, 1, 3, 4, 5), 3 invalid decks (2, 6, 7), and 2 not submitted decks (8, 9). diff --git a/tests/Util/TimeTest.php b/tests/Util/TimeTest.php index c656dd5e5..dff84ff41 100644 --- a/tests/Util/TimeTest.php +++ b/tests/Util/TimeTest.php @@ -8,6 +8,8 @@ use PHPUnit\Framework\TestCase; +use function Safe\strtotime; + final class TimeTest extends TestCase { public function testHumanDate(): void diff --git a/tests/Views/Components/TimeTest.php b/tests/Views/Components/TimeTest.php index 9a5e2e010..c55bf5913 100644 --- a/tests/Views/Components/TimeTest.php +++ b/tests/Views/Components/TimeTest.php @@ -5,8 +5,8 @@ namespace Gatherling\Tests\Views\Components; use Gatherling\Views\Components\Time; -use Gatherling\Tests\Support\Html; use PHPUnit\Framework\TestCase; +use Safe\DateTimeImmutable; use Symfony\Component\DomCrawler\Crawler; class TimeTest extends TestCase @@ -15,12 +15,12 @@ public function testRender(): void { $tz = new \DateTimeZone('UTC'); - $now = (new \DateTimeImmutable('now', $tz))->getTimestamp(); + $now = (new DateTimeImmutable('now', $tz))->getTimestamp(); $timeComponent = new Time($now, $now); $html = new Crawler($timeComponent->render()); $this->assertEquals("just now", $html->filter('time')->text()); - $specificDate = (new \DateTimeImmutable("2024-02-01 12:00:00", $tz))->getTimestamp(); + $specificDate = (new DateTimeImmutable("2024-02-01 12:00:00", $tz))->getTimestamp(); $oneYearPreviously = $specificDate - 60 * 60 * 24 * 365; $timeComponent = new Time($oneYearPreviously, $specificDate); $expected = '' . "\n"; From bcbce1ee4d81c24f5beb4bc8a94ecdd21c8ce107 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 10:41:27 -0700 Subject: [PATCH 071/114] Convince psalm that interpolateQuery will return a string not an array --- gatherling/Data/Db.php | 1 + gatherling/Exceptions/FileNotFoundException.php | 11 ----------- 2 files changed, 1 insertion(+), 11 deletions(-) delete mode 100644 gatherling/Exceptions/FileNotFoundException.php diff --git a/gatherling/Data/Db.php b/gatherling/Data/Db.php index 136a059a9..905e4db03 100644 --- a/gatherling/Data/Db.php +++ b/gatherling/Data/Db.php @@ -468,6 +468,7 @@ public function interpolateQuery(string $query, array $params): string // "?" or ":foo" inside any of the values. $query = preg_replace(['/\?/', '/(:[a-zA-Z0-9_]+)/'], ["$s?$e", "$s$1$e"], $query); // Replace placeholders with actual values + /** @var string $query */ $query = preg_replace($keys, $values, $query, -1, $count); return $query; } diff --git a/gatherling/Exceptions/FileNotFoundException.php b/gatherling/Exceptions/FileNotFoundException.php deleted file mode 100644 index 18b609718..000000000 --- a/gatherling/Exceptions/FileNotFoundException.php +++ /dev/null @@ -1,11 +0,0 @@ - Date: Thu, 31 Oct 2024 10:41:53 -0700 Subject: [PATCH 072/114] Remove FileNotFoundException We were using this when we were explicitly checking file_get_contents. Now we call the "safe" version that throws when it can't read the file. We lose a bit of fidelity in the error type but our code is simpler. Now we aren't detecting the error condition and throwing we don't need this. --- gatherling/Data/Setup.php | 1 - gatherling/Models/CardSet.php | 1 - 2 files changed, 2 deletions(-) diff --git a/gatherling/Data/Setup.php b/gatherling/Data/Setup.php index 578a213f1..be292231b 100644 --- a/gatherling/Data/Setup.php +++ b/gatherling/Data/Setup.php @@ -6,7 +6,6 @@ use Gatherling\Exceptions\ConfigurationException; use Gatherling\Exceptions\DatabaseException; -use Gatherling\Exceptions\FileNotFoundException; use function Gatherling\Helpers\config; use function Gatherling\Helpers\db; diff --git a/gatherling/Models/CardSet.php b/gatherling/Models/CardSet.php index af6758cd8..740750cbe 100644 --- a/gatherling/Models/CardSet.php +++ b/gatherling/Models/CardSet.php @@ -6,7 +6,6 @@ use stdClass; use mysqli_stmt; -use Gatherling\Exceptions\FileNotFoundException; use function Gatherling\Helpers\db; use function Gatherling\Helpers\normaliseCardName; From cd31f53801c1991a7a2f5fee6067b15657687dc9 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 10:45:04 -0700 Subject: [PATCH 073/114] Simplify setting of prereg default --- gatherling/admincp.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/gatherling/admincp.php b/gatherling/admincp.php index b24da92a2..be4ee6629 100644 --- a/gatherling/admincp.php +++ b/gatherling/admincp.php @@ -49,13 +49,6 @@ function main(): never $newactive = (int) $_POST['isactive']; $newtime = $_POST['hour']; $newday = $_POST['start_day']; - $prereg = 0; - - if (isset($_POST['preregdefault'])) { - $prereg = $_POST['preregdefault']; - } else { - $prereg = 0; - } $series = new Series(''); $newseries = $_POST['seriesname']; @@ -65,7 +58,7 @@ function main(): never $series->active = $newactive; $series->start_time = $newtime . ':00'; $series->start_day = $newday; - $series->prereg_default = (int) $prereg; + $series->prereg_default = post()->int('preregdefault'); $series->save(); } $result = "New series $series->name was created!"; From 073f96871b9d425d798424914ea71b5edada79fb Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 10:56:04 -0700 Subject: [PATCH 074/114] Move yet more stuff out of lib.php We don't want any globally available un-namespaced funcs. Partly because that's just a woolly practice, and partly because psalm can't understand that they actually exist resulting it loads of entries in our psalm baseline. --- composer.json | 3 +- gatherling/Helpers/templates.php | 78 +++++++++++++++++++ .../Views/Components/FormatSettings.php | 2 + gatherling/Views/Pages/EventForm.php | 1 + gatherling/Views/Pages/EventFrame.php | 2 + gatherling/Views/Pages/MatchList.php | 2 + gatherling/Views/Pages/PlayerList.php | 2 + .../Views/Pages/PointsAdjustmentForm.php | 2 + gatherling/lib.php | 68 ---------------- phpstan-baseline.neon | 22 +++--- .../TemplatesTest.php} | 7 +- 11 files changed, 107 insertions(+), 82 deletions(-) create mode 100644 gatherling/Helpers/templates.php rename tests/{LibTest.php => Helpers/TemplatesTest.php} (88%) diff --git a/composer.json b/composer.json index 7b90e204f..1cbb968a3 100644 --- a/composer.json +++ b/composer.json @@ -48,7 +48,8 @@ "files": [ "gatherling/Helpers/cards.php", "gatherling/Helpers/database.php", - "gatherling/Helpers/helpers.php" + "gatherling/Helpers/helpers.php", + "gatherling/Helpers/templates.php" ] } } diff --git a/gatherling/Helpers/templates.php b/gatherling/Helpers/templates.php new file mode 100644 index 000000000..e6d9e2222 --- /dev/null +++ b/gatherling/Helpers/templates.php @@ -0,0 +1,78 @@ + */ +function getObjectVarsCamelCase(object $obj): array +{ + $vars = get_object_vars($obj); + return arrayMapRecursive(fn($key) => is_string($key) ? toCamel($key) : $key, $vars); +} + +// https://stackoverflow.com/a/45440841/375262 +function toCamel(string $string): string +{ + // Convert to ASCII, remove apostrophes, and split into words + $string = iconv('UTF-8', 'ASCII//TRANSLIT', $string); + $string = str_replace("'", "", $string); + $words = preg_split('/[^a-zA-Z0-9]+/', $string); + + // Convert each word to camel case + $camelCase = array_map(function ($word) { + // Split words that are already in camel case + $word = preg_replace('/(?<=\p{Ll})(?=\p{Lu})/u', ' ', $word); + /** @var string $word */ + $word = preg_replace('/(?<=\p{Lu})(?=\p{Lu}\p{Ll})/u', ' ', $word); + $subWords = explode(' ', $word); + + // Lowercase each subword + $subWords = array_map('strtolower', $subWords); + // Capitalize each subword + $subWords = array_map('ucfirst', $subWords); + + return implode('', $subWords); + }, $words); + + // Join words and lowercase the first character + $result = implode('', $camelCase); + $result = lcfirst($result); + return $result; +} + +/** + * @param array $arr + * @return array + */ +function arrayMapRecursive(callable $func, array $arr): array +{ + $result = []; + + foreach ($arr as $key => $value) { + $newKey = $func($key); + + if (is_array($value)) { + $result[$newKey] = arrayMapRecursive($func, $value); + } elseif (is_object($value)) { + $result[$newKey] = getObjectVarsCamelCase($value); + } else { + $result[$newKey] = $value; + } + } + + return $result; +} diff --git a/gatherling/Views/Components/FormatSettings.php b/gatherling/Views/Components/FormatSettings.php index 1afa0d22b..cce3c6024 100644 --- a/gatherling/Views/Components/FormatSettings.php +++ b/gatherling/Views/Components/FormatSettings.php @@ -6,6 +6,8 @@ use Gatherling\Models\Format; +use function Gatherling\Helpers\getObjectVarsCamelCase; + class FormatSettings extends Component { public string $activeFormatName; diff --git a/gatherling/Views/Pages/EventForm.php b/gatherling/Views/Pages/EventForm.php index 05ec53603..374b01c0d 100644 --- a/gatherling/Views/Pages/EventForm.php +++ b/gatherling/Views/Pages/EventForm.php @@ -17,6 +17,7 @@ use Gatherling\Views\Components\SeriesDropMenu; use Gatherling\Views\Components\TimeDropMenu; +use function Gatherling\Helpers\getObjectVarsCamelCase; use function Safe\preg_match; class EventForm extends EventFrame diff --git a/gatherling/Views/Pages/EventFrame.php b/gatherling/Views/Pages/EventFrame.php index b9a7700ca..5987bd49d 100644 --- a/gatherling/Views/Pages/EventFrame.php +++ b/gatherling/Views/Pages/EventFrame.php @@ -8,6 +8,8 @@ // Also handles setting event and title properties. use Gatherling\Models\Event; +use function Gatherling\Helpers\getObjectVarsCamelCase; + abstract class EventFrame extends Page { /** @var array */ diff --git a/gatherling/Views/Pages/MatchList.php b/gatherling/Views/Pages/MatchList.php index b4defad45..bee73f166 100644 --- a/gatherling/Views/Pages/MatchList.php +++ b/gatherling/Views/Pages/MatchList.php @@ -11,6 +11,8 @@ use Gatherling\Views\Components\RoundDropMenu; use Gatherling\Views\Components\PlayerDropMenu; +use function Gatherling\Helpers\getObjectVarsCamelCase; + class MatchList extends EventFrame { /** @var list> */ diff --git a/gatherling/Views/Pages/PlayerList.php b/gatherling/Views/Pages/PlayerList.php index 126cc489b..b3d04131d 100644 --- a/gatherling/Views/Pages/PlayerList.php +++ b/gatherling/Views/Pages/PlayerList.php @@ -16,6 +16,8 @@ use Gatherling\Views\Components\InitialByesDropMenu; use Gatherling\Views\Components\InitialSeedDropMenu; +use function Gatherling\Helpers\getObjectVarsCamelCase; + class PlayerList extends EventFrame { public bool $isActive; diff --git a/gatherling/Views/Pages/PointsAdjustmentForm.php b/gatherling/Views/Pages/PointsAdjustmentForm.php index 997a21fe1..238c7aed0 100644 --- a/gatherling/Views/Pages/PointsAdjustmentForm.php +++ b/gatherling/Views/Pages/PointsAdjustmentForm.php @@ -6,6 +6,8 @@ use Gatherling\Models\Event; +use function Gatherling\Helpers\getObjectVarsCamelCase; + class PointsAdjustmentForm extends EventFrame { /** @var list> */ diff --git a/gatherling/lib.php b/gatherling/lib.php index 50721a74d..8e5e07fa5 100644 --- a/gatherling/lib.php +++ b/gatherling/lib.php @@ -6,11 +6,8 @@ use Gatherling\Models\Player; use function Gatherling\Helpers\config; -use function Safe\iconv; use function Safe\ob_start; use function Safe\php_sapi_name; -use function Safe\preg_replace; -use function Safe\preg_split; require_once 'bootstrap.php'; @@ -91,68 +88,3 @@ function version_tagline(): string // "Gatherling version 1.9.1 (\"It's the United States of Don't Touch That Thing Right in Front of You.\")"; // "Gatherling version 1.9 (\"It's funny 'cause the squirrel got dead\")"; } - -// Our standard template variable naming is camelCase. -// Some of our objects have properties named in snake_case. -// So when we grab the values from an object to pass into -// a template with get_object_vars let's also preserve the -// naming standard by transforming the case. -/** @return array */ -function getObjectVarsCamelCase(object $obj): array -{ - $vars = get_object_vars($obj); - return arrayMapRecursive(fn($key) => is_string($key) ? toCamel($key) : $key, $vars); -} - -// https://stackoverflow.com/a/45440841/375262 -function toCamel(string $string): string -{ - // Convert to ASCII, remove apostrophes, and split into words - $string = iconv('UTF-8', 'ASCII//TRANSLIT', $string); - $string = str_replace("'", "", $string); - $words = preg_split('/[^a-zA-Z0-9]+/', $string); - - // Convert each word to camel case - $camelCase = array_map(function ($word) { - // Split words that are already in camel case - $word = preg_replace('/(?<=\p{Ll})(?=\p{Lu})/u', ' ', $word); - /** @var string $word */ - $word = preg_replace('/(?<=\p{Lu})(?=\p{Lu}\p{Ll})/u', ' ', $word); - $subWords = explode(' ', $word); - - // Lowercase each subword - $subWords = array_map('strtolower', $subWords); - // Capitalize each subword - $subWords = array_map('ucfirst', $subWords); - - return implode('', $subWords); - }, $words); - - // Join words and lowercase the first character - $result = implode('', $camelCase); - $result = lcfirst($result); - return $result; -} - -/** - * @param array $arr - * @return array - */ -function arrayMapRecursive(callable $func, array $arr): array -{ - $result = []; - - foreach ($arr as $key => $value) { - $newKey = $func($key); - - if (is_array($value)) { - $result[$newKey] = arrayMapRecursive($func, $value); - } elseif (is_object($value)) { - $result[$newKey] = getObjectVarsCamelCase($value); - } else { - $result[$newKey] = $value; - } - } - - return $result; -} diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 1f053eb50..78766609c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -20,6 +20,16 @@ parameters: count: 1 path: gatherling/Helpers/cards.php + - + message: "#^Function Gatherling\\\\Helpers\\\\arrayMapRecursive\\(\\) should return array\\ but returns array\\\\.$#" + count: 1 + path: gatherling/Helpers/templates.php + + - + message: "#^Parameter \\#2 \\$arr of function Gatherling\\\\Helpers\\\\arrayMapRecursive expects array\\, array given\\.$#" + count: 1 + path: gatherling/Helpers/templates.php + - message: "#^Cannot access property \\$data on mixed\\.$#" count: 1 @@ -947,7 +957,7 @@ parameters: - message: "#^Cannot cast mixed to int\\.$#" - count: 2 + count: 1 path: gatherling/admincp.php - @@ -1120,16 +1130,6 @@ parameters: count: 1 path: gatherling/formatcp.php - - - message: "#^Function arrayMapRecursive\\(\\) should return array\\ but returns array\\\\.$#" - count: 1 - path: gatherling/lib.php - - - - message: "#^Parameter \\#2 \\$arr of function arrayMapRecursive expects array\\, array given\\.$#" - count: 1 - path: gatherling/lib.php - - message: "#^Property Gatherling\\\\Models\\\\Player\\:\\:\\$emailAddress \\(string\\|null\\) does not accept mixed\\.$#" count: 1 diff --git a/tests/LibTest.php b/tests/Helpers/TemplatesTest.php similarity index 88% rename from tests/LibTest.php rename to tests/Helpers/TemplatesTest.php index 9aecfd012..338cb8b9e 100644 --- a/tests/LibTest.php +++ b/tests/Helpers/TemplatesTest.php @@ -2,14 +2,17 @@ declare(strict_types=1); -namespace Gatherling\Tests; +namespace Gatherling\Tests\Helpers; require_once 'gatherling/lib.php'; use stdClass; use PHPUnit\Framework\TestCase; -final class LibTest extends TestCase +use function Gatherling\Helpers\getObjectVarsCamelCase; +use function Gatherling\Helpers\toCamel; + +final class TemplatesTest extends TestCase { public function testObjectVarsCamelCase(): void { From 3dbcf47adaa6b8f79a8178d148013bd0d6261c1b Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:00:04 -0700 Subject: [PATCH 075/114] Detect restricted sideboard cards (a little) more sanely --- gatherling/Models/Deck.php | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/gatherling/Models/Deck.php b/gatherling/Models/Deck.php index 9f1cc0590..b2f048a12 100644 --- a/gatherling/Models/Deck.php +++ b/gatherling/Models/Deck.php @@ -556,17 +556,7 @@ public function save(): void // Restricted Card list. Only one of these cards is alowed in a deck if ($format->isCardOnRestrictedList($card)) { - $restrictedError = false; - if ($amt > 1) { - $restrictedError = true; - } - foreach ($this->maindeck_cards as $restrictedCard => $mainamt) { - if ($restrictedCard == $card) { - $restrictedError = true; - break; - } - } - if ($restrictedError) { + if ($amt > 1 || array_key_exists($card, $this->maindeck_cards)) { $this->errors[] = "Sideboard card: {$amt} {$card} is on the restricted list. Only one of this card may be in a deck list."; $this->unparsed_side[$card] = ($this->unparsed_side[$card] ?? 0) + $amt; From 8ea59e1fd47242edd80dbce4f020d764b282e979 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:02:24 -0700 Subject: [PATCH 076/114] Find decks that match all criteria more simply --- gatherling/Models/Decksearch.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/gatherling/Models/Decksearch.php b/gatherling/Models/Decksearch.php index eb48a0e02..f639e3922 100644 --- a/gatherling/Models/Decksearch.php +++ b/gatherling/Models/Decksearch.php @@ -60,13 +60,8 @@ public function getFinalResults(): array|false if (count($this->results) <= 0 || count($this->errors) > 0) { return false; } - $array_keys = array_keys($this->results); - $first_key = array_shift($array_keys); - $tmp_results = $this->results[$first_key]; - foreach ($this->results as $key => $value) { - $tmp_results = array_intersect($tmp_results, $this->results[$key]); - } - if (count($tmp_results) == 0) { + $tmp_results = array_intersect(...array_values($this->results)); + if (empty($tmp_results)) { $this->errors[] = 'Your search query did not have any matches'; return false; } From 534897ed542a390a7fcf6518ea77ee562f9e5ad3 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:03:53 -0700 Subject: [PATCH 077/114] Remove unused func --- gatherling/Models/Entry.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/gatherling/Models/Entry.php b/gatherling/Models/Entry.php index 5eca9615d..80e0f833f 100644 --- a/gatherling/Models/Entry.php +++ b/gatherling/Models/Entry.php @@ -124,11 +124,6 @@ public function canDelete(): bool return count($matches) == 0; } - public function dropped(): bool - { - return $this->drop_round > 0; - } - public function canCreateDeck(string $username): bool { $player = new Player($username); From 47020211cfa6dfc4921d5e65b13d8df1d1ea8387 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:04:43 -0700 Subject: [PATCH 078/114] Remove unused func --- gatherling/Models/Series.php | 18 ------------------ phpstan-baseline.neon | 10 +++++----- 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/gatherling/Models/Series.php b/gatherling/Models/Series.php index 64b37db75..fbc539bff 100644 --- a/gatherling/Models/Series.php +++ b/gatherling/Models/Series.php @@ -198,24 +198,6 @@ public function authCheck(string $playername): bool return false; } - /** @return list */ - public function getEvents(): array - { - $db = Database::getConnection(); - $stmt = $db->prepare('SELECT name FROM events WHERE series = ?'); - $stmt->bind_param('s', $this->name); - $stmt->execute(); - $stmt->bind_result($eventname); - - $events = []; - while ($stmt->fetch()) { - $events[] = $eventname; - } - $stmt->close(); - - return $events; - } - /** @return list */ public function getRecentEvents(int $number = 10): array { diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 78766609c..303a03b36 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -647,27 +647,27 @@ parameters: - message: "#^Cannot call method bind_param\\(\\) on mysqli_stmt\\|false\\.$#" - count: 10 + count: 9 path: gatherling/Models/Series.php - message: "#^Cannot call method bind_result\\(\\) on mysqli_stmt\\|false\\.$#" - count: 10 + count: 9 path: gatherling/Models/Series.php - message: "#^Cannot call method close\\(\\) on mysqli_stmt\\|false\\.$#" - count: 8 + count: 7 path: gatherling/Models/Series.php - message: "#^Cannot call method execute\\(\\) on mysqli_stmt\\|false\\.$#" - count: 10 + count: 9 path: gatherling/Models/Series.php - message: "#^Cannot call method fetch\\(\\) on mysqli_stmt\\|false\\.$#" - count: 10 + count: 9 path: gatherling/Models/Series.php - From 88d81a80375cdef39f399acc39bde16ec6225bd3 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:06:19 -0700 Subject: [PATCH 079/114] Remove unused func --- gatherling/Models/Event.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/gatherling/Models/Event.php b/gatherling/Models/Event.php index 8239247ab..a8b07bc56 100644 --- a/gatherling/Models/Event.php +++ b/gatherling/Models/Event.php @@ -458,11 +458,6 @@ public function isHost(string $name): bool return $ishost || $iscohost; } - public function isFinalized(): bool - { - return $this->finalized != 0; - } - public function isOrganizer(string $name): bool { $isOrganizer = false; From 169ff8930fb3e4591719455e1f4f4b815655da4a Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:08:02 -0700 Subject: [PATCH 080/114] Remove two initializations that are always overwritten --- gatherling/Models/Event.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gatherling/Models/Event.php b/gatherling/Models/Event.php index a8b07bc56..831a18489 100644 --- a/gatherling/Models/Event.php +++ b/gatherling/Models/Event.php @@ -849,10 +849,8 @@ public function addMatch(Standings $playera, Standings $playerb, int $round = -9 // Assigns trophies based on the finals matches which are entered. public function assignTropiesFromMatches(): void { - $t8 = []; $t4 = []; - $sec = ''; - $win = ''; + $t8 = []; if ($this->finalrounds > 0) { $quarter_finals = $this->finalrounds >= 3; if ($quarter_finals) { From cb0e986d016dce6de244498d1482cba90655a657 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:14:48 -0700 Subject: [PATCH 081/114] Remove all concept of "Round Robin" structure, there's none in the db --- gatherling/Data/sql/migrations/76.sql | 1 + gatherling/Models/Event.php | 34 +++++---------------------- gatherling/Views/Pages/EventForm.php | 3 --- 3 files changed, 7 insertions(+), 31 deletions(-) create mode 100644 gatherling/Data/sql/migrations/76.sql diff --git a/gatherling/Data/sql/migrations/76.sql b/gatherling/Data/sql/migrations/76.sql new file mode 100644 index 000000000..0c301886e --- /dev/null +++ b/gatherling/Data/sql/migrations/76.sql @@ -0,0 +1 @@ +ALTER TABLE `subevents` MODIFY COLUMN `type` ENUM('Swiss', 'Swiss (Blossom)', 'Single Elimination', 'League', 'League Match') NOT NULL; diff --git a/gatherling/Models/Event.php b/gatherling/Models/Event.php index 831a18489..5f28cc79d 100644 --- a/gatherling/Models/Event.php +++ b/gatherling/Models/Event.php @@ -1151,9 +1151,6 @@ public function pairCurrentRound(bool $skip_invalid = false): void //$this->current_round ++; //$this->save(); break; - case 'Round Robin': - //Do later - break; } db()->releaseLock((string) $subevent_id); @@ -1622,31 +1619,12 @@ public function repairRound(): void public function assignMedals(): void { - if ($this->current_round > $this->mainrounds) { - $structure = $this->finalstruct; - $subevent_id = $this->finalid; - $round = 'final'; - } else { - $structure = $this->mainstruct; - $subevent_id = $this->mainid; - $round = 'main'; - } - - switch ($structure) { - case 'Swiss': - case 'Swiss (Blossom)': - $this->AssignMedalsbyStandings(); - break; - case 'Single Elimination': - $this->assignTropiesFromMatches(); - break; - case 'League': - case 'League Match': - $this->AssignMedalsbyStandings(); - break; - case 'Round Robin': - //Do later - break; + $structure = $this->current_round > $this->mainrounds ? $this->finalstruct : $this->mainstruct; + + if (in_array($structure, ['Swiss', 'Swiss (Blossom)', 'League', 'League Match'])) { + $this->AssignMedalsbyStandings(); + } elseif ($structure === 'Single Elimination') { + $this->assignTropiesFromMatches(); } } diff --git a/gatherling/Views/Pages/EventForm.php b/gatherling/Views/Pages/EventForm.php index 374b01c0d..268e3acf9 100644 --- a/gatherling/Views/Pages/EventForm.php +++ b/gatherling/Views/Pages/EventForm.php @@ -254,9 +254,6 @@ function structDropMenuArgs(string $field, string $def): array if ($def == 'Swiss (Blossom)') { $def = 'Swiss'; } - if ($def == 'Round Robin') { - $names[] = 'Round Robin'; - } $options = []; foreach ($names as $name) { $options[] = [ From 16f8e708eb57f773e5e760bcd030c8c68e87d404 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:15:12 -0700 Subject: [PATCH 082/114] Remove unused vars --- gatherling/Models/Event.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/gatherling/Models/Event.php b/gatherling/Models/Event.php index 5f28cc79d..c30be0caf 100644 --- a/gatherling/Models/Event.php +++ b/gatherling/Models/Event.php @@ -1577,7 +1577,6 @@ public function resetEvent(): void $stmt = $db->prepare('DELETE FROM matches WHERE subevent = ? OR subevent = ?'); $stmt->bind_param('ss', $this->mainid, $this->finalid); $stmt->execute(); - $removed = $stmt->affected_rows > 0; $stmt->close(); $db = Database::getConnection(); @@ -1607,7 +1606,6 @@ public function repairRound(): void $stmt = $db->prepare('DELETE FROM matches WHERE subevent = ? AND round = ?'); $stmt->bind_param('dd', $subevent, $round); $stmt->execute(); - $removed = $stmt->affected_rows > 0; $stmt->close(); $this->current_round--; From eccaedb9950d0409b992521ad75f0957ceb71b79 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:15:17 -0700 Subject: [PATCH 083/114] Don't call set_time_limit until after we've autoloaded in lib In the other order PHP can't see Safe\set_time_limit when we call it and blows up. --- gatherling/admin/db-upgrade.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gatherling/admin/db-upgrade.php b/gatherling/admin/db-upgrade.php index 9b8d71b9c..cd2dfcdb1 100644 --- a/gatherling/admin/db-upgrade.php +++ b/gatherling/admin/db-upgrade.php @@ -8,10 +8,11 @@ use function Gatherling\Helpers\server; use function Safe\set_time_limit; -set_time_limit(0); require_once __DIR__ . '/../lib.php'; +set_time_limit(0); + function main(): never { Setup::setupDatabase(); From e47f82dde5bf1d34036ac428387887e889287c90 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:16:22 -0700 Subject: [PATCH 084/114] Bring phpstan baseline up to date with latest fixes --- phpstan-baseline.neon | 5 ----- 1 file changed, 5 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 303a03b36..0f03c9cd1 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -190,11 +190,6 @@ parameters: count: 1 path: gatherling/Models/Event.php - - - message: "#^Cannot access property \\$affected_rows on mysqli_stmt\\|false\\.$#" - count: 2 - path: gatherling/Models/Event.php - - message: "#^Cannot access property \\$num_rows on mysqli_stmt\\|false\\.$#" count: 1 From f61ce1f95417c5ac42f9b7d214800580b4e5069a Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:18:34 -0700 Subject: [PATCH 085/114] Fix an ancient snafu found by psalm Would probably be safe just not to trim here --- gatherling/Models/Format.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gatherling/Models/Format.php b/gatherling/Models/Format.php index 04008f846..e45ab2380 100644 --- a/gatherling/Models/Format.php +++ b/gatherling/Models/Format.php @@ -174,7 +174,7 @@ public static function constructTribes(string $set = 'All'): void $type = self::removeTypeCrap($type); $types = explode(' ', $type); foreach ($types as $subtype) { - $type = trim($subtype); + $subtype = trim($subtype); if ($subtype == '') { continue; } From 0c6c43b0a69defc8a8c1ef5a2b87b370af699273 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:27:29 -0700 Subject: [PATCH 086/114] Move git_hash to inline and version_tagline to Page (only user) Part of a general strategy to remove all the truly global funcs from lib.php. The stuff that is semiglobal should be explicitly imported not just hanging around everywhere. Nearly done. --- gatherling/Views/Pages/Page.php | 52 +++++++++++++++++++++++++++++++-- gatherling/lib.php | 52 --------------------------------- 2 files changed, 49 insertions(+), 55 deletions(-) diff --git a/gatherling/Views/Pages/Page.php b/gatherling/Views/Pages/Page.php index 615771732..fbb364ece 100644 --- a/gatherling/Views/Pages/Page.php +++ b/gatherling/Views/Pages/Page.php @@ -28,15 +28,15 @@ abstract class Page extends TemplateResponse public function __construct(public string $title) { - $this->gitHash = git_hash(); + $this->gitHash = substr(config()->string('GIT_HASH', ''), 0, 7); $this->cssLink = 'styles/css/stylesheet.css?v=' . rawurlencode($this->gitHash); $this->headerLogoSrc = 'styles/images/header_logo.png'; $this->player = Player::getSessionPlayer() ?? null; $this->isHost = $this->player?->isHost() ?? false; $this->isOrganizer = count($this->player?->organizersSeries() ?? []) > 0; $this->isSuper = $this->player?->isSuper() ?? false; - $this->versionTagline = version_tagline(); - $this->jsLink = 'gatherling.js?v=' . rawurlencode(git_hash()); + $this->versionTagline = $this->versionTagline(); + $this->jsLink = 'gatherling.js?v=' . rawurlencode($this->gitHash); } public function body(): string @@ -44,4 +44,50 @@ public function body(): string $this->contentSafe = $this->render(); return TemplateHelper::render('page', $this); } + + function versionTagline(): string + { + return 'Gatherling version 6.0.3 ("Nothing is so painful to the human mind as a great and sudden change.")'; + // return 'Gatherling version 6.0.2 ("Nixon was normalizing relations with China. I figured that if he could normalize relations, then so could I.")'; + // return 'Gatherling version 6.0.1 ("A guilty system recognizes no innocents.")'; + // return 'Gatherling version 6.0.0 ("A foolish consistency is the hobgoblin of little minds")'; + // return 'Gatherling version 5.2.0 ("I mustache you a question...")'; + // return 'Gatherling version 5.1.0 ("Have no fear of perfection – you’ll never reach it.")'; + // 'Gatherling version 5.0.1 ("No rest. No mercy. No matter what.")'; + // 'Gatherling version 5.0.0 ("Hulk, no! Just for once in your life, don\'t smash!")'; + // 'Gatherling version 4.9.0 ("Where we’re going, we don’t need roads")'; + // 'Gatherling version 4.8.8 ("Fish fingers and custard")'; + // 'Gatherling version 4.8.7 ("Step 7: Steal a bagel.")'; + // 'Gatherling version 4.8.6.1 ("I\'m gonna steal the declaration of independence.")'; + // 'Gatherling version 4.8.6 ("I\'m gonna steal the declaration of independence.")'; + // 'Gatherling version 4.8.5 ("That\'s my secret, Captain: I\'m always angry...")'; + // 'Gatherling version 4.8.4 ("It doesn\'t look like anything to me.")'; + // 'Gatherling version 4.8.3 ("These violent delights have violent ends.")'; + // "Gatherling version 4.8.2 (\"Zagreus taking time apart. / Zagreus fears the hero heart. / Zagreus seeks the final part. / The reward that he is reaping..\")"; + // "Gatherling version 4.8.1 (\"Zagreus at the end of days / Zagreus lies all other ways / Zagreus comes when time's a maze / And all of history is weeping.\")"; + // "Gatherling version 4.8.0 (\"Zagreus sits inside your head / Zagreus lives among the dead / Zagreus sees you in your bed / And eats you when you're sleeping.\")"; + // "Gatherling version 4.7.0 (\"People assume that time is a strict progression of cause to effect, but actually — from a non-linear, non-subjective viewpoint — it's more like a big ball of wibbly-wobbly... timey-wimey... stuff.\")"; + // "Gatherling version 4.5.2 (\"People assume that time is a strict progression of cause to effect, but actually — from a non-linear, non-subjective viewpoint — it's more like a big ball of wibbly-wobbly... timey-wimey... stuff.\")"; + // "Gatherling version 4.0.0 (\"Call me old fashioned, but, if you really wanted peace, couldn't you just STOP FIGHTING?\")"; + // "Gatherling version 3.3.0 (\"Do not offend the Chair Leg of Truth. It is wise and terrible.\")"; + // "Gatherling version 2.1.27PK (\"Please give us a simple answer, so that we don't have to think, because if we think, we might find answers that don't fit the way we want the world to be.\")"; + // "Gatherling version 2.1.26PK (\"The program wasn't designed to alter the past. It was designed to affect the future.\")"; + // "Gatherling version 2.0.6 (\"We stole the Statue of Liberty! ... The small one, from Las Vegas.\")"; + // "Gatherling version 2.0.5 (\"No, that's perfectly normal paranoia. Everyone in the universe gets that.\")"; + // "Gatherling version 2.0.4 (\"This is no time to talk about time. We don't have the time!\")"; + // "Gatherling version 2.0.3 (\"Are you hungry? I haven't eaten since later this afternoon.\")"; + // "Gatherling version 2.0.2 (\"Woah lady, I only speak two languages, English and bad English.\")"; + // "Gatherling version 2.0.1 (\"Use this to defend yourself. It's a powerful weapon.\")"; + // "Gatherling version 2.0.0 (\"I'm here to keep you safe, Sam. I want to help you.\")"; + // "Gatherling version 1.9.9 (\"You'd think they'd never seen a girl and a cat on a broom before\")"; + // "Gatherling version 1.9.8 (\"I'm tellin' you, man, every third blink is slower.\")"; + // "Gatherling version 1.9.7 (\"Try blue, it's the new red!\")"; + // "Gatherling version 1.9.6 (\"Just relax and let your mind go blank. That shouldn't be too hard for you.\")"; + // "Gatherling version 1.9.5 (\"The grade that you receive will be your last, WE SWEAR!\")"; + // "Gatherling version 1.9.4 (\"We're gonna need some more FBI guys, I guess.\")"; + // "Gatherling version 1.9.3 (\"This is the Ocean, silly, we're not the only two in here.\")"; + // "Gatherling version 1.9.2 (\"So now you're the boss. You're the King of Bob.\")"; + // "Gatherling version 1.9.1 (\"It's the United States of Don't Touch That Thing Right in Front of You.\")"; + // "Gatherling version 1.9 (\"It's funny 'cause the squirrel got dead\")"; + } } diff --git a/gatherling/lib.php b/gatherling/lib.php index 8e5e07fa5..666e3d941 100644 --- a/gatherling/lib.php +++ b/gatherling/lib.php @@ -36,55 +36,3 @@ function json_headers(): void header('Access-Control-Allow-Origin: *'); header('HTTP_X_USERNAME: ' . Player::loginName()); } - -function git_hash(): string -{ - $hash = config()->string('GIT_HASH', ''); - return substr($hash, 0, 7); -} - -function version_tagline(): string -{ - return 'Gatherling version 6.0.3 ("Nothing is so painful to the human mind as a great and sudden change.")'; - // return 'Gatherling version 6.0.2 ("Nixon was normalizing relations with China. I figured that if he could normalize relations, then so could I.")'; - // return 'Gatherling version 6.0.1 ("A guilty system recognizes no innocents.")'; - // return 'Gatherling version 6.0.0 ("A foolish consistency is the hobgoblin of little minds")'; - // return 'Gatherling version 5.2.0 ("I mustache you a question...")'; - // return 'Gatherling version 5.1.0 ("Have no fear of perfection – you’ll never reach it.")'; - // 'Gatherling version 5.0.1 ("No rest. No mercy. No matter what.")'; - // 'Gatherling version 5.0.0 ("Hulk, no! Just for once in your life, don\'t smash!")'; - // 'Gatherling version 4.9.0 ("Where we’re going, we don’t need roads")'; - // 'Gatherling version 4.8.8 ("Fish fingers and custard")'; - // 'Gatherling version 4.8.7 ("Step 7: Steal a bagel.")'; - // 'Gatherling version 4.8.6.1 ("I\'m gonna steal the declaration of independence.")'; - // 'Gatherling version 4.8.6 ("I\'m gonna steal the declaration of independence.")'; - // 'Gatherling version 4.8.5 ("That\'s my secret, Captain: I\'m always angry...")'; - // 'Gatherling version 4.8.4 ("It doesn\'t look like anything to me.")'; - // 'Gatherling version 4.8.3 ("These violent delights have violent ends.")'; - // "Gatherling version 4.8.2 (\"Zagreus taking time apart. / Zagreus fears the hero heart. / Zagreus seeks the final part. / The reward that he is reaping..\")"; - // "Gatherling version 4.8.1 (\"Zagreus at the end of days / Zagreus lies all other ways / Zagreus comes when time's a maze / And all of history is weeping.\")"; - // "Gatherling version 4.8.0 (\"Zagreus sits inside your head / Zagreus lives among the dead / Zagreus sees you in your bed / And eats you when you're sleeping.\")"; - // "Gatherling version 4.7.0 (\"People assume that time is a strict progression of cause to effect, but actually — from a non-linear, non-subjective viewpoint — it's more like a big ball of wibbly-wobbly... timey-wimey... stuff.\")"; - // "Gatherling version 4.5.2 (\"People assume that time is a strict progression of cause to effect, but actually — from a non-linear, non-subjective viewpoint — it's more like a big ball of wibbly-wobbly... timey-wimey... stuff.\")"; - // "Gatherling version 4.0.0 (\"Call me old fashioned, but, if you really wanted peace, couldn't you just STOP FIGHTING?\")"; - // "Gatherling version 3.3.0 (\"Do not offend the Chair Leg of Truth. It is wise and terrible.\")"; - // "Gatherling version 2.1.27PK (\"Please give us a simple answer, so that we don't have to think, because if we think, we might find answers that don't fit the way we want the world to be.\")"; - // "Gatherling version 2.1.26PK (\"The program wasn't designed to alter the past. It was designed to affect the future.\")"; - // "Gatherling version 2.0.6 (\"We stole the Statue of Liberty! ... The small one, from Las Vegas.\")"; - // "Gatherling version 2.0.5 (\"No, that's perfectly normal paranoia. Everyone in the universe gets that.\")"; - // "Gatherling version 2.0.4 (\"This is no time to talk about time. We don't have the time!\")"; - // "Gatherling version 2.0.3 (\"Are you hungry? I haven't eaten since later this afternoon.\")"; - // "Gatherling version 2.0.2 (\"Woah lady, I only speak two languages, English and bad English.\")"; - // "Gatherling version 2.0.1 (\"Use this to defend yourself. It's a powerful weapon.\")"; - // "Gatherling version 2.0.0 (\"I'm here to keep you safe, Sam. I want to help you.\")"; - // "Gatherling version 1.9.9 (\"You'd think they'd never seen a girl and a cat on a broom before\")"; - // "Gatherling version 1.9.8 (\"I'm tellin' you, man, every third blink is slower.\")"; - // "Gatherling version 1.9.7 (\"Try blue, it's the new red!\")"; - // "Gatherling version 1.9.6 (\"Just relax and let your mind go blank. That shouldn't be too hard for you.\")"; - // "Gatherling version 1.9.5 (\"The grade that you receive will be your last, WE SWEAR!\")"; - // "Gatherling version 1.9.4 (\"We're gonna need some more FBI guys, I guess.\")"; - // "Gatherling version 1.9.3 (\"This is the Ocean, silly, we're not the only two in here.\")"; - // "Gatherling version 1.9.2 (\"So now you're the boss. You're the King of Bob.\")"; - // "Gatherling version 1.9.1 (\"It's the United States of Don't Touch That Thing Right in Front of You.\")"; - // "Gatherling version 1.9 (\"It's funny 'cause the squirrel got dead\")"; -} From 8ca7751688c605275c13256fadec961ac6b2f6a3 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:30:48 -0700 Subject: [PATCH 087/114] Remove dead code --- gatherling/Models/Format.php | 57 ------------------------------------ 1 file changed, 57 deletions(-) diff --git a/gatherling/Models/Format.php b/gatherling/Models/Format.php index e45ab2380..a5bcb28ed 100644 --- a/gatherling/Models/Format.php +++ b/gatherling/Models/Format.php @@ -258,63 +258,6 @@ private function insertNewFormat(): bool return true; } - public function saveAndDeleteAuthorization(string $playerName): bool - { - // this will be used to determine if the save and delete buttons will appear on the format editor - // there are 3 different format types: system, public, private - - $player = new Player($playerName); // to access isOrganizer and isSuper functions - $authorized = false; - - switch ($this->type) { - case 'System': - // Only supers can save or delete system formats - if ($player->isSuper()) { - $authorized = true; - } - break; - case 'Public': - // Only Series Organizer of the series that created the format - // and Supers can save or delete Public formats - if ($player->isOrganizer($this->series_name) || $player->isSuper()) { - $authorized = true; - } - break; - case 'Private': - // The only difference in access between a public and private format is that private formats can be - // viewed only by the series organizers of the series it belongs to - // the save and delete access is the same - if ($player->isOrganizer($this->series_name) || $player->isSuper()) { - $authorized = true; - } - break; - } - - return $authorized; - } - - public function viewAuthorization(string $playerName): bool - { - // this will be used to determine if a format will appear in the drop down to load in the format filter - // there are 3 different format types: system, public, private - - $player = new Player($playerName); // to access isOrganizer and isSuper functions - - switch ($this->type) { - case 'System': - case 'Public': - return true; // anyone can view a system and public format - case 'Private': - // Only supers and organizers can view private formats - if ($player->isOrganizer($this->series_name) || $player->isSuper()) { - return true; - } - break; - } - - return false; - } - public function save(): bool { if ($this->new) { From e4bd533e8f0c55fc5f5615af38f09fa6c1fd9f4e Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:32:13 -0700 Subject: [PATCH 088/114] Remove pointless variable assignment --- gatherling/Models/Format.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gatherling/Models/Format.php b/gatherling/Models/Format.php index a5bcb28ed..0b69a0dc0 100644 --- a/gatherling/Models/Format.php +++ b/gatherling/Models/Format.php @@ -382,10 +382,10 @@ public function rename(string $oldName = ''): bool public function delete(): bool { - $success = $this->deleteEntireLegallist(); - $success = $this->deleteEntireBanlist(); - $success = $this->deleteEntireRestrictedlist(); - $success = $this->deleteAllLegalSets(); + $this->deleteEntireLegallist(); + $this->deleteEntireBanlist(); + $this->deleteEntireRestrictedlist(); + $this->deleteAllLegalSets(); $db = Database::getConnection(); $stmt = $db->prepare('DELETE FROM formats WHERE name = ? AND series_name = ?'); $stmt->bind_param('ss', $this->name, $this->series_name); From 078de1b0862a309d35d283a4549010f1fec6cbca Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:34:35 -0700 Subject: [PATCH 089/114] Remove dead code, fix return types --- gatherling/Models/Format.php | 24 ------------------------ phpstan-baseline.neon | 5 ----- 2 files changed, 29 deletions(-) diff --git a/gatherling/Models/Format.php b/gatherling/Models/Format.php index 0b69a0dc0..45cdb0703 100644 --- a/gatherling/Models/Format.php +++ b/gatherling/Models/Format.php @@ -396,11 +396,6 @@ public function delete(): bool return $success; } - public function noFormatLoaded(): bool - { - return $this->name == ''; - } - /** @return list */ public function getLegalCardsets(): array { @@ -555,19 +550,6 @@ public function getLegalList(): array ); } - /** @return list */ - public function getTribesAllowed(): array - { - return Database::listResultSingleParam( - 'SELECT name - FROM tribe_bans - WHERE format = ? AND allowed = 1 - ORDER BY name', - 's', - $this->name - ); - } - /** @return list */ public function getRestrictedList(): array { @@ -603,12 +585,6 @@ public function getErrors(): array return $currentErrors; } - /** @return list */ - public function getFormats(): array - { - return db()->strings('SELECT name FROM formats'); - } - /** @return list */ public static function getTribesList(): array { diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 0f03c9cd1..af21cb97c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -395,11 +395,6 @@ parameters: count: 1 path: gatherling/Models/Format.php - - - message: "#^Method Gatherling\\\\Models\\\\Format\\:\\:getTribesAllowed\\(\\) should return list\\ but returns list\\\\.$#" - count: 1 - path: gatherling/Models/Format.php - - message: "#^Method Gatherling\\\\Models\\\\Format\\:\\:getTribesBanned\\(\\) should return list\\ but returns list\\\\.$#" count: 1 From 1970d7a72a6259c4816e937e1bc429396f3be8e3 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:38:43 -0700 Subject: [PATCH 090/114] Kill some unused vars --- gatherling/Models/Format.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/gatherling/Models/Format.php b/gatherling/Models/Format.php index 45cdb0703..e512839e2 100644 --- a/gatherling/Models/Format.php +++ b/gatherling/Models/Format.php @@ -807,7 +807,6 @@ public function getTribe(int $deckID): array $changelingCreatures = []; $restrictedToTribeCreatures = []; $tribesTied = []; - $tribeKey = ''; foreach ($creatures as $card => $amt) { // Begin processing tribe subtypes @@ -862,7 +861,7 @@ public function getTribe(int $deckID): array if (count($tribesTied) > 1) { // Two or more tribes are tied for largest tribe - foreach ($tribesTied as $type => $amt) { + foreach (array_keys($tribesTied) as $type) { // Checking for tribe size in database for tie breaker $sql = 'SELECT COUNT(DISTINCT name) FROM cards WHERE type LIKE :type'; $params = ['type' => '%' . db()->likeEscape($type) . '%']; @@ -911,8 +910,8 @@ public function getTribe(int $deckID): array // so that this changeling feature can be turned on or off. // here we add the changeling numbers to each of the other subtypes if (!$this->pure) { - foreach ($subTypeCount as $Type => $amt) { - $subTypeCount[$Type] += $subTypeChangeling; + foreach (array_keys($subTypeCount) as $type) { + $subTypeCount[$type] += $subTypeChangeling; } } @@ -920,11 +919,11 @@ public function getTribe(int $deckID): array // prevent duplicate adding // here we check to see if the changeling's type is already counted for // if not we add it to the list of types - foreach ($changelingCreatures as $Type => $amt) { - if (array_key_exists($Type, $subTypeCount)) { + foreach ($changelingCreatures as $type => $amt) { + if (array_key_exists($type, $subTypeCount)) { continue; } else { - $subTypeCount[$Type] = $amt; + $subTypeCount[$type] = $amt; } } From 64f2b76a4101f40c5567f68a092eb81a6192cb2f Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:46:26 -0700 Subject: [PATCH 091/114] Kill a bunch of unused code --- gatherling/Models/Format.php | 34 +++++++----------------- gatherling/Models/Matchup.php | 37 -------------------------- gatherling/Models/Player.php | 50 ----------------------------------- phpstan-baseline.neon | 17 +++++------- 4 files changed, 15 insertions(+), 123 deletions(-) diff --git a/gatherling/Models/Format.php b/gatherling/Models/Format.php index e512839e2..3a99a6a44 100644 --- a/gatherling/Models/Format.php +++ b/gatherling/Models/Format.php @@ -951,7 +951,7 @@ public function getTribe(int $deckID): array if (count($tribesTied) > 1) { // Two or more tribes are tied for largest tribe - foreach ($tribesTied as $type => $amt) { + foreach (array_keys($tribesTied) as $type) { // Checking for tribe size in database for tie breaker $sql = 'SELECT COUNT(DISTINCT name) FROM cards WHERE type LIKE :type'; $params = ['type' => '%' . db()->likeEscape($type) . '%']; @@ -1048,18 +1048,15 @@ public function isDeckCommanderLegal(int $deckID): bool { $isLegal = true; $deck = new Deck($deckID); - $commanderColors = []; $commanderCard = self::getCommanderCard($deck); if (is_null($commanderCard)) { $this->error[] = 'Cannot find a Commander in your deck. There must be a Legendary Creature on the sideboard to serve as the Commander.'; - return false; - } else { - $commanderColors = self::getCardColors($commanderCard); } - foreach ($deck->maindeck_cards as $card => $amt) { + $commanderColors = self::getCardColors($commanderCard); + foreach (array_keys($deck->maindeck_cards) as $card) { $colors = self::getCardColors($card); foreach ($colors as $color => $num) { if ($num > 0) { @@ -1093,7 +1090,7 @@ public static function getCardColors(string $card): array public static function getCommanderCard(Deck $deck): ?string { - foreach ($deck->sideboard_cards as $card => $amt) { + foreach (array_keys($deck->sideboard_cards) as $card) { if (self::isCardLegendary($card)) { return $card; } @@ -1403,40 +1400,31 @@ public function deleteEntireBanlist(): bool return $removed; } - public function deleteAllLegalSets(): bool + public function deleteAllLegalSets(): void { $db = Database::getConnection(); $stmt = $db->prepare('DELETE FROM setlegality WHERE format = ?'); $stmt->bind_param('s', $this->name); $stmt->execute(); - $removed = $stmt->affected_rows > 0; $stmt->close(); - - return $removed; } - public function deleteAllBannedTribes(): bool + public function deleteAllBannedTribes(): void { $db = Database::getConnection(); $stmt = $db->prepare('DELETE FROM tribe_bans WHERE format = ?'); $stmt->bind_param('s', $this->name); $stmt->execute(); - $removed = $stmt->affected_rows > 0; $stmt->close(); - - return $removed; } - public function deleteCardFromLegallist(string $cardName): bool + public function deleteCardFromLegallist(string $cardName): void { $db = Database::getConnection(); $stmt = $db->prepare('DELETE FROM bans WHERE format = ? AND card_name = ? AND allowed = 1'); $stmt->bind_param('ss', $this->name, $cardName); $stmt->execute(); - $removed = $stmt->affected_rows > 0; $stmt->close(); - - return $removed; } public function deleteEntireLegallist(): bool @@ -1525,26 +1513,22 @@ public static function getCardNameFromPartialDFC(string $cardname): ?string return Database::singleResultSingleParam('SELECT name FROM cards WHERE name LIKE ?', 's', $cardname . '/%'); } - public function insertNewLegalSet(string $cardsetName): bool + public function insertNewLegalSet(string $cardsetName): void { $db = Database::getConnection(); $stmt = $db->prepare('INSERT INTO setlegality(format, cardset)VALUES(?, ?)'); $stmt->bind_param('ss', $this->name, $cardsetName); $stmt->execute() or exit($stmt->error); $stmt->close(); - - return true; } - public function insertNewSubTypeBan(string $subTypeBanned): bool + public function insertNewSubTypeBan(string $subTypeBanned): void { $db = Database::getConnection(); $stmt = $db->prepare('INSERT INTO subtype_bans(name, format, allowed) VALUES(?, ?, 0)'); $stmt->bind_param('ss', $subTypeBanned, $this->name); $stmt->execute() or exit($stmt->error); $stmt->close(); - - return true; } public function insertNewTribeBan(string $tribeBanned): void diff --git a/gatherling/Models/Matchup.php b/gatherling/Models/Matchup.php index e2bd6892f..113841e8c 100644 --- a/gatherling/Models/Matchup.php +++ b/gatherling/Models/Matchup.php @@ -171,36 +171,6 @@ public function getPlayerWins(string|Player $player): int|null|false return false; } - public function getPlayerResult(string|Player $player): string - { - $playername = $this->toName($player); - if ($this->playerA($playername)) { - if ($this->isBYE()) { - return 'BYE'; - } - if ($this->result == 'A') { - return 'Won'; - } - if ($this->result == 'B') { - return 'Loss'; - } - - return 'Draw'; - } - if ($this->playerB($playername)) { - if ($this->result == 'A') { - return 'Loss'; - } - if ($this->result == 'B') { - return 'Won'; - } - - return 'Draw'; - } - - throw new Exception("Player $playername is not in match {$this->id}"); - } - public function playerDropped(string $player): bool { $entry = new Entry($this->event_id, $player); @@ -553,13 +523,6 @@ public function isDraw(): bool return $this->playera_wins == $this->playerb_wins; } - public function isReportable(): bool - { - $event = $this->getEvent(); - - return $event->player_reportable == 1; - } - public function allowsPlayerReportedDraws(): int { $event = new Event($this->eventname); diff --git a/gatherling/Models/Player.php b/gatherling/Models/Player.php index 1f8bd4aaa..54512082f 100644 --- a/gatherling/Models/Player.php +++ b/gatherling/Models/Player.php @@ -111,33 +111,6 @@ public static function checkPassword(string $username, string $password): bool return $srvpass === $hashpwd; } - public static function getClientIPAddress(): string - { - // this is used with the rememberMe feature to keep players logged in - // Test if it is a shared client - if (!empty($_SERVER['HTTP_CLIENT_IP'])) { - $ip = $_SERVER['HTTP_CLIENT_IP']; - //Is it a proxy address - } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { - $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; - } else { - $ip = $_SERVER['REMOTE_ADDR']; - } - - return $ip; - } - - public static function saveIPAddress(string $ipAddress, string $player): void - { - $ipAddress = ip2long($ipAddress); - $db = Database::getConnection(); - $stmt = $db->prepare('UPDATE players SET ipaddress = ? WHERE name = ?'); - $stmt or exit($db->error); - $stmt->bind_param('ds', $ipAddress, $player); - $stmt->execute(); - $stmt->close(); - } - public static function findByName(string $playerName): ?self { $sanitizedName = self::sanitizeUsername($playerName); @@ -420,29 +393,6 @@ public function isOrganizer(string $seriesName = ''): bool return db()->optionalString($sql, $params) !== null; } - /** @return list */ - public function getHostedEvents(): array - { - $db = Database::getConnection(); - $stmt = $db->prepare('SELECT name FROM events WHERE host = ? OR cohost = ?'); - $stmt->bind_param('ss', $this->name, $this->name); - $stmt->execute(); - $stmt->bind_result($evname); - - $evnames = []; - while ($stmt->fetch()) { - $evnames[] = $evname; - } - $stmt->close(); - - $evs = []; - foreach ($evnames as $evname) { - $evs[] = new Event($evname); - } - - return $evs; - } - public function getHostedEventsCount(): int { $db = Database::getConnection(); diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index af21cb97c..b35cd41d3 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -282,7 +282,7 @@ parameters: - message: "#^Cannot access property \\$affected_rows on mysqli_stmt\\|false\\.$#" - count: 14 + count: 11 path: gatherling/Models/Format.php - @@ -532,27 +532,27 @@ parameters: - message: "#^Cannot call method bind_param\\(\\) on mysqli_stmt\\|false\\.$#" - count: 37 + count: 36 path: gatherling/Models/Player.php - message: "#^Cannot call method bind_result\\(\\) on mysqli_stmt\\|false\\.$#" - count: 32 + count: 31 path: gatherling/Models/Player.php - message: "#^Cannot call method close\\(\\) on mysqli_stmt\\|false\\.$#" - count: 35 + count: 34 path: gatherling/Models/Player.php - message: "#^Cannot call method execute\\(\\) on mysqli_stmt\\|false\\.$#" - count: 36 + count: 35 path: gatherling/Models/Player.php - message: "#^Cannot call method fetch\\(\\) on mysqli_stmt\\|false\\.$#" - count: 32 + count: 31 path: gatherling/Models/Player.php - @@ -560,11 +560,6 @@ parameters: count: 2 path: gatherling/Models/Player.php - - - message: "#^Method Gatherling\\\\Models\\\\Player\\:\\:getClientIPAddress\\(\\) should return string but returns mixed\\.$#" - count: 1 - path: gatherling/Models/Player.php - - message: "#^Parameter \\#1 \\$string1 of function strcasecmp expects string, string\\|null given\\.$#" count: 4 From 8247c554d6f30a9e06ed31a76ac93a8425136e72 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:53:38 -0700 Subject: [PATCH 092/114] Remove unused func --- gatherling/Models/Player.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/gatherling/Models/Player.php b/gatherling/Models/Player.php index 54512082f..6436078a1 100644 --- a/gatherling/Models/Player.php +++ b/gatherling/Models/Player.php @@ -279,11 +279,6 @@ public function save(): void db()->execute($sql, $params); } - public function getIPAddresss(): ?string - { - return $this->ipAddress; - } - public function emailIsPublic(): bool { return (bool) $this->emailPrivacy; From fd6dcb318544793e178a1c6a819490361058c55c Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:54:01 -0700 Subject: [PATCH 093/114] Convert to void funcs where we don't use the return value --- gatherling/Models/Ratings.php | 5 +---- gatherling/Models/Series.php | 9 ++------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/gatherling/Models/Ratings.php b/gatherling/Models/Ratings.php index 69ab1f152..4be11a8e8 100644 --- a/gatherling/Models/Ratings.php +++ b/gatherling/Models/Ratings.php @@ -42,7 +42,7 @@ public function deleteAllRatings(): void $db->query('DELETE FROM ratings') or exit($db->error); } - public function deleteRatingByFormat(string $format): bool + public function deleteRatingByFormat(string $format): void { $db = Database::getConnection(); $stmt = $db->prepare('Delete FROM ratings WHERE format = ?'); @@ -51,10 +51,7 @@ public function deleteRatingByFormat(string $format): bool } $stmt->bind_param('s', $format); $stmt->execute(); - $removed = $stmt->affected_rows > 0; $stmt->close(); - - return $removed; } public function calcAllRatings(): void diff --git a/gatherling/Models/Series.php b/gatherling/Models/Series.php index fbc539bff..21d13089f 100644 --- a/gatherling/Models/Series.php +++ b/gatherling/Models/Series.php @@ -388,11 +388,8 @@ public function getSeasonRules(int $season_number): array return $season_rules; } - /** - * @param array $new_rules - * @return array - */ - public function setSeasonRules(int $season_number, array $new_rules): array + /** @param array $new_rules */ + public function setSeasonRules(int $season_number, array $new_rules): void { $db = Database::getConnection(); $stmt = $db->prepare('INSERT INTO series_seasons(series, season, first_pts, second_pts, semi_pts, quarter_pts, @@ -441,8 +438,6 @@ public function setSeasonRules(int $season_number, array $new_rules): array ); $stmt->execute(); $stmt->close(); - - return $new_rules; } // SCORE HELPER FUNCTIONS: From 521f98057937728544e62118146e1ade7c653a17 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:54:23 -0700 Subject: [PATCH 094/114] Remove unused vars --- gatherling/Models/Series.php | 2 +- gatherling/Models/SetScraper.php | 8 +------- gatherling/Models/Standings.php | 2 -- gatherling/Views/Components/EditSet.php | 2 -- 4 files changed, 2 insertions(+), 12 deletions(-) diff --git a/gatherling/Models/Series.php b/gatherling/Models/Series.php index 21d13089f..251854cae 100644 --- a/gatherling/Models/Series.php +++ b/gatherling/Models/Series.php @@ -831,7 +831,7 @@ public function seasonPointsTable(int $season_number): array // Make totals foreach ($total_pointarray as $player => $eventarray) { $total_pointarray[$player]['.total'] = 0; - foreach ($eventarray as $event => $points) { + foreach (array_values($eventarray) as $points) { if (is_array($points)) { if (is_int($points['points'])) { $total_pointarray[$player]['.total'] += $points['points']; diff --git a/gatherling/Models/SetScraper.php b/gatherling/Models/SetScraper.php index 3c6c2fa46..ae7bf755b 100644 --- a/gatherling/Models/SetScraper.php +++ b/gatherling/Models/SetScraper.php @@ -35,13 +35,7 @@ public static function getSetList(): array $knowncodes = db()->strings('SELECT code FROM cardsets WHERE code IS NOT NULL'); // Turn this into a dict for faster lookup - $knowncodesDict = []; - foreach ($knowncodes as $k => $v) { - // Some codes are NULL in the db, including for Alara Reborn, Ninth Edition and others. I'm not solving that now. - if ($v) { - $knowncodesDict[$v] = true; - } - } + $knowncodesDict = array_fill_keys($knowncodes, true); $unknownSets = array_filter($sets, function ($set) use ($knowncodesDict) { return !isset($knowncodesDict[$set->code]); diff --git a/gatherling/Models/Standings.php b/gatherling/Models/Standings.php index 3a74a0dfc..16d3af3ca 100644 --- a/gatherling/Models/Standings.php +++ b/gatherling/Models/Standings.php @@ -276,8 +276,6 @@ public function getOpponents(string $eventname, int $subevent, int $round): arra public function getAvailableLeagueOpponents(int $subevent, int $round, int $league_length): array { $opponentsAlreadyFaced = []; - $allPlayers = []; - $opponent_names = []; if ($round == '0') { return []; diff --git a/gatherling/Views/Components/EditSet.php b/gatherling/Views/Components/EditSet.php index bfa4012f8..fcdbb7ba6 100644 --- a/gatherling/Views/Components/EditSet.php +++ b/gatherling/Views/Components/EditSet.php @@ -20,8 +20,6 @@ class EditSet extends Component public function __construct(public string $cardSetName) { - $names = []; - $sql = ' SELECT `code`, `released`, `standard_legal`, `modern_legal` From e96f218cb1f03ad695b42f547a029eb831cc39af Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 11:54:31 -0700 Subject: [PATCH 095/114] Don't use string names of funcs as lambdas now that fn exists --- gatherling/Views/Components/SeasonStandings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gatherling/Views/Components/SeasonStandings.php b/gatherling/Views/Components/SeasonStandings.php index c61eb7f7b..c7c872bf8 100644 --- a/gatherling/Views/Components/SeasonStandings.php +++ b/gatherling/Views/Components/SeasonStandings.php @@ -24,7 +24,7 @@ public function __construct(Series $series, int $season) $seasonEventNames = $series->getSeasonEventNames($season); $points = $series->seasonPointsTable($season); $cutoff = $series->getSeasonCutoff($season); - uasort($points, [self::class, 'reverseTotalSort']); + uasort($points, fn($a, $b) => self::reverseTotalSort($a, $b)); $seasonEvents = []; foreach ($seasonEventNames as $eventName) { From 4a3fa5c128d2f3a3154abe80c1bac4d6ce4e178c Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 12:12:59 -0700 Subject: [PATCH 096/114] Reorganize code to be nearer to usage and less global --- gatherling/Views/Components/Time.php | 35 ++++++++++++++++++++++++- gatherling/lib.php | 2 -- gatherling/util/time.php | 38 --------------------------- tests/Util/TimeTest.php | 39 ---------------------------- tests/Views/Components/TimeTest.php | 24 +++++++++++++++++ 5 files changed, 58 insertions(+), 80 deletions(-) delete mode 100644 gatherling/util/time.php delete mode 100644 tests/Util/TimeTest.php diff --git a/gatherling/Views/Components/Time.php b/gatherling/Views/Components/Time.php index 2800e4c00..c9f8db164 100644 --- a/gatherling/Views/Components/Time.php +++ b/gatherling/Views/Components/Time.php @@ -12,6 +12,39 @@ class Time extends Component public function __construct(int $time, int $now, public bool $long = false) { $this->datetime = date('c', $time); - $this->text = human_date($time, $now); + $this->text = $this->humanDate($time, $now); + } + + private function humanDate(int $datetime, int $now): string + { + $elapsed = abs($now - $datetime); + if ($elapsed == 0) { + return 'just now'; + } + if ($elapsed > 60 * 60 * 24 * 365) { + return date('M Y', $datetime); + } + if ($elapsed > 60 * 60 * 24 * 28) { + return date('M jS', $datetime); + } + $suffix = $datetime > $now ? 'from now' : 'ago'; + $INTERVALS = [ + 'week' => 60 * 60 * 24 * 7, + 'day' => 60 * 60 * 24, + 'hour' => 60 * 60, + 'minute' => 60, + 'second' => 1, + ]; + foreach ($INTERVALS as $interval => $duration) { + if ($elapsed >= $duration) { + return $this->pluralize(intdiv($elapsed, $duration), $interval) . " $suffix"; + } + } + return 'unknown'; + } + + private function pluralize(int $n, string $noun): string + { + return $n . ' ' . $noun . ($n != 1 ? 's' : ''); } } diff --git a/gatherling/lib.php b/gatherling/lib.php index 666e3d941..b3b205415 100644 --- a/gatherling/lib.php +++ b/gatherling/lib.php @@ -22,8 +22,6 @@ date_default_timezone_set('US/Eastern'); // force time functions to use US/Eastern time -require_once 'util/time.php'; - const MTGO = 1; const MTGA = 2; const PAPER = 3; diff --git a/gatherling/util/time.php b/gatherling/util/time.php deleted file mode 100644 index 7171cd6bc..000000000 --- a/gatherling/util/time.php +++ /dev/null @@ -1,38 +0,0 @@ - 60 * 60 * 24 * 365) { - return date('M Y', $datetime); - } - if ($elapsed > 60 * 60 * 24 * 28) { - return date('M jS', $datetime); - } - $suffix = $datetime > $now ? 'from now' : 'ago'; - $INTERVALS = [ - 'week' => 60 * 60 * 24 * 7, - 'day' => 60 * 60 * 24, - 'hour' => 60 * 60, - 'minute' => 60, - 'second' => 1, - ]; - foreach ($INTERVALS as $interval => $duration) { - if ($elapsed >= $duration) { - return pluralize(intdiv($elapsed, $duration), $interval) . " $suffix"; - } - } - return 'unknown'; -} - -function pluralize(int $n, string $noun): string -{ - return $n . ' ' . $noun . ($n != 1 ? 's' : ''); -} diff --git a/tests/Util/TimeTest.php b/tests/Util/TimeTest.php deleted file mode 100644 index dff84ff41..000000000 --- a/tests/Util/TimeTest.php +++ /dev/null @@ -1,39 +0,0 @@ -assertEquals('just now', human_date($now, $now)); - $recently = strtotime('2024-08-29T11:30:00-07:00'); - $this->assertEquals('30 minutes ago', human_date($recently, $now)); - $soon = strtotime('2024-08-29T12:15:00-07:00'); - $this->assertEquals('15 minutes from now', human_date($soon, $now)); - $yesterday = strtotime('2024-08-28T09:30:00-07:00'); - $this->assertEquals('1 day ago', human_date($yesterday, $now)); - $lastMonth = strtotime('2024-07-28T09:30:00-07:00'); - $this->assertEquals('Jul 28th', human_date($lastMonth, $now)); - $aFewWeeks = strtotime('2024-08-02T09:30:00-07:00'); - $this->assertEquals('3 weeks ago', human_date($aFewWeeks, $now)); - $longAgo = strtotime('2023-11-01T09:30:00-07:00'); - $this->assertEquals('Nov 1st', human_date($longAgo, $now)); - $nextMonth = strtotime('2024-09-30T09:30:00-07:00'); - $this->assertEquals('Sep 30th', human_date($nextMonth, $now)); - $farFuture = strtotime('2026-08-28T09:30:00-07:00'); - $this->assertEquals('Aug 2026', human_date($farFuture, $now)); - // New York time is Gatherling's "home" time. - $differsNewYorkAndLosAngeles = strtotime('2024-06-13T23:30:00-07:00'); - $this->assertEquals('Jun 14th', human_date($differsNewYorkAndLosAngeles, $now)); - } -} diff --git a/tests/Views/Components/TimeTest.php b/tests/Views/Components/TimeTest.php index c55bf5913..83639f208 100644 --- a/tests/Views/Components/TimeTest.php +++ b/tests/Views/Components/TimeTest.php @@ -9,6 +9,8 @@ use Safe\DateTimeImmutable; use Symfony\Component\DomCrawler\Crawler; +use function Safe\strtotime; + class TimeTest extends TestCase { public function testRender(): void @@ -26,5 +28,27 @@ public function testRender(): void $expected = '' . "\n"; $actual = $timeComponent->render(); $this->assertEquals($expected, $actual); + + $now = strtotime('2024-08-29T12:00:00-07:00'); + $this->assertStringContainsString('just now', (new Time($now, $now))->render()); + $recently = strtotime('2024-08-29T11:30:00-07:00'); + $this->assertStringContainsString('30 minutes ago', (new Time($recently, $now))->render()); + $soon = strtotime('2024-08-29T12:15:00-07:00'); + $this->assertStringContainsString('15 minutes from now', (new Time($soon, $now))->render()); + $yesterday = strtotime('2024-08-28T09:30:00-07:00'); + $this->assertStringContainsString('1 day ago', (new Time($yesterday, $now))->render()); + $lastMonth = strtotime('2024-07-28T09:30:00-07:00'); + $this->assertStringContainsString('Jul 28th', (new Time($lastMonth, $now))->render()); + $aFewWeeks = strtotime('2024-08-02T09:30:00-07:00'); + $this->assertStringContainsString('3 weeks ago', (new Time($aFewWeeks, $now))->render()); + $longAgo = strtotime('2023-11-01T09:30:00-07:00'); + $this->assertStringContainsString('Nov 1st', (new Time($longAgo, $now))->render()); + $nextMonth = strtotime('2024-09-30T09:30:00-07:00'); + $this->assertStringContainsString('Sep 30th', (new Time($nextMonth, $now))->render()); + $farFuture = strtotime('2026-08-28T09:30:00-07:00'); + $this->assertStringContainsString('Aug 2026', (new Time($farFuture, $now))->render()); + // New York time is Gatherling's "home" time. + $differsNewYorkAndLosAngeles = strtotime('2024-06-13T23:30:00-07:00'); + $this->assertStringContainsString('Jun 14th', (new Time($differsNewYorkAndLosAngeles, $now))->render()); } } From af21c9592ea50e7fc7fe16afd720f93a8f836e75 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 12:19:59 -0700 Subject: [PATCH 097/114] Get more specific about types in pairing weights --- gatherling/Models/Event.php | 2 +- gatherling/Models/Pairings.php | 10 +++++----- phpstan-baseline.neon | 10 ---------- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/gatherling/Models/Event.php b/gatherling/Models/Event.php index c30be0caf..90fb3f364 100644 --- a/gatherling/Models/Event.php +++ b/gatherling/Models/Event.php @@ -1215,7 +1215,7 @@ public function swissPairingBlossom(int $subevent_id, bool $skip_invalid): void $activePlayers[$i]['paired'] = false; } - $pairings = new Pairings($activePlayers, $bye_data); + $pairings = new Pairings(array_values($activePlayers), $bye_data); $pairing = $pairings->pairing; if ($bye_data) { array_push($activePlayers, $bye_data); diff --git a/gatherling/Models/Pairings.php b/gatherling/Models/Pairings.php index 22c1e547e..acd5dc63a 100644 --- a/gatherling/Models/Pairings.php +++ b/gatherling/Models/Pairings.php @@ -15,8 +15,8 @@ class Pairings public array $pairing = []; /** - * @param array}> $players - * @param ?array{player: string, score: int, opponents: array} $bye_data + * @param list, paired: bool}> $players + * @param ?array{player: string, score: int, opponents: array, paired: bool} $bye_data */ public function __construct(array $players, ?array $bye_data) { @@ -50,7 +50,7 @@ public function __construct(array $players, ?array $bye_data) } /** - * @param array>> $players + * @param list, paired: bool}> $players * @return list */ public function weights(array $players): array @@ -70,8 +70,8 @@ public function weights(array $players): array } /** - * @param array{score: int, player: string, opponents: array} $player1 - * @param array{score: int, player: string, opponents: array} $player2 + * @param array{score: int, player: string, opponents: array, paired: bool} $player1 + * @param array{score: int, player: string, opponents: array, paired: bool} $player2 */ public function weight(int $highest_points, array $player1, array $player2): int { diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index b35cd41d3..118f7be8c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -515,16 +515,6 @@ parameters: count: 2 path: gatherling/Models/Matchup.php - - - message: "#^Parameter \\#2 \\$player1 of method Gatherling\\\\Models\\\\Pairings\\:\\:weight\\(\\) expects array\\{score\\: int, player\\: string, opponents\\: array\\\\}, array\\\\|int\\|string\\> given\\.$#" - count: 1 - path: gatherling/Models/Pairings.php - - - - message: "#^Parameter \\#3 \\$player2 of method Gatherling\\\\Models\\\\Pairings\\:\\:weight\\(\\) expects array\\{score\\: int, player\\: string, opponents\\: array\\\\}, array\\\\|int\\|string\\> given\\.$#" - count: 1 - path: gatherling/Models/Pairings.php - - message: "#^Cannot access property \\$num_rows on mysqli_stmt\\|false\\.$#" count: 1 From 7dc126b2ab5057d1907a9961a55c68349eaa31e5 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 12:46:48 -0700 Subject: [PATCH 098/114] Don't check for null if it can't be null, live up to type promises --- gatherling/Models/Event.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/gatherling/Models/Event.php b/gatherling/Models/Event.php index 90fb3f364..fa553f255 100644 --- a/gatherling/Models/Event.php +++ b/gatherling/Models/Event.php @@ -1291,11 +1291,9 @@ private function getActiveOpponents(string $playername, int $subevent): array $standing = new Standings($this->name, $playername); $opponents = $standing->getOpponents($this->name, $subevent, 1); - if ($opponents != null) { - foreach ($opponents as $opponent) { - if ($opponent->active === 1) { - $list_opponents[] = $opponent->player; - } + foreach ($opponents as $opponent) { + if ($opponent->active === 1 && $opponent->player !== null) { + $list_opponents[] = $opponent->player; } } From 254bb4cd54e454f6fd0f85b2ddc65dd4d61c6c14 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 12:47:16 -0700 Subject: [PATCH 099/114] Migrate game/platform constants to where they are used It would be nice to make this a backed enum and use it instead of ints everywhere (even disallowing ints) but for now we get it out of lib.php. --- gatherling/Views/Components/GameName.php | 12 ++++++++---- gatherling/lib.php | 4 ---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gatherling/Views/Components/GameName.php b/gatherling/Views/Components/GameName.php index 7f080bedc..bcc075cd9 100644 --- a/gatherling/Views/Components/GameName.php +++ b/gatherling/Views/Components/GameName.php @@ -8,21 +8,25 @@ class GameName extends Component { + const MTGO = 1; + const MTGA = 2; + const PAPER = 3; + public string $name; public ?string $iconClass; - public function __construct(Player $player, string|int|null $game = 'gatherling', bool $html = true) + public function __construct(Player $player, int|string|null $game = 'gatherling', bool $html = true) { $iconClass = null; $name = $player->name ?? ''; if ($html) { - if ($game == MTGO && !empty($player->mtgo_username)) { + if ($game == self::MTGO && !empty($player->mtgo_username)) { $iconClass = 'ss ss-pmodo'; $name = $player->mtgo_username; - } elseif ($game == MTGA && !empty($player->mtga_username)) { + } elseif ($game == self::MTGA && !empty($player->mtga_username)) { $iconClass = 'ss ss-parl3'; $name = $player->mtga_username; - } elseif (($game == PAPER || $game == 'discord') && !empty($player->discord_handle)) { + } elseif (($game == self::PAPER || $game == 'discord') && !empty($player->discord_handle)) { $iconClass = 'fab fa-discord'; $name = $player->discord_handle; } elseif ($game == 'gatherling') { diff --git a/gatherling/lib.php b/gatherling/lib.php index b3b205415..e96c1ae88 100644 --- a/gatherling/lib.php +++ b/gatherling/lib.php @@ -22,10 +22,6 @@ date_default_timezone_set('US/Eastern'); // force time functions to use US/Eastern time -const MTGO = 1; -const MTGA = 2; -const PAPER = 3; - function json_headers(): void { header('Content-type: application/json'); From 6e4a7ed4434f24b1e24eca3641e1a23c0a326495 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 12:48:19 -0700 Subject: [PATCH 100/114] Type fixes --- gatherling/Views/Components/Preregistration.php | 2 +- gatherling/Views/Pages/ReportsForm.php | 7 +++---- gatherling/lib.php | 1 - phpstan-baseline.neon | 15 --------------- 4 files changed, 4 insertions(+), 21 deletions(-) diff --git a/gatherling/Views/Components/Preregistration.php b/gatherling/Views/Components/Preregistration.php index fc2540f18..c95c38d1f 100644 --- a/gatherling/Views/Components/Preregistration.php +++ b/gatherling/Views/Components/Preregistration.php @@ -16,7 +16,7 @@ class Preregistration extends Component public bool $hasUpcomingEvents = false; /** @var array */ public array $upcomingEvents = []; - /** @var array */ + /** @var array */ public array $availableEvents = []; public bool $promptToLinkMtgo = false; public bool $promptToLinkMtga = false; diff --git a/gatherling/Views/Pages/ReportsForm.php b/gatherling/Views/Pages/ReportsForm.php index 3af6c309a..12aa14ce6 100644 --- a/gatherling/Views/Pages/ReportsForm.php +++ b/gatherling/Views/Pages/ReportsForm.php @@ -10,9 +10,9 @@ class ReportsForm extends EventFrame { public bool $hasEntries; - /** @var list> */ + /** @var array */ public array $standings; - /** @var list> */ + /** @var array */ public array $registrants; public function __construct(Event $event) @@ -30,11 +30,10 @@ public function __construct(Event $event) $result[] = [ 'n' => $count, 'entryName' => $entryName, - 'emailAd' => $player->emailAddress != '' ? $player->emailAddress : '---------', + 'emailAd' => $player->emailAddress ? $player->emailAddress : '---------', ]; $count++; } - return $result; }; diff --git a/gatherling/lib.php b/gatherling/lib.php index e96c1ae88..907b7bbfd 100644 --- a/gatherling/lib.php +++ b/gatherling/lib.php @@ -5,7 +5,6 @@ use Gatherling\Auth\Session; use Gatherling\Models\Player; -use function Gatherling\Helpers\config; use function Safe\ob_start; use function Safe\php_sapi_name; diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 118f7be8c..288c304e3 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -225,11 +225,6 @@ parameters: count: 1 path: gatherling/Models/Event.php - - - message: "#^Method Gatherling\\\\Models\\\\Event\\:\\:getActiveOpponents\\(\\) should return list\\ but returns list\\\\.$#" - count: 1 - path: gatherling/Models/Event.php - - message: "#^Method Gatherling\\\\Models\\\\Event\\:\\:getPlayerCount\\(\\) should return int but returns mixed\\.$#" count: 1 @@ -875,16 +870,6 @@ parameters: count: 1 path: gatherling/Views/Pages/Profile.php - - - message: "#^Property Gatherling\\\\Views\\\\Pages\\\\ReportsForm\\:\\:\\$registrants \\(list\\\\>\\) does not accept list\\, entryName\\: mixed, emailAd\\: string\\|null\\}\\>\\.$#" - count: 1 - path: gatherling/Views/Pages/ReportsForm.php - - - - message: "#^Property Gatherling\\\\Views\\\\Pages\\\\ReportsForm\\:\\:\\$standings \\(list\\\\>\\) does not accept list\\, entryName\\: mixed, emailAd\\: string\\|null\\}\\>\\.$#" - count: 1 - path: gatherling/Views/Pages/ReportsForm.php - - message: "#^Parameter \\#1 \\$datetime of function Safe\\\\strtotime expects string, string\\|null given\\.$#" count: 1 From beb20e73636e06517a48382c76530fb9b8b2adb6 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 12:51:44 -0700 Subject: [PATCH 101/114] Type fixes --- gatherling/Views/Pages/Series.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gatherling/Views/Pages/Series.php b/gatherling/Views/Pages/Series.php index b9385c0fb..48f69c2c8 100644 --- a/gatherling/Views/Pages/Series.php +++ b/gatherling/Views/Pages/Series.php @@ -12,7 +12,7 @@ class Series extends Page { - /** @var array */ + /** @var array */ public array $activeSeries; /** @param list $activeSeriesNames */ From ec94d2985b111b78bb517b20ec588ab315415bce Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 12:51:52 -0700 Subject: [PATCH 102/114] Don't pass unused var --- gatherling/formatcp.php | 4 ++-- phpstan-baseline.neon | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/gatherling/formatcp.php b/gatherling/formatcp.php index bd3f27119..8ecbd4939 100644 --- a/gatherling/formatcp.php +++ b/gatherling/formatcp.php @@ -150,7 +150,7 @@ function handleAction(string $seriesName): Component return renameFormat($seriesName, post()->string('newformat'), post()->string('format')); } if ($_POST['action'] == 'Delete') { - return deleteForm($seriesName, post()->string('format')); + return deleteForm($seriesName); } if ($_POST['action'] == 'Delete Format') { return deleteFormat(post()->string('format')); @@ -480,7 +480,7 @@ function renameFormat(string $seriesName, string $newFormatName, string $formatN return new FormatError("Format {$formatName} Could Not Be Renamed :-(", $formatName); } -function deleteForm(string $seriesName, string $formatName): Component +function deleteForm(string $seriesName): Component { return new FormatDeleteForm($seriesName); } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 288c304e3..458164bf1 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -875,11 +875,6 @@ parameters: count: 1 path: gatherling/Views/Pages/Series.php - - - message: "#^Property Gatherling\\\\Views\\\\Pages\\\\Series\\:\\:\\$activeSeries \\(array\\\\) does not accept array\\\\.$#" - count: 1 - path: gatherling/Views/Pages/Series.php - - message: "#^Cannot access property \\$deck on Gatherling\\\\Models\\\\Entry\\|null\\.$#" count: 1 From b605c4fdab0f4102115b5d56c27d564a9893753b Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 12:53:01 -0700 Subject: [PATCH 103/114] Update psalm baseline after fixing a bunch of stuff --- psalm-baseline.xml | 223 --------------------------------------------- 1 file changed, 223 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index ba515e966..39c263316 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,137 +1,8 @@ - - - - - - - - name)]]> - - - - - - - - - - - - - - - - - - - - - - - - - ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ]]> - - - - - - - - - - - - - - @@ -139,91 +10,6 @@ - - - - - - - - - - - - - - - - - - - - availableEvents]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - activeSeries]]> - - - - - - - @@ -248,9 +34,6 @@ - - - @@ -260,12 +43,6 @@ - - - - - - From 25398be6e36c24fab36cc4874a1dbb2493a2dbcf Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 12:54:15 -0700 Subject: [PATCH 104/114] Fix visibility --- gatherling/Views/Components/GameName.php | 6 +++--- gatherling/Views/Pages/Page.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gatherling/Views/Components/GameName.php b/gatherling/Views/Components/GameName.php index bcc075cd9..af4266ce5 100644 --- a/gatherling/Views/Components/GameName.php +++ b/gatherling/Views/Components/GameName.php @@ -8,9 +8,9 @@ class GameName extends Component { - const MTGO = 1; - const MTGA = 2; - const PAPER = 3; + private const MTGO = 1; + private const MTGA = 2; + private const PAPER = 3; public string $name; public ?string $iconClass; diff --git a/gatherling/Views/Pages/Page.php b/gatherling/Views/Pages/Page.php index fbb364ece..f7d2a16de 100644 --- a/gatherling/Views/Pages/Page.php +++ b/gatherling/Views/Pages/Page.php @@ -45,7 +45,7 @@ public function body(): string return TemplateHelper::render('page', $this); } - function versionTagline(): string + private function versionTagline(): string { return 'Gatherling version 6.0.3 ("Nothing is so painful to the human mind as a great and sudden change.")'; // return 'Gatherling version 6.0.2 ("Nixon was normalizing relations with China. I figured that if he could normalize relations, then so could I.")'; From 93bf5fe3918aca62537f7c73fbf2b16609cb6017 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 12:54:33 -0700 Subject: [PATCH 105/114] Lint --- gatherling/Views/Components/Prereg.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gatherling/Views/Components/Prereg.php b/gatherling/Views/Components/Prereg.php index b77580414..d29202821 100644 --- a/gatherling/Views/Components/Prereg.php +++ b/gatherling/Views/Components/Prereg.php @@ -36,6 +36,6 @@ public function __construct(public Event $event) $this->showRegister = true; $this->registerLink = 'prereg.php?action=reg&event=' . rawurlencode($event->name); } - $this->start = $event->start ? new Time(strtotime($event->start), time(), true): null; + $this->start = $event->start ? new Time(strtotime($event->start), time(), true) : null; } } From 3a6e6a03155c48bdbabe4247a104bb0c5f495eca Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 13:00:40 -0700 Subject: [PATCH 106/114] Update all libraries trying to fix a psalm issue (didn't help) --- composer.lock | 134 +++++++++++++++++++++++++------------------------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/composer.lock b/composer.lock index 66ae57d1d..3942d6382 100644 --- a/composer.lock +++ b/composer.lock @@ -1508,16 +1508,16 @@ }, { "name": "symfony/css-selector", - "version": "v6.4.8", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "4b61b02fe15db48e3687ce1c45ea385d1780fe08" + "reference": "cb23e97813c5837a041b73a6d63a9ddff0778f5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/4b61b02fe15db48e3687ce1c45ea385d1780fe08", - "reference": "4b61b02fe15db48e3687ce1c45ea385d1780fe08", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/cb23e97813c5837a041b73a6d63a9ddff0778f5e", + "reference": "cb23e97813c5837a041b73a6d63a9ddff0778f5e", "shasum": "" }, "require": { @@ -1553,7 +1553,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.4.8" + "source": "https://github.com/symfony/css-selector/tree/v6.4.13" }, "funding": [ { @@ -1569,7 +1569,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1640,16 +1640,16 @@ }, { "name": "symfony/dom-crawler", - "version": "v6.4.12", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "9d307ecbcb917001692be333cdc58f474fdb37f0" + "reference": "ae074dffb018c37a57071990d16e6152728dd972" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/9d307ecbcb917001692be333cdc58f474fdb37f0", - "reference": "9d307ecbcb917001692be333cdc58f474fdb37f0", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/ae074dffb018c37a57071990d16e6152728dd972", + "reference": "ae074dffb018c37a57071990d16e6152728dd972", "shasum": "" }, "require": { @@ -1687,7 +1687,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v6.4.12" + "source": "https://github.com/symfony/dom-crawler/tree/v6.4.13" }, "funding": [ { @@ -1703,24 +1703,24 @@ "type": "tidelift" } ], - "time": "2024-09-15T06:35:36+00:00" + "time": "2024-10-25T15:07:50+00:00" }, { "name": "symfony/options-resolver", - "version": "v6.4.13", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "0a62a9f2504a8dd27083f89d21894ceb01cc59db" + "reference": "85e95eeede2d41cd146146e98c9c81d9214cae85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/0a62a9f2504a8dd27083f89d21894ceb01cc59db", - "reference": "0a62a9f2504a8dd27083f89d21894ceb01cc59db", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/85e95eeede2d41cd146146e98c9c81d9214cae85", + "reference": "85e95eeede2d41cd146146e98c9c81d9214cae85", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", @@ -1754,7 +1754,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.4.13" + "source": "https://github.com/symfony/options-resolver/tree/v7.1.6" }, "funding": [ { @@ -1770,7 +1770,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:18:03+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3766,16 +3766,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.36", + "version": "10.5.38", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "aa0a8ce701ea7ee314b0dfaa8970dc94f3f8c870" + "reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/aa0a8ce701ea7ee314b0dfaa8970dc94f3f8c870", - "reference": "aa0a8ce701ea7ee314b0dfaa8970dc94f3f8c870", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a86773b9e887a67bc53efa9da9ad6e3f2498c132", + "reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132", "shasum": "" }, "require": { @@ -3796,7 +3796,7 @@ "phpunit/php-timer": "^6.0.0", "sebastian/cli-parser": "^2.0.1", "sebastian/code-unit": "^2.0.0", - "sebastian/comparator": "^5.0.2", + "sebastian/comparator": "^5.0.3", "sebastian/diff": "^5.1.1", "sebastian/environment": "^6.1.0", "sebastian/exporter": "^5.1.2", @@ -3847,7 +3847,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.36" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.38" }, "funding": [ { @@ -3863,7 +3863,7 @@ "type": "tidelift" } ], - "time": "2024-10-08T15:36:51+00:00" + "time": "2024-10-28T13:06:21+00:00" }, { "name": "psalm/plugin-phpunit", @@ -5099,47 +5099,46 @@ }, { "name": "symfony/console", - "version": "v6.4.13", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "f793dd5a7d9ae9923e35d0503d08ba734cec1d79" + "reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/f793dd5a7d9ae9923e35d0503d08ba734cec1d79", - "reference": "f793dd5a7d9ae9923e35d0503d08ba734cec1d79", + "url": "https://api.github.com/repos/symfony/console/zipball/bb5192af6edc797cbab5c8e8ecfea2fe5f421e57", + "reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.2", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0|^7.0" + "symfony/string": "^6.4|^7.0" }, "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", "symfony/http-foundation": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0", - "symfony/var-dumper": "^5.4|^6.0|^7.0" + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -5173,7 +5172,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.13" + "source": "https://github.com/symfony/console/tree/v7.1.6" }, "funding": [ { @@ -5189,29 +5188,29 @@ "type": "tidelift" } ], - "time": "2024-10-09T08:40:40+00:00" + "time": "2024-10-09T08:46:59+00:00" }, { "name": "symfony/filesystem", - "version": "v6.4.13", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" + "reference": "c835867b3c62bb05c7fe3d637c871c7ae52024d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", - "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/c835867b3c62bb05c7fe3d637c871c7ae52024d4", + "reference": "c835867b3c62bb05c7fe3d637c871c7ae52024d4", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^5.4|^6.4|^7.0" + "symfony/process": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -5239,7 +5238,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.13" + "source": "https://github.com/symfony/filesystem/tree/v7.1.6" }, "funding": [ { @@ -5255,7 +5254,7 @@ "type": "tidelift" } ], - "time": "2024-10-25T15:07:50+00:00" + "time": "2024-10-25T15:11:02+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -5501,20 +5500,20 @@ }, { "name": "symfony/string", - "version": "v6.4.13", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "38371c60c71c72b3d64d8d76f6b1bb81a2cc3627" + "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/38371c60c71c72b3d64d8d76f6b1bb81a2cc3627", - "reference": "38371c60c71c72b3d64d8d76f6b1bb81a2cc3627", + "url": "https://api.github.com/repos/symfony/string/zipball/61b72d66bf96c360a727ae6232df5ac83c71f626", + "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", @@ -5524,11 +5523,12 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0|^7.0", - "symfony/http-client": "^5.4|^6.0|^7.0", - "symfony/intl": "^6.2|^7.0", + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^5.4|^6.0|^7.0" + "symfony/var-exporter": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -5567,7 +5567,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.13" + "source": "https://github.com/symfony/string/tree/v7.1.6" }, "funding": [ { @@ -5583,7 +5583,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:18:03+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "theseer/tokenizer", From 8f4d0bc6be868ac0b1c936398dc6ff5db9f47111 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 13:06:57 -0700 Subject: [PATCH 107/114] Set threads=1 for psalm because of https://github.com/vimeo/psalm/issues/11067 If you actually find this slow you could remove it because it's just a visual bug really. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 1cbb968a3..abb32839e 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "tests": "vendor/bin/phpunit --stop-on-defect tests", "lint": "vendor/bin/phpcs -d memory_limit=2G --runtime-set testVersion 8.1 .", "autofix": "vendor/bin/phpcbf -d memory_limit=2G --runtime-set testVersion 8.1 .", - "analyze": "vendor/bin/phpstan --memory-limit=2G && vendor/bin/psalm --no-cache", + "analyze": "vendor/bin/phpstan --memory-limit=2G && vendor/bin/psalm --no-cache --threads=1", "jstests": "bun test" }, "require": { From 509924d26208d062b9ce8ceab68ba9cb76d12f0d Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 13:41:34 -0700 Subject: [PATCH 108/114] Don't counterfeit property with private fun name - confuses mustache --- gatherling/Views/Pages/Page.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gatherling/Views/Pages/Page.php b/gatherling/Views/Pages/Page.php index f7d2a16de..5c28cf274 100644 --- a/gatherling/Views/Pages/Page.php +++ b/gatherling/Views/Pages/Page.php @@ -35,7 +35,7 @@ public function __construct(public string $title) $this->isHost = $this->player?->isHost() ?? false; $this->isOrganizer = count($this->player?->organizersSeries() ?? []) > 0; $this->isSuper = $this->player?->isSuper() ?? false; - $this->versionTagline = $this->versionTagline(); + $this->versionTagline = $this->version(); $this->jsLink = 'gatherling.js?v=' . rawurlencode($this->gitHash); } @@ -45,7 +45,7 @@ public function body(): string return TemplateHelper::render('page', $this); } - private function versionTagline(): string + private function version(): string { return 'Gatherling version 6.0.3 ("Nothing is so painful to the human mind as a great and sudden change.")'; // return 'Gatherling version 6.0.2 ("Nixon was normalizing relations with China. I figured that if he could normalize relations, then so could I.")'; From c293a600e6b6977b79839813bdc913bcdf6f868e Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 13:51:33 -0700 Subject: [PATCH 109/114] Clearer display when an event is upcoming versus in progress, better links --- gatherling/Views/Components/HostActiveEvents.php | 12 +++++++++++- gatherling/styles/css/stylesheet.css | 2 +- .../templates/partials/hostActiveEvents.mustache | 10 ++++++++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/gatherling/Views/Components/HostActiveEvents.php b/gatherling/Views/Components/HostActiveEvents.php index cd89d7db4..b94797e17 100644 --- a/gatherling/Views/Components/HostActiveEvents.php +++ b/gatherling/Views/Components/HostActiveEvents.php @@ -4,17 +4,27 @@ namespace Gatherling\Views\Components; +use function Safe\strtotime; + class HostActiveEvents extends Component { + /** @var array */ + public array $events = []; public Icon $playersIcon; public Icon $structureIcon; public Icon $standingsIcon; /** @param array $events */ - public function __construct(public array $events) + public function __construct(array $events) { $this->playersIcon = new Icon('lucide:users'); $this->structureIcon = new Icon('lucide:trophy'); $this->standingsIcon = new Icon('lucide:chevron-right'); + + $now = time(); + foreach ($events as $event) { + $event['startTime'] = new Time(strtotime($event['start']), $now); + $this->events[] = $event; + } } } diff --git a/gatherling/styles/css/stylesheet.css b/gatherling/styles/css/stylesheet.css index 31874b2e7..beb61d2af 100644 --- a/gatherling/styles/css/stylesheet.css +++ b/gatherling/styles/css/stylesheet.css @@ -1069,7 +1069,7 @@ table.center { } /* Align text with icon */ -.event-type, .player-count, .view-standings { +.event-type, .player-count, .event-action { display: flex; align-items: center; gap: var(--spacing-5-static); diff --git a/gatherling/templates/partials/hostActiveEvents.mustache b/gatherling/templates/partials/hostActiveEvents.mustache index 39040b8a9..c488aaa44 100644 --- a/gatherling/templates/partials/hostActiveEvents.mustache +++ b/gatherling/templates/partials/hostActiveEvents.mustache @@ -23,13 +23,19 @@ From 447eff6a450967a5aa8e1e1aa03c52c7aff4cf8e Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 14:55:58 -0700 Subject: [PATCH 110/114] Complete the update to PHP 8.2 in composer, docker, github --- .github/workflows/php.yml | 2 +- Dockerfile | 4 ++-- composer.json | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 465bc1b52..d2e5d460b 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -25,7 +25,7 @@ jobs: - name: Setup PHP with PECL extension uses: shivammathur/setup-php@v2 with: - php-version: '8.1' + php-version: '8.2' extensions: curl, mysqli - name: Setup problem matchers for PHP diff --git a/Dockerfile b/Dockerfile index 5fdffb4e5..5afb532fa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM php:8.1-apache as compose +FROM php:8.2-apache as compose WORKDIR /restore COPY composer.* ./ RUN apt-get update && apt-get install -y git zip unzip @@ -8,7 +8,7 @@ RUN docker-php-ext-install mysqli pdo pdo_mysql && docker-php-ext-enable mysqli RUN curl --silent --show-error https://getcomposer.org/installer | php RUN php composer.phar --version && php composer.phar install -FROM php:8.1-apache +FROM php:8.2-apache LABEL maintainer="Katelyn Gigante" RUN apt-get update && apt-get install -y git zip unzip diff --git a/composer.json b/composer.json index abb32839e..4fcf28f2a 100644 --- a/composer.json +++ b/composer.json @@ -5,8 +5,8 @@ "scripts": { "qa": ["@tests", "@analyze", "@lint", "@jstests"], "tests": "vendor/bin/phpunit --stop-on-defect tests", - "lint": "vendor/bin/phpcs -d memory_limit=2G --runtime-set testVersion 8.1 .", - "autofix": "vendor/bin/phpcbf -d memory_limit=2G --runtime-set testVersion 8.1 .", + "lint": "vendor/bin/phpcs -d memory_limit=2G --runtime-set testVersion 8.2 .", + "autofix": "vendor/bin/phpcbf -d memory_limit=2G --runtime-set testVersion 8.2 .", "analyze": "vendor/bin/phpstan --memory-limit=2G && vendor/bin/psalm --no-cache --threads=1", "jstests": "bun test" }, From 7e844acf9d4f55fdf389c536fdba6fb4e1b75d10 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 15:21:09 -0700 Subject: [PATCH 111/114] Github CI/CD doesn't allow set_time_limit so cope with that --- gatherling/admin/db-upgrade.php | 8 +++++++- gatherling/insertcardset.php | 12 +++++++++--- gatherling/util/updateDefaultFormats.php | 9 +++++++-- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/gatherling/admin/db-upgrade.php b/gatherling/admin/db-upgrade.php index cd2dfcdb1..c591a47c3 100644 --- a/gatherling/admin/db-upgrade.php +++ b/gatherling/admin/db-upgrade.php @@ -6,12 +6,18 @@ use Gatherling\Data\Setup; +use function Gatherling\Helpers\logger; use function Gatherling\Helpers\server; use function Safe\set_time_limit; require_once __DIR__ . '/../lib.php'; -set_time_limit(0); +try { + set_time_limit(0); +} catch (\Exception $e) { + // set_time_limit is not allowed here but we'll try our best to complete in time. + logger()->warning('Failed to set time limit, trying anyway: ' . $e->getMessage()); +} function main(): never { diff --git a/gatherling/insertcardset.php b/gatherling/insertcardset.php index d44719ff0..452dd56c3 100644 --- a/gatherling/insertcardset.php +++ b/gatherling/insertcardset.php @@ -8,15 +8,21 @@ use Gatherling\Views\Redirect; use function Gatherling\Helpers\files; +use function Gatherling\Helpers\logger; use function Gatherling\Helpers\request; use function Gatherling\Helpers\server; use function Safe\set_time_limit; -set_time_limit(0); -mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); - require_once __DIR__ . '/lib.php'; +try { + set_time_limit(0); +} catch (\Exception $e) { + // set_time_limit is not allowed here but we'll try our best to complete in time. + logger()->warning('Failed to set time limit, running anyway: ' . $e->getMessage()); +} +mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); + function main(): never { global $argv; diff --git a/gatherling/util/updateDefaultFormats.php b/gatherling/util/updateDefaultFormats.php index 245e93c26..938ef6a15 100644 --- a/gatherling/util/updateDefaultFormats.php +++ b/gatherling/util/updateDefaultFormats.php @@ -14,10 +14,15 @@ use function Gatherling\Helpers\server; use function Safe\set_time_limit; -set_time_limit(0); - require_once __DIR__ . '/../lib.php'; +try { + set_time_limit(0); +} catch (\Exception $e) { + // set_time_limit is not allowed here but we'll try our best to complete in time. + logger()->warning('Failed to set time limit, running anyway: ' . $e->getMessage()); +} + function main(): never { if (PHP_SAPI != 'cli' && $_SERVER['REQUEST_METHOD'] == 'GET') { // unauthorized POST is okay From 0656764ca42cb112a3f0d9c93a2018b1ba6c12a4 Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 15:35:42 -0700 Subject: [PATCH 112/114] Don't try and use Safe\ini_set before it is available --- gatherling/seriesreport.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/gatherling/seriesreport.php b/gatherling/seriesreport.php index b8589641c..168515e43 100644 --- a/gatherling/seriesreport.php +++ b/gatherling/seriesreport.php @@ -2,20 +2,19 @@ declare(strict_types=1); -use function Safe\ini_set; - -// Some of the Pauper series seasons had 1000+ entrants and 256 tourneys. -// We should find a better way to display that but for now make them work -// without hitting the PHP memory limit. -ini_set('memory_limit', '512M'); - use Gatherling\Views\Pages\SeriesReport; use function Gatherling\Helpers\get; use function Gatherling\Helpers\server; +use function Safe\ini_set; require_once 'lib.php'; +// Some of the Pauper series seasons had 1000+ entrants and 256 tourneys. +// We should find a better way to display that but for now make them work +// without hitting the PHP memory limit. +ini_set('memory_limit', '512M'); + function main(): never { $seriesName = get()->optionalString('series'); From 98389799f8bd78d255251ec855818edef8a532bf Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Thu, 31 Oct 2024 16:04:15 -0700 Subject: [PATCH 113/114] Somewhat hacky fix to prod issue, for now --- gatherling/event.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gatherling/event.php b/gatherling/event.php index 630e3ae4e..103f3867e 100644 --- a/gatherling/event.php +++ b/gatherling/event.php @@ -320,7 +320,7 @@ function updateEvent(): Event $event->start = "{$_POST['year']}-{$_POST['month']}-{$_POST['day']} {$_POST['hour']}:00"; $event->finalized = post()->int('finalized'); $event->active = post()->int('active'); - $event->current_round = post()->int('newmatchround'); + $event->current_round = (int) post()->string('newmatchround', '0'); $event->prereg_allowed = post()->int('prereg_allowed'); $event->player_reportable = post()->int('player_reportable'); $event->prereg_cap = post()->int('prereg_cap'); From dcffbcee49c4c565a6a06113030004a43780149b Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Fri, 1 Nov 2024 10:37:26 -0700 Subject: [PATCH 114/114] Don't add a match every time Update Match Listing is clicked Only add it if they filled out the dropdowns for a new match, otherwise they might be deleting matches or reporting results, etc. and not trying to add a new match. This would've been caught by foreign keys only there's a player with the name empty string gdi. Might have to re-org the db so an FK that stops you having a match in a tournament you're not in is easier to declare. --- gatherling/event.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gatherling/event.php b/gatherling/event.php index 103f3867e..ffbd7dd58 100644 --- a/gatherling/event.php +++ b/gatherling/event.php @@ -529,7 +529,7 @@ function updateMatches(): void } $rnd = post()->int('newmatchround'); - if ($rnd) { + if ($pA !== '' && $pB !== '' && $rnd) { $playerA = new Standings($event->name, $pA); $playerB = new Standings($event->name, $pB); if ($res == 'P') {