Add framework basics #24

Merged
c.fahner merged 17 commits from issues/1 into main 2026-07-13 14:16:55 +02:00
Owner

Resolves #1.

Adds the main framework entry point: the Application class
Adds many Application-related classes: Action, Configs, Module, ActionRewriter, RequestRouter, et al.
Refactors the last parts of Resource\Handler into Application\RequestHandler
Adds a clearer separation in the tests namespace: Common, Unit and Integration

Resolves #1. Adds the main framework entry point: the `Application` class Adds many Application-related classes: `Action`, `Configs`, `Module`, `ActionRewriter`, `RequestRouter`, et al. Refactors the last parts of `Resource\Handler` into `Application\RequestHandler` Adds a clearer separation in the tests namespace: `Common`, `Unit` and `Integration`
c.fahner added this to the v0.1 milestone 2026-07-08 16:49:12 +02:00
Author
Owner

Handling of IllegalActionException in Application\RequestHandler still needs to be implemented.

An issue should be opened to trigger an "abuse report" in this situation.

Handling of `IllegalActionException` in `Application\RequestHandler` still needs to be implemented. An issue should be opened to trigger an "abuse report" in this situation.
@ -0,0 +6,4 @@
* A configured alias of an application module.
*
* Aliases can be any non-empty string, but keep in mind that they are used in URL's and database
* table names.
Author
Owner

Add that they should be kept simple: alphanumeric and underscores.

Add that they should be kept simple: alphanumeric and underscores.
c.fahner marked this conversation as resolved
@ -0,0 +43,4 @@
: new DateTimeImmutable;
return $dateValue > $futureLimit
? throw new Exception('Auto expiring modes may not be configured beyond one day into the future')
Author
Owner

Should be ApplicationException, which also means it can be tested more accurately.

Should be `ApplicationException`, which also means it can be tested more accurately.
c.fahner marked this conversation as resolved
@ -0,0 +32,4 @@
$uniqueModules = [ ];
foreach ($source as $alias => $module) {
if (isset($aliasMap[$alias])) {
throw new LogicException("Unexpected duplicate module alias `$alias`");
Author
Owner

Should be ApplicationException

Edit: the exception for duplicate aliases is unreachable from the public API and will therefore be left as a generic exception

~~Should be `ApplicationException`~~ Edit: the exception for duplicate aliases is unreachable from the public API and will therefore be left as a generic exception
c.fahner marked this conversation as resolved
@ -0,0 +35,4 @@
throw new LogicException("Unexpected duplicate module alias `$alias`");
}
if (isset($uniqueModules[$module::class])) {
throw new LogicException('Unexpected duplicate module type `'.$module::class.'`');
Author
Owner

Should be ApplicationException

Should be `ApplicationException`
c.fahner marked this conversation as resolved
@ -0,0 +14,4 @@
* @author C. Fahner
* @copyright Slendium 2026
*/
final class ApplicationException extends Exception {
Author
Owner

Should extend LogicException

Should extend `LogicException`
c.fahner marked this conversation as resolved
@ -0,0 +117,4 @@
return false;
}
private function createErrorResponse(string $errorMessage): Response {
Author
Owner

Rename to indicate this method is for "sensitive" errors, createSensitiveErrorResponse()?

Rename to indicate this method is for "sensitive" errors, `createSensitiveErrorResponse()`?
c.fahner marked this conversation as resolved
@ -0,0 +8,4 @@
* A module that provides a default resource.
*
* @since 1.0
* @author
Author
Owner

Add author/copyright

Add author/copyright
c.fahner marked this conversation as resolved
@ -0,0 +14,4 @@
*/
class BaseArgumentTest extends ResponseTestCase {
public function test_application_handleRequest_shouldRespondWithSuccess_whenRequiredQueryArgumentGiven(): void {
Author
Owner

_shouldRespond2xx

`_shouldRespond2xx`
c.fahner marked this conversation as resolved
@ -0,0 +27,4 @@
$this->assertBody($expectedResult, $result);
}
public function test_application_handleRequest_shouldRespondWith400_whenRequiredQueryArgumentOmitted(): void {
Author
Owner

_shouldRespond400

`_shouldRespond400`
c.fahner marked this conversation as resolved
@ -0,0 +38,4 @@
$this->assertResponseStatus(400, $result);
}
public function test_application_handleRequest_shouldRespondWithSucces_whenOptionalQueryArgumentOmitted(): void {
Author
Owner

_shouldRespond2xx

`_shouldRespond2xx`
c.fahner marked this conversation as resolved
@ -0,0 +51,4 @@
$this->assertBody($expectedBody, $result);
}
public function test_application_handleRequest_shouldRespondWithSuccess_whenOptionalQueryArgumentGiven(): void {
Author
Owner

_shouldRespond2xx

`_shouldRespond2xx`
c.fahner marked this conversation as resolved
@ -0,0 +98,4 @@
$this->assertBody($expectedResult, $result);
}
public function test_application_handleRequest_shouldRespondWithSuccess_whenRequiredBodyArgumentGiven(): void {
Author
Owner

_shouldRespond2xx

`_shouldRespond2xx`
c.fahner marked this conversation as resolved
@ -0,0 +112,4 @@
$this->assertBody($expectedResult, $result);
}
public function test_application_handleRequest_shouldRespondWith400_whenRequiredBodyArgumentOmitted(): void {
Author
Owner

_shouldRespond400

`_shouldRespond400`
c.fahner marked this conversation as resolved
@ -0,0 +123,4 @@
$this->assertResponseStatus(400, $result);
}
public function test_application_handleRequest_shouldRespondWithSucces_whenOptionalBodyArgumentMissing(): void {
Author
Owner

_shouldRespond2xx

`_shouldRespond2xx`
c.fahner marked this conversation as resolved
@ -0,0 +136,4 @@
$this->assertBody($expectedResult, $result);
}
public function test_application_handleRequest_shouldRespondWithSuccess_whenOptionalBodyArgumentGiven(): void {
Author
Owner

_shouldRespond2xx

`_shouldRespond2xx`
c.fahner marked this conversation as resolved
@ -0,0 +150,4 @@
$this->assertBody($expectedResult, $result);
}
public function test_application_handleRequest_shouldRespondWithSuccess_whenRequiredCookieArgumentGiven(): void {
Author
Owner

_shouldRespond2xx

`_shouldRespond2xx`
c.fahner marked this conversation as resolved
@ -0,0 +164,4 @@
$this->assertBody($expectedResult, $result);
}
public function test_application_handleRequest_shouldRespondWith400_whenRequiredCookieArgumentOmitted(): void {
Author
Owner

_shouldRespond400

`_shouldRespond400`
c.fahner marked this conversation as resolved
@ -0,0 +175,4 @@
$this->assertResponseStatus(400, $result);
}
public function test_application_handleRequest_shouldRespondWithSucces_whenOptionalCookieArgumentMissing(): void {
Author
Owner

_shouldRespond2xx

`_shouldRespond2xx`
c.fahner marked this conversation as resolved
@ -0,0 +188,4 @@
$this->assertBody($expectedResult, $result);
}
public function test_application_handleRequest_shouldRespondWithSuccess_whenOptionalCookieArgumentGiven(): void {
Author
Owner

_shouldRespond2xx

`_shouldRespond2xx`
c.fahner marked this conversation as resolved
@ -0,0 +202,4 @@
$this->assertBody($expectedResult, $result);
}
public function test_application_handleRequest_shouldRespondWithSuccess_whenRequiredHeaderArgumentGiven(): void {
Author
Owner

_shouldRespond2xx

`_shouldRespond2xx`
c.fahner marked this conversation as resolved
@ -0,0 +216,4 @@
$this->assertBody($expectedResult, $result);
}
public function test_application_handleRequest_shouldRespondWith400_whenRequiredHeaderArgumentOmitted(): void {
Author
Owner

_shouldRespond400

`_shouldRespond400`
c.fahner marked this conversation as resolved
@ -0,0 +227,4 @@
$this->assertResponseStatus(400, $result);
}
public function test_application_handleRequest_shouldRespondWithSucces_whenOptionalHeaderArgumentMissing(): void {
Author
Owner

_shouldRespond2xx

`_shouldRespond2xx`
c.fahner marked this conversation as resolved
@ -0,0 +240,4 @@
$this->assertBody($expectedResult, $result);
}
public function test_application_handleRequest_shouldRespondWithSuccess_whenOptionalHeaderArgumentGiven(): void {
Author
Owner

_shouldRespond2xx

`_shouldRespond2xx`
c.fahner marked this conversation as resolved
@ -0,0 +254,4 @@
$this->assertBody($expectedResult, $result);
}
public function test_application_handleRequest_shouldRespondWithSuccess_whenEitherAltOrMainArgumentNameGiven(): void {
Author
Owner

_shouldRespond2xx

`_shouldRespond2xx`
c.fahner marked this conversation as resolved
@ -0,0 +330,4 @@
$this->assertResponseStatusRange(400, 499, $result);
}
public function test_application_handleRequest_shouldRespondWith400_whenArgumentDoesNotValidate(): void {
Author
Owner

_shouldRespond400

`_shouldRespond400`
c.fahner marked this conversation as resolved
@ -0,0 +339,4 @@
$this->assertResponseStatus(400, $result);
}
public function test_application_handleRequest_shouldRespondWithSuccess_whenArgumentDoesValidate(): void {
Author
Owner

_shouldRespond2xx

`_shouldRespond2xx`
c.fahner marked this conversation as resolved
@ -0,0 +8,4 @@
/**
* @internal
* @author C. Fahner
* @copyright
Author
Owner

Add copyright

Add copyright
c.fahner marked this conversation as resolved
@ -0,0 +62,4 @@
$debugUntil = (new DateTime)->modify('+2 days')->format('Y-m-d');
// Assert
$this->expectException(Exception::class);
Author
Owner

Should become ApplicationException

Should become `ApplicationException`
c.fahner marked this conversation as resolved
@ -0,0 +73,4 @@
$debugUntil = \date('Y-m-d\TH:i:s');
// Assert
$this->expectException(Exception::class);
Author
Owner

Should become ApplicationException

Should become `ApplicationException`
c.fahner marked this conversation as resolved
@ -0,0 +105,4 @@
$this->assertFalse($resultFalse);
}
public function test_modules_offsetExists_shouldReturnExpectedResult_whenGivenAliasObject(): void {
Author
Owner

_whenAliasObjectGiven

`_whenAliasObjectGiven`
c.fahner marked this conversation as resolved
@ -0,0 +178,4 @@
public function test___construct_shouldThrow_whenPassingSameModuleTypeTwice(): void {
// Assert
$this->expectException(Exception::class);
Author
Owner

Should become ApplicationException

Should become `ApplicationException`
c.fahner marked this conversation as resolved
@ -0,0 +15,4 @@
* @author C. Fahner
* @copyright Slendium 2026
*/
final class IllegalActionExceptionTest extends TestCase {
Author
Owner

Should use ExceptionTestCase instead

Should use `ExceptionTestCase` instead
c.fahner marked this conversation as resolved
@ -0,0 +15,4 @@
*/
final class AliasTest extends TestCase {
public function test___construct_shouldNotCrash(): void {
Author
Owner

_shouldNotThrow

`_shouldNotThrow`
c.fahner marked this conversation as resolved
c.fahner stopped working 2026-07-13 11:50:22 +02:00
1 hour 27 minutes
c.fahner deleted branch issues/1 2026-07-13 14:16:55 +02:00
c.fahner referenced this pull request from a commit 2026-07-13 14:16:57 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Total time spent: 1 hour 27 minutes
c.fahner
1 hour 27 minutes
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
slendium/framework!24
No description provided.