Passed
Push — master ( 595151...abfc6b )
by ignace nyamagana
02:16
created

Template::createFromString()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 14
nc 5
nop 1
dl 0
loc 25
ccs 15
cts 15
cp 1
crap 7
rs 8.8333
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * League.Uri (https://uri.thephpleague.com)
5
 *
6
 * (c) Ignace Nyamagana Butera <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace League\Uri\UriTemplate;
15
16
use League\Uri\Exceptions\SyntaxError;
17
use League\Uri\Exceptions\TemplateCanNotBeExpanded;
18
use function array_merge;
19
use function array_unique;
20
use function gettype;
21
use function is_string;
22
use function method_exists;
23
use function preg_match_all;
24
use function preg_replace;
25
use function sprintf;
26
use function strpos;
27
use const PREG_SET_ORDER;
28
29
final class Template
30
{
31
    /**
32
     * Expression regular expression pattern.
33
     */
34
    private const REGEXP_EXPRESSION_DETECTOR = '/\{[^\}]*\}/x';
35
36
    /**
37
     * @var string
38
     */
39
    private $template;
40
41
    /**
42
     * @var array<string, Expression>
43
     */
44
    private $expressions = [];
45
46
    /**
47
     * @var array<string>
48
     */
49
    private $variableNames;
50
51 18
    private function __construct(string $template, Expression ...$expressions)
52
    {
53 18
        $this->template = $template;
54 18
        $variableNames = [];
55 18
        foreach ($expressions as $expression) {
56 14
            $this->expressions[$expression->toString()] = $expression;
57 14
            $variableNames[] = $expression->variableNames();
58
        }
59 18
        $this->variableNames = array_unique(array_merge([], ...$variableNames));
60 18
    }
61
62
    /**
63
     * {@inheritDoc}
64
     */
65 2
    public static function __set_state(array $properties): self
66
    {
67 2
        return new self($properties['template'], ...array_values($properties['expressions']));
68
    }
69
70
    /**
71
     * @param object|string $template a string or an object with the __toString method
72
     *
73
     * @throws \TypeError  if the template is not a string or an object with the __toString method
74
     * @throws SyntaxError if the template contains invalid expressions
75
     * @throws SyntaxError if the template contains invalid variable specification
76
     */
77 34
    public static function createFromString($template): self
78
    {
79 34
        if (!is_string($template) && !method_exists($template, '__toString')) {
80 2
            throw new \TypeError(sprintf('The template must be a string or a stringable object %s given.', gettype($template)));
81
        }
82
83 32
        $template = (string) $template;
84
85
        /** @var string $remainder */
86 32
        $remainder = preg_replace(self::REGEXP_EXPRESSION_DETECTOR, '', $template);
87 32
        if (false !== strpos($remainder, '{') || false !== strpos($remainder, '}')) {
88 12
            throw new SyntaxError('The template "'.$template.'" contains invalid expressions.');
89
        }
90
91 20
        $names = [];
92 20
        preg_match_all(self::REGEXP_EXPRESSION_DETECTOR, $template, $findings, PREG_SET_ORDER);
93 20
        $arguments = [];
94 20
        foreach ($findings as $finding) {
95 16
            if (!isset($names[$finding[0]])) {
96 16
                $arguments[] = Expression::createFromString($finding[0]);
97 14
                $names[$finding[0]] = 1;
98
            }
99
        }
100
101 18
        return new self($template, ...$arguments);
102
    }
103
104 4
    public function toString(): string
105
    {
106 4
        return $this->template;
107
    }
108
109
    /**
110
     * @return array<string>
111
     */
112 18
    public function variableNames(): array
113
    {
114 18
        return $this->variableNames;
115
    }
116
117
    /**
118
     * @throws TemplateCanNotBeExpanded if the variables is an array and a ":" modifier needs to be applied
119
     * @throws TemplateCanNotBeExpanded if the variables contains nested array values
120
     */
121 12
    public function expand(VariableBag $variables): string
122
    {
123 12
        $uriString = $this->template;
124
        /** @var Expression $expression */
125 12
        foreach ($this->expressions as $pattern => $expression) {
126 10
            $uriString = str_replace($pattern, $expression->expand($variables), $uriString);
127
        }
128
129 12
        return $uriString;
130
    }
131
}
132