Skip to content

Commit

Permalink
Corona: Add support for custom, scoped request parsers
Browse files Browse the repository at this point in the history
Next to one parser to handle every supported value, also
support custom, smaller scoped parsers for specific values.
This allows splitting up the existing parsers and eventually
only loading the parsers you actually need (and thus not wasting
resources parsing something you don't need)
  • Loading branch information
pprkut committed Jan 14, 2025
1 parent 14091c6 commit f1b09f6
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 4 deletions.
59 changes: 55 additions & 4 deletions src/Lunr/Corona/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

namespace Lunr\Corona;

use BackedEnum;
use RuntimeException;

/**
* Request abstraction class.
* Manages access to $_POST, $_GET values, as well as
Expand Down Expand Up @@ -64,7 +67,7 @@ class Request
*
* @var array<string, mixed>
*/
protected readonly array $request;
protected array $request;

/**
* Stored $_FILES values
Expand All @@ -90,6 +93,12 @@ class Request
*/
protected readonly RequestParserInterface $parser;

/**
* Set of registered request value parsers.
* @var array<class-string,RequestValueParserInterface>
*/
protected array $parsers;

/**
* The request values to mock.
* @var array<string,mixed>
Expand All @@ -103,7 +112,8 @@ class Request
*/
public function __construct($parser)
{
$this->parser = $parser;
$this->parser = $parser;
$this->parsers = [];

$this->request = $parser->parse_request();
$this->server = $parser->parse_server();
Expand All @@ -122,8 +132,8 @@ public function __construct($parser)
*/
public function __destruct()
{
// Intentionally not unsetting $this->mock and $this->raw_data, since
// that may break access to mocked request values during PHP shutdown.
// Intentionally not unsetting request value properties, since
// that may break access to them during PHP shutdown.
}

/**
Expand Down Expand Up @@ -154,6 +164,47 @@ public function __get(string $name)
}
}

/**
* Get a request value.
*
* @param BackedEnum&RequestValueInterface $key The identifier/name of the request value to get
*
* @return scalar The requested value
*/
public function get(BackedEnum&RequestValueInterface $key): bool|float|int|string|null
{
if (array_key_exists($key->value, $this->mock))
{
return $this->mock[$key->value];
}

if (array_key_exists($key->value, $this->request))
{
return $this->request[$key->value];
}

if (!isset($this->parsers[$key::class]))
{
throw new RuntimeException('No parser registered for requested value ("' . $key->value . '")!');
}

$this->request[$key->value] = $this->parsers[$key::class]->get($key);

return $this->request[$key->value];
}

/**
* Register a request value parser.
*
* @param RequestValueParserInterface $parser A request value parser
*
* @return void
*/
public function register_parser(RequestValueParserInterface $parser): void
{
$this->parsers[$parser->get_request_value_type()] = $parser;
}

/**
* Override request values detected from the request parser.
* Replace all previous mock values.
Expand Down
20 changes: 20 additions & 0 deletions src/Lunr/Corona/RequestValueInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

/**
* This file contains the request value interface.
*
* SPDX-FileCopyrightText: Copyright 2024 Move Agency Group B.V., Zwolle, The Netherlands
* SPDX-License-Identifier: MIT
*/

namespace Lunr\Corona;

/**
* Request Value Interface.
*/
interface RequestValueInterface
{

}

?>
38 changes: 38 additions & 0 deletions src/Lunr/Corona/RequestValueParserInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

/**
* This file contains the request value parser interface.
*
* SPDX-FileCopyrightText: Copyright 2024 Move Agency Group B.V., Zwolle, The Netherlands
* SPDX-License-Identifier: MIT
*/

namespace Lunr\Corona;

use BackedEnum;

/**
* Request Value Parser Interface.
*/
interface RequestValueParserInterface
{

/**
* Return the request value type the parser handles.
*
* @return class-string The FQDN of the type enum the parser handles
*/
public function get_request_value_type(): string;

/**
* Get a request value.
*
* @param BackedEnum&RequestValueInterface $key The identifier/name of the request value to get
*
* @return scalar The requested value
*/
public function get(BackedEnum&RequestValueInterface $key): bool|float|int|string|null;

}

?>
27 changes: 27 additions & 0 deletions src/Lunr/Corona/Tests/Helpers/MockRequestValue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

/**
* This file contains mock request value types.
*
* SPDX-FileCopyrightText: Copyright 2024 Move Agency Group B.V., Zwolle, The Netherlands
* SPDX-License-Identifier: MIT
*/

namespace Lunr\Corona\Tests\Helpers;

use Lunr\Corona\RequestValueInterface;

/**
* Request Data Enums
*/
enum MockRequestValue: string implements RequestValueInterface
{

/**
* Mock value.
*/
case Foo = 'foo';

}

?>
25 changes: 25 additions & 0 deletions src/Lunr/Corona/Tests/RequestBaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

namespace Lunr\Corona\Tests;

use Lunr\Corona\RequestValueParserInterface;

/**
* Basic tests for the case of empty superglobals.
*
Expand Down Expand Up @@ -95,6 +97,29 @@ public function testRequestDefaultValues($key, $value): void
$this->assertEquals($value, $request[$key]);
}

/**
* Test that register_parser() registers a parser.
*
* @covers Lunr\Corona\Request::register_parser
*/
public function testRegisterParser(): void
{
$parser = $this->getMockBuilder(RequestValueParserInterface::class)
->getMock();

$parser->expects($this->once())
->method('get_request_value_type')
->willReturn('Lunr\Corona\Parser\Foo\Foo');

$this->class->register_parser($parser);

$parsers = $this->get_reflection_property_value('parsers');

$this->assertCount(1, $parsers);
$this->assertArrayHasKey('Lunr\Corona\Parser\Foo\Foo', $parsers);
$this->assertEquals($parser, $parsers['Lunr\Corona\Parser\Foo\Foo']);
}

}

?>
106 changes: 106 additions & 0 deletions src/Lunr/Corona/Tests/RequestGetValueTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php

/**
* This file contains the RequestGetValueTest class.
*
* SPDX-FileCopyrightText: Copyright 2024 Move Agency Group B.V., Zwolle, The Netherlands
* SPDX-License-Identifier: MIT
*/

namespace Lunr\Corona\Tests;

use Lunr\Corona\RequestValueParserInterface;
use Lunr\Corona\Tests\Helpers\MockRequestValue;
use RuntimeException;

/**
* Tests for getting request values.
*
* @covers Lunr\Corona\Request
*/
class RequestGetValueTest extends RequestTest
{

/**
* Check that get() returns cached values.
*
* @covers Lunr\Corona\Request::get
*/
public function testGetWithCachedValue(): void
{
$cache = [
'foo' => 'bar',
];

$this->set_reflection_property_value('request', $cache);

$value = $this->class->get(MockRequestValue::Foo);

$this->assertEquals('bar', $value);
}

/**
* Check that get() returns mocked values.
*
* @covers Lunr\Corona\Request::get
*/
public function testGetWithMockedValue(): void
{
$cache = [
'foo' => 'bar',
];

$mock = [
'foo' => 'baz',
];

$this->set_reflection_property_value('request', $cache);
$this->set_reflection_property_value('mock', $mock);

$value = $this->class->get(MockRequestValue::Foo);

$this->assertEquals('baz', $value);
}

/**
* Check that get() throws an exception for a value with an unregistered parser.
*
* @covers Lunr\Corona\Request::get
*/
public function testGetWithUncachedValueAndUnregisteredParserThrowsException(): void
{
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('No parser registered for requested value ("foo")!');

$this->class->get(MockRequestValue::Foo);
}

/**
* Check that get() returns uncached values.
*
* @covers Lunr\Corona\Request::get
*/
public function testGetWithUncachedValue(): void
{
$parser = $this->getMockBuilder(RequestValueParserInterface::class)
->getMock();

$parsers = [
MockRequestValue::class => $parser,
];

$this->set_reflection_property_value('parsers', $parsers);

$parser->expects($this->once())
->method('get')
->with(MockRequestValue::Foo)
->willReturn('bar');

$value = $this->class->get(MockRequestValue::Foo);

$this->assertEquals('bar', $value);
}

}

?>

0 comments on commit f1b09f6

Please sign in to comment.