From 6cac25ff0727dd7808b76525fc7b91b6a97209e3 Mon Sep 17 00:00:00 2001 From: Wilmer Arambula Date: Wed, 6 Aug 2025 13:05:32 -0400 Subject: [PATCH 01/12] refactor(Request): Add resolve method to handle routing and parameters, throw `NotFoundHttpException` for unresolved requests. --- src/exception/Message.php | 7 +++++++ src/http/Request.php | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/exception/Message.php b/src/exception/Message.php index 261f6318..8d17f620 100644 --- a/src/exception/Message.php +++ b/src/exception/Message.php @@ -124,6 +124,13 @@ enum Message: string */ case NAME_MUST_BE_STRING_OR_NULL = "'name' must be a 'string' or 'null' in file specification."; + /** + * Error when the page is not found. + * + * Format: "Page not found." + */ + case PAGE_NOT_FOUND = 'Page not found.'; + /** * Error when the PSR-7 request adapter is not set. * diff --git a/src/http/Request.php b/src/http/Request.php index 492b11e6..970e0a11 100644 --- a/src/http/Request.php +++ b/src/http/Request.php @@ -5,8 +5,10 @@ namespace yii2\extensions\psrbridge\http; use Psr\Http\Message\{ServerRequestInterface, UploadedFileInterface}; +use Yii; use yii\base\InvalidConfigException; use yii\web\{CookieCollection, HeaderCollection, UploadedFile}; +use yii\web\NotFoundHttpException; use yii2\extensions\psrbridge\adapter\ServerRequestAdapter; use yii2\extensions\psrbridge\exception\Message; @@ -105,7 +107,7 @@ public function getAuthCredentials(): array /** * Apache with php-cgi does not pass HTTP Basic authentication to PHP by default. - * To make it work, add one of the following lines to to your .htaccess file: + * To make it work, add one of the following lines to your .htaccess file: * * SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0 * --OR-- @@ -519,6 +521,41 @@ public function reset(): void $this->adapter = null; } + /** + * Resolves the current request into a route and the associated parameters. + * + * This method parses the current request using the URL manager and combines the extracted route parameters with + * query parameters from both PSR-7 and traditional sources. + * + * The method ensures seamless parameter binding for controller actions in both PSR-7 and legacy Yii2 environments. + * + * @throws NotFoundHttpException if the request cannot be resolved. + * + * @return array First element is the route, and the second is the associated parameters. + * + * @phpstan-return array|string> + * + * Usage example: + * ```php + * [$route, $params] = $request->resolve(); + * ``` + */ + public function resolve(): array + { + /** @phpstan-var array{0: string, 1: array}|false $result*/ + $result = Yii::$app->getUrlManager()->parseRequest($this); + + if ($result !== false) { + [$route, $params] = $result; + + $combinedParams = $params + $this->getQueryParams(); + + return [$route, $combinedParams]; + } + + throw new NotFoundHttpException(Yii::t('yii', Message::PAGE_NOT_FOUND->getMessage())); + } + /** * Sets the PSR-7 ServerRequestInterface instance for the current request. * From 204b0281c5caae2bb366ed00894b1de91533d4d8 Mon Sep 17 00:00:00 2001 From: Wilmer Arambula Date: Wed, 6 Aug 2025 17:45:17 -0400 Subject: [PATCH 02/12] feat(ServerRequestAdapter): Add resolve method to handle routing and parameters, throw `NotFoundHttpException` class for unresolved requests. --- src/adapter/ServerRequestAdapter.php | 39 ++++++++++++++ src/http/Request.php | 40 ++++++-------- tests/TestCase.php | 6 +-- tests/http/StatelessApplicationTest.php | 69 ++++++++++++++++++++++++- tests/support/stub/SiteController.php | 16 ++++++ 5 files changed, 140 insertions(+), 30 deletions(-) diff --git a/src/adapter/ServerRequestAdapter.php b/src/adapter/ServerRequestAdapter.php index 91520756..90bd2752 100644 --- a/src/adapter/ServerRequestAdapter.php +++ b/src/adapter/ServerRequestAdapter.php @@ -9,7 +9,9 @@ use yii\base\InvalidConfigException; use yii\helpers\Json; use yii\web\{Cookie, HeaderCollection}; +use yii\web\NotFoundHttpException; use yii2\extensions\psrbridge\exception\Message; +use yii2\extensions\psrbridge\http\Request; use function implode; use function in_array; @@ -364,6 +366,43 @@ public function getUrl(): string return $url; } + /** + * Resolves the route and parameters from the given {@see Request} instance using Yii2 UrlManager. + * + * Parses the request using Yii2 UrlManager and returns the resolved route and parameters. + * + * If the route is found, combine the parsed parameters with the query parameters from the PSR-7 + * ServerRequestAdapter. + * + * @param Request $request Request instance to resolve. + * + * @throws NotFoundHttpException if the route cannot be resolved by UrlManager. + * + * @return array Array containing the resolved route and combined parameters. + * + * @phpstan-return array + * + * Usage example: + * ```php + * [$route, $params] = $adapter->resolve($request); + * ``` + */ + public function resolve(Request $request): array + { + /** @phpstan-var array{0: string, 1: array}|false $result*/ + $result = Yii::$app->getUrlManager()->parseRequest($request); + + if ($result !== false) { + [$route, $params] = $result; + + $combinedParams = $params + $this->psrRequest->getQueryParams(); + + return [$route, $combinedParams]; + } + + throw new NotFoundHttpException(Yii::t('yii', Message::PAGE_NOT_FOUND->getMessage())); + } + /** * Extracts cookies from the PSR-7 ServerRequestInterface without validation. * diff --git a/src/http/Request.php b/src/http/Request.php index 970e0a11..6f1c143e 100644 --- a/src/http/Request.php +++ b/src/http/Request.php @@ -5,10 +5,8 @@ namespace yii2\extensions\psrbridge\http; use Psr\Http\Message\{ServerRequestInterface, UploadedFileInterface}; -use Yii; use yii\base\InvalidConfigException; -use yii\web\{CookieCollection, HeaderCollection, UploadedFile}; -use yii\web\NotFoundHttpException; +use yii\web\{CookieCollection, HeaderCollection, NotFoundHttpException, UploadedFile}; use yii2\extensions\psrbridge\adapter\ServerRequestAdapter; use yii2\extensions\psrbridge\exception\Message; @@ -315,7 +313,7 @@ public function getParsedBody(): array|object|null /** * Retrieves the underlying PSR-7 ServerRequestInterface instance from the adapter. * - * Returns the PSR-7 {@see ServerRequestInterface} associated with this request via the internal adapter. + * Returns the PSR-7 ServerRequestInterface associated with this request via the internal adapter. * * If the adapter is not set, an {@see InvalidConfigException} is thrown to indicate misconfiguration or missing * bridge setup. @@ -522,45 +520,39 @@ public function reset(): void } /** - * Resolves the current request into a route and the associated parameters. + * Resolves the request parameters using PSR-7 adapter or Yii2 fallback. * - * This method parses the current request using the URL manager and combines the extracted route parameters with - * query parameters from both PSR-7 and traditional sources. + * Returns an array of resolved request parameters by delegating to the PSR-7 ServerRequestAdapter if present, or + * falling back to the parent Yii2 implementation when no adapter is set. * - * The method ensures seamless parameter binding for controller actions in both PSR-7 and legacy Yii2 environments. + * This method enables seamless interoperability between PSR-7 compatible HTTP stacks and legacy Yii2 workflows, + * ensuring consistent parameter resolution in both environments. * - * @throws NotFoundHttpException if the request cannot be resolved. + * @throws NotFoundHttpException if the route cannot be resolved by UrlManager. * - * @return array First element is the route, and the second is the associated parameters. + * @return array Array of resolved request parameters for the current request. * - * @phpstan-return array|string> + * @phpstan-return array * * Usage example: * ```php - * [$route, $params] = $request->resolve(); + * $params = $request->resolve(); * ``` */ public function resolve(): array { - /** @phpstan-var array{0: string, 1: array}|false $result*/ - $result = Yii::$app->getUrlManager()->parseRequest($this); - - if ($result !== false) { - [$route, $params] = $result; - - $combinedParams = $params + $this->getQueryParams(); - - return [$route, $combinedParams]; + if ($this->adapter !== null) { + return $this->adapter->resolve($this); } - throw new NotFoundHttpException(Yii::t('yii', Message::PAGE_NOT_FOUND->getMessage())); + return parent::resolve(); } /** * Sets the PSR-7 ServerRequestInterface instance for the current request. * - * Assigns a new {@see ServerRequestAdapter} wrapping the provided PSR-7 {@see ServerRequestInterface} to enable - * PSR-7 interoperability for the Yii2 Request component. + * Assigns a new {@see ServerRequestAdapter} wrapping the provided PSR-7 ServerRequestInterface to enable PSR-7 + * interoperability for the Yii2 Request component. * * This method is used to bridge PSR-7 compatible HTTP stacks with Yii2, allowing request data to be accessed via * the adapter. diff --git a/tests/TestCase.php b/tests/TestCase.php index 6b8cc2c8..20fa3c9c 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -149,10 +149,8 @@ protected function statelessApplication(array $config = []): StatelessApplicatio 'enableStrictParsing' => false, 'enablePrettyUrl' => true, 'rules' => [ - [ - 'pattern' => '///', - 'route' => '/', - ], + 'site/update/' => 'site/update', + '//' => '/', ], ], ], diff --git a/tests/http/StatelessApplicationTest.php b/tests/http/StatelessApplicationTest.php index f881c5cd..5a856a3f 100644 --- a/tests/http/StatelessApplicationTest.php +++ b/tests/http/StatelessApplicationTest.php @@ -10,7 +10,7 @@ use Psr\Http\Message\{ServerRequestFactoryInterface, StreamFactoryInterface, UploadedFileFactoryInterface}; use stdClass; use Yii; -use yii\base\{InvalidConfigException, Security}; +use yii\base\{Exception, InvalidConfigException, Security}; use yii\di\NotInstantiableException; use yii\helpers\Json; use yii\i18n\{Formatter, I18N}; @@ -694,6 +694,9 @@ public function testMultipleRequestsWithDifferentSessionsInWorkerMode(): void } } + /** + * @throws InvalidConfigException if the configuration is invalid or incomplete. + */ public function testRecalculateMemoryLimitAfterResetAndIniChange(): void { $originalLimit = ini_get('memory_limit'); @@ -727,6 +730,9 @@ public function testRecalculateMemoryLimitAfterResetAndIniChange(): void ini_set('memory_limit', $originalLimit); } + /** + * @throws InvalidConfigException if the configuration is invalid or incomplete. + */ #[RequiresPhpExtension('runkit7')] public function testRenderExceptionSetsDisplayErrorsInDebugMode(): void { @@ -780,6 +786,9 @@ public function testRenderExceptionSetsDisplayErrorsInDebugMode(): void @runkit_constant_redefine('YII_ENV_TEST', true); } + /** + * @throws InvalidConfigException if the configuration is invalid or incomplete. + */ #[RequiresPhpExtension('runkit7')] public function testRenderExceptionWithErrorActionReturningResponseObject(): void { @@ -829,6 +838,9 @@ public function testRenderExceptionWithErrorActionReturningResponseObject(): voi @runkit_constant_redefine('YII_DEBUG', true); } + /** + * @throws InvalidConfigException if the configuration is invalid or incomplete. + */ public function testRenderExceptionWithRawFormat(): void { HTTPFunctions::set_sapi('apache2handler'); @@ -864,7 +876,7 @@ public function testRenderExceptionWithRawFormat(): void $body = $response->getBody()->getContents(); self::assertStringContainsString( - 'yii\base\Exception', + Exception::class, $body, 'RAW format response should contain exception class name.', ); @@ -1225,6 +1237,47 @@ public function testReturnJsonResponseWithQueryParametersForSiteGetRoute(): void ); } + /** + * @throws InvalidConfigException if the configuration is invalid or incomplete. + */ + public function testReturnJsonResponseWithRouteParameterForSiteUpdateRoute(): void + { + $_SERVER = [ + 'REQUEST_METHOD' => 'GET', + 'REQUEST_URI' => 'site/update/123', + ]; + + $request = FactoryHelper::createServerRequestCreator()->createFromGlobals(); + + $app = $this->statelessApplication(); + + $response = $app->handle($request); + + self::assertSame( + 200, + $response->getStatusCode(), + "Response 'status code' should be '200' for 'site/update/123' route in 'StatelessApplication', " . + 'indicating a successful update.', + ); + self::assertSame( + 'application/json; charset=UTF-8', + $response->getHeaderLine('Content-Type'), + "Response 'content-type' should be 'application/json; charset=UTF-8' for 'site/update/123' route in " . + "'StatelessApplication'.", + ); + self::assertSame( + '{"site/update":"123"}', + $response->getBody()->getContents(), + "Response 'body' should contain valid JSON with the route parameter for 'site/update/123' in " . + "'StatelessApplication'.", + ); + self::assertSame( + 'site/update/123', + $request->getUri()->getPath(), + "Request 'path' should be 'site/update/123' for 'site/update/123' route in 'StatelessApplication'.", + ); + } + /** * @throws InvalidConfigException if the configuration is invalid or incomplete. */ @@ -1870,6 +1923,9 @@ static function () use (&$eventTriggered): void { self::assertTrue($eventTriggered, "Should trigger '{$eventName}' event during handle()"); } + /** + * if the configuration is invalid or incomplete. + */ #[RequiresPhpExtension('runkit7')] public function testUseErrorViewLogicWithDebugFalseAndException(): void { @@ -1926,6 +1982,9 @@ public function testUseErrorViewLogicWithDebugFalseAndException(): void @runkit_constant_redefine('YII_DEBUG', true); } + /** + * + */ #[RequiresPhpExtension('runkit7')] public function testUseErrorViewLogicWithDebugFalseAndUserException(): void { @@ -1982,6 +2041,9 @@ public function testUseErrorViewLogicWithDebugFalseAndUserException(): void @runkit_constant_redefine('YII_DEBUG', true); } + /** + * + */ public function testUseErrorViewLogicWithDebugTrueAndUserException(): void { $_SERVER = [ @@ -2033,6 +2095,9 @@ public function testUseErrorViewLogicWithDebugTrueAndUserException(): void ); } + /** + * + */ public function testUseErrorViewLogicWithNonHtmlFormat(): void { $_SERVER = [ diff --git a/tests/support/stub/SiteController.php b/tests/support/stub/SiteController.php index b7e12fef..775b10f2 100644 --- a/tests/support/stub/SiteController.php +++ b/tests/support/stub/SiteController.php @@ -309,13 +309,29 @@ public function actionStream(): Response return $this->response->sendStreamAsFile($tmpFile, 'stream.txt', ['mimeType' => 'text/plain']); } + /** + * @throws Exception + */ public function actionTriggerException(): never { throw new Exception('Exception error message.'); } + /** + * @throws UserException + */ public function actionTriggerUserException(): never { throw new UserException('User-friendly error message.'); } + + /** + * @phpstan-return array + */ + public function actionUpdate(string|null $id = null): array + { + $this->response->format = Response::FORMAT_JSON; + + return ['site/update' => $id]; + } } From 48734d53610bb53cc2fc7fe52f6bc4eef2e83f27 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Wed, 6 Aug 2025 21:45:51 +0000 Subject: [PATCH 03/12] Apply fixes from StyleCI --- tests/http/StatelessApplicationTest.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tests/http/StatelessApplicationTest.php b/tests/http/StatelessApplicationTest.php index 5a856a3f..49d37955 100644 --- a/tests/http/StatelessApplicationTest.php +++ b/tests/http/StatelessApplicationTest.php @@ -1982,9 +1982,6 @@ public function testUseErrorViewLogicWithDebugFalseAndException(): void @runkit_constant_redefine('YII_DEBUG', true); } - /** - * - */ #[RequiresPhpExtension('runkit7')] public function testUseErrorViewLogicWithDebugFalseAndUserException(): void { @@ -2041,9 +2038,6 @@ public function testUseErrorViewLogicWithDebugFalseAndUserException(): void @runkit_constant_redefine('YII_DEBUG', true); } - /** - * - */ public function testUseErrorViewLogicWithDebugTrueAndUserException(): void { $_SERVER = [ @@ -2095,9 +2089,6 @@ public function testUseErrorViewLogicWithDebugTrueAndUserException(): void ); } - /** - * - */ public function testUseErrorViewLogicWithNonHtmlFormat(): void { $_SERVER = [ From ada80d0c4435fa1368daa6ffd0ba55f3ca916312 Mon Sep 17 00:00:00 2001 From: Wilmer Arambula Date: Wed, 6 Aug 2025 17:49:12 -0400 Subject: [PATCH 04/12] fix(tests): Add missing @throws annotation for `InvalidConfigException` class in error view logic tests. --- tests/http/StatelessApplicationTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/http/StatelessApplicationTest.php b/tests/http/StatelessApplicationTest.php index 5a856a3f..6594b3f1 100644 --- a/tests/http/StatelessApplicationTest.php +++ b/tests/http/StatelessApplicationTest.php @@ -1924,7 +1924,7 @@ static function () use (&$eventTriggered): void { } /** - * if the configuration is invalid or incomplete. + * @throws InvalidConfigException if the configuration is invalid or incomplete. */ #[RequiresPhpExtension('runkit7')] public function testUseErrorViewLogicWithDebugFalseAndException(): void @@ -1983,7 +1983,7 @@ public function testUseErrorViewLogicWithDebugFalseAndException(): void } /** - * + * @throws InvalidConfigException if the configuration is invalid or incomplete. */ #[RequiresPhpExtension('runkit7')] public function testUseErrorViewLogicWithDebugFalseAndUserException(): void @@ -2042,7 +2042,7 @@ public function testUseErrorViewLogicWithDebugFalseAndUserException(): void } /** - * + * @throws InvalidConfigException if the configuration is invalid or incomplete. */ public function testUseErrorViewLogicWithDebugTrueAndUserException(): void { @@ -2096,7 +2096,7 @@ public function testUseErrorViewLogicWithDebugTrueAndUserException(): void } /** - * + * @throws InvalidConfigException if the configuration is invalid or incomplete. */ public function testUseErrorViewLogicWithNonHtmlFormat(): void { From cdf1cd37caead5202b65adc8772af9d6f0aa33c8 Mon Sep 17 00:00:00 2001 From: Wilmer Arambula Date: Wed, 6 Aug 2025 17:50:39 -0400 Subject: [PATCH 05/12] chore: Remove Copilot AI Coding Agent instructions file. --- .github/copilot-instructions.md | 52 --------------------------------- 1 file changed, 52 deletions(-) delete mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index b9a9bf4f..00000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,52 +0,0 @@ -# Copilot AI Coding Agent Instructions for yii2-extensions/psr-bridge - -## Project Overview -- This is a Yii2 extension providing PSR-7/PSR-15 bridge functionality for Yii2 applications. -- The codebase is organized by domain: `src/adapter/`, `src/emitter/`, `src/http/`, and `src/worker/`. -- All code is written for PHP 8.2+ and follows strict type and code style rules (see `copilot/code-style.md`). -- The extension is designed for compatibility with Yii2, PSR-7, and PSR-15 interfaces. - -## Key Architectural Patterns -- **Adapter Pattern:** `src/adapter/ServerRequestAdapter.php` bridges PSR-7 requests to Yii2's request model. -- **Immutable Value Objects:** Many classes (e.g., HTTP request/response) are immutable; use fluent methods for configuration. -- **PSR-4 Autoloading:** Namespace and directory structure strictly follow PSR-4. -- **Exception Handling:** All exceptions are custom and documented; see `src/emitter/exception/` and `src/http/exception/`. -- **Testing Mirrors Source:** Tests in `tests/` mirror the `src/` structure and use PHPUnit with full Arrange-Act-Assert. - -## Developer Workflows -- **Build/Static Analysis:** - - Run static analysis: `vendor\bin\phpstan analyse` - - Run code style: `vendor\bin\ecs check` - - Run tests: `vendor\bin\phpunit` -- **Test Coverage:** 100% coverage is required for PRs. Add tests in `tests/` with the same namespace as the class under test. -- **Conventional Commits:** All commits use `type(scope): description.` (see `copilot/general-instructions.md`). -- **CI/CD:** All PRs are validated by GitHub Actions for static analysis, style, and tests. - -## Project-Specific Conventions -- **Type Declarations:** Use `string|null` instead of `?string` for nullable types. -- **Array Types:** Use `@phpstan-var` for complex array types in PHPDoc. -- **Documentation:** All classes and methods must have English PHPDoc, following `copilot/class-documentation.md`. -- **Test Naming:** Test methods use `test` (see `copilot/unit-test.md`). -- **Data Providers:** Place in `tests/provider/` and suffix with `Provider`. -- **Error Messages:** Use enums for standardized exception messages. - -## Integration Points -- **PSR-7/PSR-15:** Integrates with any PSR-7 compatible HTTP stack. -- **Yii2:** Extends and adapts Yii2 core request/response classes. -- **External Tools:** Uses PHPStan, ECS, PHPUnit, and Codecov for quality and coverage. - -## Examples -- See `src/http/Request.php` for a typical adapter usage pattern. -- See `tests/http/PSR7RequestTest.php` for test structure and coverage expectations. -- See `copilot/` for all project-specific coding and documentation standards. - -## References -- [copilot/general-instructions.md](../copilot/general-instructions.md) -- [copilot/code-style.md](../copilot/code-style.md) -- [copilot/class-documentation.md](../copilot/class-documentation.md) -- [copilot/unit-test.md](../copilot/unit-test.md) -- [copilot/static-analysis.md](../copilot/static-analysis.md) - ---- - -If you are unsure about a pattern or workflow, check the `copilot/` directory or ask for clarification in your PR. From 7bc5bc5b7a4accdee8f33c8a54263d9fcc0660b8 Mon Sep 17 00:00:00 2001 From: Wilmer Arambula Date: Wed, 6 Aug 2025 17:52:51 -0400 Subject: [PATCH 06/12] fix(tests): Resolve merge conflict and add missing @throws annotation for `InvalidConfigException` class in error view logic tests. --- tests/http/StatelessApplicationTest.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tests/http/StatelessApplicationTest.php b/tests/http/StatelessApplicationTest.php index efa6b37f..6594b3f1 100644 --- a/tests/http/StatelessApplicationTest.php +++ b/tests/http/StatelessApplicationTest.php @@ -1982,12 +1982,9 @@ public function testUseErrorViewLogicWithDebugFalseAndException(): void @runkit_constant_redefine('YII_DEBUG', true); } -<<<<<<< HEAD /** * @throws InvalidConfigException if the configuration is invalid or incomplete. */ -======= ->>>>>>> 48734d53610bb53cc2fc7fe52f6bc4eef2e83f27 #[RequiresPhpExtension('runkit7')] public function testUseErrorViewLogicWithDebugFalseAndUserException(): void { @@ -2044,12 +2041,9 @@ public function testUseErrorViewLogicWithDebugFalseAndUserException(): void @runkit_constant_redefine('YII_DEBUG', true); } -<<<<<<< HEAD /** * @throws InvalidConfigException if the configuration is invalid or incomplete. */ -======= ->>>>>>> 48734d53610bb53cc2fc7fe52f6bc4eef2e83f27 public function testUseErrorViewLogicWithDebugTrueAndUserException(): void { $_SERVER = [ @@ -2101,12 +2095,9 @@ public function testUseErrorViewLogicWithDebugTrueAndUserException(): void ); } -<<<<<<< HEAD /** * @throws InvalidConfigException if the configuration is invalid or incomplete. */ -======= ->>>>>>> 48734d53610bb53cc2fc7fe52f6bc4eef2e83f27 public function testUseErrorViewLogicWithNonHtmlFormat(): void { $_SERVER = [ From 7fe2c44d76806a8b54da81e7559c06e6202d9c43 Mon Sep 17 00:00:00 2001 From: Wilmer Arambula Date: Wed, 6 Aug 2025 17:57:13 -0400 Subject: [PATCH 07/12] Remove unsed files. --- Page not found | 1 - copilot/class-documentation.md | 424 ------------ copilot/code-style.md | 185 ----- copilot/general-instructions.md | 97 --- copilot/index.html | 1110 ------------------------------ copilot/project-documentation.md | 192 ------ copilot/static-analysis.md | 178 ----- copilot/unit-test.md | 179 ----- phpstan.neon | 27 - phpunit.xml.dist | 28 - 10 files changed, 2421 deletions(-) delete mode 100644 Page not found delete mode 100644 copilot/class-documentation.md delete mode 100644 copilot/code-style.md delete mode 100644 copilot/general-instructions.md delete mode 100644 copilot/index.html delete mode 100644 copilot/project-documentation.md delete mode 100644 copilot/static-analysis.md delete mode 100644 copilot/unit-test.md delete mode 100644 phpstan.neon delete mode 100644 phpunit.xml.dist diff --git a/Page not found b/Page not found deleted file mode 100644 index 98b8502d..00000000 --- a/Page not found +++ /dev/null @@ -1 +0,0 @@ -Page not found \ No newline at end of file diff --git a/copilot/class-documentation.md b/copilot/class-documentation.md deleted file mode 100644 index 7ce86489..00000000 --- a/copilot/class-documentation.md +++ /dev/null @@ -1,424 +0,0 @@ -## Class Documentation Guidelines - -This guide defines PHPDoc standards for classes and methods within the PHP-Press project, also optimized for tools like -GitHub Copilot. - -## Table of Contents -1. General PHPDoc Rules. -2. Documentation Flow. -3. Class Documentation. -4. Method Documentation. -5. Property Documentation. -6. Type Hints and Templates. -7. Exception Documentation. -8. Best Practices. -9. Copilot Optimization Tips. - -## General PHPDoc Rules - -### Basic Structure -- All documentation must be in English. -- All classes, interfaces, traits, and abstract classes must have a PHPDoc block. -- All `public` and `protected` methods must have PHPDoc. -- Use complete sentences and end with a period. -- Explain **the "why"** behind design decisions. -- Keep it concise but with relevant context. -- Use proper indentation for multi-line descriptions. -- Include `@copyright` and `@license` with `{@see LICENSE}`. -- Document **only what the code actually does**. - -### Common Tags -| Tag | Purpose | -|---------------|------------------------------------------| -| `@param` | Describes each input parameter | -| `@return` | Explains the returned value | -| `@throws` | Lists exceptions thrown | -| `@template` | Declares generic templates | -| `@phpstan-var`| Specifies complex property types | - -## Documentation Flow - -1. Brief one-line description. -2. Detailed description with the "why" behind the design. -3. List of key features with bullets. -4. Practical usage examples. -5. References to related classes (`{@see}`). -6. Copyright and license. - -## Class Documentation - -### Ejemplo para Clases Regulares -path: /core/Src/Router/Route.php - -```php -/** - * Route definition with immutable configuration and matching capabilities. - * - * Represents a route with pattern matching, HTTP method restrictions, middleware support, and parameter validation. - * - * Routes are created using factory methods for specific HTTP methods and can be configured using immutable setter - * methods that return new instances with the requested changes. - * - * The matching algorithm compares incoming requests against defined route patterns, considering HTTP methods, hostname - * constraints, path patterns, query parameters, and storing matched parameters for use in actions. - * - * Key features. - * - API versioning with version prefix support (`v1`, `v2`, etc.). - * - Early validation of regex patterns during route configuration to prevent runtime errors. - * - Hostname matching with support for exact domains, wildcards, parameter capture, and regex patterns. - * - HTTP method-specific factory methods (`GET`, `POST`, `PUT`, etc.). - * - Immutable fluent interface for configuration. - * - Middleware integration for request processing. - * - Parameter validation and default values for path segments. - * - Pattern-based path matching with parameter extraction. - * - Priority-based route resolution for handling overlapping patterns. - * - Query parameter validation and extraction with regex patterns. - * - * @see \PHPPress\Router\HttpMethod for enum of valid HTTP methods. - * @see \PHPPress\Router\RouteCollection for collection, lookup, and URL generation. - * @see \PHPPress\Router\RouteParser for pattern-to-regex conversion and host matching. - * - * @copyright Copyright (C) 2023 Terabytesoftw. - * @license https://opensource.org/license/bsd-3-clause BSD 3-Clause License. - */ -class Route -``` - -### Ejemplo Abstract class -path: /core/Src/Web/View/Base/AbstractView.php - -```php -/** - * Base class for view components providing template rendering with layouts and themes. - * - * Provides a flexible and extensible foundation for rendering view templates in web applications, supporting - * context-aware view resolution, event-driven rendering lifecycle, and pluggable renderer registration. - * - * This class enables layout wrapping, theme-based path resolution, and parameter inheritance for layouts, making it - * suitable for complex UI scenarios and modular view architectures. - * - * Views are processed through registered renderers based on file extension and can be wrapped in layouts with - * inherited parameters. - * - * The rendering process is event-driven, allowing listeners to modify content before and after rendering. - * - * Key features. - * - Context-aware view path resolution using {@see ViewContextInterface}. - * - Event-driven rendering lifecycle with before/after hooks. - * - Filesystem abstraction for view file access. - * - Flexible renderer registration for multiple file extensions. - * - Layout support with parameter inheritance and optional disabling. - * - Pluggable and extensible renderer system. - * - Theme mapping for dynamic view path resolution. - * - * @see \PHPPress\Renderer\PHPEngine for PHP renderer. - * @see \PHPPress\View\ViewContext for path context handling. - * - * @copyright Copyright (C) 2023 Terabytesoftw. - * @license https://opensource.org/license/bsd-3-clause BSD 3-Clause License. - */ -abstract class AbstractView extends Component -``` - -### Ejemplo para Interfaces -path: /core/Src/View/ViewContextInterface.php - -```php -/** - * Interface for providing view resolution context in templates. - * - * Defines the contract for supplying directory context to view renderers, enabling them to resolve relative paths - * during template processing by referencing the base directory of the current view. - * - * This allows templates to reference other views using paths relative to their own location, supporting modular and - * maintainable UI architectures. - * - * This interface is essential for context-aware view resolution, ensuring that renderers can accurately locate and - * include related templates, layouts, or partials regardless of the rendering entry point. - * - * Key features. - * - Directory context for templates. - * - Implementation agnostic for flexible renderer integration. - * - Path normalization support for consistent resolution. - * - Supports modular and reusable view structures. - * - View path resolution for relative includes. - * - * @copyright Copyright (C) 2023 Terabytesoftw. - * @license https://opensource.org/license/bsd-3-clause BSD 3-Clause License. - */ -interface ViewContextInterface -``` - -### Ejemplo para Excepciones -path: /core/Src/Router/Exception/RouteNotFoundException.php - -```php -/** - * Exception thrown when a route can't be found by its identifier. - * - * This exception indicates that an attempt to retrieve a named route failed because no route with the specified - * identifier exists in the route collection. It helps identify missing or misconfigured route definitions during URL - * generation. - * - * The exception uses automatic message prefixing with "Route not found:" for consistent error reporting across the - * routing system, aiding in quick identification of route definition issues. - * - * Thrown in scenarios including. - * - Dynamic route generation failures. - * - Group route resolution errors. - * - Invalid route names. - * - Missing route definitions. - * - Route collection corruption. - * - * Key features. - * - Collection state details. - * - Exception chaining support. - * - Message enum integration. - * - Route identifier tracking. - * - Route suggestion hints. - * - * @copyright Copyright (C) 2023 Terabytesoftw. - * @license https://opensource.org/license/bsd-3-clause BSD 3-Clause License. - */ -final class RouteNotFoundException extends Exception -``` - -Enums Message for standardized error messages for Exceptions -path: /core/Src/Router/Exception/Message.php - -```php -/** - * Represents standardized error messages for router exceptions. - * - * This enum defines formatted error messages for various error conditions that may occur during router configuration - * and execution. - * - * It provides a consistent and standardized way to present error messages across the routing system. - * - * Each case represents a specific type of error, with a message template that can be populated with dynamic values - * using the {@see \PHPPress\Router\Exception\Message::getMessage()} method. - * - * This centralized approach improves the consistency of error messages and simplifies potential internationalization. - * - * Key features. - * - Centralization of an error text for easier maintenance. - * - Consistent error handling across the routing system. - * - Integration with specific exception classes. - * - Message formatting with dynamic parameters. - * - Standardized error messages for common cases. - * - * @copyright Copyright (C) 2023 Terabytesoftw. - * @license https://opensource.org/license/bsd-3-clause BSD 3-Clause License. - */ -enum Message: string -{ - /** - * Error when host doesn't match a required pattern. - * - * Format: "The passed host '%s' of doesn't match the regexp '%s'" - */ - case HOST_NOT_MATCHED = 'The passed host \'%s\' of does not match the regexp \'%s\''; - - /** - * Returns the formatted message string for the error case. - * - * Retrieves the raw message string associated with this error case without parameter interpolation. - * - * @param string ...$argument Dynamic arguments to insert into the message. - * - * @return string Error message string with interpolated arguments. - * - * Usage example: - * ```php - * throw new RouterNotFoundException(Message::ROUTE_NOT_FOUND->getMessage('/invalid/path')); - * ``` - */ - public function getMessage(int|string ...$argument): string - { - return sprintf($this->value, ...$argument); - } -} -``` - -### Ejemplo para Tests -path: /core/tests/Router/RouteTest.php - -```php -/** - * Test suite for {@see \PHPPress\Router\Route} class functionality and behavior. - * - * Verifies routing component's ability to handle various configurations and matching scenarios. - * - * These tests ensure routing features work correctly under different conditions and maintain consistent behavior after - * code changes. - * - * The tests validate complex route matching with parameters, middleware handling, and host configuration, which are - * essential for proper HTTP request routing in the framework. - * - * Test coverage. - * - Host configuration (domain patterns, subdomain support, hostname matching). - * - HTTP method handling (normalization, case-insensitivity, method restrictions). - * - Middleware configuration (single, multiple, order preservation). - * - Parameter handling (encoded paths, URL-encoded values, parameter extraction). - * - Path matching (exact match, non-matching paths, suffix handling). - * - Pattern matching (required and optional segments, path matching). - * - Priority handling (route priority management). - * - Query parameters (required, optional, validation, type handling). - * - Route immutability and builder pattern implementation. - * - Route name validation (valid names, length constraints, complex patterns). - * - Route parameters (validation, defaults, pattern matching). - * - Suffix handling and URL normalization. - * - URL encoding (percent-encoded paths, proper decoding). - * - Versioning support (version prefixing, version mismatch handling). - * - * @see \PHPPress\Tests\Provider\Router\RouteProvider for test case data providers. - * - * @copyright Copyright (C) 2023 Terabytesoftw. - * @license https://opensource.org/license/bsd-3-clause BSD 3-Clause License. - */ -#[Group('router')] -final class RouteTest extends TestCase -``` - -## Method Documentation - -### Constructor -```php -/** - * Creates a new instance of the {@see \PHPress\Router\RouteCollection} class. - * - * @param array $routes Initial routes to populate the collection, indexed by route name. - * - * @phpstan-param Route[] $routes - */ -``` - -### Regular Methods -```php -/** - * Retrieves all routes in the collection. - * - * Provides direct access to the full set of registered routes in their original indexed form. - * - * This method is useful for route inspection, debugging, and bulk operations on the entire route collection. - * - * @return array Array of all registered routes indexed by name. - * - * Usage example: - * ```php - * $allRoutes = $collection->all(); - * ``` - * - * @phpstan-return Route[] - */ -``` - -### Protected Methods -(Ensure that PHPDoc is as detailed as for `public` methods, especially if they influence behavior.) - -## Property Documentation - -- Avoid `@var` if the type is already declared in PHP. -- Use `@phpstan-var` for complex arrays. - -```php -/** - * A map of theme names to their corresponding paths. - * @phpstan-var array - */ -private array $themeMap = []; -``` - -## Type Hints and Templates - -```php -/** - * @template T of FilesystemAdapter - * @param array $config - * @param Collection $items - */ -``` - -```php -/** - * @param string|array $paths - * @return string[]|false - */ -``` - -## Exception Documentation (@throws) - -All @throws annotations must be written in English, use complete sentences, and follow a standardized structure for -consistency and tooling compatibility (for example, static analysis, Copilot, IDEs). - -### Structure - -```php -@throws ExceptionClass if [specific condition that causes the exception]. -``` - -- Use if clauses to describe when the exception is thrown. -- Avoid vague wording like “an error occurs...” — be clear and precise. -- Use the most specific exception available. Reserve Throwable or RuntimeException for unexpected or generic errors. -- Keep the description generic unless the method’s context requires specificity. - -### Standardized Examples - -```php -@throws BadRequestException if the request is invalid or can't be processed. -@throws EmptyStackException if the stack is empty when an element is expected. -@throws FilesystemException if the filesystem operation is invalid or fails. -@throws InvalidArgumentException if one or more arguments are invalid, of incorrect type or format. -@throws InvalidConfigException if the configuration is invalid or incomplete. -@throws InvalidDefinitionException if the definition is invalid or improperly configured. -@throws InvalidRouteParameterException if the route parameters are invalid or incomplete. -@throws MiddlewareResolutionException if the middleware handler is invalid or can't be resolved. -@throws NotInstantiableException if a class or service can't be instantiated. -@throws RouteAlreadyExistsException if a route with the same name is already registered. -@throws RouteNotFoundException if the route is not found or undefined. -@throws RuntimeException if a runtime error prevents the operation from completing successfully. -@throws TemplateNotFoundException if the template file is missing or can't be located. -@throws Throwable if an unexpected error occurs during execution. -@throws ViewNotFoundException if the view or associated renderer is missing or unavailable. -@throws WidgetStackException if the stack is empty, or if `end()` is called without a `begin()` call. -matching {@see AbstractBlock::begin()}. -``` - -## Best Practices - -### Precision -- Document only existing functionality. -- Update documentation when changing code. -- Don't document planned or future features. - -### Recommended Structure -1. Clear purpose. -2. How it is implemented. -3. List of features. -4. Usage example. -5. Cross-references. - -### Bad vs Good Examples -```php -// ❌ Incorrecto -/** Does something */ -function doSomething() {} - -// ✅ Correcto -/** - * Loads a config file and parses options. - * - * @param string $path Path to config file. - * @return array Parsed options. - */ -function loadConfig(string $path): array {} -``` - -## Copilot Optimization Tips -- Use descriptive names for functions and variables. -- Always declare input/output types. -- Keep PHPDoc updated. -- Use template annotations (`@template`, `@phpstan-*`). -- Include practical and realistic usage examples. -- Emphasize the "why" to improve model suggestions. -- Use comments within `php` blocks to explain each step. diff --git a/copilot/code-style.md b/copilot/code-style.md deleted file mode 100644 index fa56da0e..00000000 --- a/copilot/code-style.md +++ /dev/null @@ -1,185 +0,0 @@ -# Code Style Guidelines - -This guide defines coding conventions and PHP 8.2 usage for the PHP-Press project. -It complements `class-documentation.md`, `unit-test.md`, and `documentation.md` to improve readability, maintainability, -and Copilot-assisted development. - -## Table of Contents -1. PHP 8.1 Feature Usage. -2. Code Style Rules. -3. Best Practices. -4. Copilot Optimization Tips. - -## PHP 8.2 Feature Usage - -### Typed Properties with Union Types -Use explicit union types when needed: -```php -private string|int $value; -private readonly DependencyResolver $resolver; -private BundleInterface|null $bundle = null; -``` - -### Constructor Property Promotion -Prefer concise constructor definitions: -```php -public function __construct( - private readonly string $name, - private readonly int $value = 0, - private readonly array|null $config = null, -) {} -``` - -### Arrow Functions and Array Operations -Use arrow functions for transformation. Prefer `foreach` for complex logic or side effects. -```php -$transformed = array_map(static fn($x) => $x * 2, $items); -$hasNegative = array_any($items, static fn($x) => $x < 0); -``` - -### Match Expressions -Use match for explicit value checks: -```php -$result = match ($value) { - 1, 2 => 'low', - 3 => 'medium', - default => 'high', -}; -``` -Avoid `match(true)` constructs. - -### Named Arguments -Use for disambiguation only: -```php -// Good -$object->configure(options: ['cache' => true], resolver: $resolver); - -// Prefer ordered when obvious -$object->configure($resolver, ['cache' => true]); -``` - -### First-class Callable Syntax -```php -$handler = $object->handle(...); -$callback = strtolower(...); -``` - -### Enums -Document each case clearly: -```php -enum HttpMethod: string { - case GET = 'GET'; // Retrieves data - case POST = 'POST'; // Submits data - // ... -} -``` -## Code Style Rules - -### Formatting -- Line length: 120 characters. -- Indentation: 4 spaces. -- PSR-12 compliant. -- Files must end with newline. - -### Namespace & Imports -```php -declare(strict_types=1); - -namespace PHPPress\Component; - -use PHPPress\Event\EventDispatcher; - -use function array_map; -use function sprintf; -``` -Group `use function` separately after classes. - -### Class Structure -- One class per file. -- PSR-4 structure. -- Constants > Properties > Methods. -- Logical grouping. - -### Method Design -- Always declare visibility. -- Use return/parameter types. -- Prefer early returns. -- Keep short, focused methods. - -### Properties -```php -private readonly EventDispatcherInterface $dispatcher; -private array $options = ['debug' => false]; -``` -Use `string|null` instead of `?string` for consistency. - -### Type Declarations -- Strict types enabled. -- Prefer union with `null` instead of nullable (`?`). -- Use `mixed` sparingly. -- Document complex types in PHPDoc. - -### Function Imports -```php -use function array_map; -use function trim; -``` -One per line. Alphabetical order. - -### Error Handling -- Use specific exceptions. -- Use enums for messages. -- Use `??` operator for fallbacks. -- Document `@throws` in PHPDoc. - -### Arrays -```php -$config = [ - 'debug' => true, - 'cache' => false, - 'version' => '1.0.0', -]; -``` -Use short syntax. Align `=>` in multiline. - -### Control Structures -```php -if ($condition === false) { - return null; -} -``` -- Braces on same line. -- One space after control keywords. -- Use guard clauses. - -## Best Practices -- Composition over inheritance. -- Follow SOLID principles. -- Immutable objects when possible. -- Fluent interfaces for configuration. -- Clear and descriptive naming. -- Validate inputs early. - -### Immutable + Fluent Pattern -```php -$route = Route::get('users', '/users', $handler) - ->withParameters(['id' => '\d+']) - ->withDefaults(['format' => 'json']) - ->withHost('api.example.com'); -``` - -## Copilot Optimization Tips -- Keep structure and naming predictable. -- Add inline comments to show intent. -- Document "why" in PHPDoc. -- Use examples in comments or docblocks. -- Use type declarations consistently. -- Avoid overloading logic in one line. - -**Example for Copilot context:** -```php -// Create a route with constraints -$route = Route::get('profile', '/user/{id}', ProfileController::class) - ->withDefaults(['id' => '1']) - ->withParameters(['id' => '\d+']); -``` diff --git a/copilot/general-instructions.md b/copilot/general-instructions.md deleted file mode 100644 index 3ca3063f..00000000 --- a/copilot/general-instructions.md +++ /dev/null @@ -1,97 +0,0 @@ -# General Project Instructions - -This document outlines global standards for the PHP-Press project. -It complements `class-documentation.md`, `unit-test.md`, `documentation.md`, and `code-style.md` to provide a unified -foundation for team workflows, Copilot guidance, and contributor consistency. - -## Table of Contents -1. Communication -2. Language & Version -3. Code Organization -4. Quality Assurance -5. Documentation Standards -6. Contribution Workflow -7. Reference Documents - -## Communication - -- Respond to users **in Spanish**. -- Write all code and documentation **in English**. -- Ask for clarification if a request or requirement is ambiguous. -- Use clear, precise phrasing aligned with the **Microsoft Writing Style Guide**. -- When referencing filesystem paths related to the MCP command system, assume the project root is: `/core`. - -## Language & Version - -- Use **PHP 8.2** for all backend development. -- Adopt new features such as: - - Constructor property promotion. - - Property hooks. - - Arrow functions. - - Enum types. - - Array utility functions (`array_any`, `array_all`, etc.). -- See `code-style.md` for usage patterns and examples. - -## Code Organization - -- Follow **PSR-4 autoloading** and namespace structure. -- Group components by domain (for example, `Router`, `View`, `Asset`). -- Keep interfaces in `Interface/` or `Contracts/` folders. -- Structure tests to mirror the source tree inside the `/tests/` folder. -- Use the following directory conventions: - ```text - /core - /Src - /ComponentName - /tests - /ComponentName - ``` - ---- - -## Quality Assurance - -- Use **PHPUnit** for all unit tests. -- Aim for **100% test coverage**. -- Configure **PHPStan** at **level 5 or higher** (see `static-analysis.md`). -- Apply code style fixes using **ECS**. - -> Ensure every pull request includes validation for: -> - Static analysis. -> - Code style. -> - Unit tests passing. - -## Documentation Standards - -- Follow `class-documentation.md` for PHPDoc in classes, methods, and properties. -- Use `documentation.md` for non-code documentation. -- Add real-world usage examples in class-level docblocks. -- Maintain a structured and complete `CHANGELOG.md`. -- Document exceptions with `@throws` and type declarations. - -## Contribution Workflow - -- All contributors must: - - Fork from the `main` branch. - - Create topic branches per feature/fix. - - Use **conventional commits** format: - ``` - type(scope): description. - ``` - Example: `feat(router): add host validation`. - - Reference related issues via `#ID` in commits or PR descriptions. - - Include test coverage for new functionality. - - Pass all CI checks before requesting review. - ---- - -## Reference Documents - -For deeper implementation and writing standards, refer to: - -- [`class-documentation.md`](./class-documentation.md) — Class and method PHPDoc rules. -- [`unit-test.md`](./unit-test.md) — Test architecture and PHPUnit conventions. -- [`documentation.md`](./documentation.md) — General writing and formatting rules. -- [`code-style.md`](./code-style.md) — PHP code syntax and 8.2 features. -- [`static-analysis.md`](./static-analysis.md) — PHPStan config, rules, and usage. - diff --git a/copilot/index.html b/copilot/index.html deleted file mode 100644 index 3267de1f..00000000 --- a/copilot/index.html +++ /dev/null @@ -1,1110 +0,0 @@ - - - - - - - Exception – yii\base\Exception - - - - - - - - - - - - - -
-
- - Copied! - - - Copy Stacktrace - - - Search Stackoverflow - - - Search Google - - Exception -
-

Exceptionyii\base\Exception

-

Exception error message.

- - -
- -
-
  • -
    -
    - 1. - in K:\yii2-extensions\psr-bridge\tests\support\stub\SiteController.php - - at line 287 - -
    -
    -
    -
    -
    - 278279280281282283284285286287288289290291292293294
    -        fwrite($tmpFile, 'This is a test file content.');
    -        rewind($tmpFile);
    -
    -        return $this->response->sendStreamAsFile($tmpFile, 'stream.txt', ['mimeType' => 'text/plain']);
    -    }
    -
    -    public function actionTriggerException(): never
    -    {
    -        throw new Exception('Exception error message.');
    -    }
    -
    -    public function actionTriggerUserException(): never
    -    {
    -        throw new UserException('User-friendly error message.');
    -    }
    -}
    -
    -
    -
    -
  • -
  • -
    -
    - 2. - - - - - - yii2\extensions\psrbridge\tests\support\stub\SiteController::actionTriggerException() -
    -
    -
  • -
  • -
    -
    - 3. - in K:\yii2-extensions\psr-bridge\vendor\yiisoft\yii2\base\InlineAction.php - - at line 60 - - - – call_user_func_array() -
    -
    -
    -
    -
    - 545556575859606162
            $args = $this->controller->bindActionParams($this, $params);
    -        Yii::debug('Running action: ' . get_class($this->controller) . '::' . $this->actionMethod . '()', __METHOD__);
    -        if (Yii::$app->requestedParams === null) {
    -            Yii::$app->requestedParams = $args;
    -        }
    -
    -        return call_user_func_array([$this->controller, $this->actionMethod], $args);
    -    }
    -}
    -
    -
    -
    -
  • -
  • -
    -
    - 4. - in K:\yii2-extensions\psr-bridge\vendor\yiisoft\yii2\base\Controller.php - - at line 184 - - - – yii\base\InlineAction::runWithParams() -
    -
    -
    -
    -
    - 178179180181182183184185186187188189190
            }
    -
    -        $result = null;
    -
    -        if ($runAction && $this->beforeAction($action)) {
    -            // run the action
    -            $result = $action->runWithParams($params);
    -
    -            $result = $this->afterAction($action, $result);
    -
    -            // call afterAction on modules
    -            foreach ($modules as $module) {
    -                /** @var Module $module */
    -
    -
    -
    -
  • -
  • -
    -
    - 5. - in K:\yii2-extensions\psr-bridge\vendor\yiisoft\yii2\base\Module.php - - at line 555 - - - – yii\base\Controller::runAction() -
    -
    -
    -
    -
    - 549550551552553554555556557558559560561
            $parts = $this->createController($route);
    -        if (is_array($parts)) {
    -            /** @var Controller $controller */
    -            list($controller, $actionID) = $parts;
    -            $oldController = Yii::$app->controller;
    -            Yii::$app->controller = $controller;
    -            $result = $controller->runAction($actionID, $params);
    -            if ($oldController !== null) {
    -                Yii::$app->controller = $oldController;
    -            }
    -
    -            return $result;
    -        }
    -
    -
    -
    -
  • -
  • -
    -
    - 6. - in K:\yii2-extensions\psr-bridge\vendor\yiisoft\yii2\web\Application.php - - at line 103 - - - – yii\base\Module::runAction() -
    -
    -
    -
    -
    - 979899100101102103104105106107108109
                $params = $this->catchAll;
    -            unset($params[0]);
    -        }
    -        try {
    -            Yii::debug("Route requested: '$route'", __METHOD__);
    -            $this->requestedRoute = $route;
    -            $result = $this->runAction($route, $params);
    -            if ($result instanceof Response) {
    -                return $result;
    -            }
    -
    -            $response = $this->getResponse();
    -            if ($result !== null) {
    -
    -
    -
    -
  • -
  • -
    -
    - 7. - in K:\yii2-extensions\psr-bridge\src\http\StatelessApplication.php - - at line 270 - - - – yii\web\Application::handleRequest() -
    -
    -
    -
    -
    - 264265266267268269270271272273274275276
    -            $this->trigger(self::EVENT_BEFORE_REQUEST);
    -
    -            $this->state = self::STATE_HANDLING_REQUEST;
    -
    -            /** @phpstan-var Response $response */
    -            $response = $this->handleRequest($this->request);
    -
    -            $this->state = self::STATE_AFTER_REQUEST;
    -
    -            $this->trigger(self::EVENT_AFTER_REQUEST);
    -
    -            $this->state = self::STATE_END;
    -
    -
    -
    -
  • -
  • -
    -
    - 8. - in K:\yii2-extensions\psr-bridge\tests\http\StatelessApplicationTest.php - - at line 2106 - - - – yii2\extensions\psrbridge\http\StatelessApplication::handle() -
    -
    -
    -
    -
    - 2100210121022103210421052106210721082109211021112112
                    'errorHandler' => [
    -                    'errorAction' => null,
    -                ],
    -            ],
    -        ]);
    -
    -        $response = $app->handle($request);
    -
    -        var_dump($response->getBody()->getContents());
    -
    -        self::assertSame(
    -            '1',
    -            ini_get('display_errors'),
    -
    -
    -
    -
  • -
  • -
    -
    - 9. - in K:\yii2-extensions\psr-bridge\vendor\phpunit\phpunit\src\Framework\TestCase.php - - at line 1548 - - - – yii2\extensions\psrbridge\tests\http\StatelessApplicationTest::testRenderExceptionSetsDisplayErrorsInDebugMode() -
    -
    -
    -
    -
    - 1542154315441545154615471548154915501551155215531554
        {
    -        $testArguments = array_merge($this->data, $this->dependencyInput);
    -
    -        $this->registerMockObjectsFromTestArguments($testArguments);
    -
    -        try {
    -            $testResult = $this->{$this->name}(...array_values($testArguments));
    -        } catch (Throwable $exception) {
    -            if (!$this->shouldExceptionExpectationsBeVerified($exception)) {
    -                throw $exception;
    -            }
    -
    -            $this->verifyExceptionExpectations($exception);
    -
    -
    -
    -
  • -
  • -
    -
    - 10. - in K:\yii2-extensions\psr-bridge\vendor\phpunit\phpunit\src\Framework\TestCase.php - - at line 686 - - - – PHPUnit\Framework\TestCase::runTest() -
    -
    -
    -
    -
    - 680681682683684685686687688689690691692
    -            $emitter->testPrepared(
    -                $this->valueObjectForEvents(),
    -            );
    -
    -            $this->wasPrepared = true;
    -            $this->testResult  = $this->runTest();
    -
    -            $this->verifyMockObjects();
    -            $this->invokePostConditionHookMethods($hookMethods, $emitter);
    -
    -            $this->status = TestStatus::success();
    -        } catch (IncompleteTest $e) {
    -
    -
    -
    -
  • -
  • -
    -
    - 11. - in K:\yii2-extensions\psr-bridge\vendor\phpunit\phpunit\src\Framework\TestRunner.php - - at line 106 - - - – PHPUnit\Framework\TestCase::runBare() -
    -
    -
    -
    -
    - 100101102103104105106107108109110111112
    -        try {
    -            if ($this->canTimeLimitBeEnforced() &&
    -                $this->shouldTimeLimitBeEnforced($test)) {
    -                $risky = $this->runTestWithTimeout($test);
    -            } else {
    -                $test->runBare();
    -            }
    -        } catch (AssertionFailedError $e) {
    -            $failure = true;
    -
    -            if ($e instanceof IncompleteTestError) {
    -                $incomplete = true;
    -
    -
    -
    -
  • -
  • -
    -
    - 12. - in K:\yii2-extensions\psr-bridge\vendor\phpunit\phpunit\src\Framework\TestCase.php - - at line 516 - - - – PHPUnit\Framework\TestRunner::run() -
    -
    -
    -
    -
    - 510511512513514515516517518519520521522
        {
    -        if (!$this->handleDependencies()) {
    -            return;
    -        }
    -
    -        if (!$this->shouldRunInSeparateProcess() || $this->requirementsNotSatisfied()) {
    -            (new TestRunner)->run($this);
    -        } else {
    -            (new TestRunner)->runInSeparateProcess(
    -                $this,
    -                $this->runClassInSeparateProcess && !$this->runTestInSeparateProcess,
    -                $this->preserveGlobalState,
    -            );
    -
    -
    -
    -
  • -
  • -
    -
    - 13. - in K:\yii2-extensions\psr-bridge\vendor\phpunit\phpunit\src\Framework\TestSuite.php - - at line 374 - - - – PHPUnit\Framework\TestCase::run() -
    -
    -
    -
    -
    - 368369370371372373374375376377378379380
                if (TestResultFacade::shouldStop()) {
    -                $emitter->testRunnerExecutionAborted();
    -
    -                break;
    -            }
    -
    -            $test->run();
    -        }
    -
    -        $this->invokeMethodsAfterLastTest($emitter);
    -
    -        $emitter->testSuiteFinished($testSuiteValueObjectForEvents);
    -    }
    -
    -
    -
    -
  • -
  • -
    -
    - 14. - in K:\yii2-extensions\psr-bridge\vendor\phpunit\phpunit\src\Framework\TestSuite.php - - at line 374 - - - – PHPUnit\Framework\TestSuite::run() -
    -
    -
    -
    -
    - 368369370371372373374375376377378379380
                if (TestResultFacade::shouldStop()) {
    -                $emitter->testRunnerExecutionAborted();
    -
    -                break;
    -            }
    -
    -            $test->run();
    -        }
    -
    -        $this->invokeMethodsAfterLastTest($emitter);
    -
    -        $emitter->testSuiteFinished($testSuiteValueObjectForEvents);
    -    }
    -
    -
    -
    -
  • -
  • -
    -
    - 15. - in K:\yii2-extensions\psr-bridge\vendor\phpunit\phpunit\src\Framework\TestSuite.php - - at line 374 - - - – PHPUnit\Framework\TestSuite::run() -
    -
    -
    -
    -
    - 368369370371372373374375376377378379380
                if (TestResultFacade::shouldStop()) {
    -                $emitter->testRunnerExecutionAborted();
    -
    -                break;
    -            }
    -
    -            $test->run();
    -        }
    -
    -        $this->invokeMethodsAfterLastTest($emitter);
    -
    -        $emitter->testSuiteFinished($testSuiteValueObjectForEvents);
    -    }
    -
    -
    -
    -
  • -
  • -
    -
    - 16. - in K:\yii2-extensions\psr-bridge\vendor\phpunit\phpunit\src\TextUI\TestRunner.php - - at line 64 - - - – PHPUnit\Framework\TestSuite::run() -
    -
    -
    -
    -
    - 58596061626364656667686970
                (new TestSuiteFilterProcessor)->process($configuration, $suite);
    -
    -            Event\Facade::emitter()->testRunnerExecutionStarted(
    -                Event\TestSuite\TestSuiteBuilder::from($suite),
    -            );
    -
    -            $suite->run();
    -
    -            Event\Facade::emitter()->testRunnerExecutionFinished();
    -            Event\Facade::emitter()->testRunnerFinished();
    -        } catch (Throwable $t) {
    -            throw new RuntimeException(
    -                $t->getMessage(),
    -
    -
    -
    -
  • -
  • -
    -
    - 17. - in K:\yii2-extensions\psr-bridge\vendor\phpunit\phpunit\src\TextUI\Application.php - - at line 203 - - - – PHPUnit\TextUI\TestRunner::run() -
    -
    -
    -
    -
    - 197198199200201202203204205206207208209
    -            $timer = new Timer;
    -            $timer->start();
    -
    -            $runner = new TestRunner;
    -
    -            $runner->run(
    -                $configuration,
    -                $resultCache,
    -                $testSuite,
    -            );
    -
    -            $duration = $timer->stop();
    -
    -
    -
    -
  • -
  • -
    -
    - 18. - in K:\yii2-extensions\psr-bridge\vendor\phpunit\phpunit\phpunit - - at line 104 - - - – PHPUnit\TextUI\Application::run() -
    -
    -
    -
    -
    - 9899100101102103104 -
    -    die(1);
    -}
    -
    -unset($requiredExtensions, $unavailableExtensions);
    -
    -exit((new PHPUnit\TextUI\Application)->run($_SERVER['argv']));
    -
    -
    -
    -
  • -
  • -
    -
    - 19. - in K:\yii2-extensions\psr-bridge\vendor\bin\phpunit - - at line 122 - - - – include('K:\yii2-extensions\psr-bridge\ve...') -
    -
    -
    -
    -
    - 116117118119120121122 -
            || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
    -    ) {
    -        return include("phpvfscomposer://" . __DIR__ . '/..'.'/phpunit/phpunit/phpunit');
    -    }
    -}
    -
    -return include __DIR__ . '/..'.'/phpunit/phpunit/phpunit';
    -
    -
    -
    -
  • -
- -
-
-
$_SESSION = [
-    '__flash' => [],
-];
-
- - - - - - - - - diff --git a/copilot/project-documentation.md b/copilot/project-documentation.md deleted file mode 100644 index ce9956ee..00000000 --- a/copilot/project-documentation.md +++ /dev/null @@ -1,192 +0,0 @@ -# Documentation Guidelines - -This guide defines general documentation standards for non-code content in the PHP-Press project and is designed to work -in tandem with `class-documentation.md` and `unit-test.md`. It is optimized for clarity, maintainability, and -compatibility with GitHub Copilot. - -## Table of Contents -1. Writing Style. -2. Technical Writing. -3. Documentation Types. -4. Architecture Docs. -5. Maintenance. -6. Copilot Optimization Tips. - -## Writing Style - -### Tone and Voice -- Use a **professional but conversational** English tone. -- Write with **clarity and purpose**; prefer **active voice**. -- Address readers directly ("you"). -- Explain the **"why"** behind important guidance. - -### Microsoft Writing Style Compliance -- Use simple sentence structures (subject + verb + object). -- Keep content concise and conversational. -- Prefer contracted forms (for example, "it's", "you're"). -- Use **sentence-style capitalization**. -- Write numbers as numerals (for example, "5 users"). -- Format dates as "March 26, 2024". -- Avoid: - - Sentence fragments - - Synonyms for a single concept - - Culturally sensitive terms - - Long modifier chains - - Overly complex words - -### Formatting Guidelines -- Use headings to organize sections. -- Use whitespace for readability. -- Use bullet points for unordered lists and numbered lists for sequences. -- Keep paragraphs short (3–4 sentences). -- Include serial commas in lists. - -## Technical Writing - -### Code References -Use backticks \``\` for inline code like: - -- `composer.json` -- `DependencyResolver` -- `configure()` -- `$config` -- `PHP_VERSION` -- `['debug' => true]` - -### Code Examples -- Use fenced code blocks with language hints. -- Add context with inline comments. -- Keep examples **minimal but complete**. -- Show **input and expected output** when relevant. -- Follow a **consistent coding style**. - -**Example:** - -```php -// Configure the component with options -$component = new Component( - [ - 'debug' => true, - 'cache' => false, - ], -); - -// Returns: ['status' => 'configured'] -$result = $component->getStatus(); -``` - -### Links and References -- Use **relative links** for project docs. -- Use **descriptive link text**. -- Include version numbers for dependencies. -- Link to source code or issues when appropriate. - -## Documentation Types - -### README Files -- Project overview and purpose -- List of key features -- Quick start guide -- Installation steps -- Usage examples -- Dependencies -- License information - -### API Documentation -- Purpose and scope -- Authentication mechanism -- Request and response format -- Error structures and codes -- Rate limiting policies -- Usage examples per endpoint -- Reference to external SDKs or libraries - -### Tutorials -- Clear learning objectives -- Prerequisites list -- Step-by-step instructions -- Complete code examples -- Screenshots or expected output -- Troubleshooting tips -- References to deeper docs - -### Changelogs -- Follow **semantic versioning**. -- Group entries by type: - - Added - - Changed - - Deprecated - - Removed - - Fixed - - Security -- Provide migration notes if relevant. -- Link to related issues or PRs. - -## Architecture Docs - -### Component Documentation -- Component purpose and context -- Internal and external dependencies -- Configuration options and defaults -- Usage patterns and lifecycle -- Event triggers and listeners -- Extensibility points (hooks/plugins) -- Performance or scaling considerations - -### Integration Guides -- System/environment requirements -- Setup and configuration -- Supported platforms or stacks -- Common scenarios and walkthroughs -- Error handling and logging -- Security best practices -- Links to example projects or templates - -## Maintenance - -### Version Control -- Keep docs versioned alongside code. -- Submit doc updates as part of pull requests. -- Tag documentation per release. -- Archive obsolete versions. -- Track changes in changelogs or history. - -### Quality Checks -- Spellcheck and grammar review -- Validate links and anchors -- Run code snippets if executable -- Refresh outdated screenshots -- Test configuration instructions -- Use consistent formatting across files - -### File and Folder Organization -- Match doc folder structure to source tree -- Add index files or README in folders -- Add navigation or sidebar links -- Use version indicators where needed -- Encourage cross-referencing via `{@see}` or relative links - -### Feedback and Improvement -- Monitor issue trackers and feedback tools -- Encourage user input for unclear docs -- Update or reword misunderstood sections -- Remove or flag outdated content -- Add examples where readers get stuck - -## Copilot Optimization Tips -- Use **clear and consistent terminology**. -- Provide **working, well-commented examples**. -- Declare types when describing parameters or code structure. -- Keep each section purpose-focused (Copilot benefits from separation). -- Avoid speculative language (for example, “might do this”) and favor definitiveness. -- Include real-world usage patterns in documentation. -- Use consistent phrasing so Copilot can detect doc intent. - -**Copilot-aligned example:** - -```php -// Correct way to initialize the router -$router = new Router(); -$router->register(...); -$router->dispatch($request); -``` diff --git a/copilot/static-analysis.md b/copilot/static-analysis.md deleted file mode 100644 index 0bfa98bb..00000000 --- a/copilot/static-analysis.md +++ /dev/null @@ -1,178 +0,0 @@ -# Static Analysis Guidelines - -This document defines rules for static code analysis to ensure type safety and detect errors early in the PHP-Press project. - -## PHPStan Configuration - -### Level Settings -- Use PHPStan level 5 as the minimum requirement. -- Level 8 is mandatory for core components. -- Configure per-directory level settings as needed. - -### Base Rules -- Enable strict mode for all files. -- Require explicit method return types. -- Require explicit property types. -- Enable dead code detection. -- Validate template type constraints. - -## Type Declarations - -### Property Types -```php -/** - * @var array Registered bundles indexed by class name. - */ -private array $bundles = []; - -/** - * @var Collection CSS link tags for rendering. - */ -private array $css = []; - -/** - * @var array, array> Event listeners by event class. - */ -private array $listeners = []; -``` - -### Method Types -```php -/** - * @template T of object - * @param class-string $class - * @param array $config - * @return T - * - * @throws InvalidArgumentException - * @throws ContainerException - */ -public function create(string $class, array $config = []): object; -``` - -### Generic Types -```php -/** - * @template TKey of array-key - * @template TValue - * - * @param array $items - * @param callable(TValue): bool $predicate - * @return array - */ -public function filter(array $items, callable $predicate): array; -``` - -### Union Types -```php -/** - * @param array|string|null $paths - * @return string[] - * - * @throws InvalidArgumentException When path is invalid. - */ -public function resolvePaths(array|string|null $paths): array; -``` - -## PHPStan Baseline - -### Managing Baseline -- Generate a baseline for existing issues. -- Review and document accepted issues. -- Update the baseline with each major release. -- Track technical debt in the baseline. - -### Baseline Command -```bash -vendor/bin/phpstan analyse --generate-baseline -``` - -## Custom Rules - -### Rule Categories -- Architectural rules. -- Naming conventions. -- Type safety rules. -- Framework-specific rules. - -### Rule Implementation -```php -/** - * @implements Rule - */ -final class CustomRule implements Rule -{ - public function getNodeType(): string - { - return Node\Stmt\Class_::class; - } - - /** - * @param Node\Stmt\Class_ $node - */ - public function processNode(Node $node, Scope $scope): array - { - // Rule implementation. - } -} -``` - -## Error Categories - -### Type Safety -- Undefined methods/properties. -- Invalid argument types. -- Incompatible return types. -- Missing type declarations. -- Template type mismatches. - -### Dead Code -- Unreachable code paths. -- Unused private methods. -- Unused parameters. -- Redundant conditions. -- Dead catch blocks. - -### Method Calls -- Unknown method calls. -- Invalid argument counts. -- Type compatibility issues. -- Static call validity. -- Visibility violations. - -### Property Access -- Undefined properties. -- Invalid property types. -- Uninitialized properties. -- Readonly violations. -- Visibility checks. - -## Best Practices - -### Configuration -- Use `phpstan.neon.dist` as the base configuration. -- Include `baseline.neon`. -- Configure for the CI/CD pipeline. -- Set memory limits appropriately. -- Enable result caching. - -### Error Handling -- Document intentional suppressions. -- Use ignore patterns sparingly. -- Review suppressions regularly. -- Track technical debt items. -- Fix issues incrementally. - -### Performance -- Enable parallel analysis. -- Configure memory limits. -- Use result caching. -- Optimize ignore patterns. -- Analyze incrementally. - -### Integration -- Run in the CI/CD pipeline. -- Block merges on errors. -- Generate HTML reports. -- Track error trends. -- Review in PRs. diff --git a/copilot/unit-test.md b/copilot/unit-test.md deleted file mode 100644 index df60745c..00000000 --- a/copilot/unit-test.md +++ /dev/null @@ -1,179 +0,0 @@ -# Unit Test Guidelines - -## Table of Contents -1. Test Class Naming. -2. Test Method Naming. -3. Test Structure (Arrange-Act-Assert). -4. Data Providers. -5. Best Practices. -6. Copilot Optimization Tips. - -## Test Class Naming - -### Basic Rules -- **Clarity and Descriptiveness:** The test class name should clearly reflect the class being tested. -- **Namespace Structure:** The test class should follow the same namespace structure as the production class, - located in the `tests/` directory. -- **Naming Convention:** The class name should match the production class and end with `Test`. -- **Internal Documentation:** Document each test case and make sure to mock the necessary dependencies. - -**Example:** -```php -// Class to test in src/AbstractView/View.php -namespace PHPPress\View; - -class View { } - -// Test class in tests/AbstractView/ViewTest.php -namespace PHPPress\Tests\View; - -class ViewTest { } -``` - -## Test Method Naming - -### Recommended Pattern -Use the format `test` for test methods, where: -- **:** Describes the main action (Render, Throw, Return, etc.). -- **:** Indicates the subject under test (View, Layout, etc.). -- **:** Specifies the condition or scenario. - -**Examples:** -```php -public function testRenderLayoutWithContext(): void { } -public function testRenderViewWithTheme(): void { } -public function testThrowExceptionWhenTemplateInvalid(): void { } -``` - -### Organization -- Group and order test methods alphabetically. -- Maintain consistent nomenclature within the same test class. - -## Test Structure (Arrange-Act-Assert) - -Each test should follow the AAA pattern to ensure clarity in the intention and behavior of each test: - -```php -public function testRenderWithParametersReplacesPlaceholders(): void -{ - // Arrange: Set up the test object and necessary parameters. - $view = new View($resolver, $dispatcher); - $parameters = ['title' => 'Test']; - - // Act: Execute the main action. - $result = $view->render('index', $parameters); - - // Assert: Validate that the result meets expectations. - $this->assertStringContainsString( - 'Test', - $result, - 'Rendered view should contain the title parameter.', - ); -} -``` - -## Data Providers - -### Location and Convention -- Place data providers in the `/tests/Provider/` directory. -- Name the files and classes with a `Provider` suffix (for example, `ConverterCommandProvider`). - -### Documentation and Example -Include complete documentation in each data provider, explaining the structure and purpose of the data. - -**Example:** -path: /core/tests/Provider/Router/RouteProvider.php - -```php -namespace PHPPress\Tests\Provider\Router; - -/** - * Data provider for testing the Router component. - * - * Designed to ensure the router component correctly processes all supported configurations and appropriately handles - * edge cases with proper error messaging. - * - * The test data validates complex route configuration scenarios including security-sensitive inputs to prevent - * potential injection vulnerabilities. - * - * The provider organizes test cases with descriptive names for quick identification of failure cases during test - * execution and debugging sessions. - * - * Key features. - * - Comprehensive test cases for all router features. - * - Edge case testing for input validation. - * - Host pattern matching with domain validation. - * - Named test data sets for clear failure identification. - * - Parameter pattern validation with regular expressions. - * - Security scenario testing for potential injection patterns. - * - URL suffix and pattern matching validation. - * - * @copyright Copyright (C) 2024 PHPPress. - * @license https://opensource.org/license/gpl-3-0 GNU General Public License version 3 or later. - */ -final class RouteProvider -``` - -**Usage in Tests:** -path: /core/tests/Router/RouteTest.php - -```php -#[DataProviderExternal(RouteProvider::class, 'names')] -public function testAcceptRouteNamesWhenValid(string $name): void -{ - $route = Route::to($name, '/test', FactoryHelper::createHandler()); - - $this->assertSame($name, $route->name, "Route should accept valid route name: '{$name}'."); -} -``` - -## Best Practices - -### General Principles -- **Independence:** Each test should be independent and not depend on the execution order. -- **Cleanup:** Use `setUp()` and `tearDown()` to clean up global or static states between tests. -- **Complete Coverage:** Test both positive and negative cases, including edge values and exception scenarios. -- **Clear Messages:** Assertion messages should explain what was expected and what was obtained, making it easier to - identify failures. - -**Example of Detailed Assertion:** -```php -$this->assertSame( - $expected, - $actual, - 'Route should match when URL parameters meet the expected pattern with multiple parameters.' -); -$this->assertTrue( - $this->filesystem->fileExists("{$this->dirFrom}/js/script.js"), - 'File \'js/script.js\' should exist in the destination after excluding only the strict subdirectory legacy CSS.', -); -$this->assertTrue( - $this->filesystem->copyMatching($this->dir, $this->dirFrom), - 'Copy matching should return \'true\' when copying all files recursively without patterns.', -); -``` - -### Exceptions in Tests -- Document in the PHPDoc the conditions under which an exception is expected to be thrown, including clear examples. - -**Example:** -path: /core/tests/Router/RouteTest.php - -```php -public function testThrowExceptionWithInvalidVersion(): void -{ - $this->expectException(InvalidRouteParameterException::class); - $this->expectExceptionMessage(Message::INVALID_ROUTE_VERSION->getMessage('invalid')); - - Route::get('users', '/users', FactoryHelper::createHandler())->withVersion('invalid'); -} -``` - -## Copilot Optimization Tips - -- **Clear and Descriptive Names:** Use meaningful names for methods and variables. -- **Type Declaration:** Always specify parameter and return types in your test methods. -- **Updated Documentation:** Keep PHPDoc updated with clear and detailed examples. -- **Code Comments:** Add brief comments explaining each part of the AAA structure in your tests. -- **Explanation of "Why":** Include in the documentation the rationale behind each test so that tools - like GitHub Copilot can generate suggestions aligned with the design. diff --git a/phpstan.neon b/phpstan.neon deleted file mode 100644 index 7adfced3..00000000 --- a/phpstan.neon +++ /dev/null @@ -1,27 +0,0 @@ -includes: - - phar://phpstan.phar/conf/bleedingEdge.neon - -parameters: - bootstrapFiles: - - tests/bootstrap.php - - level: max - - paths: - - src - - tests - - tmpDir: %currentWorkingDirectory%/runtime - - yii2: - config_path: %currentWorkingDirectory%/tests/phpstan-config.php - - # Enable strict advanced checks - checkImplicitMixed: true - checkBenevolentUnionTypes: true - checkUninitializedProperties: true - checkMissingCallableSignature: true - checkTooWideReturnTypesInProtectedAndPublicMethods: true - reportAnyTypeWideningInVarTag: true - reportPossiblyNonexistentConstantArrayOffset: true - reportPossiblyNonexistentGeneralArrayOffset: true diff --git a/phpunit.xml.dist b/phpunit.xml.dist deleted file mode 100644 index 87ad9924..00000000 --- a/phpunit.xml.dist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - tests - - - - - - ./src - - - - - - - From 8f0cde068e72521d3b3deb2f085ccfa188a7a194 Mon Sep 17 00:00:00 2001 From: Wilmer Arambula Date: Wed, 6 Aug 2025 18:00:18 -0400 Subject: [PATCH 08/12] feat(tests): Add PHPUnit and PHPStan configuration files. --- Contents | 1 - This content should not be emitted | 1 - phpstan.neon | 27 +++++++++++++++++++++++++++ phpunit.xml.dist | 28 ++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) delete mode 100644 Contents delete mode 100644 This content should not be emitted create mode 100644 phpstan.neon create mode 100644 phpunit.xml.dist diff --git a/Contents b/Contents deleted file mode 100644 index 9ae9ea0a..00000000 --- a/Contents +++ /dev/null @@ -1 +0,0 @@ -Contents \ No newline at end of file diff --git a/This content should not be emitted b/This content should not be emitted deleted file mode 100644 index 2d54e228..00000000 --- a/This content should not be emitted +++ /dev/null @@ -1 +0,0 @@ -This content should not be emitted \ No newline at end of file diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 00000000..7adfced3 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,27 @@ +includes: + - phar://phpstan.phar/conf/bleedingEdge.neon + +parameters: + bootstrapFiles: + - tests/bootstrap.php + + level: max + + paths: + - src + - tests + + tmpDir: %currentWorkingDirectory%/runtime + + yii2: + config_path: %currentWorkingDirectory%/tests/phpstan-config.php + + # Enable strict advanced checks + checkImplicitMixed: true + checkBenevolentUnionTypes: true + checkUninitializedProperties: true + checkMissingCallableSignature: true + checkTooWideReturnTypesInProtectedAndPublicMethods: true + reportAnyTypeWideningInVarTag: true + reportPossiblyNonexistentConstantArrayOffset: true + reportPossiblyNonexistentGeneralArrayOffset: true diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 00000000..87ad9924 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,28 @@ + + + + + tests + + + + + + ./src + + + + + + + From b2b9a1235b7eb1f5d3e1534394be52c02aa710da Mon Sep 17 00:00:00 2001 From: Wilmer Arambula Date: Wed, 6 Aug 2025 18:03:06 -0400 Subject: [PATCH 09/12] fix: Improve exception documentation in `SiteController` actions. --- tests/support/stub/SiteController.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/support/stub/SiteController.php b/tests/support/stub/SiteController.php index 775b10f2..1a3ddb2b 100644 --- a/tests/support/stub/SiteController.php +++ b/tests/support/stub/SiteController.php @@ -122,7 +122,7 @@ public function actionErrorWithResponse(): Response } /** - * @throws Exception + * @throws Exception if an unexpected error occurs during execution. */ public function actionFile(): Response { @@ -290,8 +290,8 @@ public function actionStatuscode(): void } /** - * @throws Exception - * @throws RangeNotSatisfiableHttpException + * @throws Exception if an unexpected error occurs during execution. + * @throws RangeNotSatisfiableHttpException if the requested range is not satisfiable. */ public function actionStream(): Response { @@ -310,7 +310,7 @@ public function actionStream(): Response } /** - * @throws Exception + * @throws Exception if an unexpected error occurs during execution. */ public function actionTriggerException(): never { @@ -318,7 +318,7 @@ public function actionTriggerException(): never } /** - * @throws UserException + * @throws UserException if user-friendly error is triggered. */ public function actionTriggerUserException(): never { From 393d934cb3fae23ba2801f0648876bc2229db957 Mon Sep 17 00:00:00 2001 From: Wilmer Arambula Date: Wed, 6 Aug 2025 18:17:23 -0400 Subject: [PATCH 10/12] feat(tests): Add test for handling `NotFoundHttpException` class in `StatelessApplicationTest` class. --- tests/http/StatelessApplicationTest.php | 36 +++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/http/StatelessApplicationTest.php b/tests/http/StatelessApplicationTest.php index 6594b3f1..203b9795 100644 --- a/tests/http/StatelessApplicationTest.php +++ b/tests/http/StatelessApplicationTest.php @@ -1899,6 +1899,42 @@ public function testThrowableOccursDuringRequestHandling(): void ); } + /** + * @throws InvalidConfigException if the configuration is invalid or incomplete. + */ + public function testThrowNotFoundHttpExceptionForNonExistentRoute(): void + { + $_SERVER = [ + 'REQUEST_METHOD' => 'GET', + 'REQUEST_URI' => 'site/profile/123', + ]; + + $request = FactoryHelper::createServerRequestCreator()->createFromGlobals(); + + $app = $this->statelessApplication(); + + $response = $app->handle($request); + + self::assertSame( + 404, + $response->getStatusCode(), + "Response 'status code' should be '404' when accessing a non-existent route in 'StatelessApplication', " . + "indicating a 'Not Found' error.", + ); + self::assertSame( + 'text/html; charset=UTF-8', + $response->getHeaders()['content-type'][0] ?? '', + "Response 'content-type' should be 'text/html; charset=UTF-8' for 'NotFoundHttpException' in " . + "'StatelessApplication'.", + ); + self::assertStringContainsString( + '
Not Found: Page not found.
', + $response->getBody()->getContents(), + "Response 'body' should contain the default not found message '
Not Found: Page not found.
' " . + "when a 'NotFoundHttpException' is thrown in 'StatelessApplication'.", + ); + } + /** * @throws InvalidConfigException if the configuration is invalid or incomplete. */ From d61f3e7024c06ed595cf34f011ddde63f7fedf9c Mon Sep 17 00:00:00 2001 From: Wilmer Arambula Date: Wed, 6 Aug 2025 18:40:04 -0400 Subject: [PATCH 11/12] feat(tests): Enhance `StatelessApplicationTest` class with additional `NotFoundHttpException` class tests and improve method naming. --- tests/TestCase.php | 1 - tests/http/StatelessApplicationTest.php | 34 ++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index 20fa3c9c..0bf0284d 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -150,7 +150,6 @@ protected function statelessApplication(array $config = []): StatelessApplicatio 'enablePrettyUrl' => true, 'rules' => [ 'site/update/' => 'site/update', - '//' => '/', ], ], ], diff --git a/tests/http/StatelessApplicationTest.php b/tests/http/StatelessApplicationTest.php index 203b9795..4db07e7e 100644 --- a/tests/http/StatelessApplicationTest.php +++ b/tests/http/StatelessApplicationTest.php @@ -16,6 +16,8 @@ use yii\i18n\{Formatter, I18N}; use yii\log\Dispatcher; use yii\web\{AssetManager, Session, UrlManager, User, View}; +use yii\web\NotFoundHttpException; +use yii2\extensions\psrbridge\exception\Message; use yii2\extensions\psrbridge\http\{ErrorHandler, Request, Response}; use yii2\extensions\psrbridge\tests\provider\StatelessApplicationProvider; use yii2\extensions\psrbridge\tests\support\FactoryHelper; @@ -1902,7 +1904,7 @@ public function testThrowableOccursDuringRequestHandling(): void /** * @throws InvalidConfigException if the configuration is invalid or incomplete. */ - public function testThrowNotFoundHttpExceptionForNonExistentRoute(): void + public function testThrowNotFoundHttpExceptionWhenStrictParsingDisabledAndRouteIsMissing(): void { $_SERVER = [ 'REQUEST_METHOD' => 'GET', @@ -1935,6 +1937,36 @@ public function testThrowNotFoundHttpExceptionForNonExistentRoute(): void ); } + /** + * @throws InvalidConfigException if the configuration is invalid or incomplete. + */ + public function testThrowNotFoundHttpExceptionWhenStrictParsingEnabledAndRouteIsMissing(): void + { + $_SERVER = [ + 'REQUEST_METHOD' => 'GET', + 'REQUEST_URI' => 'site/profile/123', + ]; + + $request = FactoryHelper::createServerRequestCreator()->createFromGlobals(); + + $app = $this->statelessApplication( + [ + 'components' => [ + 'urlManager' => [ + 'enableStrictParsing' => true, + ], + ], + ], + ); + + $app->handle($request); + + $this->expectException(NotFoundHttpException::class); + $this->expectExceptionMessage(Message::PAGE_NOT_FOUND->getMessage()); + + $app->request->resolve(); + } + /** * @throws InvalidConfigException if the configuration is invalid or incomplete. */ From 3d37b87c9af601f1fae9500efe800a61a49b1a12 Mon Sep 17 00:00:00 2001 From: Wilmer Arambula Date: Wed, 6 Aug 2025 18:42:06 -0400 Subject: [PATCH 12/12] fix: Correct import order for `NotFoundHttpException` class in `StatelessApplicationTest` class. --- tests/http/StatelessApplicationTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/http/StatelessApplicationTest.php b/tests/http/StatelessApplicationTest.php index 4db07e7e..f7808a51 100644 --- a/tests/http/StatelessApplicationTest.php +++ b/tests/http/StatelessApplicationTest.php @@ -15,8 +15,7 @@ use yii\helpers\Json; use yii\i18n\{Formatter, I18N}; use yii\log\Dispatcher; -use yii\web\{AssetManager, Session, UrlManager, User, View}; -use yii\web\NotFoundHttpException; +use yii\web\{AssetManager, NotFoundHttpException, Session, UrlManager, User, View}; use yii2\extensions\psrbridge\exception\Message; use yii2\extensions\psrbridge\http\{ErrorHandler, Request, Response}; use yii2\extensions\psrbridge\tests\provider\StatelessApplicationProvider;