Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 0 additions & 34 deletions src/adapter/ServerRequestAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -281,40 +281,6 @@ public function getRawBody(): string
return $body->getContents();
}

/**
* Retrieves the script URL from server parameters, supporting both SAPI and worker environments.
*
* Returns the value of 'SCRIPT_NAME' from server parameters for traditional SAPI-based PSR-7 Application.
*
* For worker-based environments (such as RoadRunner, FrankenPHP), returns an empty string to prevent URL
* duplication, as routing is handled internally and no script file exists.
*
* This method ensures compatibility with both classic and modern PHP runtimes, providing the correct script URL
* context for Yii2 Routing and Request processing.
*
* @param bool $workerMode Whether the application is running in worker mode (RoadRunner, FrankenPHP, etc.).
*
* @return string Script URL of SAPI environments, or empty string for worker mode.
*
* Usage example:
* ```php
* $scriptUrl = $adapter->getScriptUrl(false);
* ```
*/
public function getScriptUrl(bool $workerMode): string
{
$serverParams = $this->getServerParams();

// for traditional PSR-7 apps where 'SCRIPT_NAME' is available
if ($workerMode === false && isset($serverParams['SCRIPT_NAME']) && is_string($serverParams['SCRIPT_NAME'])) {
return $serverParams['SCRIPT_NAME'];
}

// for PSR-7 workers (RoadRunner, FrankenPHP, etc.) where no script file exists
// return empty to prevent URL duplication as routing is handled internally
return '';
}

/**
* Retrieves server parameters from the PSR-7 ServerRequestInterface.
*
Expand Down
59 changes: 40 additions & 19 deletions src/http/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,16 @@ final class Request extends \yii\web\Request
* attempts to extract and decode credentials from the 'Authorization' header, handling Apache php-cgi scenarios and
* validating the decoded data for UTF-8 encoding and proper format.
*
* Usage example:
* ```php
* [$username, $password] = $request->getAuthCredentials();
* ```
*
* @return array Contains exactly two elements.
* - 0: username sent via HTTP authentication, `null` if the username is not given.
* - 1: password sent via HTTP authentication, `null` if the password is not given.
*
* @phpstan-return array{0: string|null, 1: string|null}
*
* Usage example:
* ```php
* [$username, $password] = $request->getAuthCredentials();
* ```
*/
public function getAuthCredentials(): array
{
Expand Down Expand Up @@ -155,7 +155,7 @@ public function getAuthCredentials(): array
*
* @return array|object Request body parameters with the method override parameter removed if present.
*
* @phpstan-return array<mixed, mixed>|object
* @phpstan-return array<array-key, mixed>|object
*
* Usage example:
* ```php
Expand Down Expand Up @@ -317,7 +317,7 @@ public function getMethod(): string
*
* @throws InvalidConfigException if the configuration is invalid or incomplete.
*
* @phpstan-return array<mixed, mixed>|object|null
* @phpstan-return array<array-key, mixed>|object|null
*
* Usage example:
* ```php
Expand Down Expand Up @@ -512,18 +512,19 @@ public function getRemoteIP(): string|null
}

/**
* Retrieves the script URL of the current request, supporting PSR-7 and Yii2 fallback.
* Retrieves the script URL for the current request, supporting PSR-7 and Yii2 fallback.
*
* Returns the script URL as determined by the PSR-7 adapter if present, using the configured worker mode flag.
* In worker environments (RoadRunner, FrankenPHP, etc.) with a PSR-7 adapter set, returns an empty string, since no
* script file exists and routing is handled by the worker.
*
* If no adapter is set, falls back to the parent implementation.
* In traditional mode, returns 'SCRIPT_NAME'. Falls back to the parent implementation if no PSR-7 adapter is set.
*
* This method enables seamless access to the script URL in both PSR-7 and Yii2 environments, supporting
* interoperability with modern HTTP stacks and legacy workflows.
* This method enables seamless interoperability with both PSR-7 and Yii2 environments, ensuring the correct script
* URL resolution for modern HTTP stacks and legacy workflows.
*
* @throws InvalidConfigException if the configuration is invalid or incomplete.
* @throws InvalidConfigException if unable to determine the entry script URL.
*
* @return string Script URL of the current request.
* @return string Script URL for the current request, or an empty string in worker mode.
*
* Usage example:
* ```php
Expand All @@ -533,7 +534,7 @@ public function getRemoteIP(): string|null
public function getScriptUrl(): string
{
if ($this->adapter !== null) {
return $this->adapter->getScriptUrl($this->workerMode);
return $this->workerMode ? '' : $this->getScriptName();
}

return parent::getScriptUrl();
Expand Down Expand Up @@ -590,7 +591,9 @@ public function getServerName(): string|null
*/
public function getServerParam(string $name, mixed $default = null): mixed
{
return array_key_exists($name, $this->getServerParams()) ? $this->getServerParams()[$name] : $default;
$params = $this->getServerParams();

return array_key_exists($name, $params) ? $params[$name] : $default;
}

/**
Expand Down Expand Up @@ -633,7 +636,7 @@ public function getServerParams(): array
*
* @return array Array of uploaded files for the current request.
*
* @phpstan-return array<array<UploadedFile>, mixed>
* @phpstan-return array<array<array-key, UploadedFile>, mixed>
*
* Usage example:
* ```php
Expand Down Expand Up @@ -776,9 +779,9 @@ public function setPsr7Request(ServerRequestInterface $request): void
*
* @return array Converted array of Yii2 UploadedFile instances, preserving keys and nesting.
*
* @phpstan-param array<mixed, mixed> $uploadedFiles Array of uploaded files or nested arrays to convert.
* @phpstan-param array<array-key, mixed> $uploadedFiles Array of uploaded files or nested arrays to convert.
*
* @phpstan-return array<array<UploadedFile>, mixed>
* @phpstan-return array<array<array-key, UploadedFile>, mixed>
*/
private function convertPsr7ToUploadedFiles(array $uploadedFiles): array
{
Expand Down Expand Up @@ -820,4 +823,22 @@ private function createUploadedFile(UploadedFileInterface $psrFile): UploadedFil
],
);
}

/**
* Retrieves the script name from the current server parameters.
*
* Returns the value of the 'SCRIPT_NAME' server parameter as a string, or an empty string if not set or not a
* string.
*
* This method provides a type-safe way to access the script name for the current request, supporting
* interoperability with modern HTTP stacks and legacy workflows.
*
* @return string Script name from the server parameters, or an empty string if unavailable.
*/
private function getScriptName(): string
{
$scriptUrl = $this->getServerParam('SCRIPT_NAME');

return is_string($scriptUrl) ? $scriptUrl : '';
}
}
55 changes: 55 additions & 0 deletions tests/adapter/ServerParamsPsr7Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace yii2\extensions\psrbridge\tests\adapter;

use PHPUnit\Framework\Attributes\{DataProviderExternal, Group};
use yii\base\InvalidConfigException;
use yii2\extensions\psrbridge\http\Request;
use yii2\extensions\psrbridge\tests\provider\RequestProvider;
use yii2\extensions\psrbridge\tests\support\FactoryHelper;
Expand Down Expand Up @@ -92,6 +93,40 @@ public function testResetRemoteHostAfterRequestReset(): void
);
}

/**
* @throws InvalidConfigException if the configuration is invalid or incomplete.
*/
public function testReturnEmptyScriptUrlWhenAdapterIsSetInTraditionalModeWithoutScriptName(): void
{
$request = new Request(['workerMode' => false]);

$request->setPsr7Request(
FactoryHelper::createRequest('GET', '/test'),
);

self::assertEmpty(
$request->getScriptUrl(),
"Script URL should be empty when adapter is set in traditional mode without 'SCRIPT_NAME'.",
);
}

/**
* @throws InvalidConfigException if the configuration is invalid or incomplete.
*/
public function testReturnEmptyScriptUrlWhenAdapterIsSetInWorkerMode(): void
{
$request = new Request();

$request->setPsr7Request(
FactoryHelper::createRequest('GET', '/test'),
);

self::assertEmpty(
$request->getScriptUrl(),
"Script URL should be empty when adapter is set in 'worker' mode (default).",
);
}

#[Group('server-params')]
public function testReturnEmptyServerParamsWhenAdapterIsSet(): void
{
Expand Down Expand Up @@ -184,6 +219,26 @@ public function testReturnRemoteIPFromServerParamsCases(mixed $serverValue, stri
);
}

/**
* @throws InvalidConfigException if the configuration is invalid or incomplete.
*/
public function testReturnScriptNameWhenAdapterIsSetInTraditionalMode(): void
{
$expectedScriptName = '/app/public/index.php';

$request = new Request(['workerMode' => false]);

$request->setPsr7Request(
FactoryHelper::createRequest('GET', '/test', serverParams: ['SCRIPT_NAME' => $expectedScriptName]),
);

self::assertSame(
$expectedScriptName,
$request->getScriptUrl(),
"Script URL should return 'SCRIPT_NAME' when adapter is set in traditional mode.",
);
}

#[DataProviderExternal(RequestProvider::class, 'serverNameCases')]
#[Group('server-name')]
public function testReturnServerNameFromServerParamsCases(mixed $serverValue, string|null $expected): void
Expand Down
54 changes: 0 additions & 54 deletions tests/adapter/ServerRequestAdapterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -477,40 +477,6 @@ public function testReturnEmptyQueryStringWhenAdapterIsSetWithNoQuery(): void
);
}

/**
* @throws InvalidConfigException if the configuration is invalid or incomplete.
*/
public function testReturnEmptyScriptUrlWhenAdapterIsSetInTraditionalModeWithoutScriptName(): void
{
$request = new Request(['workerMode' => false]);

$request->setPsr7Request(
FactoryHelper::createRequest('GET', '/test'),
);

self::assertEmpty(
$request->getScriptUrl(),
"Script URL should be empty when adapter is set in traditional mode without 'SCRIPT_NAME'.",
);
}

/**
* @throws InvalidConfigException if the configuration is invalid or incomplete.
*/
public function testReturnEmptyScriptUrlWhenAdapterIsSetInWorkerMode(): void
{
$request = new Request();

$request->setPsr7Request(
FactoryHelper::createRequest('GET', '/test'),
);

self::assertEmpty(
$request->getScriptUrl(),
"Script URL should be empty when adapter is set in 'worker' mode (default).",
);
}

public function testReturnEmptyStringFromHeaderWhenCsrfHeaderPresentButEmpty(): void
{
$request = new Request();
Expand Down Expand Up @@ -1179,26 +1145,6 @@ public function testReturnRawBodyWhenAdapterIsSetWithEmptyBody(): void
);
}

/**
* @throws InvalidConfigException if the configuration is invalid or incomplete.
*/
public function testReturnScriptNameWhenAdapterIsSetInTraditionalMode(): void
{
$expectedScriptName = '/app/public/index.php';

$request = new Request(['workerMode' => false]);

$request->setPsr7Request(
FactoryHelper::createRequest('GET', '/test', serverParams: ['SCRIPT_NAME' => $expectedScriptName]),
);

self::assertSame(
$expectedScriptName,
$request->getScriptUrl(),
"Script URL should return 'SCRIPT_NAME' when adapter is set in traditional mode.",
);
}

public function testReturnUploadedFilesRecursivelyConvertsNestedArrays(): void
{
$file1 = dirname(__DIR__) . '/support/stub/files/test1.txt';
Expand Down
Loading