Skip to content

Commit

Permalink
Merge pull request #64 from sroehrl/request-guard
Browse files Browse the repository at this point in the history
Introducing request guards for cleaner request handling
  • Loading branch information
sroehrl authored Feb 11, 2023
2 parents fd9a016 + 15915c0 commit eb6a1be
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 70 deletions.
2 changes: 1 addition & 1 deletion src/Cli/MigrateAllCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ private function directoryExplorer(string $startingPoint, string $nameSpace): vo
try {
$potentialRoutable = $nameSpace . mb_substr($fileOrFolder, 0, -4);
$this->checkAndMigrate($potentialRoutable);
} catch (ReflectionException $e) {
} catch (\ReflectionException $e) {
// when file doesn't even contain a class, this will gracefully fail
// Inform event?
}
Expand Down
68 changes: 68 additions & 0 deletions src/Request/RequestGuard.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace Neoan\Request;

use Neoan\Response\Response;

class RequestGuard
{
const requestTypes = ['query', 'parameter', 'post'];

const throwOnError = true;

private function getRequestTypes(): array
{
$result = [];
$possible = [
'query' => [
'method' => 'getQuery',
'keys' => array_keys(Request::getQueries())
],
'parameter' => [
'method' => 'getParameter',
'keys' => array_keys(Request::getParameters())
],
'post' => [
'method' => 'getInput',
'keys' => array_keys(Request::getInputs())
],
];
foreach (self::requestTypes as $requestType) {
$result[$possible[$requestType]['method']] = $possible[$requestType]['keys'];
}
return $result;
}

public function __invoke(): static
{
$reflection = new \ReflectionClass(static::class);

foreach ($reflection->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
$filled = false;
foreach ($this->getRequestTypes() as $which => $fillable){
if(in_array($property->getName(), $fillable)){
$filled = true;
$interim = Request::$which($property->getName());
if($property->getType()->isBuiltin()){
settype($interim, $property->getType()->getName());
} else {
$class = $property->getType()->getName();
$interim = new $class($interim);
}

$this->{$property->getName()} = $interim;
}
}
if(!$filled && self::throwOnError && !$property->getType()->allowsNull()){
$response = new Response();
http_response_code(400);
// response type?
$response->respond(json_encode([
'msg' => 'Bad Request',
'reason' => 'missing input: ' . $property->getName()
]));
}
}
return $this;
}
}
15 changes: 15 additions & 0 deletions tests/Mocks/MockRequestGuard.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Test\Mocks;

use Neoan\Model\Helper\DateTimeProperty;
use Neoan\Request\RequestGuard;

class MockRequestGuard extends RequestGuard
{
public string $fill;

public int $castToInt;

public ?DateTimeProperty $createdAt;
}
35 changes: 35 additions & 0 deletions tests/Request/RequestGuardTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Test\Request;

use Neoan\Model\Helper\DateTimeProperty;
use Test\Mocks\MockRequestGuard;
use Neoan\Request\Request;
use PHPUnit\Framework\TestCase;

class RequestGuardTest extends TestCase
{
function testSuccessAllowedIncomplete()
{
Request::setParameter('fill', 'filled');
Request::setParameter('castToInt', '1');
$request = (new MockRequestGuard())();
$this->assertIsInt($request->castToInt);
$this->assertSame('filled', $request->fill);
}
function testSuccessCustomType()
{
Request::setParameter('fill', 'filled');
Request::setParameter('castToInt', '1');
Request::setParameter('createdAt', '2023-01-01');
$request = (new MockRequestGuard())();
$this->assertInstanceOf(DateTimeProperty::class, $request->createdAt);
}
function testFailure()
{
$this->expectErrorMessage('Wanted to die');
Request::setParameters(['fill' => null]);
$request = (new MockRequestGuard())();
}
}

Loading

0 comments on commit eb6a1be

Please sign in to comment.