PageRenderTime 226ms CodeModel.GetById 81ms app.highlight 6ms RepoModel.GetById 54ms app.codeStats 1ms

/lib/mustache/src/Mustache/Context.php

https://bitbucket.org/moodle/moodle
PHP | 242 lines | 101 code | 26 blank | 115 comment | 13 complexity | 2588f5b13f49adf3973ab29867f1ad78 MD5 | raw file
  1<?php
  2
  3/*
  4 * This file is part of Mustache.php.
  5 *
  6 * (c) 2010-2017 Justin Hileman
  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 * Mustache Template rendering Context.
 14 */
 15class Mustache_Context
 16{
 17    private $stack      = array();
 18    private $blockStack = array();
 19
 20    /**
 21     * Mustache rendering Context constructor.
 22     *
 23     * @param mixed $context Default rendering context (default: null)
 24     */
 25    public function __construct($context = null)
 26    {
 27        if ($context !== null) {
 28            $this->stack = array($context);
 29        }
 30    }
 31
 32    /**
 33     * Push a new Context frame onto the stack.
 34     *
 35     * @param mixed $value Object or array to use for context
 36     */
 37    public function push($value)
 38    {
 39        array_push($this->stack, $value);
 40    }
 41
 42    /**
 43     * Push a new Context frame onto the block context stack.
 44     *
 45     * @param mixed $value Object or array to use for block context
 46     */
 47    public function pushBlockContext($value)
 48    {
 49        array_push($this->blockStack, $value);
 50    }
 51
 52    /**
 53     * Pop the last Context frame from the stack.
 54     *
 55     * @return mixed Last Context frame (object or array)
 56     */
 57    public function pop()
 58    {
 59        return array_pop($this->stack);
 60    }
 61
 62    /**
 63     * Pop the last block Context frame from the stack.
 64     *
 65     * @return mixed Last block Context frame (object or array)
 66     */
 67    public function popBlockContext()
 68    {
 69        return array_pop($this->blockStack);
 70    }
 71
 72    /**
 73     * Get the last Context frame.
 74     *
 75     * @return mixed Last Context frame (object or array)
 76     */
 77    public function last()
 78    {
 79        return end($this->stack);
 80    }
 81
 82    /**
 83     * Find a variable in the Context stack.
 84     *
 85     * Starting with the last Context frame (the context of the innermost section), and working back to the top-level
 86     * rendering context, look for a variable with the given name:
 87     *
 88     *  * If the Context frame is an associative array which contains the key $id, returns the value of that element.
 89     *  * If the Context frame is an object, this will check first for a public method, then a public property named
 90     *    $id. Failing both of these, it will try `__isset` and `__get` magic methods.
 91     *  * If a value named $id is not found in any Context frame, returns an empty string.
 92     *
 93     * @param string $id Variable name
 94     *
 95     * @return mixed Variable value, or '' if not found
 96     */
 97    public function find($id)
 98    {
 99        return $this->findVariableInStack($id, $this->stack);
100    }
101
102    /**
103     * Find a 'dot notation' variable in the Context stack.
104     *
105     * Note that dot notation traversal bubbles through scope differently than the regular find method. After finding
106     * the initial chunk of the dotted name, each subsequent chunk is searched for only within the value of the previous
107     * result. For example, given the following context stack:
108     *
109     *     $data = array(
110     *         'name' => 'Fred',
111     *         'child' => array(
112     *             'name' => 'Bob'
113     *         ),
114     *     );
115     *
116     * ... and the Mustache following template:
117     *
118     *     {{ child.name }}
119     *
120     * ... the `name` value is only searched for within the `child` value of the global Context, not within parent
121     * Context frames.
122     *
123     * @param string $id Dotted variable selector
124     *
125     * @return mixed Variable value, or '' if not found
126     */
127    public function findDot($id)
128    {
129        $chunks = explode('.', $id);
130        $first  = array_shift($chunks);
131        $value  = $this->findVariableInStack($first, $this->stack);
132
133        foreach ($chunks as $chunk) {
134            if ($value === '') {
135                return $value;
136            }
137
138            $value = $this->findVariableInStack($chunk, array($value));
139        }
140
141        return $value;
142    }
143
144    /**
145     * Find an 'anchored dot notation' variable in the Context stack.
146     *
147     * This is the same as findDot(), except it looks in the top of the context
148     * stack for the first value, rather than searching the whole context stack
149     * and starting from there.
150     *
151     * @see Mustache_Context::findDot
152     *
153     * @throws Mustache_Exception_InvalidArgumentException if given an invalid anchored dot $id
154     *
155     * @param string $id Dotted variable selector
156     *
157     * @return mixed Variable value, or '' if not found
158     */
159    public function findAnchoredDot($id)
160    {
161        $chunks = explode('.', $id);
162        $first  = array_shift($chunks);
163        if ($first !== '') {
164            throw new Mustache_Exception_InvalidArgumentException(sprintf('Unexpected id for findAnchoredDot: %s', $id));
165        }
166
167        $value  = $this->last();
168
169        foreach ($chunks as $chunk) {
170            if ($value === '') {
171                return $value;
172            }
173
174            $value = $this->findVariableInStack($chunk, array($value));
175        }
176
177        return $value;
178    }
179
180    /**
181     * Find an argument in the block context stack.
182     *
183     * @param string $id
184     *
185     * @return mixed Variable value, or '' if not found
186     */
187    public function findInBlock($id)
188    {
189        foreach ($this->blockStack as $context) {
190            if (array_key_exists($id, $context)) {
191                return $context[$id];
192            }
193        }
194
195        return '';
196    }
197
198    /**
199     * Helper function to find a variable in the Context stack.
200     *
201     * @see Mustache_Context::find
202     *
203     * @param string $id    Variable name
204     * @param array  $stack Context stack
205     *
206     * @return mixed Variable value, or '' if not found
207     */
208    private function findVariableInStack($id, array $stack)
209    {
210        for ($i = count($stack) - 1; $i >= 0; $i--) {
211            $frame = &$stack[$i];
212
213            switch (gettype($frame)) {
214                case 'object':
215                    if (!($frame instanceof Closure)) {
216                        // Note that is_callable() *will not work here*
217                        // See https://github.com/bobthecow/mustache.php/wiki/Magic-Methods
218                        if (method_exists($frame, $id)) {
219                            return $frame->$id();
220                        }
221
222                        if (isset($frame->$id)) {
223                            return $frame->$id;
224                        }
225
226                        if ($frame instanceof ArrayAccess && isset($frame[$id])) {
227                            return $frame[$id];
228                        }
229                    }
230                    break;
231
232                case 'array':
233                    if (array_key_exists($id, $frame)) {
234                        return $frame[$id];
235                    }
236                    break;
237            }
238        }
239
240        return '';
241    }
242}