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