Passed
Push — master ( 912824...1b7999 )
by Pol
01:31
created

UnalteredPsrHttpFactory::createRequest()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 24
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2.0054

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 10
c 1
b 0
f 0
dl 0
loc 24
ccs 8
cts 9
cp 0.8889
rs 9.9332
cc 2
nc 2
nop 1
crap 2.0054
1
<?php
2
3
declare(strict_types=1);
4
5
namespace loophp\UnalteredPsrHttpMessageBridgeBundle\Factory;
6
7
use Generator;
8
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
9
use Symfony\Component\HttpFoundation\Request;
10
use Symfony\Component\HttpFoundation\Response;
11
12
class UnalteredPsrHttpFactory implements HttpMessageFactoryInterface
13
{
14
    /**
15
     * @var \Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface
16
     */
17
    private $httpMessageFactory;
18
19 4
    public function __construct(HttpMessageFactoryInterface $httpMessageFactory)
20
    {
21 4
        $this->httpMessageFactory = $httpMessageFactory;
22 4
    }
23
24
    /**
25
     * {@inheritdoc}
26
     */
27 3
    public function createRequest(Request $symfonyRequest)
28
    {
29
        // Call the original object to avoid duplicating code.
30 3
        $request = $this->httpMessageFactory->createRequest($symfonyRequest);
31
32
        // If a query string does not exist, return the original object.
33 3
        if ('' === $unalteredQueryString = $symfonyRequest->server->get('QUERY_STRING')) {
34
            return $request;
35
        }
36
37
        // This is where all is happening.
38
        // We do not rely on $symfonyRequest->query->all() because it relies
39
        // on parse_str() which is altering the query string parameters.
40
        // We rely on $symfonyRequest->server->get('QUERY_STRING') so we are
41
        // sure that the query string hasn't been altered.
42
        // Create a new request with the URI and Params updated.
43
        return $request
44 3
            ->withQueryParams(
45 3
                iterator_to_array($this->parseStr($unalteredQueryString))
46
            )
47 3
            ->withUri(
48
                $request
49 3
                    ->getUri()
50 3
                    ->withQuery($unalteredQueryString)
51
            );
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     */
57
    public function createResponse(Response $symfonyResponse)
58
    {
59
        return $this->httpMessageFactory->createResponse($symfonyResponse);
60
    }
61
62
    /**
63
     * Custom parse_str() function that doesn't alter the parameters key value.
64
     *
65
     * @see: https://github.com/ecphp/cas-lib/issues/5.
66
     *
67
     * @param string $queryString
68
     *
69
     * @return Generator<string, string>
70
     */
71 3
    private function parseStr(string $queryString): Generator
72
    {
73 3
        $encodedQueryString = preg_replace_callback(
74 3
            '/(^|(?<=&))[^=[&]+/',
75
            static function (array $key): string {
76 3
                return bin2hex(urldecode(current($key)));
77 3
            },
78 3
            $queryString
79
        );
80
81 3
        if (null === $encodedQueryString) {
82
            return yield from [];
83
        }
84
85 3
        parse_str(
86 3
            $encodedQueryString,
87 3
            $parameters
88
        );
89
90 3
        foreach ($parameters as $key => $value) {
91 3
            yield (string) hex2bin((string) $key) => $value;
92
        }
93 3
    }
94
}
95