Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.08% covered (success)
95.08%
58 / 61
72.73% covered (warning)
72.73%
8 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
extension
95.08% covered (success)
95.08%
58 / 61
72.73% covered (warning)
72.73%
8 / 11
17
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTokenParsers
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 getFilters
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 getFunctions
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 getOperators
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
1
 loop_subset
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
5
 lang
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 lang_defined
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 lang_js
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 lang_raw
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3*
4* This file is part of the phpBB Forum Software package.
5*
6* @copyright (c) phpBB Limited <https://www.phpbb.com>
7* @license GNU General Public License, version 2 (GPL-2.0)
8*
9* For full copyright and license information, please see
10* the docs/CREDITS.txt file.
11*
12*/
13
14namespace phpbb\template\twig;
15
16use Twig\Error\RuntimeError;
17use Twig\Extension\CoreExtension;
18use Twig\Runtime\EscaperRuntime;
19
20class extension extends \Twig\Extension\AbstractExtension
21{
22    /** @var \phpbb\template\context */
23    protected $context;
24
25    /** @var \phpbb\template\twig\environment */
26    protected $environment;
27
28    /** @var \phpbb\language\language */
29    protected $language;
30
31    /**
32    * Constructor
33    *
34    * @param \phpbb\template\context $context
35    * @param \phpbb\template\twig\environment $environment
36    * @param \phpbb\language\language $language
37    */
38    public function __construct(\phpbb\template\context $context, \phpbb\template\twig\environment $environment, $language)
39    {
40        $this->context = $context;
41        $this->environment = $environment;
42        $this->language = $language;
43    }
44
45    /**
46    * Get the name of this extension
47    *
48    * @return string
49    */
50    public function getName()
51    {
52        return 'phpbb';
53    }
54
55    /**
56    * Returns the token parser instance to add to the existing list.
57    *
58    * @return \Twig\TokenParser\TokenParserInterface[] An array of \Twig\TokenParser\AbstractTokenParser instances
59    */
60    public function getTokenParsers()
61    {
62        return array(
63            new \phpbb\template\twig\tokenparser\defineparser,
64            new \phpbb\template\twig\tokenparser\includeparser,
65            new \phpbb\template\twig\tokenparser\includejs,
66            new \phpbb\template\twig\tokenparser\includecss,
67            new \phpbb\template\twig\tokenparser\event($this->environment),
68        );
69    }
70
71    /**
72    * Returns a list of filters to add to the existing list.
73    *
74    * @return \Twig\TwigFilter[] An array of filters
75    */
76    public function getFilters()
77    {
78        return array(
79            new \Twig\TwigFilter('subset', array($this, 'loop_subset'), array('needs_environment' => true)),
80            // @deprecated 3.2.0 Uses twig's JS escape method instead of addslashes
81            new \Twig\TwigFilter('addslashes', 'addslashes'),
82            new \Twig\TwigFilter('int', 'intval'),
83            new \Twig\TwigFilter('float', 'floatval'),
84        );
85    }
86
87    /**
88    * Returns a list of global functions to add to the existing list.
89    *
90    * @return \Twig\TwigFunction[] An array of global functions
91    */
92    public function getFunctions()
93    {
94        return array(
95            new \Twig\TwigFunction('lang', array($this, 'lang')),
96            new \Twig\TwigFunction('lang_defined', array($this, 'lang_defined')),
97            new \Twig\TwigFunction('lang_js', [$this, 'lang_js']),
98            new \Twig\TwigFunction('lang_raw', [$this, 'lang_raw']),
99            new \Twig\TwigFunction('get_class', 'get_class'),
100        );
101    }
102
103    /**
104    * Returns a list of operators to add to the existing list.
105    *
106    * @return array[] An array of operators
107    * @psalm-suppress LessSpecificImplementedReturnType
108    */
109    public function getOperators()
110    {
111        return array(
112            array(
113                '!' => array('precedence' => 50, 'class' => '\Twig\Node\Expression\Unary\NotUnary'),
114            ),
115            array(
116                // precedence settings are copied from similar operators in Twig core extension
117                '||' => array('precedence' => 10, 'class' => '\Twig\Node\Expression\Binary\OrBinary', 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT),
118                '&&' => array('precedence' => 15, 'class' => '\Twig\Node\Expression\Binary\AndBinary', 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT),
119
120                'eq' => array('precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\EqualBinary', 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT),
121
122                'ne' => array('precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\NotEqualBinary', 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT),
123                'neq' => array('precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\NotEqualBinary', 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT),
124                '<>' => array('precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\NotEqualBinary', 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT),
125
126                '===' => array('precedence' => 20, 'class' => '\phpbb\template\twig\node\expression\binary\equalequal', 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT),
127                '!==' => array('precedence' => 20, 'class' => '\phpbb\template\twig\node\expression\binary\notequalequal', 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT),
128
129                'gt' => array('precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\GreaterBinary', 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT),
130                'gte' => array('precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\GreaterEqualBinary', 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT),
131                'ge' => array('precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\GreaterEqualBinary', 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT),
132                'lt' => array('precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\LessBinary', 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT),
133                'lte' => array('precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\LessEqualBinary', 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT),
134                'le' => array('precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\LessEqualBinary', 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT),
135
136                'mod' => array('precedence' => 60, 'class' => '\Twig\Node\Expression\Binary\ModBinary', 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT),
137            ),
138        );
139    }
140
141    /**
142    * Grabs a subset of a loop
143    *
144    * @param \Twig\Environment $env          A Twig\Environment instance
145    * @param mixed            $item         A variable
146    * @param integer          $start        Start of the subset
147    * @param integer          $end            End of the subset
148    * @param boolean          $preserveKeys Whether to preserve key or not (when the input is an array)
149    *
150    * @return mixed The sliced variable
151    */
152    public function loop_subset(\Twig\Environment $env, $item, $start, $end = null, $preserveKeys = false)
153    {
154        // We do almost the same thing as Twig's slice (array_slice), except when $end is positive
155        if ($end >= 1)
156        {
157            // When end is > 1, subset will end on the last item in an array with the specified $end
158            // This is different from slice in that it is the number we end on rather than the number
159            //  of items to grab (length)
160
161            // Start must always be the actual starting number for this calculation (not negative)
162            $start = ($start < 0) ? count($item) + $start : $start;
163            $end = $end - $start;
164        }
165
166        // We always include the last element (this was the past design)
167        $end = ($end == -1 || $end === null) ? null : $end + 1;
168
169        return CoreExtension::slice($env->getCharset(), $item, $start, $end, $preserveKeys);
170    }
171
172    /**
173    * Get output for a language variable (L_FOO, LA_FOO)
174    *
175    * This function checks to see if the language var was outputted to $context
176    * (e.g. in the ACP, L_TITLE)
177    * If not, we return the result of $user->lang()
178    *
179    * @return string
180    */
181    public function lang()
182    {
183        $args = func_get_args();
184        $key = $args[0];
185
186        $context_vars = $this->context->get_root_ref();
187
188        if (is_string($key) && isset($context_vars['L_' . $key]))
189        {
190            return $context_vars['L_' . $key];
191        }
192
193        // LA_ is transformed into lang(\'$1\')|escape('js'), so we should not
194        // need to check for it
195
196        return call_user_func_array(array($this->language, 'lang'), $args);
197    }
198
199    /**
200     * Check if a language variable exists
201     *
202     * @return bool
203     */
204    public function lang_defined($key)
205    {
206        return call_user_func_array([$this->language, 'is_set'], [$key]);
207    }
208
209    /**
210     * Get output for language variable in JS code
211     *
212     * @throws RuntimeError When data passed to twig_escape_filter is not a UTF8 string
213     */
214    public function lang_js(): string
215    {
216        $args = func_get_args();
217
218        return $this->environment->getRuntime(EscaperRuntime::class)->escape(call_user_func_array([$this, 'lang'], $args), 'js');
219    }
220
221    /**
222     * Get raw value associated with lang key
223     *
224     * @param string $key
225     *
226     * @return array|string Raw value associated with lang key
227     */
228    public function lang_raw(string $key): array|string
229    {
230        return call_user_func_array(array($this->language, 'lang_raw'), [$key]);
231    }
232}