Skip to content

Commit c4dba69

Browse files
authored
feat: flat renderer (#3)
1 parent 6d37ec7 commit c4dba69

4 files changed

Lines changed: 157 additions & 6 deletions

File tree

doc/RenderAsString.md

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,30 @@ Note that the leading question mark will never be included.
1919

2020
## Change renderer
2121

22-
Remove numeric indices:
22+
### Remove numeric indices:
23+
This renderer will render indexed arrays as `foo[]=bar&foo[]=baz` instead of `foo[0]=bar&foo[1]=baz`.
24+
2325
```php
2426
use function BenTools\QueryString\withoutNumericIndices;
25-
$qs = $qs->withRenderer(
26-
withoutNumericIndices()
27-
);
28-
print(urldecode((string) $qs)); // yummy[fruits][]=strawberries&yummy[fruits][]=raspberries
27+
28+
$qs = $qs->withRenderer(withoutNumericIndices());
29+
print(urldecode((string) $qs));
2930
```
3031

31-
Or define it on a global scope for future QueryString objects:
32+
### Flat renderer
33+
This renderer will render indexed arrays as `foo=bar&foo=baz` instead of `foo[0]=bar&foo[1]=baz`.
34+
35+
```php
36+
use function BenTools\QueryString\flat;
37+
38+
$qs = $qs->withRenderer(flat());
39+
print(urldecode((string) $qs));
40+
```
41+
42+
### Global setting
43+
44+
You can define a default renderer on a global scope for future QueryString objects:
45+
3246
```php
3347
use BenTools\QueryString\QueryString;
3448
use function BenTools\QueryString\withoutNumericIndices;

src/Renderer/FlatRenderer.php

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
3+
namespace BenTools\QueryString\Renderer;
4+
5+
use BenTools\QueryString\QueryString;
6+
7+
final class FlatRenderer implements QueryStringRendererInterface
8+
{
9+
/**
10+
* @var NativeRenderer
11+
*/
12+
private $renderer;
13+
14+
protected function __construct(QueryStringRendererInterface $renderer = null)
15+
{
16+
$this->renderer = $renderer;
17+
}
18+
19+
public static function factory(QueryStringRendererInterface $renderer = null)
20+
{
21+
return new self($renderer ?? NativeRenderer::factory());
22+
}
23+
24+
/**
25+
* @inheritDoc
26+
*/
27+
public function render(QueryString $queryString): string
28+
{
29+
$separator = $this->getSeparator() ?? ini_get('arg_separator.output');
30+
$parts = [[]];
31+
32+
foreach ($queryString->getParams() as $key => $value) {
33+
$parts[] = $this->getParts($key, $value);
34+
}
35+
36+
return \implode($separator, \array_merge([], ...$parts));
37+
}
38+
39+
/**
40+
* @inheritDoc
41+
*/
42+
public function getEncoding(): int
43+
{
44+
return $this->renderer->getEncoding();
45+
}
46+
47+
/**
48+
* @inheritDoc
49+
*/
50+
public function withEncoding(int $encoding): QueryStringRendererInterface
51+
{
52+
return new self($this->renderer->withEncoding($encoding));
53+
}
54+
55+
/**
56+
* @inheritDoc
57+
*/
58+
public function getSeparator(): ?string
59+
{
60+
return $this->renderer->getSeparator();
61+
}
62+
63+
/**
64+
* @inheritDoc
65+
*/
66+
public function withSeparator(?string $separator): QueryStringRendererInterface
67+
{
68+
return new self($this->renderer->withSeparator($separator));
69+
}
70+
71+
private function getParts($key, $value): array
72+
{
73+
if (\is_iterable($value)) {
74+
$parts = [[]];
75+
foreach ($value as $sub) {
76+
$parts[] = $this->getParts($key, $sub);
77+
}
78+
79+
return \array_merge([], ...$parts);
80+
}
81+
82+
$encode = \PHP_QUERY_RFC1738 === $this->getEncoding() ? '\\urlencode' : '\\rawurlencode';
83+
84+
return [$key . '=' . \call_user_func($encode, $value)];
85+
}
86+
}

src/functions.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use BenTools\QueryString\Parser\QueryStringParserInterface;
66
use BenTools\QueryString\Renderer\ArrayValuesNormalizerRenderer;
7+
use BenTools\QueryString\Renderer\FlatRenderer;
78
use BenTools\QueryString\Renderer\QueryStringRendererInterface;
89

910
/**
@@ -25,6 +26,15 @@ function withoutNumericIndices(QueryStringRendererInterface $renderer = null): A
2526
return ArrayValuesNormalizerRenderer::factory($renderer);
2627
}
2728

29+
/**
30+
* @param QueryStringRendererInterface|null $renderer
31+
* @return FlatRenderer
32+
*/
33+
function flat(QueryStringRendererInterface $renderer = null): FlatRenderer
34+
{
35+
return FlatRenderer::factory($renderer);
36+
}
37+
2838
/**
2939
* @param string $queryString
3040
* @param bool $decodeKeys

tests/FlatRendererTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace BenTools\QueryString\Tests;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use function BenTools\QueryString\flat;
7+
use function BenTools\QueryString\query_string;
8+
9+
class FlatRendererTest extends TestCase
10+
{
11+
public function testRenderer()
12+
{
13+
$data = [
14+
'foo' => 'bar',
15+
'foos' => [
16+
'bar',
17+
'foo bar',
18+
],
19+
'fruits' => [
20+
'banana' => 'yellow',
21+
'strawberry' => 'red',
22+
],
23+
];
24+
25+
$qs = query_string($data);
26+
$renderer = flat();
27+
28+
$this->assertEquals('foo=bar&foos=bar&foos=foo%20bar&fruits=yellow&fruits=red', (string) $qs->withRenderer(
29+
$renderer
30+
));
31+
32+
$this->assertEquals('foo=bar&foos=bar&foos=foo+bar&fruits=yellow&fruits=red', (string) $qs->withRenderer(
33+
$renderer->withEncoding(PHP_QUERY_RFC1738)
34+
));
35+
36+
$this->assertEquals('foo=bar;foos=bar;foos=foo+bar;fruits=yellow;fruits=red', (string) $qs->withRenderer(
37+
$renderer->withEncoding(PHP_QUERY_RFC1738)->withSeparator(';')
38+
));
39+
}
40+
41+
}

0 commit comments

Comments
 (0)