aboutsummaryrefslogtreecommitdiffhomepage
path: root/inc/Twig/Extensions
diff options
context:
space:
mode:
Diffstat (limited to 'inc/Twig/Extensions')
-rw-r--r--inc/Twig/Extensions/Autoloader.php45
-rw-r--r--inc/Twig/Extensions/Extension/Debug.php34
-rw-r--r--inc/Twig/Extensions/Extension/I18n.php44
-rw-r--r--inc/Twig/Extensions/Extension/Intl.php66
-rw-r--r--inc/Twig/Extensions/Extension/Text.php109
-rw-r--r--inc/Twig/Extensions/Gettext/Extractor.php95
-rw-r--r--inc/Twig/Extensions/Gettext/Loader/Filesystem.php58
-rw-r--r--inc/Twig/Extensions/Gettext/Routing/Generator/UrlGenerator.php39
-rw-r--r--inc/Twig/Extensions/Gettext/Test/ExtractorTest.php123
-rw-r--r--inc/Twig/Extensions/Gettext/Test/Fixtures/twig/empty.twig1
-rw-r--r--inc/Twig/Extensions/Gettext/Test/Fixtures/twig/plural.twig5
-rw-r--r--inc/Twig/Extensions/Gettext/Test/Fixtures/twig/singular.twig9
-rw-r--r--inc/Twig/Extensions/Grammar.php30
-rw-r--r--inc/Twig/Extensions/Grammar/Arguments.php22
-rw-r--r--inc/Twig/Extensions/Grammar/Array.php22
-rw-r--r--inc/Twig/Extensions/Grammar/Body.php39
-rw-r--r--inc/Twig/Extensions/Grammar/Boolean.php24
-rw-r--r--inc/Twig/Extensions/Grammar/Constant.php37
-rw-r--r--inc/Twig/Extensions/Grammar/Expression.php22
-rw-r--r--inc/Twig/Extensions/Grammar/Hash.php22
-rw-r--r--inc/Twig/Extensions/Grammar/Number.php24
-rw-r--r--inc/Twig/Extensions/Grammar/Optional.php69
-rw-r--r--inc/Twig/Extensions/Grammar/Switch.php24
-rw-r--r--inc/Twig/Extensions/Grammar/Tag.php56
-rw-r--r--inc/Twig/Extensions/GrammarInterface.php18
-rw-r--r--inc/Twig/Extensions/Node/Debug.php69
-rw-r--r--inc/Twig/Extensions/Node/Trans.php133
-rw-r--r--inc/Twig/Extensions/SimpleTokenParser.php132
-rw-r--r--inc/Twig/Extensions/TokenParser/Debug.php42
-rw-r--r--inc/Twig/Extensions/TokenParser/Trans.php80
30 files changed, 1493 insertions, 0 deletions
diff --git a/inc/Twig/Extensions/Autoloader.php b/inc/Twig/Extensions/Autoloader.php
new file mode 100644
index 00000000..f23cced6
--- /dev/null
+++ b/inc/Twig/Extensions/Autoloader.php
@@ -0,0 +1,45 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Autoloads Twig Extensions classes.
14 *
15 * @package twig
16 * @author Fabien Potencier <fabien.potencier@symfony-project.com>
17 */
18class Twig_Extensions_Autoloader
19{
20 /**
21 * Registers Twig_Extensions_Autoloader as an SPL autoloader.
22 */
23 static public function register()
24 {
25 spl_autoload_register(array(new self, 'autoload'));
26 }
27
28 /**
29 * Handles autoloading of classes.
30 *
31 * @param string $class A class name.
32 *
33 * @return boolean Returns true if the class has been loaded
34 */
35 static public function autoload($class)
36 {
37 if (0 !== strpos($class, 'Twig_Extensions')) {
38 return;
39 }
40
41 if (file_exists($file = dirname(__FILE__).'/../../'.str_replace('_', '/', $class).'.php')) {
42 require $file;
43 }
44 }
45}
diff --git a/inc/Twig/Extensions/Extension/Debug.php b/inc/Twig/Extensions/Extension/Debug.php
new file mode 100644
index 00000000..8974ce20
--- /dev/null
+++ b/inc/Twig/Extensions/Extension/Debug.php
@@ -0,0 +1,34 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extensions_Extension_Debug extends Twig_Extension
12{
13 /**
14 * Returns the token parser instance to add to the existing list.
15 *
16 * @return array An array of Twig_TokenParser instances
17 */
18 public function getTokenParsers()
19 {
20 return array(
21 new Twig_Extensions_TokenParser_Debug(),
22 );
23 }
24
25 /**
26 * Returns the name of the extension.
27 *
28 * @return string The extension name
29 */
30 public function getName()
31 {
32 return 'debug';
33 }
34}
diff --git a/inc/Twig/Extensions/Extension/I18n.php b/inc/Twig/Extensions/Extension/I18n.php
new file mode 100644
index 00000000..3702aa22
--- /dev/null
+++ b/inc/Twig/Extensions/Extension/I18n.php
@@ -0,0 +1,44 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extensions_Extension_I18n extends Twig_Extension
12{
13 /**
14 * Returns the token parser instances to add to the existing list.
15 *
16 * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
17 */
18 public function getTokenParsers()
19 {
20 return array(new Twig_Extensions_TokenParser_Trans());
21 }
22
23 /**
24 * Returns a list of filters to add to the existing list.
25 *
26 * @return array An array of filters
27 */
28 public function getFilters()
29 {
30 return array(
31 'trans' => new Twig_Filter_Function('gettext'),
32 );
33 }
34
35 /**
36 * Returns the name of the extension.
37 *
38 * @return string The extension name
39 */
40 public function getName()
41 {
42 return 'i18n';
43 }
44}
diff --git a/inc/Twig/Extensions/Extension/Intl.php b/inc/Twig/Extensions/Extension/Intl.php
new file mode 100644
index 00000000..40f7fc20
--- /dev/null
+++ b/inc/Twig/Extensions/Extension/Intl.php
@@ -0,0 +1,66 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Extensions_Extension_Intl extends Twig_Extension
13{
14 public function __construct()
15 {
16 if (!class_exists('IntlDateFormatter')) {
17 throw new RuntimeException('The intl extension is needed to use intl-based filters.');
18 }
19 }
20
21 /**
22 * Returns a list of filters to add to the existing list.
23 *
24 * @return array An array of filters
25 */
26 public function getFilters()
27 {
28 return array(
29 'localizeddate' => new Twig_Filter_Function('twig_localized_date_filter', array('needs_environment' => true)),
30 );
31 }
32
33 /**
34 * Returns the name of the extension.
35 *
36 * @return string The extension name
37 */
38 public function getName()
39 {
40 return 'intl';
41 }
42}
43
44function twig_localized_date_filter(Twig_Environment $env, $date, $dateFormat = 'medium', $timeFormat = 'medium', $locale = null, $timezone = null, $format = null)
45{
46 $date = twig_date_converter($env, $date, $timezone);
47
48 $formatValues = array(
49 'none' => IntlDateFormatter::NONE,
50 'short' => IntlDateFormatter::SHORT,
51 'medium' => IntlDateFormatter::MEDIUM,
52 'long' => IntlDateFormatter::LONG,
53 'full' => IntlDateFormatter::FULL,
54 );
55
56 $formatter = IntlDateFormatter::create(
57 $locale !== null ? $locale : Locale::getDefault(),
58 $formatValues[$dateFormat],
59 $formatValues[$timeFormat],
60 $date->getTimezone()->getName(),
61 IntlDateFormatter::GREGORIAN,
62 $format
63 );
64
65 return $formatter->format($date->getTimestamp());
66}
diff --git a/inc/Twig/Extensions/Extension/Text.php b/inc/Twig/Extensions/Extension/Text.php
new file mode 100644
index 00000000..0a3dc35e
--- /dev/null
+++ b/inc/Twig/Extensions/Extension/Text.php
@@ -0,0 +1,109 @@
1<?php
2
3/**
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 *
11 * @author Henrik Bjornskov <hb@peytz.dk>
12 * @package Twig
13 * @subpackage Twig-extensions
14 */
15class Twig_Extensions_Extension_Text extends Twig_Extension
16{
17 /**
18 * Returns a list of filters.
19 *
20 * @return array
21 */
22 public function getFilters()
23 {
24 $filters = array(
25 'truncate' => new Twig_Filter_Function('twig_truncate_filter', array('needs_environment' => true)),
26 'wordwrap' => new Twig_Filter_Function('twig_wordwrap_filter', array('needs_environment' => true)),
27 );
28
29 if (version_compare(Twig_Environment::VERSION, '1.5.0-DEV', '<')) {
30 $filters['nl2br'] = new Twig_Filter_Function('twig_nl2br_filter', array('pre_escape' => 'html', 'is_safe' => array('html')));
31 }
32
33 return $filters;
34 }
35
36 /**
37 * Name of this extension
38 *
39 * @return string
40 */
41 public function getName()
42 {
43 return 'Text';
44 }
45}
46
47function twig_nl2br_filter($value, $sep = '<br />')
48{
49 return str_replace("\n", $sep."\n", $value);
50}
51
52if (function_exists('mb_get_info')) {
53 function twig_truncate_filter(Twig_Environment $env, $value, $length = 30, $preserve = false, $separator = '...')
54 {
55 if (mb_strlen($value, $env->getCharset()) > $length) {
56 if ($preserve) {
57 if (false !== ($breakpoint = mb_strpos($value, ' ', $length, $env->getCharset()))) {
58 $length = $breakpoint;
59 }
60 }
61
62 return rtrim(mb_substr($value, 0, $length, $env->getCharset())) . $separator;
63 }
64
65 return $value;
66 }
67
68 function twig_wordwrap_filter(Twig_Environment $env, $value, $length = 80, $separator = "\n", $preserve = false)
69 {
70 $sentences = array();
71
72 $previous = mb_regex_encoding();
73 mb_regex_encoding($env->getCharset());
74
75 $pieces = mb_split($separator, $value);
76 mb_regex_encoding($previous);
77
78 foreach ($pieces as $piece) {
79 while(!$preserve && mb_strlen($piece, $env->getCharset()) > $length) {
80 $sentences[] = mb_substr($piece, 0, $length, $env->getCharset());
81 $piece = mb_substr($piece, $length, 2048, $env->getCharset());
82 }
83
84 $sentences[] = $piece;
85 }
86
87 return implode($separator, $sentences);
88 }
89} else {
90 function twig_truncate_filter(Twig_Environment $env, $value, $length = 30, $preserve = false, $separator = '...')
91 {
92 if (strlen($value) > $length) {
93 if ($preserve) {
94 if (false !== ($breakpoint = strpos($value, ' ', $length))) {
95 $length = $breakpoint;
96 }
97 }
98
99 return rtrim(substr($value, 0, $length)) . $separator;
100 }
101
102 return $value;
103 }
104
105 function twig_wordwrap_filter(Twig_Environment $env, $value, $length = 80, $separator = "\n", $preserve = false)
106 {
107 return wordwrap($value, $length, $separator, !$preserve);
108 }
109} \ No newline at end of file
diff --git a/inc/Twig/Extensions/Gettext/Extractor.php b/inc/Twig/Extensions/Gettext/Extractor.php
new file mode 100644
index 00000000..e7fa1af2
--- /dev/null
+++ b/inc/Twig/Extensions/Gettext/Extractor.php
@@ -0,0 +1,95 @@
1<?php
2
3/**
4 * This file is part of the Twig Gettext utility.
5 *
6 * (c) Саша Стаменковић <umpirsky@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Twig\Gettext;
13
14use Symfony\Component\Filesystem\Filesystem;
15
16/**
17 * Extracts translations from twig templates.
18 *
19 * @author Саша Стаменковић <umpirsky@gmail.com>
20 */
21class Extractor
22{
23 /**
24 * @var \Twig_Environment
25 */
26 protected $environment;
27
28 /**
29 * Template cached file names.
30 *
31 * @var string[]
32 */
33 protected $templates;
34
35 /**
36 * Gettext parameters.
37 *
38 * @var string[]
39 */
40 protected $parameters;
41
42 public function __construct(\Twig_Environment $environment)
43 {
44 $this->environment = $environment;
45 $this->reset();
46 }
47
48 protected function reset()
49 {
50 $this->templates = array();
51 $this->parameters = array();
52 }
53
54 public function addTemplate($path)
55 {
56 $this->environment->loadTemplate($path);
57 $this->templates[] = $this->environment->getCacheFilename($path);
58 }
59
60 public function addGettextParameter($parameter)
61 {
62 $this->parameters[] = $parameter;
63 }
64
65 public function setGettextParameters(array $parameters)
66 {
67 $this->parameters = $parameters;
68 }
69
70 public function extract()
71 {
72 $command = 'xgettext';
73 $command .= ' '.join(' ', $this->parameters);
74 $command .= ' '.join(' ', $this->templates);
75
76 $error = 0;
77 $output = system($command, $error);
78 if (0 !== $error) {
79 throw new \RuntimeException(sprintf(
80 'Gettext command "%s" failed with error code %s and output: %s',
81 $command,
82 $error,
83 $output
84 ));
85 }
86
87 $this->reset();
88 }
89
90 public function __destruct()
91 {
92 $filesystem = new Filesystem();
93 $filesystem->remove($this->environment->getCache());
94 }
95}
diff --git a/inc/Twig/Extensions/Gettext/Loader/Filesystem.php b/inc/Twig/Extensions/Gettext/Loader/Filesystem.php
new file mode 100644
index 00000000..b011b032
--- /dev/null
+++ b/inc/Twig/Extensions/Gettext/Loader/Filesystem.php
@@ -0,0 +1,58 @@
1<?php
2
3/**
4 * This file is part of the Twig Gettext utility.
5 *
6 * (c) Саша Стаменковић <umpirsky@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Twig\Gettext\Loader;
13
14/**
15 * Loads template from the filesystem.
16 *
17 * @author Саша Стаменковић <umpirsky@gmail.com>
18 */
19class Filesystem extends \Twig_Loader_Filesystem
20{
21 /**
22 * Hacked find template to allow loading templates by absolute path.
23 *
24 * @param string $name template name or absolute path
25 */
26 protected function findTemplate($name)
27 {
28 // normalize name
29 $name = preg_replace('#/{2,}#', '/', strtr($name, '\\', '/'));
30
31 if (isset($this->cache[$name])) {
32 return $this->cache[$name];
33 }
34
35 $this->validateName($name);
36
37 $namespace = '__main__';
38 if (isset($name[0]) && '@' == $name[0]) {
39 if (false === $pos = strpos($name, '/')) {
40 throw new \InvalidArgumentException(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name));
41 }
42
43 $namespace = substr($name, 1, $pos - 1);
44
45 $name = substr($name, $pos + 1);
46 }
47
48 if (!isset($this->paths[$namespace])) {
49 throw new \Twig_Error_Loader(sprintf('There are no registered paths for namespace "%s".', $namespace));
50 }
51
52 if (is_file($name)) {
53 return $this->cache[$name] = $name;
54 }
55
56 return __DIR__.'/../Test/Fixtures/twig/empty.twig';
57 }
58}
diff --git a/inc/Twig/Extensions/Gettext/Routing/Generator/UrlGenerator.php b/inc/Twig/Extensions/Gettext/Routing/Generator/UrlGenerator.php
new file mode 100644
index 00000000..9e3431bd
--- /dev/null
+++ b/inc/Twig/Extensions/Gettext/Routing/Generator/UrlGenerator.php
@@ -0,0 +1,39 @@
1<?php
2
3/**
4 * This file is part of the Twig Gettext utility.
5 *
6 * (c) Саша Стаменковић <umpirsky@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Twig\Gettext\Routing\Generator;
13
14use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
15use Symfony\Component\Routing\RequestContext;
16
17/**
18 * Dummy url generator.
19 *
20 * @author Саша Стаменковић <umpirsky@gmail.com>
21 */
22class UrlGenerator implements UrlGeneratorInterface
23{
24 protected $context;
25
26 public function generate($name, $parameters = array(), $absolute = false)
27 {
28 }
29
30 public function getContext()
31 {
32 return $this->context;
33 }
34
35 public function setContext(RequestContext $context)
36 {
37 $this->context = $context;
38 }
39}
diff --git a/inc/Twig/Extensions/Gettext/Test/ExtractorTest.php b/inc/Twig/Extensions/Gettext/Test/ExtractorTest.php
new file mode 100644
index 00000000..d467835f
--- /dev/null
+++ b/inc/Twig/Extensions/Gettext/Test/ExtractorTest.php
@@ -0,0 +1,123 @@
1<?php
2
3/**
4 * This file is part of the Twig Gettext utility.
5 *
6 * (c) Саша Стаменковић <umpirsky@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Twig\Gettext\Test;
13
14use Twig\Gettext\Extractor;
15use Twig\Gettext\Loader\Filesystem;
16use Symfony\Component\Translation\Loader\PoFileLoader;
17
18/**
19 * @author Саша Стаменковић <umpirsky@gmail.com>
20 */
21class ExtractorTest extends \PHPUnit_Framework_TestCase
22{
23 /**
24 * @var \Twig_Environment
25 */
26 protected $twig;
27
28 /**
29 * @var PoFileLoader
30 */
31 protected $loader;
32
33 protected function setUp()
34 {
35 $this->twig = new \Twig_Environment(new Filesystem('/'), array(
36 'cache' => '/tmp/cache/'.uniqid(),
37 'auto_reload' => true
38 ));
39 $this->twig->addExtension(new \Twig_Extensions_Extension_I18n());
40
41 $this->loader = new PoFileLoader();
42 }
43
44 /**
45 * @dataProvider testExtractDataProvider
46 */
47 public function testExtract(array $templates, array $parameters, array $messages)
48 {
49 $extractor = new Extractor($this->twig);
50
51 foreach ($templates as $template) {
52 $extractor->addTemplate($template);
53 }
54 foreach ($parameters as $parameter) {
55 $extractor->addGettextParameter($parameter);
56 }
57
58 $extractor->extract();
59
60 $catalog = $this->loader->load($this->getPotFile(), null);
61
62 foreach ($messages as $message) {
63 $this->assertTrue(
64 $catalog->has($message),
65 sprintf('Message "%s" not found in catalog.', $message)
66 );
67 }
68 }
69
70 public function testExtractDataProvider()
71 {
72 return array(
73 array(
74 array(
75 __DIR__.'/Fixtures/twig/singular.twig',
76 __DIR__.'/Fixtures/twig/plural.twig',
77 ),
78 $this->getGettextParameters(),
79 array(
80 'Hello %name%!',
81 'Hello World!',
82 'Hey %name%, I have one apple.',
83 'Hey %name%, I have %count% apples.',
84 ),
85 ),
86 );
87 }
88
89 public function testExtractNoTranslations()
90 {
91 $extractor = new Extractor($this->twig);
92
93 $extractor->addTemplate(__DIR__.'/Fixtures/twig/empty.twig');
94 $extractor->setGettextParameters($this->getGettextParameters());
95
96 $extractor->extract();
97
98 $catalog = $this->loader->load($this->getPotFile(), null);
99
100 $this->assertEmpty($catalog->all('messages'));
101 }
102
103 private function getPotFile()
104 {
105 return __DIR__.'/Fixtures/messages.pot';
106 }
107
108 private function getGettextParameters()
109 {
110 return array(
111 '--force-po',
112 '-o',
113 $this->getPotFile(),
114 );
115 }
116
117 protected function tearDown()
118 {
119 if (file_exists($this->getPotFile())) {
120 unlink($this->getPotFile());
121 }
122 }
123}
diff --git a/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/empty.twig b/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/empty.twig
new file mode 100644
index 00000000..05f0d26a
--- /dev/null
+++ b/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/empty.twig
@@ -0,0 +1 @@
Nothing to translate here.
diff --git a/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/plural.twig b/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/plural.twig
new file mode 100644
index 00000000..f9754ff4
--- /dev/null
+++ b/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/plural.twig
@@ -0,0 +1,5 @@
1{% trans %}
2 Hey {{ name }}, I have one apple.
3{% plural apple_count %}
4 Hey {{ name }}, I have {{ count }} apples.
5{% endtrans %}
diff --git a/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/singular.twig b/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/singular.twig
new file mode 100644
index 00000000..d757cf90
--- /dev/null
+++ b/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/singular.twig
@@ -0,0 +1,9 @@
1{% trans "Hello World!" %}
2
3{% trans %}
4 Hello World!
5{% endtrans %}
6
7{% trans %}
8 Hello {{ name }}!
9{% endtrans %}
diff --git a/inc/Twig/Extensions/Grammar.php b/inc/Twig/Extensions/Grammar.php
new file mode 100644
index 00000000..4d031b19
--- /dev/null
+++ b/inc/Twig/Extensions/Grammar.php
@@ -0,0 +1,30 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11abstract class Twig_Extensions_Grammar implements Twig_Extensions_GrammarInterface
12{
13 protected $name;
14 protected $parser;
15
16 public function __construct($name)
17 {
18 $this->name = $name;
19 }
20
21 public function setParser(Twig_ParserInterface $parser)
22 {
23 $this->parser = $parser;
24 }
25
26 public function getName()
27 {
28 return $this->name;
29 }
30}
diff --git a/inc/Twig/Extensions/Grammar/Arguments.php b/inc/Twig/Extensions/Grammar/Arguments.php
new file mode 100644
index 00000000..158c05ac
--- /dev/null
+++ b/inc/Twig/Extensions/Grammar/Arguments.php
@@ -0,0 +1,22 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extensions_Grammar_Arguments extends Twig_Extensions_Grammar
12{
13 public function __toString()
14 {
15 return sprintf('<%s:arguments>', $this->name);
16 }
17
18 public function parse(Twig_Token $token)
19 {
20 return $this->parser->getExpressionParser()->parseArguments();
21 }
22}
diff --git a/inc/Twig/Extensions/Grammar/Array.php b/inc/Twig/Extensions/Grammar/Array.php
new file mode 100644
index 00000000..34aece0f
--- /dev/null
+++ b/inc/Twig/Extensions/Grammar/Array.php
@@ -0,0 +1,22 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extensions_Grammar_Array extends Twig_Extensions_Grammar
12{
13 public function __toString()
14 {
15 return sprintf('<%s:array>', $this->name);
16 }
17
18 public function parse(Twig_Token $token)
19 {
20 return $this->parser->getExpressionParser()->parseArrayExpression();
21 }
22}
diff --git a/inc/Twig/Extensions/Grammar/Body.php b/inc/Twig/Extensions/Grammar/Body.php
new file mode 100644
index 00000000..540cfc75
--- /dev/null
+++ b/inc/Twig/Extensions/Grammar/Body.php
@@ -0,0 +1,39 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extensions_Grammar_Body extends Twig_Extensions_Grammar
12{
13 protected $end;
14
15 public function __construct($name, $end = null)
16 {
17 parent::__construct($name);
18
19 $this->end = null === $end ? 'end'.$name : $end;
20 }
21
22 public function __toString()
23 {
24 return sprintf('<%s:body>', $this->name);
25 }
26
27 public function parse(Twig_Token $token)
28 {
29 $stream = $this->parser->getStream();
30 $stream->expect(Twig_Token::BLOCK_END_TYPE);
31
32 return $this->parser->subparse(array($this, 'decideBlockEnd'), true);
33 }
34
35 public function decideBlockEnd(Twig_Token $token)
36 {
37 return $token->test($this->end);
38 }
39}
diff --git a/inc/Twig/Extensions/Grammar/Boolean.php b/inc/Twig/Extensions/Grammar/Boolean.php
new file mode 100644
index 00000000..c0048090
--- /dev/null
+++ b/inc/Twig/Extensions/Grammar/Boolean.php
@@ -0,0 +1,24 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extensions_Grammar_Boolean extends Twig_Extensions_Grammar
12{
13 public function __toString()
14 {
15 return sprintf('<%s:boolean>', $this->name);
16 }
17
18 public function parse(Twig_Token $token)
19 {
20 $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, array('true', 'false'));
21
22 return new Twig_Node_Expression_Constant('true' === $token->getValue() ? true : false, $token->getLine());
23 }
24}
diff --git a/inc/Twig/Extensions/Grammar/Constant.php b/inc/Twig/Extensions/Grammar/Constant.php
new file mode 100644
index 00000000..9df60458
--- /dev/null
+++ b/inc/Twig/Extensions/Grammar/Constant.php
@@ -0,0 +1,37 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extensions_Grammar_Constant extends Twig_Extensions_Grammar
12{
13 protected $type;
14
15 public function __construct($name, $type = null)
16 {
17 $this->name = $name;
18 $this->type = null === $type ? Twig_Token::NAME_TYPE : $type;
19 }
20
21 public function __toString()
22 {
23 return $this->name;
24 }
25
26 public function parse(Twig_Token $token)
27 {
28 $this->parser->getStream()->expect($this->type, $this->name);
29
30 return $this->name;
31 }
32
33 public function getType()
34 {
35 return $this->type;
36 }
37}
diff --git a/inc/Twig/Extensions/Grammar/Expression.php b/inc/Twig/Extensions/Grammar/Expression.php
new file mode 100644
index 00000000..4c33df0e
--- /dev/null
+++ b/inc/Twig/Extensions/Grammar/Expression.php
@@ -0,0 +1,22 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extensions_Grammar_Expression extends Twig_Extensions_Grammar
12{
13 public function __toString()
14 {
15 return sprintf('<%s>', $this->name);
16 }
17
18 public function parse(Twig_Token $token)
19 {
20 return $this->parser->getExpressionParser()->parseExpression();
21 }
22}
diff --git a/inc/Twig/Extensions/Grammar/Hash.php b/inc/Twig/Extensions/Grammar/Hash.php
new file mode 100644
index 00000000..98b07d20
--- /dev/null
+++ b/inc/Twig/Extensions/Grammar/Hash.php
@@ -0,0 +1,22 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extensions_Grammar_Hash extends Twig_Extensions_Grammar
12{
13 public function __toString()
14 {
15 return sprintf('<%s:hash>', $this->name);
16 }
17
18 public function parse(Twig_Token $token)
19 {
20 return $this->parser->getExpressionParser()->parseHashExpression();
21 }
22}
diff --git a/inc/Twig/Extensions/Grammar/Number.php b/inc/Twig/Extensions/Grammar/Number.php
new file mode 100644
index 00000000..f0857d20
--- /dev/null
+++ b/inc/Twig/Extensions/Grammar/Number.php
@@ -0,0 +1,24 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extensions_Grammar_Number extends Twig_Extensions_Grammar
12{
13 public function __toString()
14 {
15 return sprintf('<%s:number>', $this->name);
16 }
17
18 public function parse(Twig_Token $token)
19 {
20 $this->parser->getStream()->expect(Twig_Token::NUMBER_TYPE);
21
22 return new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
23 }
24}
diff --git a/inc/Twig/Extensions/Grammar/Optional.php b/inc/Twig/Extensions/Grammar/Optional.php
new file mode 100644
index 00000000..da427485
--- /dev/null
+++ b/inc/Twig/Extensions/Grammar/Optional.php
@@ -0,0 +1,69 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extensions_Grammar_Optional extends Twig_Extensions_Grammar
12{
13 protected $grammar;
14
15 public function __construct()
16 {
17 $this->grammar = array();
18 foreach (func_get_args() as $grammar) {
19 $this->addGrammar($grammar);
20 }
21 }
22
23 public function __toString()
24 {
25 $repr = array();
26 foreach ($this->grammar as $grammar) {
27 $repr[] = (string) $grammar;
28 }
29
30 return sprintf('[%s]', implode(' ', $repr));
31 }
32
33 public function addGrammar(Twig_Extensions_GrammarInterface $grammar)
34 {
35 $this->grammar[] = $grammar;
36 }
37
38 public function parse(Twig_Token $token)
39 {
40 // test if we have the optional element before consuming it
41 if ($this->grammar[0] instanceof Twig_Extensions_Grammar_Constant) {
42 if (!$this->parser->getStream()->test($this->grammar[0]->getType(), $this->grammar[0]->getName())) {
43 return array();
44 }
45 } elseif ($this->grammar[0] instanceof Twig_Extensions_Grammar_Name) {
46 if (!$this->parser->getStream()->test(Twig_Token::NAME_TYPE)) {
47 return array();
48 }
49 } elseif ($this->parser->getStream()->test(Twig_Token::BLOCK_END_TYPE)) {
50 // if this is not a Constant or a Name, it must be the last element of the tag
51
52 return array();
53 }
54
55 $elements = array();
56 foreach ($this->grammar as $grammar) {
57 $grammar->setParser($this->parser);
58
59 $element = $grammar->parse($token);
60 if (is_array($element)) {
61 $elements = array_merge($elements, $element);
62 } else {
63 $elements[$grammar->getName()] = $element;
64 }
65 }
66
67 return $elements;
68 }
69}
diff --git a/inc/Twig/Extensions/Grammar/Switch.php b/inc/Twig/Extensions/Grammar/Switch.php
new file mode 100644
index 00000000..4245f2c8
--- /dev/null
+++ b/inc/Twig/Extensions/Grammar/Switch.php
@@ -0,0 +1,24 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extensions_Grammar_Switch extends Twig_Extensions_Grammar
12{
13 public function __toString()
14 {
15 return sprintf('<%s:switch>', $this->name);
16 }
17
18 public function parse(Twig_Token $token)
19 {
20 $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, $this->name);
21
22 return new Twig_Node_Expression_Constant(true, $token->getLine());
23 }
24}
diff --git a/inc/Twig/Extensions/Grammar/Tag.php b/inc/Twig/Extensions/Grammar/Tag.php
new file mode 100644
index 00000000..727f2610
--- /dev/null
+++ b/inc/Twig/Extensions/Grammar/Tag.php
@@ -0,0 +1,56 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extensions_Grammar_Tag extends Twig_Extensions_Grammar
12{
13 protected $grammar;
14
15 public function __construct()
16 {
17 $this->grammar = array();
18 foreach (func_get_args() as $grammar) {
19 $this->addGrammar($grammar);
20 }
21 }
22
23 public function __toString()
24 {
25 $repr = array();
26 foreach ($this->grammar as $grammar) {
27 $repr[] = (string) $grammar;
28 }
29
30 return implode(' ', $repr);
31 }
32
33 public function addGrammar(Twig_Extensions_GrammarInterface $grammar)
34 {
35 $this->grammar[] = $grammar;
36 }
37
38 public function parse(Twig_Token $token)
39 {
40 $elements = array();
41 foreach ($this->grammar as $grammar) {
42 $grammar->setParser($this->parser);
43
44 $element = $grammar->parse($token);
45 if (is_array($element)) {
46 $elements = array_merge($elements, $element);
47 } else {
48 $elements[$grammar->getName()] = $element;
49 }
50 }
51
52 $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
53
54 return $elements;
55 }
56}
diff --git a/inc/Twig/Extensions/GrammarInterface.php b/inc/Twig/Extensions/GrammarInterface.php
new file mode 100644
index 00000000..22713bf2
--- /dev/null
+++ b/inc/Twig/Extensions/GrammarInterface.php
@@ -0,0 +1,18 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11interface Twig_Extensions_GrammarInterface
12{
13 function setParser(Twig_ParserInterface $parser);
14
15 function parse(Twig_Token $token);
16
17 function getName();
18}
diff --git a/inc/Twig/Extensions/Node/Debug.php b/inc/Twig/Extensions/Node/Debug.php
new file mode 100644
index 00000000..7d01bbe5
--- /dev/null
+++ b/inc/Twig/Extensions/Node/Debug.php
@@ -0,0 +1,69 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a debug node.
14 *
15 * @package twig
16 * @subpackage Twig-extensions
17 * @author Fabien Potencier <fabien.potencier@symfony-project.com>
18 * @version SVN: $Id$
19 */
20class Twig_Extensions_Node_Debug extends Twig_Node
21{
22 public function __construct(Twig_Node_Expression $expr = null, $lineno, $tag = null)
23 {
24 parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
25 }
26
27 /**
28 * Compiles the node to PHP.
29 *
30 * @param Twig_Compiler A Twig_Compiler instance
31 */
32 public function compile(Twig_Compiler $compiler)
33 {
34 $compiler->addDebugInfo($this);
35
36 $compiler
37 ->write("if (\$this->env->isDebug()) {\n")
38 ->indent()
39 ;
40
41 if (null === $this->getNode('expr')) {
42 // remove embedded templates (macros) from the context
43 $compiler
44 ->write("\$vars = array();\n")
45 ->write("foreach (\$context as \$key => \$value) {\n")
46 ->indent()
47 ->write("if (!\$value instanceof Twig_Template) {\n")
48 ->indent()
49 ->write("\$vars[\$key] = \$value;\n")
50 ->outdent()
51 ->write("}\n")
52 ->outdent()
53 ->write("}\n")
54 ->write("var_dump(\$vars);\n")
55 ;
56 } else {
57 $compiler
58 ->write("var_dump(")
59 ->subcompile($this->getNode('expr'))
60 ->raw(");\n")
61 ;
62 }
63
64 $compiler
65 ->outdent()
66 ->write("}\n")
67 ;
68 }
69}
diff --git a/inc/Twig/Extensions/Node/Trans.php b/inc/Twig/Extensions/Node/Trans.php
new file mode 100644
index 00000000..d12564a7
--- /dev/null
+++ b/inc/Twig/Extensions/Node/Trans.php
@@ -0,0 +1,133 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a trans node.
14 *
15 * @package twig
16 * @author Fabien Potencier <fabien.potencier@symfony-project.com>
17 */
18class Twig_Extensions_Node_Trans extends Twig_Node
19{
20 public function __construct(Twig_NodeInterface $body, Twig_NodeInterface $plural = null, Twig_Node_Expression $count = null, $lineno, $tag = null)
21 {
22 parent::__construct(array('count' => $count, 'body' => $body, 'plural' => $plural), array(), $lineno, $tag);
23 }
24
25 /**
26 * Compiles the node to PHP.
27 *
28 * @param Twig_Compiler A Twig_Compiler instance
29 */
30 public function compile(Twig_Compiler $compiler)
31 {
32 $compiler->addDebugInfo($this);
33
34 list($msg, $vars) = $this->compileString($this->getNode('body'));
35
36 if (null !== $this->getNode('plural')) {
37 list($msg1, $vars1) = $this->compileString($this->getNode('plural'));
38
39 $vars = array_merge($vars, $vars1);
40 }
41
42 $function = null === $this->getNode('plural') ? 'gettext' : 'ngettext';
43
44 if ($vars) {
45 $compiler
46 ->write('echo strtr('.$function.'(')
47 ->subcompile($msg)
48 ;
49
50 if (null !== $this->getNode('plural')) {
51 $compiler
52 ->raw(', ')
53 ->subcompile($msg1)
54 ->raw(', abs(')
55 ->subcompile($this->getNode('count'))
56 ->raw(')')
57 ;
58 }
59
60 $compiler->raw('), array(');
61
62 foreach ($vars as $var) {
63 if ('count' === $var->getAttribute('name')) {
64 $compiler
65 ->string('%count%')
66 ->raw(' => abs(')
67 ->subcompile($this->getNode('count'))
68 ->raw('), ')
69 ;
70 } else {
71 $compiler
72 ->string('%'.$var->getAttribute('name').'%')
73 ->raw(' => ')
74 ->subcompile($var)
75 ->raw(', ')
76 ;
77 }
78 }
79
80 $compiler->raw("));\n");
81 } else {
82 $compiler
83 ->write('echo '.$function.'(')
84 ->subcompile($msg)
85 ;
86
87 if (null !== $this->getNode('plural')) {
88 $compiler
89 ->raw(', ')
90 ->subcompile($msg1)
91 ->raw(', abs(')
92 ->subcompile($this->getNode('count'))
93 ->raw(')')
94 ;
95 }
96
97 $compiler->raw(");\n");
98 }
99 }
100
101 protected function compileString(Twig_NodeInterface $body)
102 {
103 if ($body instanceof Twig_Node_Expression_Name || $body instanceof Twig_Node_Expression_Constant || $body instanceof Twig_Node_Expression_TempName) {
104 return array($body, array());
105 }
106
107 $vars = array();
108 if (count($body)) {
109 $msg = '';
110
111 foreach ($body as $node) {
112 if (get_class($node) === 'Twig_Node' && $node->getNode(0) instanceof Twig_Node_SetTemp) {
113 $node = $node->getNode(1);
114 }
115
116 if ($node instanceof Twig_Node_Print) {
117 $n = $node->getNode('expr');
118 while ($n instanceof Twig_Node_Expression_Filter) {
119 $n = $n->getNode('node');
120 }
121 $msg .= sprintf('%%%s%%', $n->getAttribute('name'));
122 $vars[] = new Twig_Node_Expression_Name($n->getAttribute('name'), $n->getLine());
123 } else {
124 $msg .= $node->getAttribute('data');
125 }
126 }
127 } else {
128 $msg = $body->getAttribute('data');
129 }
130
131 return array(new Twig_Node(array(new Twig_Node_Expression_Constant(trim($msg), $body->getLine()))), $vars);
132 }
133}
diff --git a/inc/Twig/Extensions/SimpleTokenParser.php b/inc/Twig/Extensions/SimpleTokenParser.php
new file mode 100644
index 00000000..49546487
--- /dev/null
+++ b/inc/Twig/Extensions/SimpleTokenParser.php
@@ -0,0 +1,132 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11abstract class Twig_Extensions_SimpleTokenParser extends Twig_TokenParser
12{
13 /**
14 * Parses a token and returns a node.
15 *
16 * @param Twig_Token $token A Twig_Token instance
17 *
18 * @return Twig_NodeInterface A Twig_NodeInterface instance
19 */
20 public function parse(Twig_Token $token)
21 {
22 $grammar = $this->getGrammar();
23 if (!is_object($grammar)) {
24 $grammar = self::parseGrammar($grammar);
25 }
26
27 $grammar->setParser($this->parser);
28 $values = $grammar->parse($token);
29
30 return $this->getNode($values, $token->getLine());
31 }
32
33 /**
34 * Gets the grammar as an object or as a string.
35 *
36 * @return string|Twig_Extensions_Grammar A Twig_Extensions_Grammar instance or a string
37 */
38 abstract protected function getGrammar();
39
40 /**
41 * Gets the nodes based on the parsed values.
42 *
43 * @param array $values An array of values
44 * @param integer $line The parser line
45 */
46 abstract protected function getNode(array $values, $line);
47
48 protected function getAttribute($node, $attribute, $arguments = array(), $type = Twig_Node_Expression_GetAttr::TYPE_ANY, $line = -1)
49 {
50 return new Twig_Node_Expression_GetAttr(
51 $node instanceof Twig_NodeInterface ? $node : new Twig_Node_Expression_Name($node, $line),
52 $attribute instanceof Twig_NodeInterface ? $attribute : new Twig_Node_Expression_Constant($attribute, $line),
53 $arguments instanceof Twig_NodeInterface ? $arguments : new Twig_Node($arguments),
54 $type,
55 $line
56 );
57 }
58
59 protected function call($node, $attribute, $arguments = array(), $line = -1)
60 {
61 return $this->getAttribute($node, $attribute, $arguments, Twig_Node_Expression_GetAttr::TYPE_METHOD, $line);
62 }
63
64 protected function markAsSafe(Twig_NodeInterface $node, $line = -1)
65 {
66 return new Twig_Node_Expression_Filter(
67 $node,
68 new Twig_Node_Expression_Constant('raw', $line),
69 new Twig_Node(),
70 $line
71 );
72 }
73
74 protected function output(Twig_NodeInterface $node, $line = -1)
75 {
76 return new Twig_Node_Print($node, $line);
77 }
78
79 protected function getNodeValues(array $values)
80 {
81 $nodes = array();
82 foreach ($values as $value) {
83 if ($value instanceof Twig_NodeInterface) {
84 $nodes[] = $value;
85 }
86 }
87
88 return $nodes;
89 }
90
91 static public function parseGrammar($str, $main = true)
92 {
93 static $cursor;
94
95 if (true === $main) {
96 $cursor = 0;
97 $grammar = new Twig_Extensions_Grammar_Tag();
98 } else {
99 $grammar = new Twig_Extensions_Grammar_Optional();
100 }
101
102 while ($cursor < strlen($str)) {
103 if (preg_match('/\s+/A', $str, $match, null, $cursor)) {
104 $cursor += strlen($match[0]);
105 } elseif (preg_match('/<(\w+)(?:\:(\w+))?>/A', $str, $match, null, $cursor)) {
106 $class = sprintf('Twig_Extensions_Grammar_%s', ucfirst(isset($match[2]) ? $match[2] : 'Expression'));
107 if (!class_exists($class)) {
108 throw new Twig_Error_Runtime(sprintf('Unable to understand "%s" in grammar (%s class does not exist)', $match[0], $class));
109 }
110 $grammar->addGrammar(new $class($match[1]));
111 $cursor += strlen($match[0]);
112 } elseif (preg_match('/\w+/A', $str, $match, null, $cursor)) {
113 $grammar->addGrammar(new Twig_Extensions_Grammar_Constant($match[0]));
114 $cursor += strlen($match[0]);
115 } elseif (preg_match('/,/A', $str, $match, null, $cursor)) {
116 $grammar->addGrammar(new Twig_Extensions_Grammar_Constant($match[0], Twig_Token::PUNCTUATION_TYPE));
117 $cursor += strlen($match[0]);
118 } elseif (preg_match('/\[/A', $str, $match, null, $cursor)) {
119 $cursor += strlen($match[0]);
120 $grammar->addGrammar(self::parseGrammar($str, false));
121 } elseif (true !== $main && preg_match('/\]/A', $str, $match, null, $cursor)) {
122 $cursor += strlen($match[0]);
123
124 return $grammar;
125 } else {
126 throw new Twig_Error_Runtime(sprintf('Unable to parse grammar "%s" near "...%s..."', $str, substr($str, $cursor, 10)));
127 }
128 }
129
130 return $grammar;
131 }
132}
diff --git a/inc/Twig/Extensions/TokenParser/Debug.php b/inc/Twig/Extensions/TokenParser/Debug.php
new file mode 100644
index 00000000..4a7dfcc0
--- /dev/null
+++ b/inc/Twig/Extensions/TokenParser/Debug.php
@@ -0,0 +1,42 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009-2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extensions_TokenParser_Debug extends Twig_TokenParser
12{
13 /**
14 * Parses a token and returns a node.
15 *
16 * @param Twig_Token $token A Twig_Token instance
17 *
18 * @return Twig_NodeInterface A Twig_NodeInterface instance
19 */
20 public function parse(Twig_Token $token)
21 {
22 $lineno = $token->getLine();
23
24 $expr = null;
25 if (!$this->parser->getStream()->test(Twig_Token::BLOCK_END_TYPE)) {
26 $expr = $this->parser->getExpressionParser()->parseExpression();
27 }
28 $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
29
30 return new Twig_Extensions_Node_Debug($expr, $lineno, $this->getTag());
31 }
32
33 /**
34 * Gets the tag name associated with this token parser.
35 *
36 * @param string The tag name
37 */
38 public function getTag()
39 {
40 return 'debug';
41 }
42}
diff --git a/inc/Twig/Extensions/TokenParser/Trans.php b/inc/Twig/Extensions/TokenParser/Trans.php
new file mode 100644
index 00000000..5e2dc464
--- /dev/null
+++ b/inc/Twig/Extensions/TokenParser/Trans.php
@@ -0,0 +1,80 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extensions_TokenParser_Trans extends Twig_TokenParser
12{
13 /**
14 * Parses a token and returns a node.
15 *
16 * @param Twig_Token $token A Twig_Token instance
17 *
18 * @return Twig_NodeInterface A Twig_NodeInterface instance
19 */
20 public function parse(Twig_Token $token)
21 {
22 $lineno = $token->getLine();
23 $stream = $this->parser->getStream();
24 $count = null;
25 $plural = null;
26
27 if (!$stream->test(Twig_Token::BLOCK_END_TYPE)) {
28 $body = $this->parser->getExpressionParser()->parseExpression();
29 } else {
30 $stream->expect(Twig_Token::BLOCK_END_TYPE);
31 $body = $this->parser->subparse(array($this, 'decideForFork'));
32 if ('plural' === $stream->next()->getValue()) {
33 $count = $this->parser->getExpressionParser()->parseExpression();
34 $stream->expect(Twig_Token::BLOCK_END_TYPE);
35 $plural = $this->parser->subparse(array($this, 'decideForEnd'), true);
36 }
37 }
38
39 $stream->expect(Twig_Token::BLOCK_END_TYPE);
40
41 $this->checkTransString($body, $lineno);
42
43 return new Twig_Extensions_Node_Trans($body, $plural, $count, $lineno, $this->getTag());
44 }
45
46 public function decideForFork(Twig_Token $token)
47 {
48 return $token->test(array('plural', 'endtrans'));
49 }
50
51 public function decideForEnd(Twig_Token $token)
52 {
53 return $token->test('endtrans');
54 }
55
56 /**
57 * Gets the tag name associated with this token parser.
58 *
59 * @param string The tag name
60 */
61 public function getTag()
62 {
63 return 'trans';
64 }
65
66 protected function checkTransString(Twig_NodeInterface $body, $lineno)
67 {
68 foreach ($body as $i => $node) {
69 if (
70 $node instanceof Twig_Node_Text
71 ||
72 ($node instanceof Twig_Node_Print && $node->getNode('expr') instanceof Twig_Node_Expression_Name)
73 ) {
74 continue;
75 }
76
77 throw new Twig_Error_Syntax(sprintf('The text to be translated with "trans" can only contain references to simple variables'), $lineno);
78 }
79 }
80}