1: <?php
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24: namespace Thelia\Tpex\Tokenizer;
25:
26: use Thelia\Tpex\TpexToken;
27: use Thelia\Tpex\Element\ElementCollection;
28: use Thelia\Tpex\Element\SimpleLoopElement;
29: use Thelia\Tpex\Element\TextElement;
30: use Thelia\Tpex\Element\RepeatLoopElement;
31: use Thelia\Tpex\Element\ConditionalLoopElement;
32: use Thelia\Tpex\Element\TestLoopElement;
33: use Thelia\Tpex\Element\ConditionalVarLoopElement;
34: use Thelia\Tpex\Element\VariablesTemplate;
35: use Thelia\Tpex\Exception\TokenNotFoundException;
36: use Thelia\Tpex\Exception\TokenSyntaxException;
37:
38: use Symfony\Component\HttpFoundation\Request;
39: use Symfony\Component\EventDispatcher\EventDispatcherInterface;
40:
41: class Tokenize implements TokenizeInterface
42: {
43: protected ;
44:
45: 46: 47:
48: protected $request;
49:
50: 51: 52:
53: protected $dispatcher;
54:
55: 56: 57: 58: 59: 60:
61: protected $tags = array(
62: "THELIA_" => TpexToken::OSL,
63: "/THELIA_" => TpexToken::CSL,
64: "TEST_" => TpexToken::OTL,
65: "/TEST_" => TpexToken::ETL,
66: "//TEST_" => TpexToken::CTL,
67: "T_" => TpexToken::OCL,
68: "/T_" => TpexToken::ECL,
69: "//T_" => TpexToken::CCL,
70: "REM" => TpexToken::OCM,
71: "/REM" => TpexToken::CCM,
72: "REPEAT_" => TpexToken::ORL,
73: "/REPEAT_" => TpexToken::CRL,
74: "T:" => TpexToken::OCVL,
75: "/T:" => TpexToken::ECVL,
76: "//T:" => TpexToken::CCVL
77: );
78:
79: protected $tokens = array();
80:
81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96:
97: protected $loop = array();
98:
99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114:
115: protected $filter = array();
116:
117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131:
132: protected $testLoop = array();
133:
134: protected $loopIdStack = array();
135:
136: protected $currentLoopStack = array();
137:
138: public function __construct(Request $request, EventDispatcherInterface $dispatcher, $content = null)
139: {
140: if (is_null($content) === false) {
141: $this->setContent($content);
142: }
143:
144: $this->dispatcher = $dispatcher;
145: $this->request = $request;
146: }
147:
148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165:
166: public function setLoop(array $loops)
167: {
168: foreach ($loops as $name => $className) {
169: if (array_key_exists($name, $this->loop)) {
170: throw new \InvalidArgumentException(sprintf("%s loop name already exists for %s class name", $name, $className));
171: }
172:
173: $this->loop[$name] = $className;
174: }
175: }
176:
177: 178: 179:
180: public function getLoop()
181: {
182: return $this->loop;
183: }
184:
185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201:
202: public function setFilter(array $filters)
203: {
204: foreach ($filters as $name => $className) {
205: if (array_key_exists($name, $this->filter)) {
206: throw new \InvalidArgumentException(sprintf("%s filter name already exists for %s class name", $name, $className));
207: }
208:
209: $this->filter[$name] = $className;
210: }
211: }
212:
213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228:
229: public function setTestLoop(array $testLoops)
230: {
231: foreach ($testLoops as $name => $className) {
232: if (array_key_exists($name, $this->filter)) {
233: throw new \InvalidArgumentException(sprintf("%s test loop name already exists for %s class name",
234: $name, $className));
235: }
236:
237: $this->testLoop[$name] = $className;
238: }
239: }
240:
241: public function getTestLoop()
242: {
243: return $this->testLoop;
244: }
245:
246: public function getFilter()
247: {
248: return $this->filter;
249: }
250:
251: public function setContent($content)
252: {
253: $this->tokens = preg_split("/( |\t|\n|\r|<|>|\\#|\")/", $content, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
254:
255:
256: array_unshift($this->tokens, '');
257: }
258:
259: 260: 261:
262: public function tokenize()
263: {
264: VariablesTemplate::init($this->getFilter(), $this->request);
265: $compileContent = $this->parseContent();
266:
267: return $compileContent;
268: }
269:
270: 271: 272:
273: protected function parseContent()
274: {
275: $collection = new ElementCollection();
276:
277: while (true) {
278: $token = next($this->tokens);
279:
280: if ($token === false) {
281:
282: break;
283: }
284:
285: $res = $this->processToken($token);
286:
287: if (empty($res))
288: continue;
289: else if ($res == 'stop')
290: break;
291: else
292: $collection->addElement($res);
293: }
294:
295: return $collection;
296: }
297:
298: protected function parseArgs()
299: {
300: $args = "";
301:
302: $in_quote = false;
303:
304: while (1) {
305: $tok = next($this->tokens);
306:
307: if ($tok == '#') {
308: $token = next($this->tokens);
309:
310: if (preg_match('/([A-Z0-9_]+)/', $token, $varname) > 0) {
311: $tok = '#'.$varname[1];
312:
313: }
314:
315: $this->addVar($tok);
316:
317:
318: $tok = '#'.$token;
319: } elseif ($tok == '"') {
320: $in_quote = ! $in_quote;
321: } elseif ($tok == '>') {
322: if (! $in_quote) break;
323: } elseif ($tok === FALSE) {
324: break;
325: }
326:
327: $args .= $tok;
328: }
329:
330: return $args;
331: }
332:
333: protected function addVar($token)
334: {
335:
336: if (strstr($token, 'GET{') !== FALSE || strstr($token, 'SET{') !== FALSE) return;
337:
338: $count = count($this->currentLoopStack);
339:
340:
341: if($count == 0) return;
342:
343: $currentLoop = $this->currentLoopStack[$count-1];
344:
345:
346: if($currentLoop->type() != TpexToken::TPEX_SIMPLE_LOOP) return;
347:
348: if (preg_match('/([A-Z0-9_]+)/', $token, $varname) > 0) {
349: $currentLoop->addVar($varname[1]);
350: }
351: }
352:
353: protected function processToken($atoken)
354: {
355: $token_type = TpexToken::TXT;
356: $token = '';
357:
358: if ($atoken == '<') {
359: $no_match = false;
360:
361: $token = next($this->tokens);
362:
363: if ($token[0] != 'T' && strpos($token, '/T') !== 0 && strpos($token, '//T') !== 0 && $token[0] != 'R' && strpos($token, '/R') !== 0) {
364: $no_match = true;
365: } else {
366: try {
367: $token_type = $this->findTokenType($token);
368: } catch (TokenNotFoundException $e) {
369: $no_match = true;
370: }
371: }
372:
373: if ($no_match) {
374: prev($this->tokens);
375:
376: $token = $atoken;
377: }
378: } elseif ($atoken == "#") {
379:
380: $tmp = next($this->tokens);
381:
382: if ($tmp == '#') {
383: $token = '#';
384:
385: prev($this->tokens);
386: } else {
387: $token = '#' . $tmp;
388:
389: $this->addVar($token);
390: }
391: } else {
392: $token = $atoken;
393: }
394:
395: if ($this->in_comment && $token_type !== TpexToken::CCM) {
396: return;
397: }
398:
399: if ($token_type === TpexToken::OSL) {
400: return $this->initSimpleLoop($token);
401:
402: } elseif ($token_type === TpexToken::CSL || $token_type === TpexToken::CRL) {
403: $this->controlEndLoop($token);
404:
405: array_pop($this->currentLoopStack);
406:
407: return 'stop';
408:
409: } elseif ($token_type === TpexToken::ORL) {
410: return $this->initRepeatLoop($token);
411:
412: } elseif ($token_type == TpexToken::OCL) {
413: return $this->initConditionalLoop($token);
414:
415: } elseif ($token_type === TpexToken::OTL) {
416: return $this->initTestLoop($token);
417:
418: } elseif ($token_type === TpexToken::OCM) {
419:
420: $this->in_comment = true;
421:
422: return;
423:
424: } elseif ($token_type === TpexToken::OCVL) {
425: return $this->initConditionalVarLoop($token);
426: }else if (
427: $token_type === TpexToken::ECL ||
428: $token_type === TpexToken::CCL ||
429: $token_type === TpexToken::ETL ||
430: $token_type === TpexToken::CTL ||
431: $token_type === TpexToken::ECVL ||
432: $token_type === TpexToken::CCVL
433: ) {
434:
435: $this->controlEndLoop($token);
436:
437: return "stop";
438:
439: } elseif ($token_type === TpexToken::CCM) {
440: $this->in_comment = false;
441: $this->skipTo(">");
442:
443: return ;
444: } elseif ($token !== '') {
445: return new TextElement($token);
446: } else {
447: return;
448: }
449: }
450:
451: 452: 453: 454: 455: 456: 457: 458:
459: protected function controlEndLoop($idLoop)
460: {
461: $idStackLoop = array_pop($this->loopIdStack);
462:
463: if ($idLoop != "/".$idStackLoop) {
464: if ($idStackLoop == '') {
465: throw new TokenSyntaxException(sprintf("no end tag found for %s loop"), $idLoop);
466: } else {
467: throw new TokenSyntaxException(sprintf("%s loop found, /%s expected", $idLoop, $idStackLoop));
468: }
469: }
470: }
471:
472: 473: 474: 475: 476: 477: 478: 479: 480: 481: 482: 483:
484: protected function initSimpleLoop($token)
485: {
486:
487: $loop = new SimpleLoopElement(substr($token, 7), $this->loop, $this->request, $this->dispatcher);
488:
489: array_push($this->loopIdStack, $token);
490:
491: $loop->setArgs($this->parseArgs());
492:
493: array_push($this->currentLoopStack, $loop);
494:
495: $loop->addContent($this->parseContent());
496:
497: $this->skipTo(">");
498:
499: return $loop;
500: }
501:
502: protected function initRepeatLoop($token)
503: {
504: $loop = new RepeatLoopElement(substr($token, 7));
505:
506: array_push($this->loopIdStack, $token);
507:
508: $loop->setArgs($this->parseArgs());
509:
510: array_push($this->currentLoopStack, $loop);
511:
512: $loop->addContent($this->parseContent());
513:
514: $this->skipTo(">");
515:
516: return $loop;
517: }
518:
519: protected function initConditionalLoop($token)
520: {
521: $loop = new ConditionalLoopElement(substr($token, 2));
522: $this->skipTo(">");
523:
524: array_push($this->loopIdStack, $token);
525:
526: $loop->addContent($this->parseContent());
527: $this->skipTo(">");
528:
529: array_push($this->loopIdStack, "/".$token);
530:
531: $loop->addContent($this->parseContent());
532: $this->skipTo(">");
533:
534: return $loop;
535: }
536:
537: protected function initConditionalVarLoop($token)
538: {
539: $var = substr($token, 2);
540:
541: $this->addVar($var);
542:
543: $loop = new ConditionalVarLoopElement($var);
544: $this->skipTo(">");
545:
546: array_push($this->loopIdStack, $token);
547:
548: $loop->addContent($this->parseContent());
549: $this->skipTo(">");
550:
551: array_push($this->loopIdStack, "/".$token);
552:
553: $loop->addContent($this->parseContent());
554: $this->skipTo(">");
555:
556: return $loop;
557: }
558:
559: protected function initTestLoop($token)
560: {
561: $loop = new TestLoopElement(substr($token, 5), $this->testLoop, $this->request, $this->dispatcher);
562: $loop->setArgs($this->parseArgs());
563:
564:
565: array_push($this->loopIdStack, $token);
566:
567: $loop->addContent($this->parseContent());
568: $this->skipTo(">");
569:
570: array_push($this->loopIdStack, "/".$token);
571:
572: $loop->addContent($this->parseContent());
573: $this->skipTo(">");
574:
575: return $loop;
576: }
577:
578: 579: 580: 581: 582: 583:
584: protected function skipTo($val)
585: {
586: $ret = false;
587:
588: while ( ( ($tok = next($this->tokens)) !== false) && $tok != $val ) {
589: $ret = $tok;
590: }
591: }
592:
593:
594:
595: 596: 597: 598: 599: 600: 601: 602: 603: 604:
605: protected function findTokenType($token)
606: {
607: $token_type = false;
608:
609: if (strpos($token, 'THELIA_') === 0) {
610: $token_type = $this->tags['THELIA_'];
611: } elseif (strpos($token, '/THELIA_') === 0) {
612: $token_type = $this->tags['/THELIA_'];
613: } elseif (strpos($token, 'TEST_') === 0) {
614: $token_type = $this->tags['TEST_'];
615: } elseif (strpos($token, '/TEST_') === 0) {
616: $token_type = $this->tags['/TEST_'];
617: } elseif (strpos($token, '//TEST_') === 0) {
618: $token_type = $this->tags['//TEST_'];
619: } elseif (strpos($token, 'T_') === 0) {
620: $token_type = $this->tags['T_'];
621: } elseif (strpos($token, '/T_') === 0) {
622: $token_type = $this->tags['/T_'];
623: } elseif (strpos($token, '//T_') === 0) {
624: $token_type = $this->tags['//T_'];
625: } elseif (strpos($token, 'REM') === 0) {
626: $token_type = $this->tags['REM'];
627: } elseif (strpos($token, '/REM') === 0) {
628: $token_type = $this->tags['/REM'];
629: } elseif (strpos($token, 'REPEAT_') === 0) {
630: $token_type = $this->tags['REPEAT_'];
631: } elseif (strpos($token, '/REPEAT_') === 0) {
632: $token_type = $this->tags['/REPEAT_'];
633: } elseif (strpos($token, 'T:') === 0) {
634: $token_type = $this->tags['T:'];
635: } elseif (strpos($token, '/T:') === 0) {
636: $token_type = $this->tags['/T:'];
637: } elseif (strpos($token, '//T:') === 0) {
638: $token_type = $this->tags['//T:'];
639: } else {
640: throw new TokenNotFoundException("Token not found in Tpex tags");
641: }
642:
643: return $token_type;
644:
645: }
646:
647: }
648: