Complex classes like Validator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Validator, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 31 | class Validator { |
||
| 32 | /** |
||
| 33 | * Verify template |
||
| 34 | * |
||
| 35 | * @param array<string,array|string|integer> $context Current context |
||
| 36 | * @param string $template handlebars template |
||
| 37 | */ |
||
| 38 | 758 | public static function verify(&$context, $template) { |
|
| 39 | 758 | $template = SafeString::stripExtendedComments($template); |
|
| 40 | 758 | $context['level'] = 0; |
|
| 41 | 758 | Parser::setDelimiter($context); |
|
| 42 | |||
| 43 | 758 | while (preg_match($context['tokens']['search'], $template, $matches)) { |
|
| 44 | // Skip a token when it is slash escaped |
||
| 45 | 747 | if ($context['flags']['slash'] && ($matches[Token::POS_LSPACE] === '') && preg_match('/^(.*?)(\\\\+)$/s', $matches[Token::POS_LOTHER], $escmatch)) { |
|
| 46 | 4 | if (strlen($escmatch[2]) % 4) { |
|
| 47 | 2 | static::pushToken($context, substr($matches[Token::POS_LOTHER], 0, -2) . $context['tokens']['startchar']); |
|
| 48 | 2 | $matches[Token::POS_BEGINTAG] = substr($matches[Token::POS_BEGINTAG], 1); |
|
| 49 | 2 | $template = implode('', array_slice($matches, Token::POS_BEGINTAG)); |
|
| 50 | 2 | continue; |
|
| 51 | } else { |
||
| 52 | 2 | $matches[Token::POS_LOTHER] = $escmatch[1] . str_repeat('\\', strlen($escmatch[2]) / 2); |
|
| 53 | } |
||
| 54 | } |
||
| 55 | 745 | $context['tokens']['count']++; |
|
| 56 | 745 | $V = static::token($matches, $context); |
|
| 57 | 745 | static::pushLeft($context); |
|
| 58 | 745 | if ($V) { |
|
| 59 | 706 | if (is_array($V)) { |
|
| 60 | 699 | array_push($V, $matches, $context['tokens']['partialind']); |
|
| 61 | } |
||
| 62 | 706 | static::pushToken($context, $V); |
|
| 63 | } |
||
| 64 | 745 | $template = "{$matches[Token::POS_RSPACE]}{$matches[Token::POS_ROTHER]}"; |
|
| 65 | } |
||
| 66 | 758 | static::pushToken($context, $template); |
|
| 67 | |||
| 68 | 758 | if ($context['level'] > 0) { |
|
| 69 | 8 | array_pop($context['stack']); |
|
| 70 | 8 | array_pop($context['stack']); |
|
| 71 | 8 | $token = array_pop($context['stack']); |
|
| 72 | 8 | $context['error'][] = 'Unclosed token ' . ($context['rawblock'] ? "{{{{{$token}}}}}" : ( $context['partialblock'] ? "{{#>{$token}}}" : "{{#{$token}}}")) . ' !!'; |
|
| 73 | } |
||
| 74 | 758 | } |
|
| 75 | |||
| 76 | /** |
||
| 77 | * push left string of current token and clear it |
||
| 78 | * |
||
| 79 | * @param array<string,array|string|integer> $context Current context |
||
| 80 | */ |
||
| 81 | 745 | protected static function pushLeft(&$context) { |
|
| 82 | 745 | $L = $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE]; |
|
| 83 | |||
| 84 | 745 | if ($context['currentToken'][Token::POS_OP] === '!') { |
|
| 85 | $appender = function (&$pb) use ($context, $L) { |
||
| 86 | 1 | $pb .= $L; |
|
| 87 | 27 | }; |
|
| 88 | 27 | if (count($context['partialblock']) > 0) { |
|
| 89 | 1 | array_walk($context['partialblock'], $appender); |
|
| 90 | } |
||
| 91 | 27 | if (count($context['inlinepartial']) > 0) { |
|
| 92 | array_walk($context['inlinepartial'], $appender); |
||
| 93 | } |
||
| 94 | } |
||
| 95 | |||
| 96 | 745 | static::pushToken($context, $L); |
|
| 97 | 745 | $context['currentToken'][Token::POS_LOTHER] = $context['currentToken'][Token::POS_LSPACE] = ''; |
|
| 98 | 745 | } |
|
| 99 | |||
| 100 | /** |
||
| 101 | * push a token into the stack when it is not empty string |
||
| 102 | * |
||
| 103 | * @param array<string,array|string|integer> $context Current context |
||
| 104 | * @param string|array $token a parsed token or a string |
||
| 105 | */ |
||
| 106 | 758 | protected static function pushToken(&$context, $token) { |
|
| 118 | |||
| 119 | /** |
||
| 120 | * push current token into the section stack |
||
| 121 | * |
||
| 122 | * @param array<string,array|string|integer> $context Current context |
||
| 123 | * @param string $operation operation string |
||
| 124 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 125 | */ |
||
| 126 | 374 | protected static function pushStack(&$context, $operation, $vars) { |
|
| 133 | |||
| 134 | /** |
||
| 135 | * Verify delimiters and operators |
||
| 136 | * |
||
| 137 | * @param string[] $token detected handlebars {{ }} token |
||
| 138 | * @param array<string,array|string|integer> $context current compile context |
||
| 139 | * |
||
| 140 | * @return boolean|null Return true when invalid |
||
| 141 | * |
||
| 142 | * @expect null when input array_fill(0, 11, ''), array() |
||
| 143 | * @expect null when input array(0, 0, 0, 0, 0, '{{', '#', '...', '}}'), array() |
||
| 144 | * @expect true when input array(0, 0, 0, 0, 0, '{', '#', '...', '}'), array() |
||
| 145 | */ |
||
| 146 | 746 | protected static function delimiter($token, &$context) { |
|
| 158 | |||
| 159 | /** |
||
| 160 | * Verify operators |
||
| 161 | * |
||
| 162 | * @param string $operator the operator string |
||
| 163 | * @param array<string,array|string|integer> $context current compile context |
||
| 164 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 165 | * |
||
| 166 | * @return boolean|integer|null Return true when invalid or detected |
||
| 167 | * |
||
| 168 | * @expect null when input '', array(), array() |
||
| 169 | * @expect 2 when input '^', array('usedFeature' => array('isec' => 1), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'elselvl' => array(), 'flags' => array('spvar' => 0), 'elsechain' => false, 'helperresolver' => 0), array(array('foo')) |
||
| 170 | * @expect true when input '/', array('stack' => array('[with]', '#'), 'level' => 1, 'currentToken' => array(0,0,0,0,0,0,0,'with'), 'flags' => array('nohbh' => 0)), array(array()) |
||
| 171 | * @expect 4 when input '#', array('usedFeature' => array('sec' => 3), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0), 'elsechain' => false, 'elselvl' => array(), 'helperresolver' => 0), array(array('x')) |
||
| 172 | * @expect 5 when input '#', array('usedFeature' => array('if' => 4), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0, 'nohbh' => 0), 'elsechain' => false, 'elselvl' => array(), 'helperresolver' => 0), array(array('if')) |
||
| 173 | * @expect 6 when input '#', array('usedFeature' => array('with' => 5), 'level' => 0, 'flags' => array('nohbh' => 0, 'runpart' => 0, 'spvar' => 0), 'currentToken' => array(0,0,0,0,0,0,0,0), 'elsechain' => false, 'elselvl' => array(), 'helperresolver' => 0), array(array('with')) |
||
| 174 | * @expect 7 when input '#', array('usedFeature' => array('each' => 6), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0, 'nohbh' => 0), 'elsechain' => false, 'elselvl' => array(), 'helperresolver' => 0), array(array('each')) |
||
| 175 | * @expect 8 when input '#', array('usedFeature' => array('unless' => 7), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0, 'nohbh' => 0), 'elsechain' => false, 'elselvl' => array(), 'helperresolver' => 0), array(array('unless')) |
||
| 176 | * @expect 9 when input '#', array('helpers' => array('abc' => ''), 'usedFeature' => array('helper' => 8), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0), 'elsechain' => false, 'elselvl' => array()), array(array('abc')) |
||
| 177 | * @expect 11 when input '#', array('helpers' => array('abc' => ''), 'usedFeature' => array('helper' => 10), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0), 'elsechain' => false, 'elselvl' => array()), array(array('abc')) |
||
| 178 | * @expect true when input '>', array('partialresolver' => false, 'usedFeature' => array('partial' => 7), 'level' => 0, 'flags' => array('skippartial' => 0, 'runpart' => 0, 'spvar' => 0), 'currentToken' => array(0,0,0,0,0,0,0,0), 'elsechain' => false, 'elselvl' => array()), array('test') |
||
| 179 | */ |
||
| 180 | 707 | protected static function operator($operator, &$context, &$vars) { |
|
| 181 | switch ($operator) { |
||
| 182 | 707 | case '#*': |
|
| 183 | 14 | if (!$context['compile']) { |
|
| 184 | 14 | static::pushLeft($context); |
|
| 185 | 14 | $context['stack'][] = count($context['parsed'][0]); |
|
| 186 | 14 | static::pushStack($context, '#*', $vars); |
|
| 187 | 14 | array_unshift($context['inlinepartial'], ''); |
|
| 188 | } |
||
| 189 | 14 | return static::inline($context, $vars); |
|
| 190 | |||
| 191 | 704 | case '#>': |
|
| 192 | 22 | if (!$context['compile']) { |
|
| 193 | 22 | static::pushLeft($context); |
|
| 194 | 22 | $context['stack'][] = count($context['parsed'][0]); |
|
| 195 | 22 | $vars[Parser::PARTIALBLOCK] = ++$context['usedFeature']['pblock']; |
|
| 196 | 22 | static::pushStack($context, '#>', $vars); |
|
|
|
|||
| 197 | 22 | array_unshift($context['partialblock'], ''); |
|
| 198 | } |
||
| 199 | 22 | return static::partial($context, $vars); |
|
| 200 | |||
| 201 | 700 | case '>': |
|
| 202 | 99 | return static::partial($context, $vars); |
|
| 203 | |||
| 204 | 661 | case '^': |
|
| 205 | 64 | if (!isset($vars[0][0])) { |
|
| 206 | 24 | if (!$context['flags']['else']) { |
|
| 207 | 1 | $context['error'][] = 'Do not support {{^}}, you should do compile with LightnCandy::FLAG_ELSE flag'; |
|
| 208 | 1 | return; |
|
| 209 | } else { |
||
| 210 | 23 | return static::doElse($context, $vars); |
|
| 211 | } |
||
| 212 | } |
||
| 213 | |||
| 214 | 40 | static::doElseChain($context); |
|
| 215 | |||
| 216 | 40 | if (static::isBlockHelper($context, $vars)) { |
|
| 217 | 1 | static::pushStack($context, '#', $vars); |
|
| 218 | 1 | return static::blockCustomHelper($context, $vars, true); |
|
| 219 | } |
||
| 220 | |||
| 221 | 39 | static::pushStack($context, '^', $vars); |
|
| 222 | 39 | return static::invertedSection($context, $vars); |
|
| 223 | |||
| 224 | 661 | case '/': |
|
| 225 | 347 | $r = static::blockEnd($context, $vars); |
|
| 226 | 347 | if ($r !== Token::POS_BACKFILL) { |
|
| 227 | 347 | array_pop($context['stack']); |
|
| 228 | 347 | array_pop($context['stack']); |
|
| 229 | 347 | array_pop($context['stack']); |
|
| 230 | } |
||
| 231 | 347 | return $r; |
|
| 232 | |||
| 233 | 636 | case '#': |
|
| 234 | 323 | static::doElseChain($context); |
|
| 235 | 323 | static::pushStack($context, '#', $vars); |
|
| 236 | |||
| 237 | 323 | if (static::isBlockHelper($context, $vars)) { |
|
| 238 | 63 | return static::blockCustomHelper($context, $vars); |
|
| 239 | } |
||
| 240 | |||
| 241 | 269 | return static::blockBegin($context, $vars); |
|
| 242 | } |
||
| 243 | 536 | } |
|
| 244 | |||
| 245 | /** |
||
| 246 | * validate inline partial begin token |
||
| 247 | * |
||
| 248 | * @param array<string,array|string|integer> $context current compile context |
||
| 249 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 250 | * |
||
| 251 | * @return boolean|null Return true when inline partial ends |
||
| 252 | */ |
||
| 253 | 706 | protected static function inlinePartial(&$context, $vars) { |
|
| 254 | 706 | if (count($context['inlinepartial']) > 0) { |
|
| 255 | 14 | $ended = false; |
|
| 256 | 14 | $append = $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE]; |
|
| 257 | array_walk($context['inlinepartial'], function (&$pb) use ($context, $append) { |
||
| 258 | 14 | $pb .= $append; |
|
| 259 | 14 | }); |
|
| 260 | 14 | if ($context['currentToken'][Token::POS_OP] === '/') { |
|
| 261 | 14 | if (static::blockEnd($context, $vars, '#*') !== null) { |
|
| 262 | 14 | $context['usedFeature']['inlpartial']++; |
|
| 263 | 14 | $tmpl = array_shift($context['inlinepartial']); |
|
| 264 | 14 | $c = $context['stack'][count($context['stack']) - 4]; |
|
| 265 | 14 | $context['parsed'][0] = array_slice($context['parsed'][0], 0, $c + 1); |
|
| 266 | 14 | $P = &$context['parsed'][0][$c]; |
|
| 267 | 14 | if (isset($P[1][1][0])) { |
|
| 268 | 13 | $context['usedPartial'][$P[1][1][0]] = $tmpl; |
|
| 269 | 13 | $P[1][0][0] = Partial::compileDynamic($context, $P[1][1][0]); |
|
| 270 | } |
||
| 271 | 14 | $ended = true; |
|
| 272 | } |
||
| 273 | } |
||
| 274 | 14 | $append = Token::toString($context['currentToken']); |
|
| 275 | array_walk($context['inlinepartial'], function (&$pb) use ($context, $append) { |
||
| 276 | 3 | $pb .= $append; |
|
| 277 | 14 | }); |
|
| 278 | 14 | return $ended; |
|
| 279 | } |
||
| 280 | 706 | } |
|
| 281 | |||
| 282 | /** |
||
| 283 | * validate partial block token |
||
| 284 | * |
||
| 285 | * @param array<string,array|string|integer> $context current compile context |
||
| 286 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 287 | * |
||
| 288 | * @return boolean|null Return true when partial block ends |
||
| 289 | */ |
||
| 290 | 706 | protected static function partialBlock(&$context, $vars) { |
|
| 291 | 706 | if (count($context['partialblock']) > 0) { |
|
| 292 | 21 | $ended = false; |
|
| 293 | 21 | $append = $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE]; |
|
| 294 | array_walk($context['partialblock'], function (&$pb) use ($context, $append) { |
||
| 295 | 21 | $pb .= $append; |
|
| 296 | 21 | }); |
|
| 297 | 21 | if ($context['currentToken'][Token::POS_OP] === '/') { |
|
| 298 | 21 | if (static::blockEnd($context, $vars, '#>') !== null) { |
|
| 299 | 21 | $c = $context['stack'][count($context['stack']) - 4]; |
|
| 300 | 21 | $found = Partial::resolve($context, $vars[0][0]) !== null; |
|
| 301 | 21 | $v = $found ? "@partial-block{$context['parsed'][0][$c][1][Parser::PARTIALBLOCK]}" : "{$vars[0][0]}"; |
|
| 302 | 21 | if ($found) { |
|
| 303 | 17 | $context['partials'][$v] = $context['partialblock'][0]; |
|
| 304 | } |
||
| 305 | 21 | $context['usedPartial'][$v] = $context['partialblock'][0]; |
|
| 306 | 21 | Partial::compileDynamic($context, $v); |
|
| 307 | 21 | if ($found) { |
|
| 308 | 17 | Partial::read($context, $vars[0][0]); |
|
| 309 | } |
||
| 310 | 21 | array_shift($context['partialblock']); |
|
| 311 | 21 | $context['parsed'][0] = array_slice($context['parsed'][0], 0, $c + 1); |
|
| 312 | 21 | $ended = true; |
|
| 313 | } |
||
| 314 | } |
||
| 315 | 21 | $append = Token::toString($context['currentToken']); |
|
| 316 | 21 | array_walk($context['partialblock'], function (&$pb) use ($context, $append) { |
|
| 317 | 15 | $pb .= $append; |
|
| 318 | 21 | }); |
|
| 319 | 21 | return $ended; |
|
| 320 | } |
||
| 321 | 706 | } |
|
| 322 | |||
| 323 | /** |
||
| 324 | * handle else chain |
||
| 325 | * |
||
| 326 | * @param array<string,array|string|integer> $context current compile context |
||
| 327 | */ |
||
| 328 | 352 | protected static function doElseChain(&$context) { |
|
| 329 | 352 | if ($context['elsechain']) { |
|
| 330 | 12 | $context['elsechain'] = false; |
|
| 331 | } else { |
||
| 332 | 352 | array_unshift($context['elselvl'], array()); |
|
| 333 | } |
||
| 334 | 352 | } |
|
| 335 | |||
| 336 | /** |
||
| 337 | * validate block begin token |
||
| 338 | * |
||
| 339 | * @param array<string,array|string|integer> $context current compile context |
||
| 340 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 341 | * |
||
| 342 | * @return boolean Return true always |
||
| 343 | */ |
||
| 344 | 268 | protected static function blockBegin(&$context, $vars) { |
|
| 345 | 268 | switch ((isset($vars[0][0]) && is_string($vars[0][0])) ? $vars[0][0] : null) { |
|
| 346 | 268 | case 'with': |
|
| 347 | 34 | return static::with($context, $vars); |
|
| 348 | 241 | case 'each': |
|
| 349 | 56 | return static::section($context, $vars, true); |
|
| 350 | 196 | case 'unless': |
|
| 351 | 7 | return static::unless($context, $vars); |
|
| 352 | 190 | case 'if': |
|
| 353 | 77 | return static::doIf($context, $vars); |
|
| 354 | default: |
||
| 355 | 120 | return static::section($context, $vars); |
|
| 356 | } |
||
| 357 | } |
||
| 358 | |||
| 359 | /** |
||
| 360 | * validate builtin helpers |
||
| 361 | * |
||
| 362 | * @param array<string,array|string|integer> $context current compile context |
||
| 363 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 364 | */ |
||
| 365 | 157 | protected static function builtin(&$context, $vars) { |
|
| 377 | |||
| 378 | /** |
||
| 379 | * validate section token |
||
| 380 | * |
||
| 381 | * @param array<string,array|string|integer> $context current compile context |
||
| 382 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 383 | * @param boolean $isEach the section is #each |
||
| 384 | * |
||
| 385 | * @return boolean Return true always |
||
| 386 | */ |
||
| 387 | 176 | protected static function section(&$context, $vars, $isEach = false) { |
|
| 398 | |||
| 399 | /** |
||
| 400 | * validate with token |
||
| 401 | * |
||
| 402 | * @param array<string,array|string|integer> $context current compile context |
||
| 403 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 404 | * |
||
| 405 | * @return boolean Return true always |
||
| 406 | */ |
||
| 407 | 34 | protected static function with(&$context, $vars) { |
|
| 411 | |||
| 412 | /** |
||
| 413 | * validate unless token |
||
| 414 | * |
||
| 415 | * @param array<string,array|string|integer> $context current compile context |
||
| 416 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 417 | * |
||
| 418 | * @return boolean Return true always |
||
| 419 | */ |
||
| 420 | 7 | protected static function unless(&$context, $vars) { |
|
| 424 | |||
| 425 | /** |
||
| 426 | * validate if token |
||
| 427 | * |
||
| 428 | * @param array<string,array|string|integer> $context current compile context |
||
| 429 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 430 | * |
||
| 431 | * @return boolean Return true always |
||
| 432 | */ |
||
| 433 | 77 | protected static function doIf(&$context, $vars) { |
|
| 437 | |||
| 438 | /** |
||
| 439 | * validate block custom helper token |
||
| 440 | * |
||
| 441 | * @param array<string,array|string|integer> $context current compile context |
||
| 442 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 443 | * @param boolean $inverted the logic will be inverted |
||
| 444 | * |
||
| 445 | * @return integer|null Return number of used custom helpers |
||
| 446 | */ |
||
| 447 | 63 | protected static function blockCustomHelper(&$context, $vars, $inverted = false) { |
|
| 448 | 63 | if (is_string($vars[0][0])) { |
|
| 449 | 63 | if (static::resolveHelper($context, $vars)) { |
|
| 450 | 63 | return ++$context['usedFeature']['helper']; |
|
| 451 | } |
||
| 452 | } |
||
| 453 | } |
||
| 454 | |||
| 455 | /** |
||
| 456 | * validate inverted section |
||
| 457 | * |
||
| 458 | * @param array<string,array|string|integer> $context current compile context |
||
| 459 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 460 | * |
||
| 461 | * @return integer Return number of inverted sections |
||
| 462 | */ |
||
| 463 | 38 | protected static function invertedSection(&$context, $vars) { |
|
| 466 | |||
| 467 | /** |
||
| 468 | * Return compiled PHP code for a handlebars block end token |
||
| 469 | * |
||
| 470 | * @param array<string,array|string|integer> $context current compile context |
||
| 471 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 472 | * @param string|null $match should also match to this operator |
||
| 473 | * |
||
| 474 | * @return boolean Return true |
||
| 475 | */ |
||
| 476 | 367 | protected static function blockEnd(&$context, &$vars, $match = null) { |
|
| 477 | 367 | $context['level']--; |
|
| 478 | 367 | $c = count($context['stack']) - 2; |
|
| 479 | 367 | $pop = ($c >= 0) ? $context['stack'][$c + 1] : ''; |
|
| 480 | 367 | if (($match !== null) && ($match !== $pop)) { |
|
| 481 | 6 | return; |
|
| 482 | } |
||
| 483 | 367 | $pop2 = ($c >= 0) ? $context['stack'][$c]: ''; |
|
| 484 | 367 | switch ($context['currentToken'][Token::POS_INNERTAG]) { |
|
| 485 | 367 | case 'with': |
|
| 486 | 38 | if (!$context['flags']['nohbh']) { |
|
| 487 | 36 | if ($pop2 !== '[with]') { |
|
| 488 | 1 | $context['error'][] = 'Unexpect token: {{/with}} !'; |
|
| 489 | 1 | return; |
|
| 490 | } |
||
| 491 | } |
||
| 492 | 37 | return true; |
|
| 493 | } |
||
| 494 | |||
| 495 | switch($pop) { |
||
| 496 | 347 | case '#': |
|
| 497 | 70 | case '^': |
|
| 498 | 318 | $elsechain = array_shift($context['elselvl']); |
|
| 499 | 318 | if (isset($elsechain[0])) { |
|
| 500 | 12 | $context['currentToken'][Token::POS_RSPACE] = $context['currentToken'][Token::POS_BACKFILL] = '{{/' . implode('}}{{/', $elsechain) . '}}' . Token::toString($context['currentToken']) . $context['currentToken'][Token::POS_RSPACE]; |
|
| 501 | 12 | return Token::POS_BACKFILL; |
|
| 502 | } |
||
| 503 | 32 | case '#>': |
|
| 504 | 15 | case '#*': |
|
| 505 | 346 | list($levels, $spvar, $var) = Expression::analyze($context, $vars[0]); |
|
| 506 | 346 | $v = Expression::toString($levels, $spvar, $var); |
|
| 507 | 346 | if ($pop2 !== $v) { |
|
| 508 | 2 | $context['error'][] = 'Unexpect token ' . Token::toString($context['currentToken']) . " ! Previous token {{{$pop}$pop2}} is not closed"; |
|
| 509 | 2 | return; |
|
| 510 | } |
||
| 511 | 345 | return true; |
|
| 512 | default: |
||
| 513 | 1 | $context['error'][] = 'Unexpect token: ' . Token::toString($context['currentToken']) . ' !'; |
|
| 514 | 1 | return; |
|
| 515 | } |
||
| 516 | } |
||
| 517 | |||
| 518 | /** |
||
| 519 | * handle delimiter change |
||
| 520 | * |
||
| 521 | * @param array<string,array|string|integer> $context current compile context |
||
| 522 | * |
||
| 523 | * @return boolean|null Return true when delimiter changed |
||
| 524 | */ |
||
| 525 | 735 | protected static function isDelimiter(&$context) { |
|
| 532 | |||
| 533 | /** |
||
| 534 | * handle raw block |
||
| 535 | * |
||
| 536 | * @param string[] $token detected handlebars {{ }} token |
||
| 537 | * @param array<string,array|string|integer> $context current compile context |
||
| 538 | * |
||
| 539 | * @return boolean|null Return true when in rawblock mode |
||
| 540 | */ |
||
| 541 | 745 | protected static function rawblock(&$token, &$context) { |
|
| 571 | |||
| 572 | /** |
||
| 573 | * handle comment |
||
| 574 | * |
||
| 575 | * @param string[] $token detected handlebars {{ }} token |
||
| 576 | * @param array<string,array|string|integer> $context current compile context |
||
| 577 | * |
||
| 578 | * @return boolean|null Return true when is comment |
||
| 579 | */ |
||
| 580 | 726 | protected static function comment(&$token, &$context) { |
|
| 586 | |||
| 587 | /** |
||
| 588 | * Collect handlebars usage information, detect template error. |
||
| 589 | * |
||
| 590 | * @param string[] $token detected handlebars {{ }} token |
||
| 591 | * @param array<string,array|string|integer> $context current compile context |
||
| 592 | */ |
||
| 593 | 745 | protected static function token(&$token, &$context) { |
|
| 594 | 745 | $context['currentToken'] = &$token; |
|
| 595 | |||
| 596 | 745 | if (static::rawblock($token, $context)) { |
|
| 597 | 4 | return Token::toString($token); |
|
| 598 | } |
||
| 599 | |||
| 600 | 745 | if (static::delimiter($token, $context)) { |
|
| 601 | 10 | return; |
|
| 602 | } |
||
| 603 | |||
| 604 | 735 | if (static::isDelimiter($context)) { |
|
| 605 | 15 | static::spacing($token, $context); |
|
| 606 | 15 | return; |
|
| 607 | } |
||
| 608 | |||
| 609 | 726 | if (static::comment($token, $context)) { |
|
| 610 | 26 | static::spacing($token, $context); |
|
| 611 | 26 | return; |
|
| 612 | } |
||
| 613 | |||
| 614 | 706 | list($raw, $vars) = Parser::parse($token, $context); |
|
| 615 | |||
| 616 | 706 | $partials = static::partialBlock($context, $vars); |
|
| 617 | 706 | $partials = static::inlinePartial($context, $vars) || $partials; |
|
| 618 | |||
| 619 | 706 | if ($partials) { |
|
| 620 | 31 | $context['stack'] = array_slice($context['stack'], 0, -4); |
|
| 621 | 31 | $context['currentToken'][Token::POS_LOTHER] = ''; |
|
| 622 | 31 | $context['currentToken'][Token::POS_LSPACE] = ''; |
|
| 623 | 31 | return; |
|
| 624 | } |
||
| 625 | |||
| 626 | // Handle spacing (standalone tags, partial indent) |
||
| 627 | 706 | static::spacing($token, $context, (($token[Token::POS_OP] === '') || ($token[Token::POS_OP] === '&')) && (!$context['flags']['else'] || !isset($vars[0][0]) || ($vars[0][0] !== 'else')) || ($context['flags']['nostd'] > 0)); |
|
| 628 | |||
| 629 | 706 | if (static::operator($token[Token::POS_OP], $context, $vars)) { |
|
| 630 | 426 | return isset($token[Token::POS_BACKFILL]) ? null : array($raw, $vars); |
|
| 631 | } |
||
| 632 | |||
| 633 | 539 | if (count($vars) == 0) { |
|
| 634 | 6 | return $context['error'][] = 'Wrong variable naming in ' . Token::toString($token); |
|
| 635 | } |
||
| 636 | |||
| 637 | 533 | if (!isset($vars[0])) { |
|
| 638 | 1 | return $context['error'][] = 'Do not support name=value in ' . Token::toString($token) . ', you should use it after a custom helper.'; |
|
| 639 | } |
||
| 640 | |||
| 641 | 532 | $context['usedFeature'][$raw ? 'raw' : 'enc']++; |
|
| 642 | |||
| 643 | 532 | foreach ($vars as $var) { |
|
| 644 | 532 | if (!isset($var[0]) || ($var[0] === 0)) { |
|
| 645 | 73 | if ($context['level'] == 0) { |
|
| 646 | 25 | $context['usedFeature']['rootthis']++; |
|
| 647 | } |
||
| 648 | 532 | $context['usedFeature']['this']++; |
|
| 649 | } |
||
| 650 | } |
||
| 651 | |||
| 652 | 532 | if (!isset($vars[0][0])) { |
|
| 653 | 54 | return array($raw, $vars); |
|
| 654 | } |
||
| 655 | |||
| 656 | 498 | if (($vars[0][0] === 'else') && $context['flags']['else']) { |
|
| 657 | 36 | static::doElse($context, $vars); |
|
| 658 | 36 | return array($raw, $vars); |
|
| 659 | } |
||
| 660 | |||
| 661 | 474 | if (!static::helper($context, $vars)) { |
|
| 662 | 359 | static::lookup($context, $vars); |
|
| 663 | 359 | static::log($context, $vars); |
|
| 664 | } |
||
| 665 | |||
| 666 | 474 | return array($raw, $vars); |
|
| 667 | } |
||
| 668 | |||
| 669 | /** |
||
| 670 | * Return 1 or larger number when else token detected |
||
| 671 | * |
||
| 672 | * @param array<string,array|string|integer> $context current compile context |
||
| 673 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 674 | * |
||
| 675 | * @return integer Return 1 or larger number when else token detected |
||
| 676 | */ |
||
| 677 | 58 | protected static function doElse(&$context, $vars) { |
|
| 678 | 58 | if ($context['level'] == 0) { |
|
| 679 | 1 | $context['error'][] = '{{else}} only valid in if, unless, each, and #section context'; |
|
| 680 | } |
||
| 681 | |||
| 682 | 58 | if (isset($vars[1][0])) { |
|
| 683 | 12 | $token = $context['currentToken']; |
|
| 684 | 12 | $context['currentToken'][Token::POS_RSPACE] = "{{#{$vars[1][0]} " . preg_replace('/^\\s*else\\s+' . $vars[1][0] . '\\s*/', '', $token[Token::POS_INNERTAG]) . '}}' . $context['currentToken'][Token::POS_RSPACE]; |
|
| 685 | 12 | array_unshift($context['elselvl'][0], $vars[1][0]); |
|
| 686 | 12 | $context['elsechain'] = true; |
|
| 687 | } |
||
| 688 | |||
| 689 | 58 | return ++$context['usedFeature']['else']; |
|
| 690 | } |
||
| 691 | |||
| 692 | /** |
||
| 693 | * Return true when this is {{log ...}} |
||
| 694 | * |
||
| 695 | * @param array<string,array|string|integer> $context current compile context |
||
| 696 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 697 | * |
||
| 698 | * @return boolean|null Return true when it is custom helper |
||
| 699 | */ |
||
| 700 | 359 | public static function log(&$context, $vars) { |
|
| 701 | 359 | if (isset($vars[0][0]) && ($vars[0][0] === 'log')) { |
|
| 702 | 3 | if (!$context['flags']['nohbh']) { |
|
| 703 | 3 | if (count($vars) < 2) { |
|
| 704 | 1 | $context['error'][] = "No argument after {{log}} !"; |
|
| 705 | } |
||
| 706 | 3 | $context['usedFeature']['log']++; |
|
| 707 | 3 | return true; |
|
| 708 | } |
||
| 709 | } |
||
| 710 | 356 | } |
|
| 711 | |||
| 712 | /** |
||
| 713 | * Return true when this is {{lookup ...}} |
||
| 714 | * |
||
| 715 | * @param array<string,array|string|integer> $context current compile context |
||
| 716 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 717 | * |
||
| 718 | * @return boolean|null Return true when it is custom helper |
||
| 719 | */ |
||
| 720 | 359 | public static function lookup(&$context, $vars) { |
|
| 733 | |||
| 734 | /** |
||
| 735 | * Return true when the name is listed in helper table |
||
| 736 | * |
||
| 737 | * @param array<string,array|string|integer> $context current compile context |
||
| 738 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 739 | * @param boolean $checkSubexp true when check for subexpression |
||
| 740 | * |
||
| 741 | * @return boolean Return true when it is custom helper |
||
| 742 | */ |
||
| 743 | 482 | public static function helper(&$context, $vars, $checkSubexp = false) { |
|
| 744 | 482 | if (static::resolveHelper($context, $vars)) { |
|
| 745 | 130 | $context['usedFeature']['helper']++; |
|
| 762 | |||
| 763 | /** |
||
| 764 | * use helperresolver to resolve helper, return true when helper founded |
||
| 765 | * |
||
| 766 | * @param array<string,array|string|integer> $context Current context of compiler progress. |
||
| 767 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 768 | * |
||
| 769 | * @return boolean $found helper exists or not |
||
| 770 | */ |
||
| 771 | 644 | public static function resolveHelper(&$context, &$vars) { |
|
| 789 | |||
| 790 | /** |
||
| 791 | * detect for block custom helper |
||
| 792 | * |
||
| 793 | * @param array<string,array|string|integer> $context current compile context |
||
| 794 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 795 | * |
||
| 796 | * @return boolean|null Return true when this token is block custom helper |
||
| 797 | */ |
||
| 798 | 352 | protected static function isBlockHelper($context, $vars) { |
|
| 809 | |||
| 810 | /** |
||
| 811 | * validate inline partial |
||
| 812 | * |
||
| 813 | * @param array<string,array|string|integer> $context current compile context |
||
| 814 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 815 | * |
||
| 816 | * @return boolean Return true always |
||
| 817 | */ |
||
| 818 | 14 | protected static function inline(&$context, $vars) { |
|
| 830 | |||
| 831 | /** |
||
| 832 | * validate partial |
||
| 833 | * |
||
| 834 | * @param array<string,array|string|integer> $context current compile context |
||
| 835 | * @param array<boolean|integer|string|array> $vars parsed arguments list |
||
| 836 | * |
||
| 837 | * @return integer|boolean Return 1 or larger number for runtime partial, return true for other case |
||
| 838 | */ |
||
| 839 | 105 | protected static function partial(&$context, $vars) { |
|
| 861 | |||
| 862 | /** |
||
| 863 | * Modify $token when spacing rules matched. |
||
| 864 | * |
||
| 865 | * @param array<string> $token detected handlebars {{ }} token |
||
| 866 | * @param array<string,array|string|integer> $context current compile context |
||
| 867 | * @param boolean $nost do not do stand alone logic |
||
| 868 | * |
||
| 869 | * @return string|null Return compiled code segment for the token |
||
| 870 | */ |
||
| 871 | 735 | protected static function spacing(&$token, &$context, $nost = false) { |
|
| 922 | } |
||
| 923 | |||
| 924 |
It seems like the type of the argument is not accepted by the function/method which you are calling.
In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.
We suggest to add an explicit type cast like in the following example: