Skip to content

Add SESSION hash support to Request class#147

Open
mambax7 wants to merge 5 commits intoXOOPS:masterfrom
mambax7:feature/add-session-hash-to-request
Open

Add SESSION hash support to Request class#147
mambax7 wants to merge 5 commits intoXOOPS:masterfrom
mambax7:feature/add-session-hash-to-request

Conversation

@mambax7
Copy link
Contributor

@mambax7 mambax7 commented Mar 11, 2026

Summary

  • Add SESSION as a valid $hash parameter value for Request::getVar(), Request::get(), and Request::setVar()
  • When session is active, reads from/writes to $_SESSION
  • When session is not active, getVar() returns the default value and setVar() silently skips — no errors
  • Updated docblocks for getVar() and get() to document the new option

Test plan

  • testGetVarSessionWithActiveSession — reads value from active session
  • testGetVarSessionReturnsDefaultWhenKeyMissing — returns default for missing keys
  • testGetVarSessionReturnsDefaultWhenNoSession — returns default when no session active
  • testGetIntFromSession — integer filtering works with session hash
  • testGetSessionHashRequest::get('session') returns full session array
  • testGetSessionHashReturnsEmptyWhenNoSession — returns empty array when no session
  • testSetVarSession — writes value to $_SESSION
  • testSetVarSessionIgnoredWhenNoSession — silently skips write when no session
  • testHasVarSessionhasVar() works with session hash
  • PHPStan level max passes clean
  • All 28 Request tests pass

Summary by CodeRabbit

  • New Features

    • Request handling now recognizes SESSION as a supported source/destination: reads use session data when active and return empty/defaults when not; writes to session are performed only when a session is active.
  • Tests

    • Added comprehensive session-aware tests for reads, writes, parsing, presence checks, and inactive-session behavior.
  • Documentation

    • Updated docs to list SESSION among sources/destinations and note default/empty and silent-write behavior when no session is active.

Copilot AI review requested due to automatic review settings March 11, 2026 15:01
@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'tools'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

Adds SESSION as a supported source/destination in Request: read methods (getVar/get/typed getters/hasVar) now return values from $_SESSION when a session is active (otherwise treat as empty/default). setVar will write to $_SESSION only when a session is active. Unit tests added for active/inactive session behaviors.

Changes

Cohort / File(s) Summary
Request class — SESSION source support
src/Request.php
Adds SESSION to documented and internal source/destination hashes. getVar/get switch now handles SESSION, returning $_SESSION when session_status() indicates an active session, otherwise using empty input/defaults. hasVar uses the same path. setVar includes a SESSION case that silently skips writes if no active session. Docblocks updated to include SESSION.
Request tests — session-aware coverage
tests/unit/RequestTest.php
Adds session lifecycle helpers (startTestSession, closeTestSession) and a suite of tests exercising read/write/has semantics for the session source under active and inactive sessions: present/missing keys, typed getters (int), hash retrieval, ignored writes when no session, and teardown cleanup. Also stores/restores relevant INI state.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add SESSION hash support to Request class' clearly and specifically describes the main change: adding SESSION as a valid hash parameter to the Request class methods.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Request.php (1)

1-1: 🧹 Nitpick | 🔵 Trivial

Pipeline failures exist in unchanged code.

The CI pipeline reports formatting issues (header block separation at line 1, blank lines at end of control structures at lines 125 and 131, and missing visibility on constants at lines 35-37). These issues predate this PR's changes. Consider addressing them in a separate cleanup PR to avoid scope creep.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Request.php` at line 1, The CI reports style issues in Request.php that
should be fixed in a cleanup PR: add the required header block separation
(ensure a blank line after the opening <?php), declare explicit visibility for
the constants defined around the identifiers at lines 35-37 (e.g., add public
const for each constant in the Request class), and remove the extra blank lines
at the end of the control structures referenced near the methods/blocks around
lines 125 and 131 so there are no stray blank lines before closing braces;
update the file accordingly and run the formatter/linter to confirm compliance.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/unit/RequestTest.php`:
- Around line 283-295: The test testGetVarSessionReturnsDefaultWhenNoSession
manipulates global session state and reopens it at the end, creating implicit
inter-test dependencies; refactor by adding setUp() and tearDown() methods on
the test class to ensure a consistent session state for every test (use
session_status(), session_start(), and session_write_close() there) so
individual tests like testGetVarSessionReturnsDefaultWhenNoSession can close the
session without needing to restart it themselves and other tests no longer rely
on execution order.

---

Outside diff comments:
In `@src/Request.php`:
- Line 1: The CI reports style issues in Request.php that should be fixed in a
cleanup PR: add the required header block separation (ensure a blank line after
the opening <?php), declare explicit visibility for the constants defined around
the identifiers at lines 35-37 (e.g., add public const for each constant in the
Request class), and remove the extra blank lines at the end of the control
structures referenced near the methods/blocks around lines 125 and 131 so there
are no stray blank lines before closing braces; update the file accordingly and
run the formatter/linter to confirm compliance.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8a993894-69b0-410c-a695-6849f7988cc5

📥 Commits

Reviewing files that changed from the base of the PR and between 469c26f and a6dc00a.

📒 Files selected for processing (2)
  • src/Request.php
  • tests/unit/RequestTest.php

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds SESSION as a supported input source to the XMF Request utility, enabling Request::getVar(), Request::get(), and Request::setVar() to read/write $_SESSION when a session is active, with safe fallbacks when it isn’t.

Changes:

  • Add SESSION handling paths to Request::getVar(), Request::get(), and Request::setVar()
  • Update Request docblocks to mention the new session hash option
  • Add PHPUnit coverage for session-based reads/writes and no-session behavior

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
src/Request.php Adds SESSION as a supported hash for request accessors/mutators and updates docs accordingly
tests/unit/RequestTest.php Adds unit tests validating session read/write behavior and fallbacks when no session is active

src/Request.php Outdated
* @param string $name Variable name
* @param mixed $default Default value if the variable does not exist
* @param string $hash Source of variable value (POST, GET, FILES, COOKIE, METHOD)
* @param string $hash Source of variable value (POST, GET, FILES, COOKIE, SESSION, METHOD)
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getVar() docblock’s $hash parameter description is now incomplete/inaccurate: the method supports ENV, SERVER, and DEFAULT/REQUEST in addition to the listed values. Update the @param $hash text (and/or the earlier bullet list) so the documented options match the actual switch cases.

Suggested change
* @param string $hash Source of variable value (POST, GET, FILES, COOKIE, SESSION, METHOD)
* @param string $hash Source of variable value (GET, POST, FILES, COOKIE, ENV, SERVER, SESSION, METHOD, DEFAULT/REQUEST)

Copilot uses AI. Check for mistakes.
src/Request.php Outdated
* - default $_REQUEST
*
* @param string $hash to get (POST, GET, FILES, METHOD)
* @param string $hash to get (POST, GET, FILES, SESSION, METHOD)
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The get() docblock’s “@param string $hash to get (...)” list is incomplete: get() also supports COOKIE, ENV, SERVER and DEFAULT/REQUEST per the switch. Please update the parameter documentation so callers don’t miss supported hashes.

Suggested change
* @param string $hash to get (POST, GET, FILES, SESSION, METHOD)
* @param string $hash to get (POST, GET, FILES, COOKIE, ENV, SERVER, SESSION, METHOD, DEFAULT/REQUEST)

Copilot uses AI. Check for mistakes.
Comment on lines +448 to +452
case 'SESSION':
if (session_status() === PHP_SESSION_ACTIVE) {
$_SESSION[$name] = $value;
}
break;
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SESSION support was added to setVar(), but the method’s docblock doesn’t mention SESSION or the no-op behavior when no session is active. Please update the setVar() documentation to include SESSION and clarify what happens when the session isn’t active.

Copilot uses AI. Check for mistakes.
Comment on lines +262 to +264
if (session_status() !== PHP_SESSION_ACTIVE) {
@session_start();
}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests suppress session_start() errors and don’t assert that the session actually became active. That can hide environment/config problems and make failures harder to diagnose. Consider asserting PHP_SESSION_ACTIVE after attempting to start (or mark the test skipped if sessions can’t be started).

Copilot uses AI. Check for mistakes.
Comment on lines +262 to +307
if (session_status() !== PHP_SESSION_ACTIVE) {
@session_start();
}
$varname = 'RequestTestSession';
$_SESSION[$varname] = 'session_value';

$this->assertEquals('session_value', Request::getVar($varname, null, 'session'));

unset($_SESSION[$varname]);
}

public function testGetVarSessionReturnsDefaultWhenKeyMissing()
{
if (session_status() !== PHP_SESSION_ACTIVE) {
@session_start();
}

$this->assertNull(Request::getVar('no_such_session_key', null, 'session'));
$this->assertEquals('fallback', Request::getVar('no_such_session_key', 'fallback', 'session'));
}

public function testGetVarSessionReturnsDefaultWhenNoSession()
{
// When session is not active, getVar should return the default
if (session_status() === PHP_SESSION_ACTIVE) {
session_write_close();
}

$this->assertNull(Request::getVar('any_key', null, 'session'));
$this->assertEquals('default_val', Request::getVar('any_key', 'default_val', 'session'));

// Restart session for other tests
@session_start();
}

public function testGetIntFromSession()
{
if (session_status() !== PHP_SESSION_ACTIVE) {
@session_start();
}
$varname = 'RequestTestSessionInt';
$_SESSION[$varname] = '42';

$this->assertEquals(42, Request::getInt($varname, 0, 'session'));

unset($_SESSION[$varname]);
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test mutates global session state (session_write_close() / session_start()) without ensuring it’s restored if an assertion fails. Wrapping the state changes and cleanup in try/finally (and restoring the prior session status) would prevent session state from leaking into subsequent tests.

Suggested change
if (session_status() !== PHP_SESSION_ACTIVE) {
@session_start();
}
$varname = 'RequestTestSession';
$_SESSION[$varname] = 'session_value';
$this->assertEquals('session_value', Request::getVar($varname, null, 'session'));
unset($_SESSION[$varname]);
}
public function testGetVarSessionReturnsDefaultWhenKeyMissing()
{
if (session_status() !== PHP_SESSION_ACTIVE) {
@session_start();
}
$this->assertNull(Request::getVar('no_such_session_key', null, 'session'));
$this->assertEquals('fallback', Request::getVar('no_such_session_key', 'fallback', 'session'));
}
public function testGetVarSessionReturnsDefaultWhenNoSession()
{
// When session is not active, getVar should return the default
if (session_status() === PHP_SESSION_ACTIVE) {
session_write_close();
}
$this->assertNull(Request::getVar('any_key', null, 'session'));
$this->assertEquals('default_val', Request::getVar('any_key', 'default_val', 'session'));
// Restart session for other tests
@session_start();
}
public function testGetIntFromSession()
{
if (session_status() !== PHP_SESSION_ACTIVE) {
@session_start();
}
$varname = 'RequestTestSessionInt';
$_SESSION[$varname] = '42';
$this->assertEquals(42, Request::getInt($varname, 0, 'session'));
unset($_SESSION[$varname]);
$previousStatus = session_status();
$varname = 'RequestTestSession';
try {
if ($previousStatus !== PHP_SESSION_ACTIVE) {
@session_start();
}
$_SESSION[$varname] = 'session_value';
$this->assertEquals('session_value', Request::getVar($varname, null, 'session'));
} finally {
if (isset($_SESSION[$varname])) {
unset($_SESSION[$varname]);
}
$currentStatus = session_status();
if ($previousStatus !== $currentStatus) {
if ($previousStatus === PHP_SESSION_ACTIVE && $currentStatus !== PHP_SESSION_ACTIVE) {
@session_start();
} elseif ($previousStatus !== PHP_SESSION_ACTIVE && $currentStatus === PHP_SESSION_ACTIVE) {
session_write_close();
}
}
}
}
public function testGetVarSessionReturnsDefaultWhenKeyMissing()
{
$previousStatus = session_status();
try {
if ($previousStatus !== PHP_SESSION_ACTIVE) {
@session_start();
}
$this->assertNull(Request::getVar('no_such_session_key', null, 'session'));
$this->assertEquals('fallback', Request::getVar('no_such_session_key', 'fallback', 'session'));
} finally {
$currentStatus = session_status();
if ($previousStatus !== $currentStatus) {
if ($previousStatus === PHP_SESSION_ACTIVE && $currentStatus !== PHP_SESSION_ACTIVE) {
@session_start();
} elseif ($previousStatus !== PHP_SESSION_ACTIVE && $currentStatus === PHP_SESSION_ACTIVE) {
session_write_close();
}
}
}
}
public function testGetVarSessionReturnsDefaultWhenNoSession()
{
$previousStatus = session_status();
try {
// When session is not active, getVar should return the default
if ($previousStatus === PHP_SESSION_ACTIVE) {
session_write_close();
}
$this->assertNull(Request::getVar('any_key', null, 'session'));
$this->assertEquals('default_val', Request::getVar('any_key', 'default_val', 'session'));
} finally {
$currentStatus = session_status();
if ($previousStatus !== $currentStatus) {
if ($previousStatus === PHP_SESSION_ACTIVE && $currentStatus !== PHP_SESSION_ACTIVE) {
@session_start();
} elseif ($previousStatus !== PHP_SESSION_ACTIVE && $currentStatus === PHP_SESSION_ACTIVE) {
session_write_close();
}
}
}
}
public function testGetIntFromSession()
{
$previousStatus = session_status();
$varname = 'RequestTestSessionInt';
try {
if ($previousStatus !== PHP_SESSION_ACTIVE) {
@session_start();
}
$_SESSION[$varname] = '42';
$this->assertEquals(42, Request::getInt($varname, 0, 'session'));
} finally {
if (isset($_SESSION[$varname])) {
unset($_SESSION[$varname]);
}
$currentStatus = session_status();
if ($previousStatus !== $currentStatus) {
if ($previousStatus === PHP_SESSION_ACTIVE && $currentStatus !== PHP_SESSION_ACTIVE) {
@session_start();
} elseif ($previousStatus !== PHP_SESSION_ACTIVE && $currentStatus === PHP_SESSION_ACTIVE) {
session_write_close();
}
}
}

Copilot uses AI. Check for mistakes.
Allow reading from and writing to $_SESSION via the $hash parameter.
When session is not active, getVar() returns the default value and
setVar() silently skips the write, preventing errors.
@mambax7 mambax7 force-pushed the feature/add-session-hash-to-request branch from a6dc00a to 69edcc8 Compare March 11, 2026 15:12
- Add setUp()/tearDown() session lifecycle management instead of
  per-test @session_start() calls with implicit cross-test dependencies
- Add requireActiveSession() helper that skips tests when sessions
  are unavailable, removing @ error suppression
- Complete @param $hash docs on getVar(), get(), setVar(), and set()
  to list all supported hashes (GET, POST, FILES, COOKIE, ENV, SERVER,
  SESSION, METHOD, DEFAULT/REQUEST)
- Document setVar() SESSION no-op behavior when no session is active
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/Request.php (2)

32-35: ⚠️ Potential issue | 🟡 Minor

Add the missing blank line before the constants block.

CI is still failing on Line 35 with “Header blocks must be separated by a single blank line.” Insert a blank line between this docblock and const MASK_NO_TRIM.

As per coding guidelines, src/**/*.php: Follow PSR-12 with a line length limit of 200 characters.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Request.php` around lines 32 - 35, Add a single blank line between the
docblock for "Available masks for cleaning variables" and the constants block so
the header block is separated from the code; specifically insert one empty line
above the declaration of const MASK_NO_TRIM in Request.php (the constants block
containing MASK_NO_TRIM) to satisfy PSR-12 header separation and the CI rule.

122-132: ⚠️ Potential issue | 🟡 Minor

Remove the trailing blank lines inside getVar().

The extra blank lines after the assignments in the if/else chain are still tripping PHPCS on Line 125 and Line 131.

♻️ Suggested fix
         if (isset($input[$name]) && null !== $input[$name]) {
             // Get the variable from the input hash and clean it
             $var = static::cleanVar($input[$name], $mask, $type);
-
         } elseif (null !== $default) {
             // Clean the default value
             $var = static::cleanVar($default, $mask, $type);
         } else {
             $var = $default;
-
         }

As per coding guidelines, src/**/*.php: Follow PSR-12 with a line length limit of 200 characters.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Request.php` around lines 122 - 132, Remove the extra blank lines inside
the getVar() if/elseif/else blocks so PHPCS/PSR-12 complaints are resolved: in
the code that assigns $var (the branch using static::cleanVar($input[$name],
$mask, $type), the elseif branch using static::cleanVar($default, $mask, $type),
and the final else setting $var = $default) delete the blank lines immediately
after those assignments so each branch contains only the assignment and no
trailing empty lines.
tests/unit/RequestTest.php (1)

17-35: ⚠️ Potential issue | 🟠 Major

Clear $_SESSION in the fixture hooks.

Starting or reopening the session does not reset its contents. If one of the new session tests fails before its unset(), that key survives into later methods and makes the class order-dependent. Reset the session array in setUp()/tearDown() so these cases stay isolated.

♻️ Suggested fix
     protected function setUp(): void
     {
         $this->object = new Request;
         // Ensure a consistent session state for tests that need it
         if (session_status() !== PHP_SESSION_ACTIVE) {
             session_start();
         }
+        $_SESSION = array();
     }
@@
     protected function tearDown(): void
     {
         // Restore active session state after each test
         if (session_status() !== PHP_SESSION_ACTIVE) {
             session_start();
         }
+        $_SESSION = array();
     }

As per coding guidelines, tests/unit/**/*.php: Tests must be fully isolated with no XOOPS installation required.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/RequestTest.php` around lines 17 - 35, The tests leave session
state between runs: in RequestTest::setUp() and RequestTest::tearDown() ensure
the session is started (session_status() check) and then reset $_SESSION to an
empty array (e.g., $_SESSION = []) so each test starts/ends with a clean
session; update the setUp() and tearDown() methods to start the session if
needed and immediately clear $_SESSION to guarantee isolation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/Request.php`:
- Around line 32-35: Add a single blank line between the docblock for "Available
masks for cleaning variables" and the constants block so the header block is
separated from the code; specifically insert one empty line above the
declaration of const MASK_NO_TRIM in Request.php (the constants block containing
MASK_NO_TRIM) to satisfy PSR-12 header separation and the CI rule.
- Around line 122-132: Remove the extra blank lines inside the getVar()
if/elseif/else blocks so PHPCS/PSR-12 complaints are resolved: in the code that
assigns $var (the branch using static::cleanVar($input[$name], $mask, $type),
the elseif branch using static::cleanVar($default, $mask, $type), and the final
else setting $var = $default) delete the blank lines immediately after those
assignments so each branch contains only the assignment and no trailing empty
lines.

In `@tests/unit/RequestTest.php`:
- Around line 17-35: The tests leave session state between runs: in
RequestTest::setUp() and RequestTest::tearDown() ensure the session is started
(session_status() check) and then reset $_SESSION to an empty array (e.g.,
$_SESSION = []) so each test starts/ends with a clean session; update the
setUp() and tearDown() methods to start the session if needed and immediately
clear $_SESSION to guarantee isolation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 391cc42e-05ac-42c7-b124-4b1e143b26d4

📥 Commits

Reviewing files that changed from the base of the PR and between a6dc00a and 2fcef08.

📒 Files selected for processing (2)
  • src/Request.php
  • tests/unit/RequestTest.php

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

Comments suppressed due to low confidence (1)

src/Request.php:404

  • The setVar() PHPDoc types look too narrow: $value isn’t necessarily a string (it’s mixed in practice), and the return value can be null/mixed (not always string). Since this docblock was updated, it would be good to align @param/@return types with the actual signature/behavior to help static analysis and API consumers.
     * For SESSION, the write is silently skipped if no session is active.
     *
     * @param string $name      Name
     * @param string $value     Value
     * @param string $hash      Hash (GET, POST, REQUEST, COOKIE, FILES, ENV, SERVER, SESSION, METHOD)
     * @param bool   $overwrite Boolean
     *
     * @return string Previous value
     */

Comment on lines +20 to +23
// Ensure a consistent session state for tests that need it
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting a session in setUp() for every test can emit warnings/failures in environments where sessions are disabled or headers can’t be modified, and it also forces global state changes on tests that don’t need sessions. Consider removing session_start() from setUp() and instead have requireActiveSession() attempt to start the session (e.g., with @session_start()) and mark the specific session-dependent tests skipped if it can’t be activated.

Copilot uses AI. Check for mistakes.
Comment on lines +32 to +35
// Restore active session state after each test
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tearDown() currently tries to start a session when it’s not active, which doesn’t really “restore” state and can re-introduce the same session_start() warnings after a test closes the session (including skipped tests). Prefer leaving session state alone in tearDown(), or explicitly closing/cleaning up (e.g., session_write_close() and clearing $_SESSION) if you need isolation.

Suggested change
// Restore active session state after each test
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
}
// Do not modify session state here; individual tests or setUp() manage it.
$this->object = null;

Copilot uses AI. Check for mistakes.
* Helper to ensure an active session, skipping the test if unavailable.
*/
private function requireActiveSession(): void
{
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requireActiveSession() only checks session_status() but never attempts to start the session, so its skip message can be misleading and it relies on setUp() having already started the session. If you move session initialization out of setUp(), update this helper to attempt to start the session (and skip only if activation fails).

Suggested change
{
{
if (session_status() === PHP_SESSION_ACTIVE) {
return;
}
// Try to start a session if one is not already active.
if (headers_sent()) {
$this->markTestSkipped('Cannot start a session: headers already sent in this environment.');
}
try {
@session_start();
} catch (\Throwable $exception) {
$this->markTestSkipped('Cannot start a session in this environment: ' . $exception->getMessage());
}

Copilot uses AI. Check for mistakes.
Comment on lines +355 to +364
$this->requireActiveSession();
session_write_close();

$varname = 'XMF_TEST_SESSION_NO_WRITE';
Request::setVar($varname, 'should_not_persist', 'session');

// tearDown() restarts the session; verify nothing was set
session_start();
$this->assertArrayNotHasKey($varname, $_SESSION);
}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test assumes the session doesn’t already contain $varname. If a prior run/environment leaves the key set, the final assertArrayNotHasKey() can fail even though setVar() correctly skipped writing. Make the test deterministic by unsetting the key before session_write_close() and after session_start(), or by using a fresh session id / clearing $_SESSION for the test.

Copilot uses AI. Check for mistakes.
Comment on lines 61 to 75
* - post $_POST
* - get $_GET
* - files $_FILES
* - cookie $_COOKIE
* - env $_ENV
* - server $_SERVER
* - session $_SESSION (returns default if no active session)
* - method via current $_SERVER['REQUEST_METHOD']
* - default $_REQUEST
*
* @param string $name Variable name
* @param mixed $default Default value if the variable does not exist
* @param string $hash Source of variable value (POST, GET, FILES, COOKIE, METHOD)
* @param string $hash Source of variable value (GET, POST, FILES, COOKIE, ENV, SERVER, SESSION, METHOD, DEFAULT/REQUEST)
* @param string $type Return type for the variable (INT, FLOAT, BOOLEAN, WORD,
* ALPHANUM, CMD, BASE64, STRING, ARRAY, PATH, NONE) For more
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getVar() docblock now advertises SESSION support, but the proxy methods (e.g., getInt/getFloat/getBool/etc.) still document only POST/GET/FILES/COOKIE/METHOD even though they accept the same $hash values via getVar(). Consider updating those docblocks too so the public API documentation stays consistent.

Copilot uses AI. Check for mistakes.
- Remove session_start() from setUp()/tearDown() — it emits warnings
  in CLI environments where headers are already sent, causing PHPUnit
  to convert them to test failures
- Add startTestSession() helper that disables cookie-based session IDs
  (session.use_cookies=0, session.use_only_cookies=0) before starting,
  then asserts PHP_SESSION_ACTIVE or skips the test
- Add closeTestSession() helper that closes session and asserts it is
  no longer active
- Each session test is self-contained with try/finally cleanup, no
  cross-test state dependencies
- No @ error suppression anywhere
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

Comment on lines +272 to +275
ini_set('session.use_cookies', '0');
ini_set('session.use_only_cookies', '0');
ini_set('session.cache_limiter', '');
session_start();
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

startTestSession() mutates global session ini settings (session.use_cookies, session.use_only_cookies, session.cache_limiter) but never restores the previous values. This can leak configuration into other unit tests running in the same process. Capture the previous ini values and restore them in closeTestSession()/tearDown() (or via a try/finally around the mutation).

Copilot uses AI. Check for mistakes.
Comment on lines +395 to +399
// Start a fresh session and verify nothing leaked
$this->startTestSession();
try {
$this->assertArrayNotHasKey($varname, $_SESSION);
} finally {
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test says “Start a fresh session”, but closeTestSession() only calls session_write_close() and does not destroy the session or reset the session id, so the next session_start() may resume the same session. Either update the comment/assertion to match the behavior, or ensure a truly new session (e.g., destroy/reset/regenerate id) before asserting no data leaked.

Copilot uses AI. Check for mistakes.
Comment on lines +275 to +276
session_start();
if (session_status() !== PHP_SESSION_ACTIVE) {
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

startTestSession() calls session_start() directly. With failOnWarning="true" in phpunit.xml, any warning emitted by session_start() (e.g., session misconfiguration, headers already sent) will fail the test run before markTestSkipped() is reached. Consider using @session_start() (or checking the boolean return value) and skipping when startup fails, so environments that can’t start sessions don’t produce warnings.

Suggested change
session_start();
if (session_status() !== PHP_SESSION_ACTIVE) {
$started = @session_start();
if ($started === false || session_status() !== PHP_SESSION_ACTIVE) {

Copilot uses AI. Check for mistakes.
PHPStan 2.1 no longer reports the array_filter callable error for
Metagen.php, causing the baseline entry to be unmatched. This setting
prevents unmatched baseline entries from being treated as errors during
incremental baseline cleanup.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/unit/RequestTest.php`:
- Around line 267-293: The helper leaves INI changes and $_SESSION around;
modify startTestSession() to save the original INI values (e.g., into a private
property like $savedSessionIni) before calling
ini_set('session.use_cookies','0'), ini_set('session.use_only_cookies','0'),
ini_set('session.cache_limiter',''), then start the session as before; update
closeTestSession() to fully clean up by, when session_status() ===
PHP_SESSION_ACTIVE, unsetting session data (session_unset() or $_SESSION = []),
calling session_destroy(), calling session_write_close(), and then restoring the
original INI values from $savedSessionIni so process-wide settings and $_SESSION
are reset for other tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f84febeb-82f0-416a-8856-19d1c5bd69db

