diff --git a/doc/RenderAsString.md b/doc/RenderAsString.md index 4396452..daf3323 100644 --- a/doc/RenderAsString.md +++ b/doc/RenderAsString.md @@ -19,16 +19,30 @@ Note that the leading question mark will never be included. ## Change renderer -Remove numeric indices: +### Remove numeric indices: +This renderer will render indexed arrays as `foo[]=bar&foo[]=baz` instead of `foo[0]=bar&foo[1]=baz`. + ```php use function BenTools\QueryString\withoutNumericIndices; -$qs = $qs->withRenderer( - withoutNumericIndices() -); -print(urldecode((string) $qs)); // yummy[fruits][]=strawberries&yummy[fruits][]=raspberries + +$qs = $qs->withRenderer(withoutNumericIndices()); +print(urldecode((string) $qs)); ``` -Or define it on a global scope for future QueryString objects: +### Flat renderer +This renderer will render indexed arrays as `foo=bar&foo=baz` instead of `foo[0]=bar&foo[1]=baz`. + +```php +use function BenTools\QueryString\flat; + +$qs = $qs->withRenderer(flat()); +print(urldecode((string) $qs)); +``` + +### Global setting + +You can define a default renderer on a global scope for future QueryString objects: + ```php use BenTools\QueryString\QueryString; use function BenTools\QueryString\withoutNumericIndices; diff --git a/src/Renderer/FlatRenderer.php b/src/Renderer/FlatRenderer.php new file mode 100644 index 0000000..f2ffc80 --- /dev/null +++ b/src/Renderer/FlatRenderer.php @@ -0,0 +1,86 @@ +renderer = $renderer; + } + + public static function factory(QueryStringRendererInterface $renderer = null) + { + return new self($renderer ?? NativeRenderer::factory()); + } + + /** + * @inheritDoc + */ + public function render(QueryString $queryString): string + { + $separator = $this->getSeparator() ?? ini_get('arg_separator.output'); + $parts = [[]]; + + foreach ($queryString->getParams() as $key => $value) { + $parts[] = $this->getParts($key, $value); + } + + return \implode($separator, \array_merge([], ...$parts)); + } + + /** + * @inheritDoc + */ + public function getEncoding(): int + { + return $this->renderer->getEncoding(); + } + + /** + * @inheritDoc + */ + public function withEncoding(int $encoding): QueryStringRendererInterface + { + return new self($this->renderer->withEncoding($encoding)); + } + + /** + * @inheritDoc + */ + public function getSeparator(): ?string + { + return $this->renderer->getSeparator(); + } + + /** + * @inheritDoc + */ + public function withSeparator(?string $separator): QueryStringRendererInterface + { + return new self($this->renderer->withSeparator($separator)); + } + + private function getParts($key, $value): array + { + if (\is_iterable($value)) { + $parts = [[]]; + foreach ($value as $sub) { + $parts[] = $this->getParts($key, $sub); + } + + return \array_merge([], ...$parts); + } + + $encode = \PHP_QUERY_RFC1738 === $this->getEncoding() ? '\\urlencode' : '\\rawurlencode'; + + return [$key . '=' . \call_user_func($encode, $value)]; + } +} diff --git a/src/functions.php b/src/functions.php index ee62e0b..20414f2 100644 --- a/src/functions.php +++ b/src/functions.php @@ -4,6 +4,7 @@ use BenTools\QueryString\Parser\QueryStringParserInterface; use BenTools\QueryString\Renderer\ArrayValuesNormalizerRenderer; +use BenTools\QueryString\Renderer\FlatRenderer; use BenTools\QueryString\Renderer\QueryStringRendererInterface; /** @@ -25,6 +26,15 @@ function withoutNumericIndices(QueryStringRendererInterface $renderer = null): A return ArrayValuesNormalizerRenderer::factory($renderer); } +/** + * @param QueryStringRendererInterface|null $renderer + * @return FlatRenderer + */ +function flat(QueryStringRendererInterface $renderer = null): FlatRenderer +{ + return FlatRenderer::factory($renderer); +} + /** * @param string $queryString * @param bool $decodeKeys diff --git a/tests/FlatRendererTest.php b/tests/FlatRendererTest.php new file mode 100644 index 0000000..363a0f0 --- /dev/null +++ b/tests/FlatRendererTest.php @@ -0,0 +1,41 @@ + 'bar', + 'foos' => [ + 'bar', + 'foo bar', + ], + 'fruits' => [ + 'banana' => 'yellow', + 'strawberry' => 'red', + ], + ]; + + $qs = query_string($data); + $renderer = flat(); + + $this->assertEquals('foo=bar&foos=bar&foos=foo%20bar&fruits=yellow&fruits=red', (string) $qs->withRenderer( + $renderer + )); + + $this->assertEquals('foo=bar&foos=bar&foos=foo+bar&fruits=yellow&fruits=red', (string) $qs->withRenderer( + $renderer->withEncoding(PHP_QUERY_RFC1738) + )); + + $this->assertEquals('foo=bar;foos=bar;foos=foo+bar;fruits=yellow;fruits=red', (string) $qs->withRenderer( + $renderer->withEncoding(PHP_QUERY_RFC1738)->withSeparator(';') + )); + } + +}