Passed
Branch dev (8e1e05)
by Alex
03:51
created

Parser::buildSegments()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 17
ccs 11
cts 11
cp 1
rs 9.2
cc 4
eloc 10
nc 3
nop 1
crap 4
1
<?php
2
3
/**
4
 * Codeburner Framework.
5
 *
6
 * @author Alex Rohleder <[email protected]>
7
 * @copyright 2016 Alex Rohleder
8
 * @license http://opensource.org/licenses/MIT
9
 */
10
11
namespace Codeburner\Router;
12
13
use Codeburner\Router\Exceptions\BadRouteException;
14
15
/**
16
 * Representation of a group of several routes with same
17
 * controller and respecting the resourceful actions.
18
 *
19
 * @author Alex Rohleder <[email protected]>
20
 */
21
22
class Parser
23
{
24
25
    /**
26
     * These regex define the structure of a dynamic segment in a pattern.
27
     *
28
     * @var string
29
     */
30
31
    const DYNAMIC_REGEX = "{\s*(\w*)\s*(?::\s*([^{}]*(?:{(?-1)}*)*))?\s*}";
32
33
    /**
34
     * Some regex wildcards for easily definition of dynamic routes. ps. all keys and values must start with :
35
     *
36
     * @var array
37
     */
38
39
    protected $wildcards = [
40
        ":uid"     => ":uid-[a-zA-Z0-9]",
41
        ":slug"    => ":[a-z0-9-]",
42
        ":string"  => ":\w",
43
        ":int"     => ":\d",
44
        ":integer" => ":\d",
45
        ":float"   => ":[-+]?\d*?[.]?\d",
46
        ":double"  => ":[-+]?\d*?[.]?\d",
47
        ":hex"     => ":0[xX][0-9a-fA-F]",
48
        ":octal"   => ":0[1-7][0-7]",
49
        ":bool"    => ":1|0|true|false|yes|no",
50
        ":boolean" => ":1|0|true|false|yes|no",
51
    ];
52
53
    /**
54
     * Separate routes pattern with optional parts into n new patterns.
55
     *
56
     * @param string $pattern
57
     *
58
     * @throws BadRouteException
59
     * @return array
60
     */
61
62 53
    public function parsePattern($pattern)
63
    {
64 53
        $withoutClosing = rtrim($pattern, "]");
65 53
        $closingNumber  = strlen($pattern) - strlen($withoutClosing);
66
67 53
        $segments = preg_split("~" . self::DYNAMIC_REGEX . "(*SKIP)(*F)|\[~x", $withoutClosing);
68 53
        $this->parseSegments($segments, $closingNumber, $withoutClosing);
69
70 51
        return $this->buildSegments($segments);
71
    }
72
73
    /**
74
     * Parse all the possible patterns seeking for an incorrect or incompatible pattern.
75
     *
76
     * @param string[] $segments       Segments are all the possible patterns made on top of a pattern with optional segments.
77
     * @param int      $closingNumber  The count of optional segments.
78
     * @param string   $withoutClosing The pattern without the closing token of an optional segment. aka: ]
79
     *
80
     * @throws BadRouteException
81
     */
82
83 53
    protected function parseSegments(array $segments, $closingNumber, $withoutClosing)
84
    {
85 53
        if ($closingNumber !== count($segments) - 1) {
86 2
            if (preg_match("~" . self::DYNAMIC_REGEX . "(*SKIP)(*F)|\]~x", $withoutClosing)) {
87 1
                   throw new BadRouteException(BadRouteException::OPTIONAL_SEGMENTS_ON_MIDDLE);
88 1
            } else throw new BadRouteException(BadRouteException::UNCLOSED_OPTIONAL_SEGMENTS);
89
        }
90 51
    }
91
92
    /**
93
     * @param string[] $segments
94
     *
95
     * @throws BadRouteException
96
     * @return array
97
     */
98
99 51
    protected function buildSegments(array $segments)
100
    {
101 51
        $pattern  = "";
102 51
        $patterns = [];
103 51
        $wildcardTokens = array_keys($this->wildcards);
104 51
        $wildcardRegex  = $this->wildcards;
105
106 51
        foreach ($segments as $n => $segment) {
107 51
            if ($segment === "" && $n !== 0) {
108 1
                throw new BadRouteException(BadRouteException::EMPTY_OPTIONAL_PARTS);
109
            }
110
111 51
            $patterns[] = $pattern .= str_replace($wildcardTokens, $wildcardRegex, $segment);
112 51
        }
113
114 50
        return $patterns;
115
    }
116
117
    /**
118
     * @return string[]
119
     */
120
121 7
    public function getWildcards()
122
    {
123 7
        $wildcards = [];
124 7
        foreach ($this->wildcards as $token => $regex)
125 7
            $wildcards[substr($token, 1)] = substr($regex, 1);
126 7
        return $wildcards;
127
    }
128
129
    /**
130
     * @return string[]
131
     */
132
133 1
    public function getWildcardTokens()
134
    {
135 1
        return $this->wildcards;
136
    }
137
138
    /**
139
     * @param string $wildcard
140
     * @return string|null
141
     */
142
143 1
    public function getWildcard($wildcard)
144
    {
145 1
        return isset($this->wildcards[":$wildcard"]) ? substr($this->wildcards[":$wildcard"], 1) : null;
146
    }
147
148
    /**
149
     * @param string $wildcard
150
     * @param string $pattern
151
     *
152
     * @return self
153
     */
154
155 1
    public function setWildcard($wildcard, $pattern)
156
    {
157 1
        $this->wildcards[":$wildcard"] = ":$pattern";
158 1
        return $this;
159
    }
160
161
}
162