-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #64 from sroehrl/request-guard
Introducing request guards for cleaner request handling
- Loading branch information
Showing
5 changed files
with
225 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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())(); | ||
} | ||
} | ||
|
Oops, something went wrong.