Template::prepare()   F
last analyzed

Complexity

Conditions 21
Paths 436

Size

Total Lines 129
Code Lines 62

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 73
CRAP Score 21

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 129
ccs 73
cts 73
cp 1
rs 3.4286
cc 21
eloc 62
nc 436
nop 1
crap 21

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
  namespace ReRoute\Template;
4
5
6
  /**
7
   * @package ReRoute\Template
8
   */
9
  class Template {
10
11
    /**
12
     * @var string
13
     */
14
    protected $templateMatch = null;
15
16
    /**
17
     * @var string
18
     */
19
    protected $templateBuild = null;
20
21
    /**
22
     * @var array
23
     */
24
    protected $parameters = [];
25
26
27
    /**
28
     * @param $template
29
     */
30 177
    public function __construct($template) {
31
32 177
      if (!is_string($template)) {
33 3
        throw new \InvalidArgumentException("Invalid template. Expect string. Given: " . gettype($template));
34
      }
35
36 174
      $this->prepare($template);
37 156
    }
38
39
40
    /**
41
     * @param string $template
42
     */
43 174
    protected function prepare($template) {
44
45 174
      $this->templateMatch = $template;
46 174
      $this->templateBuild = $template;
47
48 174
      preg_match_all('![\{\}]!', $template, $delimiters, PREG_OFFSET_CAPTURE);
49
50
51 174
      $delimiters = $delimiters[0];
52 174
      if (empty($delimiters)) {
53 96
        return;
54
      }
55
      # 1. skip quoted delimiters
56 117
      foreach ($delimiters as $key => $rawParameterInfo) {
57 117
        $symbol = mb_substr($template, $rawParameterInfo[1] - 1, 1);
58 117
        if ($symbol == '\\') {
59 12
          unset($delimiters[$key]);
60 12
        }
61 117
      }
62
63 117
      $delimiters = array_values($delimiters);
64
65
66 117
      if (count($delimiters) % 2 !== 0) {
67 6
        throw new \InvalidArgumentException("Invalid template. Different number of delimiters");
68
      }
69
70
71 111
      $parameters = [];
72 111
      $previousStartIndex = null;
73 111
      $state = null;
74
75 111
      foreach ($delimiters as $rawParameterInfo) {
76 108
        if ($state == null) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $state of type null|integer against null; this is ambiguous if the integer can be zero. Consider using a strict comparison === instead.
Loading history...
77 108
          $state = 1;
78 108
          $previousStartIndex = $rawParameterInfo[1];
79 108
          continue;
80
        }
81
82 108
        if ($rawParameterInfo[0] == '}') {
83 108
          $state--;
84 108
        } else {
85 30
          $state++;
86
        }
87
88
89 108
        if ($state == 0 and $previousStartIndex !== null) {
90 108
          $parameters[] = mb_substr($template, $previousStartIndex, $rawParameterInfo[1] - $previousStartIndex + 1);
91 108
          $previousStartIndex = null;
92 108
        }
93
94 111
      }
95
96
97
      # rebuild groups with names and value params
98 111
      foreach ($parameters as $i => $rawParameterInfo) {
99 108
        unset($parameters[$i]);
100
101 108
        preg_match('!^\{([a-z][0-9a-z]*)(:|\})!i', $rawParameterInfo, $rawGroupName);
102
103
104 108
        if (empty($rawGroupName[1])) {
105 3
          throw new \InvalidArgumentException("Cant detect parameter name");
106
        }
107
108 105
        $name = $rawGroupName[1];
109
110 105
        if (isset($parameters[$name])) {
111 3
          throw new \InvalidArgumentException("Parameter with name already defined:" . $name);
112
        }
113
114 105
        $regexp = '[^/]+';
115 105
        $defaultValue = null;
116
117 105
        if ($rawGroupName[2] != '}') {
118
          # detect default parameter
119 99
          $parameterInfo = substr($rawParameterInfo, strlen($rawGroupName[0]), -1);
120
121
122 99
          $rawRegexpAndValue = preg_split('#(?<!\\\)\:#', $parameterInfo, -1, PREG_SPLIT_DELIM_CAPTURE);
123
124
125 99
          $count = count($rawRegexpAndValue);
126
127 99
          if ($count > 2) {
128 3
            throw new \InvalidArgumentException("Cant detect default value and regexp for parameter:" . $name);
129
          }
130
131
132 96
          if (isset($rawRegexpAndValue[1])) {
133 60
            $defaultValue = preg_replace('#(\\\\)([:\{\}])#', '$2', $rawRegexpAndValue[1]);
134 60
          }
135
136 96
          if ($defaultValue !== null and empty($rawRegexpAndValue[0])) {
137 3
            throw new \InvalidArgumentException('Please specify regexp for parameter:' . $name);
138
          }
139
140
141 93
          if (!empty($rawRegexpAndValue[0])) {
142 93
            $regexp = $rawRegexpAndValue[0];
143 93
          }
144
145 93
          if ($defaultValue !== null) {
146 57
            $regexp = $regexp . '|';
147 57
          }
148
149 93
        }
150
151
152 99
        $replaceToString = '(?P<' . $name . '>' . $regexp . ')$1';
153
154 99
        if ($defaultValue !== null) {
155 57
          $replaceToString .= '?';
156 57
        }
157
158 99
        $this->templateMatch = preg_replace('!' . preg_quote($rawParameterInfo) . '(.)!', $replaceToString, $this->templateMatch);
159 99
        $this->templateBuild = str_replace($rawParameterInfo, '{' . $name . '}', $this->templateBuild);
160
161 99
        $parameters[$name] = [
162 99
          $regexp,
163 99
          $defaultValue,
164
        ];
165
166 102
      }
167
168
169 99
      $this->parameters = $parameters;
170
171 99
    }
172
173
174
    /**
175
     * @param string $input
176
     * @param null $matchedParams
177
     * @return bool
178
     */
179 111
    public function match($input, &$matchedParams = null) {
180 111
      $matchedParams = array();
181
182 111
      if (preg_match('!^' . $this->templateMatch . '$!', $input, $match)) {
183 99
        foreach ($match as $matchId => $matchValue) {
184 99
          if (is_string($matchId)) {
185 48
            $matchedParams[$matchId] = $matchValue;
186 48
          }
187 99
        }
188
189
190 99
        foreach ($this->parameters as $name => $parameter) {
191 48
          if ($parameter[1] == null) {
192 39
            continue;
193
          }
194
195 9
          if (isset($matchedParams[$name]) and $matchedParams[$name] !== '') {
196 6
            continue;
197
          }
198
199 6
          $matchedParams[$name] = $parameter[1];
200 99
        }
201
202 99
        return true;
203
      } else {
204 60
        return false;
205
      }
206
    }
207
208
209
    /**
210
     * @param array $parameters
211
     * @param array $usedParameters
212
     * @return string
213
     */
214 48
    public function build(array $parameters = array(), &$usedParameters = null) {
215 48
      $usedParameters = array();
216 48
      $path = $this->templateBuild;
217
218 48
      foreach ($this->parameters as $name => $parameterInfo) {
219 27
        $regexp = $parameterInfo[0];
220 27
        $defaultValue = $parameterInfo[1];
221
222 27
        $value = (isset($parameters[$name])) ? $parameters[$name] : null;
223
224
225 27
        if ($value == null and $defaultValue === null) {
226 6
          throw new \InvalidArgumentException("Require parameter:" . $name);
227
        }
228
229
230 24
        if ($value == $defaultValue or $value == null) {
231
          # skip default values
232 15
          $usedParameters[$name] = $name;
233 15
          $path = preg_replace('!\{' . $name . '\}.?!', '', $path);
234 15
          continue;
235
        }
236
237
        # not empty value 
238 21
        if (!preg_match('~' . $regexp . '~', $value)) {
239 3
          throw new \InvalidArgumentException("Invalid parameter: " . $name);
240
        }
241
242 18
        $usedParameters[$name] = $name;
243 18
        $path = preg_replace('!\{' . $name . '\}!', $value, $path);
244 42
      }
245
246
247 42
      return $path;
248
249
    }
250
251
252
    /**
253
     * @return string
254
     */
255 3
    public function getTemplateMatch() {
256 3
      return $this->templateMatch;
257
    }
258
259
260
    /**
261
     * @return array
262
     */
263 3
    public function getParameters() {
264 3
      return $this->parameters;
265
    }
266
267
268
    /**
269
     * @return string
270
     */
271 3
    public function getTemplateBuild() {
272 3
      return $this->templateBuild;
273
    }
274
275
  }