Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
7 / 7
CRAP
100.00% covered (success)
100.00%
1 / 1
ParserQueryString
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
7 / 7
11
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getQueryString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setQueryString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSeparator
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setSeparator
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 parseValue
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
4
 setMsgContent
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * @author: Doug Wilbourne (dougwilbourne@gmail.com)
4 */
5
6declare(strict_types=1);
7
8namespace pvc\parser\url;
9
10use pvc\err\stock\Exception;
11use pvc\interfaces\http\QueryStringInterface;
12use pvc\interfaces\msg\MsgInterface;
13use pvc\interfaces\parser\ParserQueryStringInterface;
14use pvc\parser\err\InvalidQuerystringSeparatorException;
15use pvc\parser\Parser;
16
17/**
18 * Class ParserQueryString
19 *
20 * @extends Parser<QueryStringInterface>
21 *
22 * parse_str has some issues, like it will mangle a parameter name in order to get it to conform to a PHP
23 * variable name.  This class does not do that.  The parameter names end up as indices in an associative array.
24 *
25 * See the discussion in the QueryString class for more information, but note that this class mirrors the behavior of
26 * http_build_query in some ways.  Just as http_build_query cannot generate a querystring with duplicate parameter
27 * names (e.g. it cannot produce '?a=4&a=5', this class cannot parse duplicate parameters as separates.  If it runs
28 * into '?a=4&a=5', the value of a will be 5.
29 *
30 */
31class ParserQueryString extends Parser implements ParserQueryStringInterface
32{
33    /**
34     * @var non-empty-string
35     */
36    protected string $separator = '&';
37
38    /**
39     * @var QueryStringInterface
40     */
41    protected QueryStringInterface $qstr;
42
43    /**
44     * @param MsgInterface $msg
45     * @param QueryStringInterface $qstr
46     */
47    public function __construct(MsgInterface $msg, QueryStringInterface $qstr)
48    {
49        parent::__construct($msg);
50        $this->qstr = $qstr;
51    }
52
53    /**
54     * getQueryString
55     * @return QueryStringInterface
56     */
57    public function getQueryString(): QueryStringInterface
58    {
59        return $this->qstr;
60    }
61
62    /**
63     * setQueryString
64     * @param QueryStringInterface $queryString
65     */
66    public function setQueryString(QueryStringInterface $queryString): void
67    {
68        $this->qstr = $queryString;
69    }
70
71    /**
72     * getSeparator
73     * @return non-empty-string
74     */
75    public function getSeparator(): string
76    {
77        return $this->separator;
78    }
79
80    /**
81     * setSeparator
82     * @param non-empty-string $separator
83     * @throws InvalidQuerystringSeparatorException
84     */
85    public function setSeparator(string $separator): void
86    {
87        if (empty($separator)) {
88            throw new InvalidQuerystringSeparatorException();
89        }
90        $this->separator = $separator;
91    }
92
93    /**
94     * parseValue
95     * parses a querystring to the html standard
96     * @param string $data
97     * @return bool
98     *
99     */
100    public function parseValue(string $data): bool
101    {
102        $params = [];
103        $data = trim($data, '?');
104
105        $paramStrings = explode($this->getSeparator(), $data);
106
107        foreach ($paramStrings as $paramString) {
108            $array = explode('=', $paramString);
109
110            /**
111             * cannot have a string like 'a=1=2'.  Need 0 or 1 equals signs.  Zero equals signs is a parameter with no
112             * value attached
113             */
114            if (count($array) > 2) {
115                return false;
116            }
117
118            $paramName = $array[0];
119            $paramValue = $array[1] ?? '';
120
121            /**
122             * if the parameter name is duplicated in the querystring, this results in the last value being used
123             */
124            $params[$paramName] = $paramValue;
125        }
126
127        try {
128            $this->getQueryString()->setParams($params);
129        } catch (Exception $e) {
130            /** swallow the exception - parsers just return false if they fail */
131            return false;
132        }
133        $this->parsedValue = $this->qstr;
134        return true;
135    }
136
137    protected function setMsgContent(MsgInterface $msg): void
138    {
139        $msgId = 'invalid_querystring';
140        $msgParameters = [];
141        $msg->setContent($this->getMsgDomain(), $msgId, $msgParameters);
142    }
143}