xref: /plugin/combo/vendor/salesforce/handlebars-php/src/Handlebars/Context.php (revision 04fd306c7c155fa133ebb3669986875d65988276)
1*04fd306cSNickeau<?php
2*04fd306cSNickeau/**
3*04fd306cSNickeau * Handlebars context
4*04fd306cSNickeau * Context for a template
5*04fd306cSNickeau *
6*04fd306cSNickeau * @category  Xamin
7*04fd306cSNickeau * @package   Handlebars
8*04fd306cSNickeau * @author    fzerorubigd <fzerorubigd@gmail.com>
9*04fd306cSNickeau * @author    Behrooz Shabani <everplays@gmail.com>
10*04fd306cSNickeau * @author    Mardix <https://github.com/mardix>
11*04fd306cSNickeau * @copyright 2012 (c) ParsPooyesh Co
12*04fd306cSNickeau * @copyright 2013 (c) Behrooz Shabani
13*04fd306cSNickeau * @copyright 2013 (c) Mardix
14*04fd306cSNickeau * @license   MIT
15*04fd306cSNickeau * @link      http://voodoophp.org/docs/handlebars
16*04fd306cSNickeau */
17*04fd306cSNickeau
18*04fd306cSNickeaunamespace Handlebars;
19*04fd306cSNickeau
20*04fd306cSNickeauuse InvalidArgumentException;
21*04fd306cSNickeauuse LogicException;
22*04fd306cSNickeau
23*04fd306cSNickeauclass Context
24*04fd306cSNickeau{
25*04fd306cSNickeau    const DATA_KEY = 'key';
26*04fd306cSNickeau    const DATA_INDEX = 'index';
27*04fd306cSNickeau    const DATA_FIRST = 'first';
28*04fd306cSNickeau    const DATA_LAST = 'last';
29*04fd306cSNickeau
30*04fd306cSNickeau    /**
31*04fd306cSNickeau     * @var array stack for context only top stack is available
32*04fd306cSNickeau     */
33*04fd306cSNickeau    protected $stack = [];
34*04fd306cSNickeau
35*04fd306cSNickeau    /**
36*04fd306cSNickeau     * @var array index stack for sections
37*04fd306cSNickeau     */
38*04fd306cSNickeau    protected $index = [];
39*04fd306cSNickeau
40*04fd306cSNickeau    /**
41*04fd306cSNickeau     * @var array dataStack stack for data within sections
42*04fd306cSNickeau     */
43*04fd306cSNickeau    protected $dataStack = [];
44*04fd306cSNickeau
45*04fd306cSNickeau    /**
46*04fd306cSNickeau     * @var array key stack for objects
47*04fd306cSNickeau     */
48*04fd306cSNickeau    protected $key = [];
49*04fd306cSNickeau
50*04fd306cSNickeau    /**
51*04fd306cSNickeau     * @var bool enableDataVariables true if @data variables should be used.
52*04fd306cSNickeau     */
53*04fd306cSNickeau    protected $enableDataVariables = false;
54*04fd306cSNickeau
55*04fd306cSNickeau    /**
56*04fd306cSNickeau     * Mustache rendering Context constructor.
57*04fd306cSNickeau     *
58*04fd306cSNickeau     * @param mixed $context Default rendering context (default: null)
59*04fd306cSNickeau     * @param array $options Options for the context. It may contain the following: (default: empty array)
60*04fd306cSNickeau     *                       enableDataVariables => Boolean, Enables @data variables (default: false)
61*04fd306cSNickeau     *
62*04fd306cSNickeau     * @throws InvalidArgumentException when calling this method when enableDataVariables is not a boolean.
63*04fd306cSNickeau     */
64*04fd306cSNickeau    public function __construct($context = null, $options = [])
65*04fd306cSNickeau    {
66*04fd306cSNickeau        if ($context !== null) {
67*04fd306cSNickeau            $this->stack = [$context];
68*04fd306cSNickeau        }
69*04fd306cSNickeau
70*04fd306cSNickeau        if (isset($options[Handlebars::OPTION_ENABLE_DATA_VARIABLES])) {
71*04fd306cSNickeau            if (!is_bool($options[Handlebars::OPTION_ENABLE_DATA_VARIABLES])) {
72*04fd306cSNickeau                throw new InvalidArgumentException(
73*04fd306cSNickeau                    'Context Constructor "' . Handlebars::OPTION_ENABLE_DATA_VARIABLES . '" option must be a boolean'
74*04fd306cSNickeau                );
75*04fd306cSNickeau            }
76*04fd306cSNickeau            $this->enableDataVariables = $options[Handlebars::OPTION_ENABLE_DATA_VARIABLES];
77*04fd306cSNickeau        }
78*04fd306cSNickeau    }
79*04fd306cSNickeau
80*04fd306cSNickeau    /**
81*04fd306cSNickeau     * Push a new Context frame onto the stack.
82*04fd306cSNickeau     *
83*04fd306cSNickeau     * @param mixed $value Object or array to use for context
84*04fd306cSNickeau     *
85*04fd306cSNickeau     * @return void
86*04fd306cSNickeau     */
87*04fd306cSNickeau    public function push($value)
88*04fd306cSNickeau    {
89*04fd306cSNickeau        array_push($this->stack, $value);
90*04fd306cSNickeau    }
91*04fd306cSNickeau
92*04fd306cSNickeau    /**
93*04fd306cSNickeau     * Push an Index onto the index stack
94*04fd306cSNickeau     *
95*04fd306cSNickeau     * @param integer $index Index of the current section item.
96*04fd306cSNickeau     *
97*04fd306cSNickeau     * @return void
98*04fd306cSNickeau     */
99*04fd306cSNickeau    public function pushIndex($index)
100*04fd306cSNickeau    {
101*04fd306cSNickeau        array_push($this->index, $index);
102*04fd306cSNickeau    }
103*04fd306cSNickeau
104*04fd306cSNickeau    /**
105*04fd306cSNickeau     * Pushes data variables onto the stack. This is used to support @data variables.
106*04fd306cSNickeau     * @param array $data Associative array where key is the name of the @data variable and value is the value.
107*04fd306cSNickeau     * @throws LogicException when calling this method without having enableDataVariables.
108*04fd306cSNickeau     */
109*04fd306cSNickeau    public function pushData($data)
110*04fd306cSNickeau    {
111*04fd306cSNickeau        if (!$this->enableDataVariables) {
112*04fd306cSNickeau            throw new LogicException('Data variables are not supported due to the enableDataVariables configuration. Remove the call to data variables or change the setting.');
113*04fd306cSNickeau        }
114*04fd306cSNickeau        array_push($this->dataStack, $data);
115*04fd306cSNickeau    }
116*04fd306cSNickeau
117*04fd306cSNickeau    /**
118*04fd306cSNickeau     * Push a Key onto the key stack
119*04fd306cSNickeau     *
120*04fd306cSNickeau     * @param string $key Key of the current object property.
121*04fd306cSNickeau     *
122*04fd306cSNickeau     * @return void
123*04fd306cSNickeau     */
124*04fd306cSNickeau    public function pushKey($key)
125*04fd306cSNickeau    {
126*04fd306cSNickeau        array_push($this->key, $key);
127*04fd306cSNickeau    }
128*04fd306cSNickeau
129*04fd306cSNickeau    /**
130*04fd306cSNickeau     * Pop the last Context frame from the stack.
131*04fd306cSNickeau     *
132*04fd306cSNickeau     * @return mixed Last Context frame (object or array)
133*04fd306cSNickeau     */
134*04fd306cSNickeau    public function pop()
135*04fd306cSNickeau    {
136*04fd306cSNickeau        return array_pop($this->stack);
137*04fd306cSNickeau    }
138*04fd306cSNickeau
139*04fd306cSNickeau    /**
140*04fd306cSNickeau     * Pop the last index from the stack.
141*04fd306cSNickeau     *
142*04fd306cSNickeau     * @return int Last index
143*04fd306cSNickeau     */
144*04fd306cSNickeau    public function popIndex()
145*04fd306cSNickeau    {
146*04fd306cSNickeau        return array_pop($this->index);
147*04fd306cSNickeau    }
148*04fd306cSNickeau
149*04fd306cSNickeau    /**
150*04fd306cSNickeau     * Pop the last section data from the stack.
151*04fd306cSNickeau     *
152*04fd306cSNickeau     * @return array Last data
153*04fd306cSNickeau     * @throws LogicException when calling this method without having enableDataVariables.
154*04fd306cSNickeau     */
155*04fd306cSNickeau    public function popData()
156*04fd306cSNickeau    {
157*04fd306cSNickeau        if (!$this->enableDataVariables) {
158*04fd306cSNickeau            throw new LogicException('Data variables are not supported due to the enableDataVariables configuration. Remove the call to data variables or change the setting.');
159*04fd306cSNickeau        }
160*04fd306cSNickeau        return array_pop($this->dataStack);
161*04fd306cSNickeau    }
162*04fd306cSNickeau
163*04fd306cSNickeau    /**
164*04fd306cSNickeau     * Pop the last key from the stack.
165*04fd306cSNickeau     *
166*04fd306cSNickeau     * @return string Last key
167*04fd306cSNickeau     */
168*04fd306cSNickeau    public function popKey()
169*04fd306cSNickeau    {
170*04fd306cSNickeau        return array_pop($this->key);
171*04fd306cSNickeau    }
172*04fd306cSNickeau
173*04fd306cSNickeau    /**
174*04fd306cSNickeau     * Get the last Context frame.
175*04fd306cSNickeau     *
176*04fd306cSNickeau     * @return mixed Last Context frame (object or array)
177*04fd306cSNickeau     */
178*04fd306cSNickeau    public function last()
179*04fd306cSNickeau    {
180*04fd306cSNickeau        return end($this->stack);
181*04fd306cSNickeau    }
182*04fd306cSNickeau
183*04fd306cSNickeau    /**
184*04fd306cSNickeau     * Get the index of current section item.
185*04fd306cSNickeau     *
186*04fd306cSNickeau     * @return mixed Last index
187*04fd306cSNickeau     */
188*04fd306cSNickeau    public function lastIndex()
189*04fd306cSNickeau    {
190*04fd306cSNickeau        return end($this->index);
191*04fd306cSNickeau    }
192*04fd306cSNickeau
193*04fd306cSNickeau    /**
194*04fd306cSNickeau     * Get the key of current object property.
195*04fd306cSNickeau     *
196*04fd306cSNickeau     * @return mixed Last key
197*04fd306cSNickeau     */
198*04fd306cSNickeau    public function lastKey()
199*04fd306cSNickeau    {
200*04fd306cSNickeau        return end($this->key);
201*04fd306cSNickeau    }
202*04fd306cSNickeau
203*04fd306cSNickeau    /**
204*04fd306cSNickeau     * Change the current context to one of current context members
205*04fd306cSNickeau     *
206*04fd306cSNickeau     * @param string $variableName name of variable or a callable on current context
207*04fd306cSNickeau     *
208*04fd306cSNickeau     * @return mixed actual value
209*04fd306cSNickeau     */
210*04fd306cSNickeau    public function with($variableName)
211*04fd306cSNickeau    {
212*04fd306cSNickeau        $value = $this->get($variableName);
213*04fd306cSNickeau        $this->push($value);
214*04fd306cSNickeau
215*04fd306cSNickeau        return $value;
216*04fd306cSNickeau    }
217*04fd306cSNickeau
218*04fd306cSNickeau    /**
219*04fd306cSNickeau     * Get a avariable from current context
220*04fd306cSNickeau     * Supported types :
221*04fd306cSNickeau     * variable , ../variable , variable.variable , .
222*04fd306cSNickeau     *
223*04fd306cSNickeau     * @param string  $variableName variavle name to get from current context
224*04fd306cSNickeau     * @param boolean $strict       strict search? if not found then throw exception
225*04fd306cSNickeau     *
226*04fd306cSNickeau     * @throws InvalidArgumentException in strict mode and variable not found
227*04fd306cSNickeau     * @return mixed
228*04fd306cSNickeau     */
229*04fd306cSNickeau    public function get($variableName, $strict = false)
230*04fd306cSNickeau    {
231*04fd306cSNickeau        //Need to clean up
232*04fd306cSNickeau        $variableName = trim($variableName);
233*04fd306cSNickeau
234*04fd306cSNickeau        //Handle data variables (@index, @first, @last, etc)
235*04fd306cSNickeau        if ($this->enableDataVariables && substr($variableName, 0, 1) == '@') {
236*04fd306cSNickeau            return $this->getDataVariable($variableName, $strict);
237*04fd306cSNickeau        }
238*04fd306cSNickeau
239*04fd306cSNickeau        $level = 0;
240*04fd306cSNickeau        while (substr($variableName, 0, 3) == '../') {
241*04fd306cSNickeau            $variableName = trim(substr($variableName, 3));
242*04fd306cSNickeau            $level++;
243*04fd306cSNickeau        }
244*04fd306cSNickeau        if (count($this->stack) < $level) {
245*04fd306cSNickeau            if ($strict) {
246*04fd306cSNickeau                throw new InvalidArgumentException(
247*04fd306cSNickeau                    'can not find variable in context'
248*04fd306cSNickeau                );
249*04fd306cSNickeau            }
250*04fd306cSNickeau
251*04fd306cSNickeau            return '';
252*04fd306cSNickeau        }
253*04fd306cSNickeau        end($this->stack);
254*04fd306cSNickeau        while ($level) {
255*04fd306cSNickeau            prev($this->stack);
256*04fd306cSNickeau            $level--;
257*04fd306cSNickeau        }
258*04fd306cSNickeau        $current = current($this->stack);
259*04fd306cSNickeau        if (!$variableName) {
260*04fd306cSNickeau            if ($strict) {
261*04fd306cSNickeau                throw new InvalidArgumentException(
262*04fd306cSNickeau                    'can not find variable in context'
263*04fd306cSNickeau                );
264*04fd306cSNickeau            }
265*04fd306cSNickeau            return '';
266*04fd306cSNickeau        } elseif ($variableName == '.' || $variableName == 'this') {
267*04fd306cSNickeau            return $current;
268*04fd306cSNickeau        } else {
269*04fd306cSNickeau            $chunks = explode('.', $variableName);
270*04fd306cSNickeau            foreach ($chunks as $chunk) {
271*04fd306cSNickeau                if (is_string($current) and $current == '') {
272*04fd306cSNickeau                    return $current;
273*04fd306cSNickeau                }
274*04fd306cSNickeau                $current = $this->findVariableInContext($current, $chunk, $strict);
275*04fd306cSNickeau            }
276*04fd306cSNickeau        }
277*04fd306cSNickeau        return $current;
278*04fd306cSNickeau    }
279*04fd306cSNickeau
280*04fd306cSNickeau    /**
281*04fd306cSNickeau     * Given a data variable, retrieves the value associated.
282*04fd306cSNickeau     *
283*04fd306cSNickeau     * @param $variableName
284*04fd306cSNickeau     * @param bool $strict
285*04fd306cSNickeau     * @return mixed
286*04fd306cSNickeau     * @throws LogicException when calling this method without having enableDataVariables.
287*04fd306cSNickeau     */
288*04fd306cSNickeau    public function getDataVariable($variableName, $strict = false)
289*04fd306cSNickeau    {
290*04fd306cSNickeau        if (!$this->enableDataVariables) {
291*04fd306cSNickeau            throw new LogicException('Data variables are not supported due to the enableDataVariables configuration. Remove the call to data variables or change the setting.');
292*04fd306cSNickeau        }
293*04fd306cSNickeau
294*04fd306cSNickeau        $variableName = trim($variableName);
295*04fd306cSNickeau
296*04fd306cSNickeau        // make sure we get an at-symbol prefix
297*04fd306cSNickeau        if (substr($variableName, 0, 1) != '@') {
298*04fd306cSNickeau            if ($strict) {
299*04fd306cSNickeau                throw new InvalidArgumentException(
300*04fd306cSNickeau                    'Can not find variable in context'
301*04fd306cSNickeau                );
302*04fd306cSNickeau            }
303*04fd306cSNickeau            return '';
304*04fd306cSNickeau        }
305*04fd306cSNickeau
306*04fd306cSNickeau        // Remove the at-symbol prefix
307*04fd306cSNickeau        $variableName = substr($variableName, 1);
308*04fd306cSNickeau
309*04fd306cSNickeau        // determine the level of relative @data variables
310*04fd306cSNickeau        $level = 0;
311*04fd306cSNickeau        while (substr($variableName, 0, 3) == '../') {
312*04fd306cSNickeau            $variableName = trim(substr($variableName, 3));
313*04fd306cSNickeau            $level++;
314*04fd306cSNickeau        }
315*04fd306cSNickeau
316*04fd306cSNickeau        // make sure the stack actually has the specified number of levels
317*04fd306cSNickeau        if (count($this->dataStack) < $level) {
318*04fd306cSNickeau            if ($strict) {
319*04fd306cSNickeau                throw new InvalidArgumentException(
320*04fd306cSNickeau                    'Can not find variable in context'
321*04fd306cSNickeau                );
322*04fd306cSNickeau            }
323*04fd306cSNickeau
324*04fd306cSNickeau            return '';
325*04fd306cSNickeau        }
326*04fd306cSNickeau
327*04fd306cSNickeau        // going from the top of the stack to the bottom, traverse the number of levels specified
328*04fd306cSNickeau        end($this->dataStack);
329*04fd306cSNickeau        while ($level) {
330*04fd306cSNickeau            prev($this->dataStack);
331*04fd306cSNickeau            $level--;
332*04fd306cSNickeau        }
333*04fd306cSNickeau
334*04fd306cSNickeau        /** @var array $current */
335*04fd306cSNickeau        $current = current($this->dataStack);
336*04fd306cSNickeau
337*04fd306cSNickeau        if (!array_key_exists($variableName, $current)) {
338*04fd306cSNickeau            if ($strict) {
339*04fd306cSNickeau                throw new InvalidArgumentException(
340*04fd306cSNickeau                    'Can not find variable in context'
341*04fd306cSNickeau                );
342*04fd306cSNickeau            }
343*04fd306cSNickeau
344*04fd306cSNickeau            return '';
345*04fd306cSNickeau        }
346*04fd306cSNickeau
347*04fd306cSNickeau        return $current[$variableName];
348*04fd306cSNickeau    }
349*04fd306cSNickeau
350*04fd306cSNickeau    /**
351*04fd306cSNickeau     * Check if $variable->$inside is available
352*04fd306cSNickeau     *
353*04fd306cSNickeau     * @param mixed   $variable variable to check
354*04fd306cSNickeau     * @param string  $inside   property/method to check
355*04fd306cSNickeau     * @param boolean $strict   strict search? if not found then throw exception
356*04fd306cSNickeau     *
357*04fd306cSNickeau     * @throws \InvalidArgumentException in strict mode and variable not found
358*04fd306cSNickeau     * @return boolean true if exist
359*04fd306cSNickeau     */
360*04fd306cSNickeau    private function findVariableInContext($variable, $inside, $strict = false)
361*04fd306cSNickeau    {
362*04fd306cSNickeau        $value = '';
363*04fd306cSNickeau        if (($inside !== '0' && empty($inside)) || ($inside == 'this')) {
364*04fd306cSNickeau            return $variable;
365*04fd306cSNickeau        } elseif (is_array($variable)) {
366*04fd306cSNickeau            if (isset($variable[$inside])) {
367*04fd306cSNickeau                $value = $variable[$inside];
368*04fd306cSNickeau            }
369*04fd306cSNickeau        } elseif (is_object($variable)) {
370*04fd306cSNickeau            if (isset($variable->$inside)) {
371*04fd306cSNickeau                $value = $variable->$inside;
372*04fd306cSNickeau            } elseif (is_callable(array($variable, $inside))) {
373*04fd306cSNickeau                $value = call_user_func(array($variable, $inside));
374*04fd306cSNickeau            }
375*04fd306cSNickeau        } elseif ($inside === '.') {
376*04fd306cSNickeau            $value = $variable;
377*04fd306cSNickeau        } elseif ($strict) {
378*04fd306cSNickeau            throw new InvalidArgumentException('can not find variable in context');
379*04fd306cSNickeau        }
380*04fd306cSNickeau        return $value;
381*04fd306cSNickeau    }
382*04fd306cSNickeau}
383