1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13: 14: 15: 16: 17: 18:
19:
20: namespace LightnCandy;
21:
22: 23: 24:
25: class Validator
26: {
27: 28: 29: 30: 31: 32:
33: public static function verify(&$context, $template)
34: {
35: $template = SafeString::stripExtendedComments($template);
36: $context['level'] = 0;
37: Parser::setDelimiter($context);
38:
39: while (preg_match($context['tokens']['search'], $template, $matches)) {
40:
41: if ($context['flags']['slash'] && ($matches[Token::POS_LSPACE] === '') && preg_match('/^(.*?)(\\\\+)$/s', $matches[Token::POS_LOTHER], $escmatch)) {
42: if (strlen($escmatch[2]) % 4) {
43: static::pushToken($context, substr($matches[Token::POS_LOTHER], 0, -2) . $context['tokens']['startchar']);
44: $matches[Token::POS_BEGINTAG] = substr($matches[Token::POS_BEGINTAG], 1);
45: $template = implode('', array_slice($matches, Token::POS_BEGINTAG));
46: continue;
47: } else {
48: $matches[Token::POS_LOTHER] = $escmatch[1] . str_repeat('\\', strlen($escmatch[2]) / 2);
49: }
50: }
51: $context['tokens']['count']++;
52: $V = static::token($matches, $context);
53: static::pushLeft($context);
54: if ($V) {
55: if (is_array($V)) {
56: array_push($V, $matches, $context['tokens']['partialind']);
57: }
58: static::pushToken($context, $V);
59: }
60: $template = "{$matches[Token::POS_RSPACE]}{$matches[Token::POS_ROTHER]}";
61: }
62: static::pushToken($context, $template);
63:
64: if ($context['level'] > 0) {
65: array_pop($context['stack']);
66: array_pop($context['stack']);
67: $token = array_pop($context['stack']);
68: $context['error'][] = 'Unclosed token ' . ($context['rawblock'] ? "{{{{{$token}}}}}" : ($context['partialblock'] ? "{{#>{$token}}}" : "{{#{$token}}}")) . ' !!';
69: }
70: }
71:
72: 73: 74: 75: 76:
77: protected static function pushLeft(&$context)
78: {
79: $L = $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE];
80: static::pushToken($context, $L);
81: $context['currentToken'][Token::POS_LOTHER] = $context['currentToken'][Token::POS_LSPACE] = '';
82: }
83:
84: 85: 86: 87: 88: 89:
90: protected static function pushPartial(&$context, $append)
91: {
92: $appender = function (&$p) use ($append) {
93: $p .= $append;
94: };
95: array_walk($context['inlinepartial'], $appender);
96: array_walk($context['partialblock'], $appender);
97: }
98:
99: 100: 101: 102: 103: 104:
105: protected static function pushToken(&$context, $token)
106: {
107: if ($token === '') {
108: return;
109: }
110: if (is_string($token)) {
111: static::pushPartial($context, $token);
112: $append = $token;
113: if (is_string(end($context['parsed'][0]))) {
114: $context['parsed'][0][key($context['parsed'][0])] .= $token;
115: return;
116: }
117: } else {
118: static::pushPartial($context, Token::toString($context['currentToken']));
119: switch ($context['currentToken'][Token::POS_OP]) {
120: case '#*':
121: array_unshift($context['inlinepartial'], '');
122: break;
123: case '#>':
124: array_unshift($context['partialblock'], '');
125: break;
126: }
127: }
128: $context['parsed'][0][] = $token;
129: }
130:
131: 132: 133: 134: 135: 136: 137:
138: protected static function pushStack(&$context, $operation, $vars)
139: {
140: list($levels, $spvar, $var) = Expression::analyze($context, $vars[0]);
141: $context['stack'][] = $context['currentToken'][Token::POS_INNERTAG];
142: $context['stack'][] = Expression::toString($levels, $spvar, $var);
143: $context['stack'][] = $operation;
144: $context['level']++;
145: }
146:
147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158:
159: protected static function delimiter($token, &$context)
160: {
161:
162: if (strlen($token[Token::POS_BEGINRAW]) !== strlen($token[Token::POS_ENDRAW])) {
163: $context['error'][] = 'Bad token ' . Token::toString($token) . ' ! Do you mean ' . Token::toString($token, array(Token::POS_BEGINRAW => '', Token::POS_ENDRAW => '')) . ' or ' . Token::toString($token, array(Token::POS_BEGINRAW => '{', Token::POS_ENDRAW => '}')) . '?';
164: return true;
165: }
166:
167: if ((strlen($token[Token::POS_BEGINRAW]) == 1) && $token[Token::POS_OP] && ($token[Token::POS_OP] !== '&')) {
168: $context['error'][] = 'Bad token ' . Token::toString($token) . ' ! Do you mean ' . Token::toString($token, array(Token::POS_BEGINRAW => '', Token::POS_ENDRAW => '')) . ' ?';
169: return true;
170: }
171: }
172:
173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193:
194: protected static function operator($operator, &$context, &$vars)
195: {
196: switch ($operator) {
197: case '#*':
198: if (!$context['compile']) {
199: $context['stack'][] = count($context['parsed'][0]) + ($context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE] === '' ? 0 : 1);
200: static::pushStack($context, '#*', $vars);
201: }
202: return static::inline($context, $vars);
203:
204: case '#>':
205: if (!$context['compile']) {
206: $context['stack'][] = count($context['parsed'][0]) + ($context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE] === '' ? 0 : 1);
207: $vars[Parser::PARTIALBLOCK] = ++$context['usedFeature']['pblock'];
208: static::pushStack($context, '#>', $vars);
209: }
210:
211: case '>':
212: return static::partial($context, $vars);
213:
214: case '^':
215: if (!isset($vars[0][0])) {
216: if (!$context['flags']['else']) {
217: $context['error'][] = 'Do not support {{^}}, you should do compile with LightnCandy::FLAG_ELSE flag';
218: return;
219: } else {
220: return static::doElse($context, $vars);
221: }
222: }
223:
224: static::doElseChain($context);
225:
226: if (static::isBlockHelper($context, $vars)) {
227: static::pushStack($context, '#', $vars);
228: return static::blockCustomHelper($context, $vars, true);
229: }
230:
231: static::pushStack($context, '^', $vars);
232: return static::invertedSection($context, $vars);
233:
234: case '/':
235: $r = static::blockEnd($context, $vars);
236: if ($r !== Token::POS_BACKFILL) {
237: array_pop($context['stack']);
238: array_pop($context['stack']);
239: array_pop($context['stack']);
240: }
241: return $r;
242:
243: case '#':
244: static::doElseChain($context);
245: static::pushStack($context, '#', $vars);
246:
247: if (static::isBlockHelper($context, $vars)) {
248: return static::blockCustomHelper($context, $vars);
249: }
250:
251: return static::blockBegin($context, $vars);
252: }
253: }
254:
255: 256: 257: 258: 259: 260: 261: 262:
263: protected static function inlinePartial(&$context, $vars)
264: {
265: $ended = false;
266: if ($context['currentToken'][Token::POS_OP] === '/') {
267: if (static::blockEnd($context, $vars, '#*') !== null) {
268: $context['usedFeature']['inlpartial']++;
269: $tmpl = array_shift($context['inlinepartial']) . $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE];
270: $c = $context['stack'][count($context['stack']) - 4];
271: $context['parsed'][0] = array_slice($context['parsed'][0], 0, $c + 1);
272: $P = &$context['parsed'][0][$c];
273: if (isset($P[1][1][0])) {
274: $context['usedPartial'][$P[1][1][0]] = $tmpl;
275: $P[1][0][0] = Partial::compileDynamic($context, $P[1][1][0]);
276: }
277: $ended = true;
278: }
279: }
280: return $ended;
281: }
282:
283: 284: 285: 286: 287: 288: 289: 290:
291: protected static function partialBlock(&$context, $vars)
292: {
293: $ended = false;
294: if ($context['currentToken'][Token::POS_OP] === '/') {
295: if (static::blockEnd($context, $vars, '#>') !== null) {
296: $c = $context['stack'][count($context['stack']) - 4];
297: $context['parsed'][0] = array_slice($context['parsed'][0], 0, $c + 1);
298: $found = Partial::resolve($context, $vars[0][0]) !== null;
299: $v = $found ? "@partial-block{$context['parsed'][0][$c][1][Parser::PARTIALBLOCK]}" : "{$vars[0][0]}";
300: if (count($context['partialblock']) == 1) {
301: $tmpl = $context['partialblock'][0] . $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE];
302: if ($found) {
303: $context['partials'][$v] = $tmpl;
304: }
305: $context['usedPartial'][$v] = $tmpl;
306: Partial::compileDynamic($context, $v);
307: if ($found) {
308: Partial::read($context, $vars[0][0]);
309: }
310: }
311: array_shift($context['partialblock']);
312: $ended = true;
313: }
314: }
315: return $ended;
316: }
317:
318: 319: 320: 321: 322:
323: protected static function doElseChain(&$context)
324: {
325: if ($context['elsechain']) {
326: $context['elsechain'] = false;
327: } else {
328: array_unshift($context['elselvl'], array());
329: }
330: }
331:
332: 333: 334: 335: 336: 337: 338: 339:
340: protected static function blockBegin(&$context, $vars)
341: {
342: switch ((isset($vars[0][0]) && is_string($vars[0][0])) ? $vars[0][0] : null) {
343: case 'with':
344: return static::with($context, $vars);
345: case 'each':
346: return static::section($context, $vars, true);
347: case 'unless':
348: return static::unless($context, $vars);
349: case 'if':
350: return static::doIf($context, $vars);
351: default:
352: return static::section($context, $vars);
353: }
354: }
355:
356: 357: 358: 359: 360: 361:
362: protected static function builtin(&$context, $vars)
363: {
364: if ($context['flags']['nohbh']) {
365: if (isset($vars[1][0])) {
366: $context['error'][] = "Do not support {{#{$vars[0][0]} var}} because you compile with LightnCandy::FLAG_NOHBHELPERS flag";
367: }
368: } else {
369: if (count($vars) < 2) {
370: $context['error'][] = "No argument after {{#{$vars[0][0]}}} !";
371: }
372: }
373: $context['usedFeature'][$vars[0][0]]++;
374: }
375:
376: 377: 378: 379: 380: 381: 382: 383: 384:
385: protected static function section(&$context, $vars, $isEach = false)
386: {
387: if ($isEach) {
388: static::builtin($context, $vars);
389: } else {
390: if ((count($vars) > 1) && !$context['flags']['lambda']) {
391: $context['error'][] = "Custom helper not found: {$vars[0][0]} in " . Token::toString($context['currentToken']) . ' !';
392: }
393: $context['usedFeature']['sec']++;
394: }
395: return true;
396: }
397:
398: 399: 400: 401: 402: 403: 404: 405:
406: protected static function with(&$context, $vars)
407: {
408: static::builtin($context, $vars);
409: return true;
410: }
411:
412: 413: 414: 415: 416: 417: 418: 419:
420: protected static function unless(&$context, $vars)
421: {
422: static::builtin($context, $vars);
423: return true;
424: }
425:
426: 427: 428: 429: 430: 431: 432: 433:
434: protected static function doIf(&$context, $vars)
435: {
436: static::builtin($context, $vars);
437: return true;
438: }
439:
440: 441: 442: 443: 444: 445: 446: 447: 448:
449: protected static function blockCustomHelper(&$context, $vars, $inverted = false)
450: {
451: if (is_string($vars[0][0])) {
452: if (static::resolveHelper($context, $vars)) {
453: return ++$context['usedFeature']['helper'];
454: }
455: }
456: }
457:
458: 459: 460: 461: 462: 463: 464: 465:
466: protected static function invertedSection(&$context, $vars)
467: {
468: return ++$context['usedFeature']['isec'];
469: }
470:
471: 472: 473: 474: 475: 476: 477: 478: 479:
480: protected static function blockEnd(&$context, &$vars, $match = null)
481: {
482: $c = count($context['stack']) - 2;
483: $pop = ($c >= 0) ? $context['stack'][$c + 1] : '';
484: if (($match !== null) && ($match !== $pop)) {
485: return;
486: }
487:
488: $context['level']--;
489: $pop2 = ($c >= 0) ? $context['stack'][$c]: '';
490: switch ($context['currentToken'][Token::POS_INNERTAG]) {
491: case 'with':
492: if (!$context['flags']['nohbh']) {
493: if ($pop2 !== '[with]') {
494: $context['error'][] = 'Unexpect token: {{/with}} !';
495: return;
496: }
497: }
498: return true;
499: }
500:
501: switch ($pop) {
502: case '#':
503: case '^':
504: $elsechain = array_shift($context['elselvl']);
505: if (isset($elsechain[0])) {
506:
507: $context['level']++;
508: $context['currentToken'][Token::POS_RSPACE] = $context['currentToken'][Token::POS_BACKFILL] = '{{/' . implode('}}{{/', $elsechain) . '}}' . Token::toString($context['currentToken']) . $context['currentToken'][Token::POS_RSPACE];
509: return Token::POS_BACKFILL;
510: }
511:
512: case '#>':
513: case '#*':
514: list($levels, $spvar, $var) = Expression::analyze($context, $vars[0]);
515: $v = Expression::toString($levels, $spvar, $var);
516: if ($pop2 !== $v) {
517: $context['error'][] = 'Unexpect token ' . Token::toString($context['currentToken']) . " ! Previous token {{{$pop}$pop2}} is not closed";
518: return;
519: }
520: return true;
521: default:
522: $context['error'][] = 'Unexpect token: ' . Token::toString($context['currentToken']) . ' !';
523: return;
524: }
525: }
526:
527: 528: 529: 530: 531: 532: 533:
534: protected static function isDelimiter(&$context)
535: {
536: if (preg_match('/^=\s*([^ ]+)\s+([^ ]+)\s*=$/', $context['currentToken'][Token::POS_INNERTAG], $matched)) {
537: $context['usedFeature']['delimiter']++;
538: Parser::setDelimiter($context, $matched[1], $matched[2]);
539: return true;
540: }
541: }
542:
543: 544: 545: 546: 547: 548: 549: 550:
551: protected static function rawblock(&$token, &$context)
552: {
553: $inner = $token[Token::POS_INNERTAG];
554: trim($inner);
555:
556:
557: if ($context['rawblock'] && !(($token[Token::POS_BEGINRAW] === '{{') && ($token[Token::POS_OP] === '/') && ($context['rawblock'] === $inner))) {
558: return true;
559: }
560:
561: $token[Token::POS_INNERTAG] = $inner;
562:
563:
564: if ($token[Token::POS_BEGINRAW] === '{{') {
565: if ($token[Token::POS_ENDRAW] !== '}}') {
566: $context['error'][] = 'Bad token ' . Token::toString($token) . ' ! Do you mean ' . Token::toString($token, array(Token::POS_ENDRAW => '}}')) . ' ?';
567: }
568: if ($context['rawblock']) {
569: Parser::setDelimiter($context);
570: $context['rawblock'] = false;
571: } else {
572: if ($token[Token::POS_OP]) {
573: $context['error'][] = "Wrong raw block begin with " . Token::toString($token) . ' ! Remove "' . $token[Token::POS_OP] . '" to fix this issue.';
574: }
575: $context['rawblock'] = $token[Token::POS_INNERTAG];
576: Parser::setDelimiter($context);
577: $token[Token::POS_OP] = '#';
578: }
579: $token[Token::POS_ENDRAW] = '}}';
580: }
581: }
582:
583: 584: 585: 586: 587: 588: 589: 590:
591: protected static function comment(&$token, &$context)
592: {
593: if ($token[Token::POS_OP] === '!') {
594: $context['usedFeature']['comment']++;
595: return true;
596: }
597: }
598:
599: 600: 601: 602: 603: 604: 605: 606:
607: protected static function token(&$token, &$context)
608: {
609: $context['currentToken'] = &$token;
610:
611: if (static::rawblock($token, $context)) {
612: return Token::toString($token);
613: }
614:
615: if (static::delimiter($token, $context)) {
616: return;
617: }
618:
619: if (static::isDelimiter($context)) {
620: static::spacing($token, $context);
621: return;
622: }
623:
624: if (static::comment($token, $context)) {
625: static::spacing($token, $context);
626: return;
627: }
628:
629: list($raw, $vars) = Parser::parse($token, $context);
630:
631:
632: 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));
633:
634: $inlinepartial = static::inlinePartial($context, $vars);
635: $partialblock = static::partialBlock($context, $vars);
636:
637: if ($partialblock || $inlinepartial) {
638: $context['stack'] = array_slice($context['stack'], 0, -4);
639: static::pushPartial($context, $context['currentToken'][Token::POS_LOTHER] . $context['currentToken'][Token::POS_LSPACE] . Token::toString($context['currentToken']));
640: $context['currentToken'][Token::POS_LOTHER] = '';
641: $context['currentToken'][Token::POS_LSPACE] = '';
642: return;
643: }
644:
645: if (static::operator($token[Token::POS_OP], $context, $vars)) {
646: return isset($token[Token::POS_BACKFILL]) ? null : array($raw, $vars);
647: }
648:
649: if (count($vars) == 0) {
650: return $context['error'][] = 'Wrong variable naming in ' . Token::toString($token);
651: }
652:
653: if (!isset($vars[0])) {
654: return $context['error'][] = 'Do not support name=value in ' . Token::toString($token) . ', you should use it after a custom helper.';
655: }
656:
657: $context['usedFeature'][$raw ? 'raw' : 'enc']++;
658:
659: foreach ($vars as $var) {
660: if (!isset($var[0]) || ($var[0] === 0)) {
661: if ($context['level'] == 0) {
662: $context['usedFeature']['rootthis']++;
663: }
664: $context['usedFeature']['this']++;
665: }
666: }
667:
668: if (!isset($vars[0][0])) {
669: return array($raw, $vars);
670: }
671:
672: if (($vars[0][0] === 'else') && $context['flags']['else']) {
673: static::doElse($context, $vars);
674: return array($raw, $vars);
675: }
676:
677: if (!static::helper($context, $vars)) {
678: static::lookup($context, $vars);
679: static::log($context, $vars);
680: }
681:
682: return array($raw, $vars);
683: }
684:
685: 686: 687: 688: 689: 690: 691: 692:
693: protected static function doElse(&$context, $vars)
694: {
695: if ($context['level'] == 0) {
696: $context['error'][] = '{{else}} only valid in if, unless, each, and #section context';
697: }
698:
699: if (isset($vars[1][0])) {
700: $token = $context['currentToken'];
701: $context['currentToken'][Token::POS_INNERTAG] = 'else';
702: $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];
703: array_unshift($context['elselvl'][0], $vars[1][0]);
704: $context['elsechain'] = true;
705: }
706:
707: return ++$context['usedFeature']['else'];
708: }
709:
710: 711: 712: 713: 714: 715: 716: 717:
718: public static function log(&$context, $vars)
719: {
720: if (isset($vars[0][0]) && ($vars[0][0] === 'log')) {
721: if (!$context['flags']['nohbh']) {
722: if (count($vars) < 2) {
723: $context['error'][] = "No argument after {{log}} !";
724: }
725: $context['usedFeature']['log']++;
726: return true;
727: }
728: }
729: }
730:
731: 732: 733: 734: 735: 736: 737: 738:
739: public static function lookup(&$context, $vars)
740: {
741: if (isset($vars[0][0]) && ($vars[0][0] === 'lookup')) {
742: if (!$context['flags']['nohbh']) {
743: if (count($vars) < 2) {
744: $context['error'][] = "No argument after {{lookup}} !";
745: } elseif (count($vars) < 3) {
746: $context['error'][] = "{{lookup}} requires 2 arguments !";
747: }
748: $context['usedFeature']['lookup']++;
749: return true;
750: }
751: }
752: }
753:
754: 755: 756: 757: 758: 759: 760: 761: 762:
763: public static function helper(&$context, $vars, $checkSubexp = false)
764: {
765: if (static::resolveHelper($context, $vars)) {
766: $context['usedFeature']['helper']++;
767: return true;
768: }
769:
770: if ($checkSubexp) {
771: switch ($vars[0][0]) {
772: case 'if':
773: case 'unless':
774: case 'with':
775: case 'each':
776: case 'lookup':
777: return $context['flags']['nohbh'] ? false : true;
778: }
779: }
780:
781: return false;
782: }
783:
784: 785: 786: 787: 788: 789: 790: 791:
792: public static function resolveHelper(&$context, &$vars)
793: {
794: if (count($vars[0]) !== 1) {
795: return false;
796: }
797: if (isset($context['helpers'][$vars[0][0]])) {
798: return true;
799: }
800:
801: if ($context['helperresolver']) {
802: $helper = $context['helperresolver']($context, $vars[0][0]);
803: if ($helper) {
804: $context['helpers'][$vars[0][0]] = $helper;
805: return true;
806: }
807: }
808:
809: return false;
810: }
811:
812: 813: 814: 815: 816: 817: 818: 819:
820: protected static function isBlockHelper($context, $vars)
821: {
822: if (!isset($vars[0][0])) {
823: return;
824: }
825:
826: if (!static::resolveHelper($context, $vars)) {
827: return;
828: }
829:
830: return true;
831: }
832:
833: 834: 835: 836: 837: 838: 839: 840:
841: protected static function inline(&$context, $vars)
842: {
843: if (!$context['flags']['runpart']) {
844: $context['error'][] = "Do not support {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}, you should do compile with LightnCandy::FLAG_RUNTIMEPARTIAL flag";
845: }
846: if (!isset($vars[0][0]) || ($vars[0][0] !== 'inline')) {
847: $context['error'][] = "Do not support {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}, now we only support {{#*inline \"partialName\"}}template...{{/inline}}";
848: }
849: if (!isset($vars[1][0])) {
850: $context['error'][] = "Error in {{#*{$context['currentToken'][Token::POS_INNERTAG]}}}: inline require 1 argument for partial name!";
851: }
852: return true;
853: }
854:
855: 856: 857: 858: 859: 860: 861: 862:
863: protected static function partial(&$context, $vars)
864: {
865: if (Parser::isSubExp($vars[0])) {
866: if ($context['flags']['runpart']) {
867: return $context['usedFeature']['dynpartial']++;
868: } else {
869: $context['error'][] = "You use dynamic partial name as '{$vars[0][2]}', this only works with option FLAG_RUNTIMEPARTIAL enabled";
870: return true;
871: }
872: } else {
873: if ($context['currentToken'][Token::POS_OP] !== '#>') {
874: Partial::read($context, $vars[0][0]);
875: }
876: }
877: if (!$context['flags']['runpart']) {
878: $named = count(array_diff_key($vars, array_keys(array_keys($vars)))) > 0;
879: if ($named || (count($vars) > 1)) {
880: $context['error'][] = "Do not support {{>{$context['currentToken'][Token::POS_INNERTAG]}}}, you should do compile with LightnCandy::FLAG_RUNTIMEPARTIAL flag";
881: }
882: }
883:
884: return true;
885: }
886:
887: 888: 889: 890: 891: 892: 893: 894: 895:
896: protected static function spacing(&$token, &$context, $nost = false)
897: {
898:
899: $lsp = preg_match('/^(.*)(\\r?\\n)([ \\t]*?)$/s', $token[Token::POS_LSPACE], $lmatch);
900: $ind = $lsp ? $lmatch[3] : $token[Token::POS_LSPACE];
901:
902: $rsp = preg_match('/^([ \\t]*?)(\\r?\\n)(.*)$/s', $token[Token::POS_RSPACE], $rmatch);
903: $st = true;
904:
905: $ahead = $context['tokens']['ahead'];
906: $context['tokens']['ahead'] = preg_match('/^[^\n]*{{/s', $token[Token::POS_RSPACE] . $token[Token::POS_ROTHER]);
907:
908: $context['tokens']['partialind'] = '';
909:
910: if (!$lsp && $ahead) {
911: $st = false;
912: }
913: if ($nost) {
914: $st = false;
915: }
916:
917: if ($token[Token::POS_LOTHER] && !$token[Token::POS_LSPACE]) {
918: $st = false;
919: }
920:
921: if ($token[Token::POS_ROTHER] && !$token[Token::POS_RSPACE]) {
922: $st = false;
923: }
924: if ($st && (
925: ($lsp && $rsp)
926: || ($rsp && !$token[Token::POS_LOTHER])
927: || ($lsp && !$token[Token::POS_ROTHER])
928: )) {
929:
930: if ($token[Token::POS_OP] === '>') {
931: if (!$context['flags']['noind']) {
932: $context['tokens']['partialind'] = $token[Token::POS_LSPACECTL] ? '' : $ind;
933: $token[Token::POS_LSPACE] = (isset($lmatch[2]) ? ($lmatch[1] . $lmatch[2]) : '');
934: }
935: } else {
936: $token[Token::POS_LSPACE] = (isset($lmatch[2]) ? ($lmatch[1] . $lmatch[2]) : '');
937: }
938: $token[Token::POS_RSPACE] = isset($rmatch[3]) ? $rmatch[3] : '';
939: }
940:
941:
942: if ($token[Token::POS_LSPACECTL]) {
943: $token[Token::POS_LSPACE] = '';
944: }
945: if ($token[Token::POS_RSPACECTL]) {
946: $token[Token::POS_RSPACE] = '';
947: }
948: }
949: }
950: