Completed
Pull Request — v3 (#279)
by Gabriel
11:00
created

Template::render()   A

Complexity

Conditions 4
Paths 16

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4.5923

Importance

Changes 0
Metric Value
dl 0
loc 31
ccs 14
cts 21
cp 0.6667
rs 9.424
c 0
b 0
f 0
cc 4
nc 16
nop 1
crap 4.5923
1
<?php
2
3
namespace League\Plates\Template;
4
5
use Exception;
6
use League\Plates\Engine;
7
use League\Plates\Exception\TemplateNotFound;
8
use LogicException;
9
use Throwable;
10
11
/**
12
 * Container which holds template data and provides access to template functions.
13
 */
14
class Template
15
{
16
    const SECTION_MODE_REWRITE = 1;
17
    const SECTION_MODE_PREPEND = 2;
18
    const SECTION_MODE_APPEND = 3;
19
20
    /**
21
     * Set section content mode: rewrite/append/prepend
22
     * @var int
23
     */
24
    protected $sectionMode = self::SECTION_MODE_REWRITE;
25
26
    /**
27
     * Instance of the template engine.
28
     * @var Engine
29
     */
30
    protected $engine;
31
32
    /**
33
     * The name of the template.
34
     * @var Name
35
     */
36
    protected $name;
37
38
    /**
39
     * The data assigned to the template.
40
     * @var array
41
     */
42
    protected $data = array();
43
44
    /**
45
     * An array of section content.
46
     * @var array
47
     */
48
    protected $sections = array();
49
50
    /**
51
     * The name of the section currently being rendered.
52
     * @var string
53
     */
54
    protected $sectionName;
55
56
    /**
57
     * Whether the section should be appended or not.
58
     * @deprecated stayed for backward compatibility, use $sectionMode instead
59
     * @var boolean
60
     */
61
    protected $appendSection;
62
63
    /**
64
     * The name of the template layout.
65
     * @var string
66
     */
67
    protected $layoutName;
68 62
69
    /**
70 62
     * The data assigned to the template layout.
71 62
     * @var array
72
     */
73 62
    protected $layoutData;
74 62
75
    /**
76
     * Create new Template instance.
77
     * @param Engine $engine
78
     * @param string $name
79
     */
80
    public function __construct(Engine $engine, $name)
81
    {
82 4
        $this->engine = $engine;
83
        $this->name = new Name($engine, $name);
84 4
85
        $this->data($this->engine->getData($name));
86
    }
87
88
    /**
89
     * Magic method used to call extension functions.
90
     * @param  string $name
91
     * @param  array  $arguments
92
     * @return mixed
93 2
     */
94
    public function __call($name, $arguments)
95 2
    {
96
        return $this->engine->getFunction($name)->call($this, $arguments);
97
    }
98
99
    /**
100
     * Alias for render() method.
101
     * @throws \Throwable
102
     * @throws \Exception
103 62
     * @return string
104
     */
105 62
    public function __toString()
106 2
    {
107
        return $this->render();
108
    }
109 62
110 62
    /**
111
     * Assign or get template data.
112
     * @param  array $data
113
     * @return mixed
114
     */
115
    public function data(array $data = null)
116 54
    {
117
        if (is_null($data)) {
118 54
            return $this->data;
119
        }
120
121
        $this->data = array_merge($this->data, $data);
122
    }
123
124
    /**
125 52
     * Check if the template exists.
126
     * @return boolean
127 52
     */
128
    public function exists()
129
    {
130
        try {
131
            ($this->engine->getResolveTemplatePath())($this->name);
132
            return true;
133
        } catch (TemplateNotFound $e) {
134
            return false;
135
        }
136
    }
137 50
138
    /**
139 50
     * Get the template path.
140 50
     * @return string
141 50
     */
142
    public function path()
143 50
    {
144 2
        try {
145 2
            return ($this->engine->getResolveTemplatePath())($this->name);
146 2
        } catch (TemplateNotFound $e) {
147
            return $e->paths()[0];
148
        }
149
    }
150 48
151 48
    /**
152
     * Render the template and layout.
153 48
     * @param  array  $data
154
     * @throws \Throwable
155 38
     * @throws \Exception
156
     * @return string
157 38
     */
158 12
    public function render(array $data = array())
159 12
    {
160 12
        $this->data($data);
161 12
        unset($data);
162
        extract($this->data);
163 38
164 10
        $path = ($this->engine->getResolveTemplatePath())($this->name);
165
166
        try {
167
            $level = ob_get_level();
168
            ob_start();
169
170 10
            include $path;
171 10
172 10
            $content = ob_get_clean();
173 10
174
            if (isset($this->layoutName)) {
175 10
                $layout = $this->engine->make($this->layoutName);
176
                $layout->sections = array_merge($this->sections, array('content' => $content));
177
                $content = $layout->render($this->layoutData);
178
            }
179
180
            return $content;
181
        } catch (Throwable $e) {
182
            while (ob_get_level() > $level) {
183
                ob_end_clean();
184
            }
185 12
186
            throw $e;
187 12
        }
188 12
    }
189 12
190
    /**
191
     * Set the template's layout.
192
     * @param  string $name
193
     * @param  array  $data
194
     * @return null
195
     */
196 12
    public function layout($name, array $data = array())
197
    {
198 12
        $this->layoutName = $name;
199 2
        $this->layoutData = $data;
200
    }
201 2
202
    /**
203
     * Start a new section block.
204 10
     * @param  string  $name
205 2
     * @return null
206
     */
207
    public function start($name)
208 10
    {
209
        if ($name === 'content') {
210 10
            throw new LogicException(
211 10
                'The section name "content" is reserved.'
212
            );
213
        }
214
215
        if ($this->sectionName) {
216
            throw new LogicException('You cannot nest sections within other sections.');
217
        }
218 4
219
        $this->sectionName = $name;
220 4
221
        ob_start();
222 4
    }
223 4
224
    /**
225
     * Start a new section block in APPEND mode.
226
     * @param  string $name
227
     * @return null
228
     */
229 10
    public function push($name)
230
    {
231 10
        $this->appendSection = true; /* for backward compatibility */
0 ignored issues
show
Deprecated Code introduced by
The property League\Plates\Template\Template::$appendSection has been deprecated with message: stayed for backward compatibility, use $sectionMode instead

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
232 2
        $this->sectionMode = self::SECTION_MODE_APPEND;
233
        $this->start($name);
234 2
    }
235
236
    /**
237 8
     * Start a new section block in PREPEND mode.
238 8
     * @param  string $name
239 8
     * @return null
240
     */
241 8
    public function unshift($name)
242 8
    {
243 8
        $this->appendSection = false; /* for backward compatibility */
0 ignored issues
show
Deprecated Code introduced by
The property League\Plates\Template\Template::$appendSection has been deprecated with message: stayed for backward compatibility, use $sectionMode instead

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
244 8
        $this->sectionMode = self::SECTION_MODE_PREPEND;
245
        $this->start($name);
246
    }
247
248
    /**
249
     * Stop the current section block.
250 4
     * @return null
251
     */
252 4
    public function stop()
253 4
    {
254
        if (is_null($this->sectionName)) {
255
            throw new LogicException(
256
                'You must start a section before you can stop it.'
257
            );
258
        }
259
260
        if (!isset($this->sections[$this->sectionName])) {
261 12
            $this->sections[$this->sectionName] = '';
262
        }
263 12
264 4
        switch ($this->sectionMode) {
265
266
            case self::SECTION_MODE_REWRITE:
267 8
                $this->sections[$this->sectionName] = ob_get_clean();
268
                break;
269
270
            case self::SECTION_MODE_APPEND:
271
                $this->sections[$this->sectionName] .= ob_get_clean();
272
                break;
273
274
            case self::SECTION_MODE_PREPEND:
275
                $this->sections[$this->sectionName] = ob_get_clean().$this->sections[$this->sectionName];
276 2
                break;
277
278 2
        }
279
        $this->sectionName = null;
280
        $this->sectionMode = self::SECTION_MODE_REWRITE;
281
        $this->appendSection = false; /* for backward compatibility */
0 ignored issues
show
Deprecated Code introduced by
The property League\Plates\Template\Template::$appendSection has been deprecated with message: stayed for backward compatibility, use $sectionMode instead

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
282
    }
283
284
    /**
285
     * Alias of stop().
286
     * @return null
287 2
     */
288
    public function end()
289 2
    {
290 2
        $this->stop();
291
    }
292
293
    /**
294
     * Returns the content for a section block.
295
     * @param  string      $name    Section name
296
     * @param  string      $default Default section content
297
     * @return string|null
298 6
     */
299
    public function section($name, $default = null)
300 6
    {
301 6
        if (!isset($this->sections[$name])) {
302 2
            return $default;
303 6
        }
304 4
305 4
        return $this->sections[$name];
306 2
    }
307 2
308 2
    /**
309
     * Fetch a rendered template.
310 4
     * @param  string $name
311
     * @param  array  $data
312 4
     * @return string
313
     */
314
    public function fetch($name, array $data = array())
315
    {
316
        return $this->engine->render($name, $data);
317
    }
318
319
    /**
320
     * Output a rendered template.
321 6
     * @param  string $name
322
     * @param  array  $data
323 6
     * @return null
324
     */
325 6
    public function insert($name, array $data = array())
326 2
    {
327 2
        echo $this->engine->render($name, $data);
328
    }
329 6
330 2
    /**
331 2
     * Apply multiple functions to variable.
332
     * @param  mixed  $var
333 6
     * @param  string $functions
334
     * @return mixed
335
     */
336
    public function batch($var, $functions)
337
    {
338
        foreach (explode('|', $functions) as $function) {
339
            if ($this->engine->doesFunctionExist($function)) {
340
                $var = call_user_func(array($this, $function), $var);
341
            } elseif (is_callable($function)) {
342 2
                $var = call_user_func($function, $var);
343
            } else {
344 2
                throw new LogicException(
345
                    'The batch function could not find the "' . $function . '" function.'
346
                );
347
            }
348
        }
349
350
        return $var;
351
    }
352
353
    /**
354
     * Escape string.
355
     * @param  string      $string
356
     * @param  null|string $functions
357
     * @return string
358
     */
359
    public function escape($string, $functions = null)
360
    {
361
        static $flags;
362
363
        if (!isset($flags)) {
364
            $flags = ENT_QUOTES | (defined('ENT_SUBSTITUTE') ? ENT_SUBSTITUTE : 0);
365
        }
366
367
        if ($functions) {
368
            $string = $this->batch($string, $functions);
369
        }
370
371
        return htmlspecialchars($string, $flags, 'UTF-8');
372
    }
373
374
    /**
375
     * Alias to escape function.
376
     * @param  string      $string
377
     * @param  null|string $functions
378
     * @return string
379
     */
380
    public function e($string, $functions = null)
381
    {
382
        return $this->escape($string, $functions);
383
    }
384
}
385