diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index 3fab75b6c..c001207c7 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -27,7 +27,6 @@ parameters:
- RECAPTCHA_PRIVATE
- SMTP_HOSTNAME
- TWITTER_CONSUMER_KEY
- - USING_AJAX
# We code in protection against a value of 0
- SmrMines::TOTAL_ENEMY_MINES_MODIFIER
diff --git a/src/bootstrap.php b/src/bootstrap.php
index 5af39c6b7..c6eb4f5a8 100644
--- a/src/bootstrap.php
+++ b/src/bootstrap.php
@@ -27,6 +27,8 @@ function logException(Throwable $e): void {
$message .= $delim;
}
+ $message .= 'ajax: ' . var_export($session->ajax, true) . "\n";
+
$var = $session->hasCurrentVar() ? $session->getCurrentVar() : null;
$message .= '$var: ' . print_r($var, true) . $delim;
}
@@ -41,7 +43,6 @@ function logException(Throwable $e): void {
$message .=
'User IP: ' . getIpAddress() . "\n" .
'User Agent: ' . ($_SERVER['HTTP_USER_AGENT'] ?? 'undefined') . "\n" .
- 'USING_AJAX: ' . (defined('USING_AJAX') ? var_export(USING_AJAX, true) : 'undefined') . "\n" .
'URL: ' . (defined('URL') ? URL : 'undefined');
// Try to release lock so they can carry on normally
@@ -122,7 +123,7 @@ function handleException(Throwable $e): void {
// If this is an ajax update, we don't really have a way to redirect
// to an error page at this time.
- if (!ENABLE_DEBUG && (!defined('USING_AJAX') || !USING_AJAX)) {
+ if (!ENABLE_DEBUG) {
header('location: /error.php?msg=' . urlencode($errorType));
}
}
diff --git a/src/config.php b/src/config.php
index 28aa96aeb..66719078f 100644
--- a/src/config.php
+++ b/src/config.php
@@ -471,5 +471,3 @@
const AJAX_DEFAULT_REFRESH_TIME = 1500;
const AJAX_UNPROTECTED_REFRESH_TIME = 800;
-
-define('USING_AJAX', isset($_REQUEST['ajax']) && $_REQUEST['ajax'] == 1);
diff --git a/src/engine/Default/message_view.php b/src/engine/Default/message_view.php
index 05823ef3e..9e09c42df 100644
--- a/src/engine/Default/message_view.php
+++ b/src/engine/Default/message_view.php
@@ -51,11 +51,6 @@
$template->assign('NextPageHREF', $container->href());
}
-// remove entry for this folder from unread msg table
-if ($page == 0 && !USING_AJAX) {
- $player->setMessagesRead($messageBox['Type']);
-}
-
$messageBox['Name'] = Messages::getMessageTypeNames($folderID);
$template->assign('PageTopic', 'Viewing ' . $messageBox['Name']);
@@ -94,10 +89,12 @@
$messageBox['Messages'][] = displayMessage($dbRecord->getInt('message_id'), $dbRecord->getInt('account_id'), $dbRecord->getInt('sender_id'), $player->getGameID(), $dbRecord->getString('message_text'), $dbRecord->getInt('send_time'), $dbRecord->getBoolean('msg_read'), $folderID, $player->getAccount());
}
}
-if (!USING_AJAX) {
- $db->write('UPDATE message SET msg_read = \'TRUE\'
- WHERE message_type_id = ' . $db->escapeNumber($folderID) . ' AND ' . $player->getSQL());
+
+// This should really be part of a (pre)processing page
+if ($page == 0 && !$session->ajax) {
+ $player->setMessagesRead($folderID);
}
+
$template->assign('MessageBox', $messageBox);
diff --git a/src/engine/Default/toggle_processing.php b/src/engine/Default/toggle_processing.php
index 0260c62b3..18d15182f 100644
--- a/src/engine/Default/toggle_processing.php
+++ b/src/engine/Default/toggle_processing.php
@@ -5,7 +5,7 @@
$player->setDisplayWeapons(!$player->isDisplayWeapons());
// If this is called by ajax, we don't want to do any forwarding
-if (USING_AJAX) {
+if ($session->ajax) {
exit;
}
diff --git a/src/htdocs/loader.php b/src/htdocs/loader.php
index 5ab491b7a..6cabcf903 100644
--- a/src/htdocs/loader.php
+++ b/src/htdocs/loader.php
@@ -19,8 +19,6 @@
// *
// ********************************
- //echo '
';echo_r($session);echo'
';
- //exit;
// do we have a session?
$session = Smr\Session::getInstance();
if (!$session->hasAccount()) {
@@ -30,7 +28,7 @@
// check if we got a sn number with our url
if (empty($session->getSN())) {
- if (!USING_AJAX) {
+ if (!$session->ajax) {
create_error('Your browser lost the SN. Try to reload the page!');
} else {
exit;
@@ -39,7 +37,7 @@
// do we have such a container object in the db?
if ($session->hasCurrentVar() === false) {
- if (!USING_AJAX) {
+ if (!$session->ajax) {
create_error('Please avoid using the back button!');
} else {
exit;
diff --git a/src/lib/Default/AbstractSmrPlayer.php b/src/lib/Default/AbstractSmrPlayer.php
index e8b7c5fcf..b26515469 100644
--- a/src/lib/Default/AbstractSmrPlayer.php
+++ b/src/lib/Default/AbstractSmrPlayer.php
@@ -873,6 +873,8 @@ public static function sendMessageFromRace(int $raceID, int $gameID, int $receiv
public function setMessagesRead(int $messageTypeID): void {
$this->db->write('DELETE FROM player_has_unread_messages
WHERE ' . $this->SQL . ' AND message_type_id = ' . $this->db->escapeNumber($messageTypeID));
+ $this->db->write('UPDATE message SET msg_read = ' . $this->db->escapeBoolean(true) . '
+ WHERE message_type_id = ' . $this->db->escapeNumber($messageTypeID) . ' AND ' . $this->SQL);
}
public function getSafeAttackRating(): int {
@@ -2254,7 +2256,7 @@ public function removeUnderAttack(): bool {
return $var['UnderAttack'];
}
$underAttack = $this->isUnderAttack();
- if ($underAttack && !USING_AJAX) {
+ if ($underAttack && !$session->ajax) {
$var['UnderAttack'] = $underAttack; //Remember we are under attack for AJAX
}
$this->setUnderAttack(false);
diff --git a/src/lib/Default/Page.php b/src/lib/Default/Page.php
index f366d801f..a64a97da3 100644
--- a/src/lib/Default/Page.php
+++ b/src/lib/Default/Page.php
@@ -11,12 +11,12 @@
*/
class Page extends ArrayObject {
- private const ALWAYS_AVAILABLE = 999999;
+ private const ALWAYS_AVAILABLE = true;
- // Defines the number of pages that can be loaded after
- // this page before the links on this page become invalid
- // (i.e. before you get a back button error).
- private const URL_DEFAULT_REMAINING_PAGE_LOADS = [
+ // Defines if the page is is always available, or if it is invalid after one
+ // use (i.e. if you get a back button error when navigating back to it).
+ public const ALWAYS_AVAILABLE_PAGES = [
+ 'album_edit.php' => self::ALWAYS_AVAILABLE,
'alliance_broadcast.php' => self::ALWAYS_AVAILABLE,
'alliance_forces.php' => self::ALWAYS_AVAILABLE,
'alliance_list.php' => self::ALWAYS_AVAILABLE,
@@ -33,7 +33,7 @@ class Page extends ArrayObject {
'course_plot.php' => self::ALWAYS_AVAILABLE,
'changelog_view.php' => self::ALWAYS_AVAILABLE,
'chat_rules.php' => self::ALWAYS_AVAILABLE,
- 'chess_play.php' => self::ALWAYS_AVAILABLE,
+ 'chess.php' => self::ALWAYS_AVAILABLE,
'combat_log_list.php' => self::ALWAYS_AVAILABLE,
'combat_log_viewer.php' => self::ALWAYS_AVAILABLE,
'current_sector.php' => self::ALWAYS_AVAILABLE,
@@ -50,11 +50,13 @@ class Page extends ArrayObject {
'feature_request.php' => self::ALWAYS_AVAILABLE,
'forces_list.php' => self::ALWAYS_AVAILABLE,
'forces_mass_refresh.php' => self::ALWAYS_AVAILABLE,
- 'hall_of_fame_player_new.php' => self::ALWAYS_AVAILABLE,
+ 'galactic_post_current.php' => self::ALWAYS_AVAILABLE,
+ 'hall_of_fame_new.php' => self::ALWAYS_AVAILABLE,
'hall_of_fame_player_detail.php' => self::ALWAYS_AVAILABLE,
'leave_newbie.php' => self::ALWAYS_AVAILABLE,
'logoff.php' => self::ALWAYS_AVAILABLE,
'map_local.php' => self::ALWAYS_AVAILABLE,
+ 'message_box.php' => self::ALWAYS_AVAILABLE,
'message_view.php' => self::ALWAYS_AVAILABLE,
'message_send.php' => self::ALWAYS_AVAILABLE,
'news_read_advanced.php' => self::ALWAYS_AVAILABLE,
@@ -82,6 +84,7 @@ class Page extends ArrayObject {
'rankings_race.php' => self::ALWAYS_AVAILABLE,
'rankings_sector_kill.php' => self::ALWAYS_AVAILABLE,
'rankings_view.php' => self::ALWAYS_AVAILABLE,
+ 'smr_file_create.php' => self::ALWAYS_AVAILABLE,
'trader_bounties.php' => self::ALWAYS_AVAILABLE,
'trader_relations.php' => self::ALWAYS_AVAILABLE,
'trader_savings.php' => self::ALWAYS_AVAILABLE,
@@ -93,7 +96,7 @@ class Page extends ArrayObject {
'alliance_message_add_processing.php' => self::ALWAYS_AVAILABLE,
'alliance_message_delete_processing.php' => self::ALWAYS_AVAILABLE,
'alliance_pick_processing.php' => self::ALWAYS_AVAILABLE,
- 'chess_move_processing.php' => self::ALWAYS_AVAILABLE,
+ 'game_leave_processing.php' => self::ALWAYS_AVAILABLE,
'toggle_processing.php' => self::ALWAYS_AVAILABLE,
//Admin pages
'admin/account_edit.php' => self::ALWAYS_AVAILABLE,
@@ -116,7 +119,7 @@ class Page extends ArrayObject {
'admin/unigen/universe_create_warps.php' => self::ALWAYS_AVAILABLE,
];
- public int $remainingPageLoads;
+ public readonly bool $reusable;
/**
* @param array $data
@@ -127,7 +130,9 @@ protected function __construct(
public readonly bool $skipRedirect // to skip redirect hooks at beginning of page processing
) {
parent::__construct($data);
- $this->remainingPageLoads = self::URL_DEFAULT_REMAINING_PAGE_LOADS[$file] ?? 1; // Allow refreshing
+
+ // Pages are single-use unless explicitly whitelisted
+ $this->reusable = self::ALWAYS_AVAILABLE_PAGES[$file] ?? false;
}
/**
@@ -188,11 +193,8 @@ public function addVar(string $source, string $dest = null): void {
* the next request, we can grab the container out of the Smr\Session.
*/
public function href(bool $forceFullURL = false): string {
-
- // We need to make a clone because the instance saved in the Session
- // must not be modified if we re-use this instance to create more
- // links. Ideally this would not be necessary, but the usage of this
- // method would need to change globally first (no Page re-use).
+ // We need to clone this instance in case it is modified after being added
+ // to the session links. This would not be necessary if Page was readonly.
$sn = Smr\Session::getInstance()->addLink(self::copy($this));
$href = '?sn=' . $sn;
@@ -202,22 +204,6 @@ public function href(bool $forceFullURL = false): string {
return $href;
}
- /**
- * Returns a hash of the contents of the container to identify when two
- * containers are equivalent (apart from page-load tracking metadata, which
- * we strip out to prevent false differences).
- *
- * CommonID MUST be unique to a specific action. If there will be different
- * outcomes from containers given the same ID then problems will arise.
- */
- public function getCommonID(): string {
- $commonContainer = $this->getArrayCopy();
- $commonContainer['_FILE'] = $this->file; // must include file in ID
- // NOTE: This ID will change if the order of elements in the container
- // changes. If this causes unnecessary SN changes, sort the container!
- return md5(serialize($commonContainer));
- }
-
/**
* Process this page by executing the associated file.
*/
diff --git a/src/lib/Default/smr.inc.php b/src/lib/Default/smr.inc.php
index 806263abf..eb64c024c 100644
--- a/src/lib/Default/smr.inc.php
+++ b/src/lib/Default/smr.inc.php
@@ -202,7 +202,8 @@ function handleUserError(string $message): never {
// Template from the original page.
DiContainer::getContainer()->reset(Template::class);
- if (Session::getInstance()->hasGame()) {
+ $session = Session::getInstance();
+ if ($session->hasGame()) {
$container = Page::create('current_sector.php');
$errorMsg = 'ERROR: ' . $message;
$container['errorMsg'] = $errorMsg;
@@ -211,7 +212,7 @@ function handleUserError(string $message): never {
$container['message'] = $message;
}
- if (USING_AJAX) {
+ if ($session->ajax) {
// To avoid the page just not refreshing when an error is encountered
// during ajax updates, use javascript to auto-redirect to the
// appropriate error page.
@@ -308,7 +309,7 @@ function do_voodoo(): never {
define('AJAX_CONTAINER', isset($var['AJAX']) && $var['AJAX'] === true);
}
- if (!AJAX_CONTAINER && USING_AJAX && $session->hasChangedSN()) {
+ if (!AJAX_CONTAINER && $session->ajax && $session->hasChangedSN()) {
exit;
}
//ob_clean();
@@ -322,7 +323,7 @@ function do_voodoo(): never {
$player = $session->getPlayer();
$sectorID = $player->getSectorID();
- if (!USING_AJAX //AJAX should never do anything that requires a lock.
+ if (!$session->ajax //AJAX should never do anything that requires a lock.
//&& ($var->file == 'current_sector.php' || $var->file == 'map_local.php') //Neither should CS or LM and they gets loaded a lot so should reduce lag issues with big groups.
) {
// We skip locking if we've already failed to display error page
@@ -358,7 +359,7 @@ function do_voodoo(): never {
$player->updateTurns();
// Check if we need to redirect to a different page
- if (!$var->skipRedirect && !USING_AJAX) {
+ if (!$var->skipRedirect && !$session->ajax) {
if ($player->getGame()->hasEnded()) {
Page::create('game_leave_processing.php', ['forward_to' => 'game_play.php', 'errorMsg' => 'The game has ended.'], skipRedirect: true)->go();
}
@@ -408,7 +409,7 @@ function do_voodoo(): never {
}
$template->assign('AJAX_ENABLE_REFRESH', $ajaxRefresh);
- $template->display('skeleton.php', USING_AJAX || AJAX_CONTAINER);
+ $template->display('skeleton.php', $session->ajax || AJAX_CONTAINER);
$session->update();
diff --git a/src/lib/Smr/Session.php b/src/lib/Smr/Session.php
index cc0580044..c2b454c86 100644
--- a/src/lib/Smr/Session.php
+++ b/src/lib/Smr/Session.php
@@ -30,10 +30,10 @@ class Session {
private string $sessionID;
private int $gameID;
/** @var array */
- private array $var;
- /** @var array */
- private array $commonIDs = [];
+ private array $links = [];
+ private ?Page $currentPage = null;
private bool $generate;
+ public readonly bool $ajax;
private string $SN;
private string $lastSN;
private int $accountID;
@@ -83,12 +83,13 @@ public function __construct() {
$this->db->write('DELETE FROM active_session WHERE last_accessed < ' . $this->db->escapeNumber(time() - self::TIME_BEFORE_EXPIRY));
// try to get current session
+ $this->ajax = Request::getInt('ajax', 0) === 1;
$this->SN = Request::get('sn', '');
$this->fetchVarInfo();
- if (!USING_AJAX && !empty($this->SN) && !empty($this->var[$this->SN])) {
- $var = $this->var[$this->SN];
- $loadDelay = self::URL_LOAD_DELAY[$var->file] ?? 0;
+ if (!$this->ajax && $this->hasCurrentVar()) {
+ $file = $this->getCurrentVar()->file;
+ $loadDelay = self::URL_LOAD_DELAY[$file] ?? 0;
$timeBetweenLoads = microtime(true) - $this->lastAccessed;
if ($timeBetweenLoads < $loadDelay) {
$sleepTime = IRound(($loadDelay - $timeBetweenLoads) * 1000000);
@@ -97,7 +98,7 @@ public function __construct() {
}
if (ENABLE_DEBUG) {
$this->db->insert('debug', [
- 'debug_type' => $this->db->escapeString('Delay: ' . $var->file),
+ 'debug_type' => $this->db->escapeString('Delay: ' . $file),
'account_id' => $this->db->escapeNumber($this->accountID),
'value' => $this->db->escapeNumber($timeBetweenLoads),
]);
@@ -118,24 +119,22 @@ public function fetchVarInfo(): void {
// We may not have ajax_returns if ajax was disabled
$this->previousAjaxReturns = $dbRecord->getObject('ajax_returns', true, true);
- $this->var = $dbRecord->getObject('session_var', true);
-
- foreach ($this->var as $sn => $var) {
- if ($var->remainingPageLoads < 0) {
- //This link is no longer valid
- unset($this->var[$sn]);
- } else {
- // The following is skipped for the current SN, because:
- // a) If we decremented RemainingPageLoads, we wouldn't be
- // able to refresh the current page.
- // b) If we register its CommonID and then subsequently
- // modify its data (which is quite common for the
- // "current var"), the CommonID is not updated. Then any
- // var with the same data as the original will wrongly
- // share its CommonID.
- if ($sn !== $this->SN) {
- $var->remainingPageLoads -= 1;
- $this->commonIDs[$var->getCommonID()] = $sn;
+ [$this->links, $lastPage] = $dbRecord->getObject('session_var', true);
+
+ $ajaxRefresh = $this->ajax && !$this->hasChangedSN();
+ if ($ajaxRefresh) {
+ $this->currentPage = $lastPage;
+ } elseif (isset($this->links[$this->SN])) {
+ // If the current page is modified during page processing, we need
+ // to make sure the original link is unchanged. So we clone it here.
+ $this->currentPage = clone $this->links[$this->SN];
+ }
+
+ if (!$ajaxRefresh) { // since form pages don't ajax refresh properly
+ foreach ($this->links as $sn => $link) {
+ if (!$link->reusable) {
+ // This link is no longer valid
+ unset($this->links[$sn]);
}
}
}
@@ -143,21 +142,15 @@ public function fetchVarInfo(): void {
$this->generate = true;
$this->accountID = 0;
$this->gameID = 0;
- $this->var = [];
}
}
public function update(): void {
- foreach ($this->var as $sn => $var) {
- if ($var->remainingPageLoads <= 0) {
- //This link was valid this load but will not be in the future, removing it now saves database space and data transfer.
- unset($this->var[$sn]);
- }
- }
+ $sessionVar = [$this->links, $this->currentPage];
if (!$this->generate) {
- $this->db->write('UPDATE active_session SET account_id=' . $this->db->escapeNumber($this->accountID) . ',game_id=' . $this->db->escapeNumber($this->gameID) . (!USING_AJAX ? ',last_accessed=' . $this->db->escapeNumber(Epoch::microtime()) : '') . ',session_var=' . $this->db->escapeObject($this->var, true) .
+ $this->db->write('UPDATE active_session SET account_id=' . $this->db->escapeNumber($this->accountID) . ',game_id=' . $this->db->escapeNumber($this->gameID) . (!$this->ajax ? ',last_accessed=' . $this->db->escapeNumber(Epoch::microtime()) : '') . ',session_var=' . $this->db->escapeObject($sessionVar, true) .
',last_sn=' . $this->db->escapeString($this->SN) .
- ' WHERE session_id=' . $this->db->escapeString($this->sessionID) . (USING_AJAX ? ' AND last_sn=' . $this->db->escapeString($this->lastSN) : ''));
+ ' WHERE session_id=' . $this->db->escapeString($this->sessionID) . ($this->ajax ? ' AND last_sn=' . $this->db->escapeString($this->lastSN) : ''));
} else {
$this->db->write('DELETE FROM active_session WHERE account_id = ' . $this->db->escapeNumber($this->accountID) . ' AND game_id = ' . $this->db->escapeNumber($this->gameID));
$this->db->insert('active_session', [
@@ -165,7 +158,7 @@ public function update(): void {
'account_id' => $this->db->escapeNumber($this->accountID),
'game_id' => $this->db->escapeNumber($this->gameID),
'last_accessed' => $this->db->escapeNumber(Epoch::microtime()),
- 'session_var' => $this->db->escapeObject($this->var, true),
+ 'session_var' => $this->db->escapeObject($sessionVar, true),
]);
$this->generate = false;
}
@@ -257,14 +250,14 @@ public function getLastAccessed(): float {
* Check if the session has a var associated with the current SN.
*/
public function hasCurrentVar(): bool {
- return isset($this->var[$this->SN]);
+ return $this->currentPage !== null;
}
/**
* Returns the session var associated with the current SN.
*/
public function getCurrentVar(): Page {
- return $this->var[$this->SN];
+ return $this->currentPage;
}
/**
@@ -302,36 +295,29 @@ public function getRequestVarIntArray(string $varName, array $default = null): a
* Replace the global $var with the given $container.
*/
public function setCurrentVar(Page $container): void {
- //Do not allow sharing SN, useful for forwarding.
- if ($this->hasCurrentVar()) {
- $var = $this->getCurrentVar();
- unset($this->commonIDs[$var->getCommonID()]); //Do not store common id for reset page, to allow refreshing to always give the same page in response
- }
-
- $this->var[$this->SN] = $container;
+ $this->currentPage = $container;
}
public function clearLinks(): void {
- $this->var = [$this->SN => $this->var[$this->SN]];
- $this->commonIDs = [];
+ $this->links = [];
}
+ /**
+ * Add a page to the session so that it can be used on next page load.
+ * It will be associated with an SN that will be used for linking.
+ */
public function addLink(Page $container): string {
- $sn = $this->generateSN($container);
- $this->var[$sn] = $container;
- return $sn;
- }
-
- protected function generateSN(Page $container): string {
- $commonID = $container->getCommonID();
- if (isset($this->commonIDs[$commonID])) {
- $sn = $this->commonIDs[$commonID];
- } else {
- do {
- $sn = random_alphabetic_string(6);
- } while (isset($this->var[$sn]));
- $this->commonIDs[$commonID] = $sn;
+ // If we already had a link to this exact page, use the existing SN for it.
+ foreach ($this->links as $sn => $link) {
+ if ($container == $link) { // loose equality to compare contents
+ return $sn;
+ }
}
+ // This page isn't an existing link, so give it a new SN.
+ do {
+ $sn = random_alphabetic_string(6);
+ } while (isset($this->links[$sn]));
+ $this->links[$sn] = $container;
return $sn;
}
diff --git a/test/SmrTest/lib/DefaultGame/PageIntegrationTest.php b/test/SmrTest/lib/DefaultGame/PageIntegrationTest.php
index 0de8d71c2..420bf007d 100644
--- a/test/SmrTest/lib/DefaultGame/PageIntegrationTest.php
+++ b/test/SmrTest/lib/DefaultGame/PageIntegrationTest.php
@@ -75,16 +75,6 @@ public function test_copy(): void {
self::assertEquals($page, $copy);
}
- public function test_remainingPageLoads(): void {
- // Create a Page that does not use the default page loads
- $page = Page::create('current_sector.php');
- self::assertSame(999999, $page->remainingPageLoads);
-
- // Create a Page that does use the default page loads
- $page = Page::create('test');
- self::assertSame(1, $page->remainingPageLoads);
- }
-
public function test_href(): void {
// Create an arbitrary Page
$page = Page::create('file');
@@ -142,23 +132,4 @@ public function test_skipRedirect(): void {
self::assertFalse($page2->skipRedirect);
}
- public function test_getCommonID(): void {
- // Create an arbitrary Page with data
- $data = ['some' => 'data'];
- $page1 = Page::create('test1', $data);
- // Check the value of the common ID
- $expected = 'b5cce3676aa819381ad18dc1a5f41710';
- self::assertSame($expected, $page1->getCommonID());
-
- // Create a page with the same data but a different file
- $page2 = Page::create('test2', $page1);
- // Check that the common ID changes
- self::assertNotEquals($page1->getCommonID(), $page2->getCommonID());
-
- // Create a page with the same file but different data
- $page3 = Page::create('test1', ['different' => 'data']);
- // Check that the common ID changes
- self::assertNotEquals($page1->getCommonID(), $page3->getCommonID());
- }
-
}
diff --git a/test/SmrTest/lib/DefaultGame/SessionIntegrationTest.php b/test/SmrTest/lib/DefaultGame/SessionIntegrationTest.php
index a19e6d19f..b903817c5 100644
--- a/test/SmrTest/lib/DefaultGame/SessionIntegrationTest.php
+++ b/test/SmrTest/lib/DefaultGame/SessionIntegrationTest.php
@@ -89,6 +89,17 @@ public function test_getSessionID(): void {
self::assertNotEquals($sessionID, $session->getSessionID());
}
+ public function test_ajax(): void {
+ // If $_REQUEST is empty, ajax is false
+ self::assertFalse($this->session->ajax);
+
+ // Test other values in $_REQUEST
+ $_REQUEST['ajax'] = 1;
+ self::assertTrue(DiContainer::make(Session::class)->ajax);
+ $_REQUEST['ajax'] = 'anything other than 1';
+ self::assertFalse(DiContainer::make(Session::class)->ajax);
+ }
+
public function test_current_var(): void {
// With an empty session, there should be no current var
self::assertFalse($this->session->hasCurrentVar());
@@ -112,10 +123,6 @@ public function test_current_var(): void {
$var = $session->getCurrentVar();
self::assertSame('some_page', $var->file);
- // The RemainingPageLoads should still be 1 because we effectively
- // reloaded the page by creating a new Session.
- self::assertSame(1, $var->remainingPageLoads);
-
// We can now change the current var
$page2 = Page::create('another_page');
$session->setCurrentVar($page2);
@@ -125,6 +132,14 @@ public function test_current_var(): void {
$var2 = $session->getCurrentVar();
self::assertSame('another_page', $var2->file);
+ // If we make a new session, but keep the same SN (e.g. ajax),
+ // we should still get the updated var, even though it wasn't
+ // the one originally associated with this SN.
+ $session->update();
+ $_REQUEST['ajax'] = 1;
+ $session = DiContainer::make(Session::class);
+ self::assertEquals($var2, $session->getCurrentVar());
+
// If we destroy the Session, then the current var should no longer
// be accessible to a new Session.
$session->destroy();
@@ -132,6 +147,42 @@ public function test_current_var(): void {
self::assertFalse($session->hasCurrentVar());
}
+ public function test_addLink(): void {
+ // If we add two different pages, we should get different SNs.
+ $page = Page::create('some_page');
+ $sn = $this->session->addLink($page);
+
+ $page2 = Page::create('another_page');
+ self::assertNotEquals($sn, $this->session->addLink($page2));
+ }
+
+ public function test_addLink_page_already_added(): void {
+ // If we add the same page object twice, it will give the same SN.
+ $page = Page::create('some_page');
+ $sn = $this->session->addLink($page);
+ self::assertSame($sn, $this->session->addLink($page));
+
+ // This works if the pages are equal, but not the same object.
+ $page2 = clone $page;
+ self::assertNotSame($page, $page2);
+ self::assertSame($sn, $this->session->addLink($page2));
+
+ // It also works if we modify the page object (though this isn't
+ // recommended, we clone when adding from Page::href to avoid this).
+ $page['bla'] = true;
+ self::assertSame($sn, $this->session->addLink($page));
+ }
+
+ public function test_clearLinks(): void {
+ srand(0); // seed rng to avoid getting the same random SN twice
+ $page = Page::create('some_page');
+ $sn = $this->session->addLink($page);
+
+ // After clearing links, the same page will return a different SN.
+ $this->session->clearLinks();
+ self::assertNotSame($sn, $this->session->addLink($page));
+ }
+
public function test_getRequestVar(): void {
// Initialize the current var so that we can update it
$page = Page::create('some_page');