Passed
Push — master ( c3b510...74c5d9 )
by Pol
02:16
created

UnalteredPsrHttpFactory::createRequest()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 24
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 10
c 1
b 0
f 0
dl 0
loc 24
ccs 9
cts 9
cp 1
rs 9.9332
cc 2
nc 2
nop 1
crap 2
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
final class UnalteredPsrHttpFactory implements HttpMessageFactoryInterface
13
{
14
    /**
15
     * @var \Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface
16
     */
17
    private $httpMessageFactory;
18
19 5
    public function __construct(HttpMessageFactoryInterface $httpMessageFactory)
20
    {
21 5
        $this->httpMessageFactory = $httpMessageFactory;
22 5
    }
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 1
            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 2
            ->withQueryParams(
45 2
                iterator_to_array($this->parseStr($unalteredQueryString))
46
            )
47 2
            ->withUri(
48
                $request
49 2
                    ->getUri()
50 2
                    ->withQuery($unalteredQueryString)
51
            );
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     */
57 1
    public function createResponse(Response $symfonyResponse)
58
    {
59 1
        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 2
    private function parseStr(string $queryString)
72
    {
73 2
        $parameters = array_map(
74
            static function (string $keyValue): string {
75 2
                return (string) preg_replace_callback(
76 2
                    '/^(?<key>[^&=]+?)(?:\[[^&=]*\])?=(?<value>[^&=]+)/',
77
                    static function (array $match): string {
78 2
                        return str_replace($match['key'], bin2hex($match['key']), $match[0]);
79 2
                    },
80 2
                    $keyValue
81
                );
82 2
            },
83 2
            explode('&', $queryString)
84
        );
85
86 2
        parse_str(
87 2
            implode('&', $parameters),
88 2
            $parameters
89
        );
90
91 2
        foreach ($parameters as $key => $value) {
92 2
            yield urldecode((string) hex2bin($key)) => $value;
93
        }
94 2
    }
95
}
96