Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.73% covered (success)
97.73%
86 / 88
90.91% covered (success)
90.91%
20 / 22
CRAP
0.00% covered (danger)
0.00%
0 / 1
Value
97.73% covered (success)
97.73%
86 / 88
90.91% covered (success)
90.91%
20 / 22
54
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
 parse
96.88% covered (success)
96.88%
31 / 32
0.00% covered (danger)
0.00%
0 / 1
17
 parseParameter
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
6
 setScheme
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getScheme
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasScheme
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setValue
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getValue
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addParameters
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 addParameter
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getParameters
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getParametersAsString
87.50% covered (success)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
6.07
 getParameter
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasParameters
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasParameter
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setDelimiter
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getDelimiter
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasDelimiter
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setForceQuote
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 isForceQuote
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 render
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 __toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * Pop PHP Framework (http://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@nolainteractive.com>
7 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Mime\Part\Header;
15
16/**
17 * MIME part header value class
18 *
19 * @category   Pop
20 * @package    Pop\Mime
21 * @author     Nick Sagona, III <dev@nolainteractive.com>
22 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
23 * @license    http://www.popphp.org/license     New BSD License
24 * @version    2.0.0
25 */
26class Value
27{
28
29    /**
30     * Header value scheme
31     * @var ?string
32     */
33    protected ?string $scheme = null;
34
35    /**
36     * Header value
37     * @var ?string
38     */
39    protected ?string $value = null;
40
41    /**
42     * Header value parameters
43     * @var array
44     */
45    protected array $parameters = [];
46
47    /**
48     * Header value delimiter
49     * @var string
50     */
51    protected string $delimiter = ';';
52
53    /**
54     * Force quotes for parameter values
55     * @var bool
56     */
57    protected bool $forceQuote = false;
58
59    /**
60     * Constructor
61     *
62     * Instantiate the header value object
63     *
64     * @param ?string $value
65     * @param ?string $scheme
66     * @param array   $parameters
67     * @param bool    $forceQuote
68     */
69    public function __construct(?string $value = null, ?string $scheme = null, array $parameters = [], bool $forceQuote = false)
70    {
71        if ($value !== null) {
72            $this->setValue($value);
73        }
74        if ($scheme !== null) {
75            $this->setScheme($scheme);
76        }
77        if (!empty($parameters)) {
78            $this->addParameters($parameters);
79        }
80        if ($forceQuote) {
81            $this->setForceQuote($forceQuote);
82        }
83    }
84
85    /**
86     * Parse header value
87     *
88     * @param  string $value
89     * @return Value
90     */
91    public static function parse(string $value): Value
92    {
93        $valueObject = new Value();
94        $parameters  = [];
95
96        if ((str_contains($value, ';')) || (str_contains($value, ','))) {
97            $matches = [];
98            preg_match_all('/\w+=[\\a-zA-Z0-9_\s\.\"\/]/mi', $value, $matches, PREG_OFFSET_CAPTURE);
99            if (isset($matches[0]) && isset($matches[0][0]) && isset($matches[0][0][1])) {
100                $val = trim(substr($value, 0, $matches[0][0][1]));
101                if (str_ends_with($val, ';') || str_ends_with($val, ',')) {
102                    $val = substr($val, 0, -1);
103                }
104                if ((stripos($val, 'Basic') !== false) || (stripos($val, 'Bearer') !== false) || (stripos($val, 'Digest') !== false)) {
105                    if (str_contains($val, ' ')) {
106                        $valueObject->setScheme(substr($val, 0, strpos($val, ' ')) . ' ');
107                        $valueObject->setValue(substr($val, (strpos($val, ' ') + 1)));
108                    } else {
109                        $valueObject->setScheme($val . ' ');
110                    }
111                } else {
112                    $valueObject->setValue($val);
113                }
114
115                $params       = trim(substr($value, $matches[0][0][1]));
116                $paramValues  = [];
117                $paramMatches = [];
118                preg_match_all('/\w+=/mi', $params, $paramMatches, PREG_OFFSET_CAPTURE);
119                foreach ($paramMatches[0] as $i => $paramMatch) {
120                    if (isset($paramMatches[0][$i + 1])) {
121                        $paramValues[] = trim(substr($params, $paramMatch[1], $paramMatches[0][$i + 1][1] - $paramMatch[1]));
122                    } else {
123                        $paramValues[] = trim(substr($params, $paramMatch[1]));
124                    }
125                }
126                foreach ($paramValues as $pValue) {
127                    [$paramName, $paramValue, $delimiter] = self::parseParameter($pValue);
128                    $parameters[$paramName]  = $paramValue;
129                    if ($delimiter !== null) {
130                        $valueObject->setDelimiter($delimiter);
131                    }
132                }
133            }
134        } else {
135            $valueObject->setValue($value);
136        }
137
138        if (!empty($parameters)) {
139            $valueObject->addParameters($parameters);
140        }
141
142        return $valueObject;
143    }
144
145    /**
146     * Parse a parameter value
147     *
148     * @param  string $parameter
149     * @return array
150     */
151    public static function parseParameter(string $parameter): array
152    {
153        $paramName  = substr($parameter, 0, strpos($parameter, '='));
154        $paramValue = substr($parameter, (strpos($parameter, '=') + 1));
155        $delimiter  = null;
156        if (str_ends_with($paramValue, ';') || str_ends_with($paramValue, ',')) {
157            $delimiter = (str_ends_with($paramValue, ';')) ? ';' : ',';
158            $paramValue = substr($paramValue, 0, -1);
159        }
160        if ((str_starts_with($paramValue, '"')) && (str_ends_with($paramValue, '"'))) {
161            $paramValue = substr($paramValue, 1);
162            $paramValue = substr($paramValue, 0, -1);
163        }
164        return [$paramName, $paramValue, $delimiter];
165    }
166
167    /**
168     * Set the header value scheme
169     *
170     * @param  string $scheme
171     * @return Value
172     */
173    public function setScheme(string $scheme): Value
174    {
175        $this->scheme = $scheme;
176        return $this;
177    }
178
179    /**
180     * Get the header value scheme
181     *
182     * @return string|null
183     */
184    public function getScheme(): string|null
185    {
186        return $this->scheme;
187    }
188
189    /**
190     * Has a header value scheme
191     *
192     * @return bool
193     */
194    public function hasScheme(): bool
195    {
196        return ($this->delimiter !== null);
197    }
198
199    /**
200     * Set the header value
201     *
202     * @param  string $value
203     * @return Value
204     */
205    public function setValue(string $value): Value
206    {
207        $this->value = $value;
208        return $this;
209    }
210
211    /**
212     * Get the header value
213     *
214     * @return string|null
215     */
216    public function getValue(): string|null
217    {
218        return $this->value;
219    }
220
221    /**
222     * Add the header value parameters
223     *
224     * @param  array $parameters
225     * @return Value
226     */
227    public function addParameters(array $parameters): Value
228    {
229        $this->parameters = $parameters;
230        return $this;
231    }
232
233    /**
234     * Set a header value parameter
235     *
236     * @param string $name
237     * @param string $value
238     * @return Value
239     */
240    public function addParameter(string $name, string $value): Value
241    {
242        $this->parameters[$name] = $value;
243        return $this;
244    }
245
246    /**
247     * Get the header value parameters
248     *
249     * @return array
250     */
251    public function getParameters(): array
252    {
253        return $this->parameters;
254    }
255
256    /**
257     * Get the header value parameters as string
258     *
259     * @throws Exception
260     * @return string
261     */
262    public function getParametersAsString(): string
263    {
264        if (!$this->hasDelimiter()) {
265            throw new Exception('Error: No delimiter has been set.');
266        }
267
268        $parameters = [];
269
270        foreach ($this->parameters as $name => $value) {
271            if (!str_contains($value, '"') && (str_contains($value, ' ') || ($this->forceQuote))) {
272                $value = '"' . $value . '"';
273            }
274            $parameters[] = $name . '=' . $value;
275        }
276
277        return implode($this->delimiter . ' ', $parameters);
278    }
279
280    /**
281     * Get a header value parameter
282     *
283     * @param  string $name
284     * @return string|null
285     */
286    public function getParameter(string $name): string|null
287    {
288        return $this->parameters[$name] ?? null;
289    }
290
291    /**
292     * Has header value parameters
293     *
294     * @return bool
295     */
296    public function hasParameters(): bool
297    {
298        return (count($this->parameters) > 0);
299    }
300
301    /**
302     * Has a header value parameter
303     *
304     * @param  string $name
305     * @return bool
306     */
307    public function hasParameter(string $name): bool
308    {
309        return (isset($this->parameters[$name]));
310    }
311
312    /**
313     * Set the header value delimiter
314     *
315     * @param  string $delimiter
316     * @return Value
317     */
318    public function setDelimiter(string $delimiter): Value
319    {
320        $this->delimiter = $delimiter;
321        return $this;
322    }
323
324    /**
325     * Get the header value delimiter
326     *
327     * @return string
328     */
329    public function getDelimiter(): string
330    {
331        return $this->delimiter;
332    }
333
334    /**
335     * Has a header value delimiter
336     *
337     * @return bool
338     */
339    public function hasDelimiter(): bool
340    {
341        return ($this->delimiter !== null);
342    }
343
344    /**
345     * Set the header value delimiter
346     *
347     * @param  bool $forceQuote
348     * @return Value
349     */
350    public function setForceQuote(bool $forceQuote = false): Value
351    {
352        $this->forceQuote = $forceQuote;
353        return $this;
354    }
355
356    /**
357     * Is set to force quote
358     *
359     * @return bool
360     */
361    public function isForceQuote(): bool
362    {
363        return $this->forceQuote;
364    }
365
366    /**
367     * Render the header value string
368     *
369     * @throws Exception
370     * @return string
371     */
372    public function render(): string
373    {
374        $value = $this->scheme . $this->value;
375
376        if (count($this->parameters) > 0) {
377            $parameters = $this->getParametersAsString();
378            if (!str_ends_with($value, ' ')) {
379                $value .= $this->delimiter . ' ';
380            }
381            $value .= $parameters;
382        }
383
384        return $value;
385    }
386
387    /**
388     * Render the header value string
389     *
390     * @return string
391     */
392    public function __toString(): string
393    {
394        return $this->render();
395    }
396
397}