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