Overview

Namespaces

  • Thelia
    • Action
    • Config
    • Controller
    • Core
      • Bundle
      • DependencyInjection
        • Compiler
        • Loader
      • Event
      • EventListener
      • Template
        • BaseParam
    • Exception
    • Log
      • Destination
    • Model
      • map
      • om
    • Routing
      • Matcher
    • Tools
    • Tpex
      • BaseParam
      • Element
        • Loop
        • TestLoop
      • Event
      • Exception
      • Filter
      • Tokenizer

Classes

  • Tokenize

Interfaces

  • TokenizeInterface
  • Overview
  • Namespace
  • Class
  • Tree
  1: <?php
  2: /*************************************************************************************/
  3: /*                                                                                   */
  4: /*      Thelia                                                                       */
  5: /*                                                                                   */
  6: /*      Copyright (c) OpenStudio                                                     */
  7: /*  email : info@thelia.net                                                      */
  8: /*      web : http://www.thelia.net                                                  */
  9: /*                                                                                   */
 10: /*      This program is free software; you can redistribute it and/or modify         */
 11: /*      it under the terms of the GNU General Public License as published by         */
 12: /*      the Free Software Foundation; either version 3 of the License                */
 13: /*                                                                                   */
 14: /*      This program is distributed in the hope that it will be useful,              */
 15: /*      but WITHOUT ANY WARRANTY; without even the implied warranty of               */
 16: /*      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                */
 17: /*      GNU General Public License for more details.                                 */
 18: /*                                                                                   */
 19: /*      You should have received a copy of the GNU General Public License            */
 20: /*      along with this program. If not, see <http://www.gnu.org/licenses/>.     */
 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 $in_comment;
 44: 
 45:     /**
 46:      * @var \Symfony\Component\HttpFoundation\Request
 47:      */
 48:     protected $request;
 49: 
 50:     /**
 51:      * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
 52:      */
 53:     protected $dispatcher;
 54: 
 55:     /**
 56:      *
 57:      * @todo add filter here
 58:      *
 59:      * @var array $tags
 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:      * associative array containing information for loop execution
 83:      *
 84:      * key is loop name
 85:      * value is the class implementing/extending base loop classes
 86:      *
 87:      * ex :
 88:      *
 89:      * $loop = array(
 90:      *  "product" => "Thelia\Loop\Product",
 91:      *  "category" => "Thelia\Loop\Category",
 92:      *  "myLoop" => "My\Own\Loop"
 93:      * );
 94:      *
 95:      * @var Array
 96:      */
 97:     protected $loop = array();
 98: 
 99:     /**
100:      * associative array containing information for filter execution
101:      *
102:      * key is filter name
103:      * value is the class implementing/extending base filter classes
104:      *
105:      * ex :
106:      *
107:      * $filter = array(
108:      *  "upperCase" => "Thelia\Filter\UpperCase",
109:      *  "lowerCase" => "Thelia\Filter\LowerCase",
110:      *  "myFilter" => "My\Own\Filter"
111:      * );
112:      *
113:      * @var Array
114:      */
115:     protected $filter = array();
116: 
117:     /**
118:      * associative array containing information for loop test execution
119:      *
120:      * key is test loop name
121:      * value is the class implementing/extending base test loop classes
122:      *
123:      * ex :
124:      *
125:      * $testLoop = array(
126:      *  "equal" => "Thelia\testLoop\Equal",
127:      *  "myTestLoop" => "My\Own\TestLoop"
128:      * );
129:      *
130:      * @var Array
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:      * associative array containing information for loop execution
151:      *
152:      * key is loop name
153:      * value is the class implementing/extending base loop classes
154:      *
155:      * ex :
156:      *
157:      * $loop = array(
158:      *  "product" => "Thelia\Loop\Product",
159:      *  "category" => "Thelia\Loop\Category",
160:      *  "myLoop" => "My\Own\Loop"
161:      * );
162:      *
163:      * @param  array                     $loops
164:      * @throws \InvalidArgumentException if loop name already exists
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:      * @return array containing all loop. array's key are name loop and array value is the class of this loop
179:      */
180:     public function getLoop()
181:     {
182:         return $this->loop;
183:     }
184: 
185:     /**
186:      * associative array containing information for filter execution
187:      *
188:      * key is filter name
189:      * value is the class implementing/extending base loop classes
190:      *
191:      * ex :
192:      *
193:      * $filter = array(
194:      *  "upperCase" => "Thelia\Filter\UpperCase",
195:      *  "lowerCase" => "Thelia\Filter\LowerCase",
196:      *  "myFilter" => "My\Own\Filter"
197:      * );
198:      *
199:      * @param  array                     $filters
200:      * @throws \InvalidArgumentException if filter name already exists
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:      * associative array containing information for loop test execution
215:      *
216:      * key is test loop name
217:      * value is the class implementing/extending base test loop classes
218:      *
219:      * ex :
220:      *
221:      * $testLoop = array(
222:      *  "equal" => "Thelia\testLoop\Equal",
223:      *  "myTestLoop" => "My\Own\TestLoop"
224:      * );
225:      *
226:      * @param  array                     $testLoops
227:      * @throws \InvalidArgumentException
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:         // push blank at array start for using next in parseContent method
256:         array_unshift($this->tokens, '');
257:     }
258: 
259:     /**
260:      * @return \Thelia\Tpex\Element|ElementCollection
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:      * @return \Thelia\Tpex\Element\ElementCollection
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 == '#') { //we are in a value case
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:                 // Il faut placer dans les args la valeur originale (sinon bug. Ex: #PROMO[X][Y])
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:         // Ne pas prendre en compte les filtres et les get/set
336:         if (strstr($token, 'GET{') !== FALSE || strstr($token, 'SET{') !== FALSE) return;
337: 
338:         $count = count($this->currentLoopStack);
339: 
340:         //there is already no loop open
341:         if($count == 0) return;
342: 
343:         $currentLoop = $this->currentLoopStack[$count-1];
344: 
345:         //current loop is not a simple loop so we don't have to save variable
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:             //similar case as ##REF
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:      * When end tag is found like /THELIA, Tpex control that this end tag
454:      * is good and ended good loop.
455:      *
456:      * @param  string                                      $idLoop
457:      * @throws \Thelia\Tpex\Exception\TokenSyntaxException
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:      * Initialize a simpleLoopElement with arguments and content.
475:      *
476:      * name is the seconde part of THELIA_xxx tag (ex : for THELIA_foo foo is the loop name).
477:      *
478:      * $token and \Thelia\Tpex\Element\SimpleLoopElement are push into self::loopIdStack and self::currentLoopStack
479:      * respectively
480:      *
481:      * @param $token
482:      * @return \Thelia\Tpex\Element\SimpleLoopElement
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:      * skip to the next element containing in $val
581:      *
582:      * @param $val search value
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:      * This function search if tag is knowing by Tpex and return is int code.
598:      *
599:      * An exception is thrown if token is unknown by Tpex
600:      *
601:      * @param $token the token to find
602:      * @return int
603:      * @throws \Thelia\Tpex\Exception\TokenNotFoundException
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: 
thelia API documentation generated by ApiGen 2.8.0