📥 Commits

Reviewing files that changed from the base of the PR and between 2fcef08 and ae74064.

📒 Files selected for processing (1)
  • tests/unit/RequestTest.php

@codecov
Copy link

codecov bot commented Mar 11, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 33.62%. Comparing base (36d9d5b) to head (eec0b4d).
⚠️ Report is 6 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff              @@
##             master     #147      +/-   ##
============================================
+ Coverage     33.23%   33.62%   +0.38%     
- Complexity     1058     1064       +6     
============================================
  Files            45       45              
  Lines          2750     2766      +16     
============================================
+ Hits            914      930      +16     
  Misses         1836     1836              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

phpstan.neon Outdated
excludePaths:
- tests/
treatPhpDocTypesAsCertain: false
reportUnmatchedIgnoredErrors: false
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is scoped to adding SESSION support in Request, but it also changes PHPStan behavior by disabling reportUnmatchedIgnoredErrors. Turning this off reduces signal from static analysis (stale ignoreErrors entries will no longer be reported) and isn’t mentioned in the PR description; consider reverting it (or documenting why it’s required and addressing the underlying unmatched ignore instead).

Copilot uses AI. Check for mistakes.
  src/Request.php

  1. Proxy method docblocks updated — all 10 methods (getInt, getFloat, getBool, getWord, getCmd, getString, getArray, getText, getUrl, getPath) now list
  (POST, GET, FILES, COOKIE, ENV, SERVER, SESSION, METHOD) instead of just (POST, GET, FILES, COOKIE, METHOD)
  2. PSR-12 fix — removed extra blank lines at lines 125 and 131 inside getVar() if/else blocks

  tests/unit/RequestTest.php

  3. startTestSession() — now saves original INI values before overriding, uses @session_start() to suppress warnings, and clears $_SESSION on entry
  4. closeTestSession() — now calls $_SESSION = [] and session_destroy() instead of just session_write_close()
  5. tearDown() — added full cleanup: destroys active session, clears $_SESSION, restores saved INI values
  6. Fixed misleading comment — "Start a fresh session" → "Re-open session and verify the write was skipped"

  phpstan.neon + phpstan-baseline.neon

  7. Removed reportUnmatchedIgnoredErrors: false and regenerated baseline (done earlier)
@mambax7 mambax7 requested a review from Copilot March 11, 2026 17:41
@sonarqubecloud
Copy link

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

Comment on lines +306 to +317
private function closeTestSession(): void
{
if (session_status() === PHP_SESSION_ACTIVE) {
$_SESSION = [];
session_destroy();
}
$this->assertNotSame(
PHP_SESSION_ACTIVE,
session_status(),
'Session should not be active after close.'
);
}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

session_destroy() clears session data but does not actually close the session in the current process, so session_status() can remain PHP_SESSION_ACTIVE. This makes the assertion in closeTestSession() unreliable and can leave a session active across tests. Prefer explicitly closing the session (e.g., call session_write_close()/session_abort() after clearing) and only use session_destroy() if you also close the session afterward.

Copilot uses AI. Check for mistakes.
// Ensure full cleanup even if a test fails mid-way
if (session_status() === PHP_SESSION_ACTIVE) {
$_SESSION = [];
session_destroy();
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In tearDown(), calling session_destroy() does not close the session; session_status() may still be PHP_SESSION_ACTIVE, which can leak session state into subsequent tests. Consider explicitly closing the session (e.g., session_write_close()/session_abort()) as part of teardown, and only destroy after ensuring the session is closed.

Suggested change
session_destroy();
session_destroy();
if (session_status() === PHP_SESSION_ACTIVE) {
session_write_close();
}

Copilot uses AI. Check for mistakes.
Comment on lines +110 to +116
case 'SESSION':
if (session_status() !== PHP_SESSION_ACTIVE) {
$input = [];
break;
}
$input = &$_SESSION;
break;
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

session_status() and PHP_SESSION_ACTIVE are provided by the session extension; if ext-session is disabled, these calls/constants can be undefined and will fatal when $hash is SESSION. Since composer.json doesn’t declare ext-session, consider guarding the SESSION branch (and treating it as “no active session”) when session functions/constants aren’t available.

Copilot uses AI. Check for mistakes.
Comment on lines +449 to +450
if (session_status() === PHP_SESSION_ACTIVE) {
$_SESSION[$name] = $value;
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request::setVar(..., 'session') currently assumes the session extension is enabled (session_status()/PHP_SESSION_ACTIVE). If ext-session is disabled, this will fatal instead of “silently skipping” as documented. Guard this branch so that in environments without sessions it safely no-ops.

Suggested change
if (session_status() === PHP_SESSION_ACTIVE) {
$_SESSION[$name] = $value;
if (function_exists('session_status') && defined('PHP_SESSION_ACTIVE')) {
if (session_status() === PHP_SESSION_ACTIVE) {
$_SESSION[$name] = $value;
}

Copilot uses AI. Check for mistakes.
Comment on lines +509 to +513
case 'SESSION':
if (session_status() !== PHP_SESSION_ACTIVE) {
$input = [];
break;
}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same concern as getVar(): the SESSION branch uses session_status()/PHP_SESSION_ACTIVE, which can be undefined if the session extension is disabled. Guarding here would prevent fatals and let Request::get('session') degrade to an empty array as intended.

Suggested change
case 'SESSION':
if (session_status() !== PHP_SESSION_ACTIVE) {
$input = [];
break;
}
case 'SESSION':
if (!function_exists('session_status') || !defined('PHP_SESSION_ACTIVE')) {
$input = [];
break;
}
if (session_status() !== PHP_SESSION_ACTIVE) {
$input = [];
break;
}
if (!isset($_SESSION)) {
$input = [];
break;
}

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Request.php`:
- Around line 144-145: Update the docblocks for the Request proxy getter methods
to include the fallback values DEFAULT and REQUEST in the `@param` $hash
description (same set accepted by getVar()), so calls like
Request::getArray(..., 'request') are documented; locate the docblocks for the
proxy getters (e.g., Request::getArray and the other similar wrapper methods
that call getVar()) and add "DEFAULT/REQUEST" to the list of supported hash
sources in each `@param` $hash line.

In `@tests/unit/RequestTest.php`:
- Around line 427-440: Add an assertion for the inactive-session branch in
testHasVarSession: after unsetting $_SESSION[$varname] and calling
$this->closeTestSession() (use the existing startTestSession/closeTestSession
helpers), call Request::hasVar($varname, 'session') and assertFalse to verify
SESSION is treated as empty when no session is active; keep the existing
try/finally cleanup and ensure the inactive assertion runs after the session is
closed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 49242484-c490-4e47-97e1-463b8d58ad05

📥 Commits

Reviewing files that changed from the base of the PR and between ae74064 and e3cb224.

📒 Files selected for processing (2)
  • src/Request.php
  • tests/unit/RequestTest.php

Comment on lines +144 to 145
* @param string $hash Where the var should come from (POST, GET, FILES, COOKIE, ENV, SERVER, SESSION, METHOD)
*
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Include DEFAULT/REQUEST in the proxy getter docs.

These wrappers still accept the same fallback hashes as getVar(), but the updated @param $hash text now lists only the explicit superglobals plus SESSION. That makes supported calls like Request::getArray(..., 'request') look undocumented.

📝 Suggested docblock tweak
- * `@param` string $hash    Where the var should come from (POST, GET, FILES, COOKIE, ENV, SERVER, SESSION, METHOD)
+ * `@param` string $hash    Where the var should come from (POST, GET, FILES, COOKIE, ENV, SERVER, SESSION, METHOD, DEFAULT/REQUEST)

Also applies to: 162-163, 180-181, 198-199, 215-216, 233-234, 249-250, 263-264, 277-278, 291-292, 305-306, 320-321

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Request.php` around lines 144 - 145, Update the docblocks for the Request
proxy getter methods to include the fallback values DEFAULT and REQUEST in the
`@param` $hash description (same set accepted by getVar()), so calls like
Request::getArray(..., 'request') are documented; locate the docblocks for the
proxy getters (e.g., Request::getArray and the other similar wrapper methods
that call getVar()) and add "DEFAULT/REQUEST" to the list of supported hash
sources in each `@param` $hash line.

Comment on lines +427 to +440
public function testHasVarSession()
{
$this->startTestSession();
$varname = 'RequestTestHasVarSession';

try {
$this->assertFalse(Request::hasVar($varname, 'session'));
$_SESSION[$varname] = 'exists';
$this->assertTrue(Request::hasVar($varname, 'session'));
} finally {
unset($_SESSION[$varname]);
$this->closeTestSession();
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add the inactive-session hasVar() case.

The new behavior path is “treat SESSION as empty when no session is active,” but this test only covers the active-session branch. A direct assertFalse(Request::hasVar(..., 'session')) after closing the session would lock that edge case down too. As per coding guidelines, tests/**/*.php: Review test code for proper assertions, test isolation, and edge case coverage.

🧰 Tools
🪛 PHPMD (2.15.0)

[error] 427-440: testHasVarSession accesses the super-global variable $_SESSION. (undefined)

(Superglobals)


[error] 427-440: testHasVarSession accesses the super-global variable $_SESSION. (undefined)

(Superglobals)


[error] 433-433: Avoid using static access to class '\Xmf\Request' in method 'testHasVarSession'. (undefined)

(StaticAccess)


[error] 435-435: Avoid using static access to class '\Xmf\Request' in method 'testHasVarSession'. (undefined)

(StaticAccess)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/RequestTest.php` around lines 427 - 440, Add an assertion for the
inactive-session branch in testHasVarSession: after unsetting
$_SESSION[$varname] and calling $this->closeTestSession() (use the existing
startTestSession/closeTestSession helpers), call Request::hasVar($varname,
'session') and assertFalse to verify SESSION is treated as empty when no session
is active; keep the existing try/finally cleanup and ensure the inactive
assertion runs after the session is closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants