Skip to content

Conversation

@terabytesoftw
Copy link
Member

@terabytesoftw terabytesoftw commented Aug 11, 2025

Q A
Is bugfix? ✔️
New feature?
Breaks BC?

Summary by CodeRabbit

  • New Features

    • Server port detection now works with PSR-7 requests and the existing request system, respects configurable forwarded headers and trusted hosts, and falls back gracefully when headers or adapter data are unavailable.
    • Retrieval of uploaded files from PSR-7 requests with automatic conversion to native upload objects for seamless file handling.
  • Tests

    • Added data-driven and scenario tests covering trusted/untrusted proxies, multiple forwarded headers, fallbacks, numeric casting, request resets, and independent requests with different ports.

terabytesoftw and others added 2 commits August 11, 2025 01:34
… in `Request` class to ensure correct handling of forwarded ports and server parameters.
@coderabbitai
Copy link

coderabbitai bot commented Aug 11, 2025

Walkthrough

Adds PSR-7–aware server port retrieval and PSR-7 uploaded-file conversion to Request; introduces data-driven server-port tests and a provider enumerating port-resolution scenarios and edge cases.

Changes

Cohort / File(s) Summary of changes
PSR-7-aware Request enhancements
src/http/Request.php
Added `public function getServerPort(): int
Server port resolution tests
tests/adapter/ServerParamsPsr7Test.php
Added tests (#[Group('server-port')]) verifying port resolution across scenarios: forwarded-header handling, trusted vs untrusted hosts, numeric casting, null/missing/invalid SERVER_PORT, comma-separated header values, precedence after request reset, and independent requests with different ports; imports provider.
Test data provider
tests/provider/ServerParamsPsr7Provider.php
New ServerParamsPsr7Provider::serverPortCases() returning structured cases for port resolution (config: portHeaders, secureHeaders, trustedHosts; PSR-7 serverParams; headers; expected serverParams; expected port; message).

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Request
  participant Adapter
  participant PSR7 as "PSR-7 Request"

  Client->>Request: getServerPort()
  alt Adapter present
    Request->>Adapter: getHeaders()
    Adapter-->>Request: header values
    Note right of Request: Check configured portHeaders\nfor numeric forwarded values and trusted host
    alt found numeric forwarded port
      Request-->>Client: return int port
    else
      Request->>Adapter: getServerParams()
      Adapter-->>Request: SERVER_PORT
      alt numeric SERVER_PORT
        Request-->>Client: return int port
      else
        Request-->>Client: return null
      end
    end
  else No adapter
    Request-->>Client: return parent::getServerPort()
  end
Loading
sequenceDiagram
  participant Client
  participant Request
  participant Adapter
  participant PSR7 as "PSR-7 UploadedFiles"

  Client->>Request: getUploadedFiles()
  alt Adapter present
    Request->>Adapter: getUploadedFiles()
    Adapter-->>Request: PSR-7 UploadedFileInterface[]
    Request->>Request: convert recursively to Yii UploadedFile[]
    Request-->>Client: return Yii UploadedFile[]
  else No adapter
    Request-->>Client: return []
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15 minutes

Possibly related PRs

Poem

I nibble headers, count each port with care,
I stitch lost files from PSR-7 hair.
Pawed conversions, tidy and spry,
Ports found true, uploads reply.
🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between db07386 and 9ca786a.

📒 Files selected for processing (1)
  • tests/provider/ServerParamsPsr7Provider.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/provider/ServerParamsPsr7Provider.php
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: phpunit / PHP 8.3-windows-latest
  • GitHub Check: phpunit / PHP 8.4-windows-latest
  • GitHub Check: phpunit / PHP 8.2-windows-latest
  • GitHub Check: phpunit / PHP 8.1-windows-latest
  • GitHub Check: mutation / PHP 8.3-ubuntu-latest
  • GitHub Check: mutation / PHP 8.3-ubuntu-latest
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix_mini_71

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • 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

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

🧹 Nitpick comments (6)
src/http/Request.php (2)

650-651: Prefer getHeaders() over magic property to access HeaderCollection

Using $this->getHeaders() is clearer and avoids relying on magic property resolution.

Apply this minimal change:

-                if ($this->headers->has($portHeader)) {
-                    $port = $this->headers->get($portHeader);
+                $headers = $this->getHeaders();
+                if ($headers->has($portHeader)) {
+                    $port = $headers->get($portHeader);

648-662: Consolidate header access and validation (combined improvement)

If you prefer a single diff, this folds both the header access nit and input validation into one change.

-        if ($this->adapter !== null) {
-            foreach ($this->portHeaders as $portHeader) {
-                if ($this->headers->has($portHeader)) {
-                    $port = $this->headers->get($portHeader);
-
-                    if (is_numeric($port)) {
-                        return (int) $port;
-                    }
-                }
-            }
-
-            $port = $this->getServerParam('SERVER_PORT');
-
-            return is_numeric($port) ? (int) $port : null;
-        }
+        if ($this->adapter !== null) {
+            $headers = $this->getHeaders();
+            foreach ($this->portHeaders as $portHeader) {
+                if ($headers->has($portHeader)) {
+                    $raw = $headers->get($portHeader);
+                    $first = is_array($raw) ? reset($raw) : (string) $raw;
+                    $candidate = trim(strtok($first, ','));
+                    $validated = filter_var(
+                        $candidate,
+                        FILTER_VALIDATE_INT,
+                        ['options' => ['min_range' => 1, 'max_range' => 65535]],
+                    );
+                    if ($validated !== false) {
+                        return $validated;
+                    }
+                }
+            }
+
+            $rawServerPort = $this->getServerParam('SERVER_PORT');
+            $first = is_array($rawServerPort) ? reset($rawServerPort) : $rawServerPort;
+            if (is_string($first) || is_int($first)) {
+                $validated = filter_var(
+                    trim((string) $first),
+                    FILTER_VALIDATE_INT,
+                    ['options' => ['min_range' => 1, 'max_range' => 65535]],
+                );
+                return $validated !== false ? $validated : null;
+            }
+            return null;
+        }
tests/adapter/ServerParamsPsr7Test.php (4)

20-51: Good untrusted-host case; consider restoring $_SERVER after test

Solid assertion of header filtering. To avoid cross-test leakage, snapshot and restore $_SERVER in a finally block or use tearDown to reset.

$prev = $_SERVER;
try {
    $_SERVER = ['REMOTE_ADDR' => '192.168.1.100'];
    // ...test body...
} finally {
    $_SERVER = $prev;
}

183-212: Trusted proxy coverage looks correct

Forwarded port wins when host is trusted. Same minor note about restoring $_SERVER applies here.


549-567: Covers numeric-string to int coercion

As a micro-nit, store the value once to avoid calling getServerPort() twice, but it’s minor in tests.


569-615: Multiple configured headers precedence covered

Nice. Consider adding a case for comma-separated header values (e.g. 'X-Forwarded-Port: 9443, 7443') to ensure only the first numeric value is used.

I can add a test like:

public function testReturnServerPortFromCommaSeparatedForwardedHeader(): void
{
    $_SERVER = ['REMOTE_ADDR' => '127.0.0.1'];
    $request = new Request([
        'portHeaders' => ['X-Forwarded-Port'],
        'secureHeaders' => ['X-Forwarded-Port'],
        'trustedHosts' => ['127.0.0.1'],
    ]);
    $request->setPsr7Request(
        FactoryHelper::createRequest(
            'GET',
            '/test',
            ['X-Forwarded-Port' => '9443, 7443'],
            serverParams: ['SERVER_PORT' => '80', 'REMOTE_ADDR' => '127.0.0.1'],
        ),
    );
    self::assertSame(9443, $request->getServerPort());
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4bdcc2a and 9ddb4d2.

📒 Files selected for processing (2)
  • src/http/Request.php (2 hunks)
  • tests/adapter/ServerParamsPsr7Test.php (4 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: terabytesoftw
PR: yii2-extensions/psr-bridge#78
File: src/http/Request.php:0-0
Timestamp: 2025-08-10T13:17:04.252Z
Learning: In the yii2-extensions/psr-bridge project, PHPStan type annotations should be defensive when dealing with external input from PSR-7 sources. For methods like `getUploadedFiles()` and `convertPsr7ToUploadedFiles()`, use `mixed` in the return type annotation to account for untrusted input values, even if the implementation filters them.
🧬 Code Graph Analysis (1)
tests/adapter/ServerParamsPsr7Test.php (2)
src/http/Request.php (4)
  • Request (55-884)
  • setPsr7Request (802-805)
  • getServerPort (646-665)
  • reset (737-740)
tests/support/FactoryHelper.php (2)
  • FactoryHelper (46-268)
  • createRequest (68-89)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: mutation / PHP 8.3-ubuntu-latest
  • GitHub Check: mutation / PHP 8.3-ubuntu-latest
🔇 Additional comments (10)
src/http/Request.php (1)

20-20: Import of is_numeric looks good

Useful for numeric gate checks. No issues.

tests/adapter/ServerParamsPsr7Test.php (9)

215-229: Empty array -> null case is correct

Covers malformed server param shape well.


231-245: Absent SERVER_PORT -> null case is correct


247-261: Non-string SERVER_PORT -> null case is correct


263-277: Explicit null SERVER_PORT -> null case is correct


616-646: Forwarded header preferred over server param (trusted) — correct


648-664: Falls back to SERVER_PORT when header absent — correct


665-690: Fallback to SERVER_PORT when all headers empty — correct


769-808: Reset path validated

Good coverage ensuring independence across adapter resets.


809-847: Independence across instances validated

Clear and correct assertions.

…aders variable for better readability and performance.
@terabytesoftw
Copy link
Member Author

@coderabbitai Server port tests could be summarized using a data provider such as testReturnServerParamFromPsr7RequestCases()

@coderabbitai
Copy link

coderabbitai bot commented Aug 11, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's examine the server port tests in ServerParamsPsr7Test.php
cat tests/adapter/ServerParamsPsr7Test.php | grep -A 30 "testIgnoreForwardedPortWhenRequestFromUntrustedHost\|testReturnForwardedPortWhenRequestFromTrustedProxy\|testReturnNullWhenPsr7RequestServerPortIsEmptyArray"

Length of output: 3263


🏁 Script executed:

#!/bin/bash
# Let's look for all server port test methods
rg "public function test.*ServerPort" tests/adapter/ServerParamsPsr7Test.php -A 2

# Also search for the data provider pattern example mentioned by the user
rg "testReturnServerParamFromPsr7RequestCases|@dataProvider|serverParamProvider" tests/adapter/ServerParamsPsr7Test.php -B 2 -A 10

Length of output: 2189


🏁 Script executed:

#!/bin/bash
# Let's find and examine the RequestProvider class
fd "RequestProvider.php" --type f --exec cat {}

# Also, let's get a complete view of one of the server port tests to understand the full structure
cat tests/adapter/ServerParamsPsr7Test.php | grep -A 20 "testReturnServerPortFromForwardedHeaderWhenAdapterIsSet"

Length of output: 37799


@terabytesoftw Great suggestion! Using data providers would indeed make the server port tests more maintainable and reduce code duplication. Here's a refactored version:

Add to RequestProvider.php:

/**
 * @phpstan-return array<string, array{
 *     array<string, mixed>|null,
 *     array<string, mixed>,
 *     array<string, mixed>,
 *     int|null,
 *     string
 * }>
 */
public static function serverPortCases(): array
{
    return [
        'untrustedHostIgnoresForwardedPort' => [
            ['portHeaders' => ['X-Forwarded-Port'], 'secureHeaders' => ['X-Forwarded-Port'], 'trustedHosts' => ['10.0.0.0/24']],
            ['REMOTE_ADDR' => '192.168.1.100'],
            ['X-Forwarded-Port' => '443', 'serverParams' => ['REMOTE_ADDR' => '192.168.1.100', 'SERVER_PORT' => '8080']],
            8080,
            "'getServerPort()' should ignore forwarded port header from untrusted hosts and use 'SERVER_PORT'.",
        ],
        'trustedProxyReturnsForwardedPort' => [
            ['portHeaders' => ['X-Forwarded-Port'], 'trustedHosts' => ['10.0.0.0/24']],
            ['REMOTE_ADDR' => '10.0.0.1'],
            ['X-Forwarded-Port' => '443', 'serverParams' => ['SERVER_PORT' => '8080', 'REMOTE_ADDR' => '10.0.0.1']],
            443,
            "'getServerPort()' should return forwarded port when request comes from trusted proxy.",
        ],
        'serverPortEmptyArray' => [
            null,
            [],
            ['serverParams' => ['SERVER_PORT' => []]],
            null,
            "'SERVER_PORT' should return 'null' from PSR-7 'serverParams' when adapter is set but 'SERVER_PORT' is an empty array.",
        ],
        'serverPortNotPresent' => [
            null,
            [],
            ['serverParams' => ['HTTP_HOST' => 'example.com']],
            null,
            "'SERVER_PORT' should return 'null' from PSR-7 'serverParams' when adapter is set but 'SERVER_PORT' is not present.",
        ],
        'serverPortNotString' => [
            null,
            [],
            ['serverParams' => ['SERVER_PORT' => false]],
            null,
            "'SERVER_PORT' should return 'null' from PSR-7 'serverParams' when adapter is set but 'SERVER_PORT' is not a string.",
        ],
        'serverPortNull' => [
            null,
            [],
            ['serverParams' => ['SERVER_PORT' => null]],
            null,
            "'SERVER_PORT' should return 'null' from PSR-7 'serverParams' when adapter is set but 'SERVER_PORT' is null.",
        ],
        'serverPortNumericString' => [
            null,
            [],
            ['serverParams' => ['SERVER_PORT' => '8080']],
            8080,
            "'SERVER_PORT' should be cast to integer when PSR-7 'serverParams' contains numeric string.",
        ],
        'commaSeparatedForwardedHeader' => [
            ['portHeaders' => ['X-Forwarded-Port'], 'trustedHosts' => ['127.0.0.1']],
            ['REMOTE_ADDR' => '127.0.0.1'],
            ['X-Forwarded-Port' => '9443, 7443', 'serverParams' => ['REMOTE_ADDR' => '127.0.0.1', 'SERVER_PORT' => '8080']],
            9443,
            "'getServerPort()' should return first port from comma-separated forwarded header.",
        ],
        'firstValidForwardedHeaderMultipleConfigured' => [
            ['portHeaders' => ['X-Forwarded-Port', 'X-Real-Port', 'Front-End-Port'], 'trustedHosts' => ['127.0.0.1']],
            ['REMOTE_ADDR' => '127.0.0.1'],
            ['X-Real-Port' => '7000', 'Front-End-Port' => '9000', 'serverParams' => ['REMOTE_ADDR' => '127.0.0.1', 'SERVER_PORT' => '8080']],
            7000,
            "'getServerPort()' should return port from first valid configured header.",
        ],
        'forwardedHeaderWhenAdapterSet' => [
            ['portHeaders' => ['X-Forwarded-Port'], 'trustedHosts' => ['127.0.0.1']],
            ['REMOTE_ADDR' => '127.0.0.1'],
            ['X-Forwarded-Port' => '443', 'serverParams' => ['REMOTE_ADDR' => '127.0.0.1', 'SERVER_PORT' => '8080']],
            443,
            "'getServerPort()' should return forwarded port when adapter is set and header is present.",
        ],
        'psr7ServerPortWhenAdapterSet' => [
            null,
            [],
            ['serverParams' => ['SERVER_PORT' => '8080']],
            8080,
            "'SERVER_PORT' should be retrieved from PSR-7 'serverParams' when adapter is set.",
        ],
        'allForwardedHeadersNullOrMissing' => [
            null,
            [],
            ['X-Forwarded-Port' => null, 'serverParams' => ['SERVER_PORT' => '80']],
            80,
            "'getServerPort()' should fallback to 'SERVER_PORT' when forwarded headers are null or missing.",
        ],
    ];
}

Refactored test method in ServerParamsPsr7Test.php:

#[DataProviderExternal(RequestProvider::class, 'serverPortCases')]
#[Group('server-port')]
public function testServerPortCases(
    ?array $requestConfig,
    array $serverGlobal,
    array $psr7RequestData,
    ?int $expected,
    string $message
): void {
    $_SERVER = $serverGlobal;
    
    $request = $requestConfig !== null ? new Request($requestConfig) : new Request();
    
    // Extract headers and serverParams from psr7RequestData
    $headers = [];
    $serverParams = [];
    
    foreach ($psr7RequestData as $key => $value) {
        if ($key === 'serverParams') {
            $serverParams = $value;
        } else {
            $headers[$key] = $value;
        }
    }
    
    $request->setPsr7Request(
        FactoryHelper::createRequest('GET', '/test', $headers, serverParams: $serverParams)
    );
    
    self::assertSame($expected, $request->getServerPort(), $message);
}

// Keep these two separate tests as they have unique logic for reset and multiple requests
#[Group('server-port')]
public function testServerPortAfterRequestReset(): void
{
    $initialPort = 8080;
    $resetPort = 9000;
    
    $request = new Request();
    
    $request->setPsr7Request(
        FactoryHelper::createRequest('GET', '/test', serverParams: ['SERVER_PORT' => (string) $initialPort])
    );
    
    self::assertSame($initialPort, $request->getServerPort());
    
    $request->reset();
    
    $request->setPsr7Request(
        FactoryHelper::createRequest('GET', '/test', serverParams: ['SERVER_PORT' => (string) $resetPort])
    );
    
    self::assertSame($resetPort, $request->getServerPort(), 'Server port should update after request reset.');
}

#[Group('server-port')]
public function testServerPortIndependentRequestsWithDifferentPorts(): void
{
    $port1 = 8080;
    $port2 = 9000;
    
    $request1 = new Request();
    $request1->setPsr7Request(
        FactoryHelper::createRequest('GET', '/test', serverParams: ['SERVER_PORT' => (string) $port1])
    );
    
    $request2 = new Request();
    $request2->setPsr7Request(
        FactoryHelper::createRequest('GET', '/test', serverParams: ['SERVER_PORT' => (string) $port2])
    );
    
    self::assertSame($port1, $request1->getServerPort(), 'First request should maintain its port.');
    self::assertSame($port2, $request2->getServerPort(), 'Second request should have different port.');
}

This refactoring:

  • Consolidates 12 test methods into a single data-driven test
  • Maintains the same test coverage
  • Makes it easier to add new test cases
  • Follows the existing pattern used in testReturnServerParamFromPsr7RequestCases()
  • Keeps the reset and independent requests tests separate as they have unique multi-step logic

@terabytesoftw terabytesoftw added the bug Something isn't working label Aug 11, 2025
…ider for server port cases in `ServerParamsPsr7Provider` class.
@codecov
Copy link

codecov bot commented Aug 11, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@4bdcc2a). Learn more about missing BASE report.
⚠️ Report is 1 commits behind head on main.
⚠️ Test Analytics upload error: File not in storage

Additional details and impacted files
@@           Coverage Diff            @@
##             main       #84   +/-   ##
========================================
  Coverage        ?   100.00%           
  Complexity      ?       321           
========================================
  Files           ?        12           
  Lines           ?       805           
  Branches        ?         0           
========================================
  Hits            ?       805           
  Misses          ?         0           
  Partials        ?         0           

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

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

🧹 Nitpick comments (5)
tests/provider/ServerParamsPsr7Provider.php (5)

10-21: Tighten type shape and document tuple positions for clarity and tooling

Narrow header value types to match PSR-7 (strings or list of strings) and label tuple positions. This improves IDE/static analysis and reduces misuse.

-    /**
-     * @phpstan-return array<
-     *   string,
-     *   array{
-     *     array<string, mixed>,
-     *     array<string, mixed>,
-     *     array<string, array<int, string>|int|string>,
-     *     array<string, mixed>,
-     *     int|null,
-     *     string,
-     *   }
-     * >
-     */
+    /**
+     * @phpstan-return array<
+     *   string,
+     *   array{
+     *     0: array{portHeaders?: list<string>, secureHeaders?: list<string>, trustedHosts?: list<string>},
+     *     1: array<string, mixed>,                // SAPI/env server params (e.g., REMOTE_ADDR)
+     *     2: array<string, string|list<string>>,  // HTTP headers
+     *     3: array<string, mixed>,                // PSR-7 serverParams (e.g., SERVER_PORT)
+     *     4: int|null,                            // expected resolved port
+     *     5: string                               // assertion message
+     *   }
+     * >
+     */

25-33: Be explicit about proxy trust gating for forwarded headers

This positive case uses forwarded port but omits secureHeaders. To make intent unambiguous (and symmetric with the “untrusted host” case), include X-Forwarded-Port in secureHeaders so forwarded data is only honored when the client is trusted.

             'Forwarded port when request from trusted proxy' => [
                 [
                     'portHeaders' => ['X-Forwarded-Port'],
+                    'secureHeaders' => ['X-Forwarded-Port'],
                     'trustedHosts' => ['10.0.0.0/24'], // trust this subnet
                 ],

If the Request implementation already gates by trustedHosts regardless of secureHeaders, keeping secureHeaders here still clarifies the contract.


81-89: Consider adding an explicit integer SERVER_PORT case

Some PSR-7 implementations may surface SERVER_PORT as an int. Add a case to assert intended behavior (accept and cast or reject as non-string).

             'Null when PSR-7 request server port is not string' => [
                 [],
                 [],
                 [],
                 ['SERVER_PORT' => ['invalid' => 'array']],
                 null,
                 "'SERVER_PORT' should return 'null' from PSR-7 'serverParams' when adapter is set but 'SERVER_PORT' " .
                 'is not a string.',
             ],
+            'Handle integer when PSR-7 request server port is int' => [
+                [],
+                [],
+                [],
+                ['SERVER_PORT' => 443],
+                null, // or 443 if you intend to accept ints
+                "'SERVER_PORT' should " . (/* choose one */ true ? "return 'null' when it's an integer." : "accept integer and return 443.") ,
+            ],

Tell me which behavior you prefer and I can update the provider and tests accordingly.


144-158: Forwarded-port case: consider including secureHeaders for parity

Like the trusted/untrusted scenarios, include X-Forwarded-Port in secureHeaders to make the trust requirement explicit.

             'Server port from forwarded header when adapter is set' => [
                 [
                     'portHeaders' => ['X-Forwarded-Port'],
+                    'secureHeaders' => ['X-Forwarded-Port'],
                     'trustedHosts' => ['127.0.0.1'],
                 ],

159-171: Message nit: reflect “empty string” explicitly

The case uses an empty string header value, so tweak the message to match the input.

-                "'getServerPort()' should fallback to 'SERVER_PORT' when all forwarded headers are 'null' or missing.",
+                "'getServerPort()' should fallback to 'SERVER_PORT' when all forwarded headers are null/empty or missing.",
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 521a4f8 and 34108e8.

📒 Files selected for processing (2)
  • tests/adapter/ServerParamsPsr7Test.php (3 hunks)
  • tests/provider/ServerParamsPsr7Provider.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/adapter/ServerParamsPsr7Test.php
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-20T16:35:15.341Z
Learnt from: terabytesoftw
PR: yii2-extensions/psr-bridge#6
File: tests/http/RequestTest.php:1536-1552
Timestamp: 2025-07-20T16:35:15.341Z
Learning: In the yii2-extensions/psr-bridge project, the base TestCase class already handles $_SERVER cleanup in setUp() and tearDown() methods, so individual test methods that extend TestCase don't need manual $_SERVER restoration.

Applied to files:

  • tests/provider/ServerParamsPsr7Provider.php
📚 Learning: 2025-07-20T16:35:15.341Z
Learnt from: terabytesoftw
PR: yii2-extensions/psr-bridge#6
File: tests/http/RequestTest.php:1536-1552
Timestamp: 2025-07-20T16:35:15.341Z
Learning: In the yii2-extensions/psr-bridge project, the base TestCase class already handles $_SERVER cleanup in setUp() and tearDown() methods (lines 28 and 32), so individual test methods that extend TestCase don't need manual $_SERVER restoration.

Applied to files:

  • tests/provider/ServerParamsPsr7Provider.php
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: phpunit / PHP 8.4-windows-latest
  • GitHub Check: phpunit / PHP 8.3-windows-latest
  • GitHub Check: mutation / PHP 8.3-ubuntu-latest
  • GitHub Check: mutation / PHP 8.3-ubuntu-latest
🔇 Additional comments (3)
tests/provider/ServerParamsPsr7Provider.php (3)

54-89: Good coverage of null/missing/invalid PSR-7 SERVER_PORT cases

These cases exercise empty array, null, missing, and non-string. Solid negative-path coverage.


98-112: Comma-separated header case is on point

Covers trimming and first-value selection for X-Forwarded-Port. Nice.


113-143: First valid header among multiple configured is well covered

This ensures precedence is deterministic across configured portHeaders.

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants