Completed
Push — components-poc ( 6ce8f4...e1010e )
by
unknown
13:39
created

Template   A

Complexity

Total Complexity 40

Size/Duplication

Total Lines 372
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 40
lcom 1
cbo 3
dl 0
loc 372
rs 9.2
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A __call() 0 4 1
A __toString() 0 4 1
A data() 0 8 2
A exists() 0 4 1
A path() 0 4 1
B render() 0 41 7
A layout() 0 5 1
A start() 0 16 3
A push() 0 6 1
A unshift() 0 6 1
B stop() 0 31 6
A end() 0 4 1
A section() 0 8 2
A fetch() 0 4 1
A insert() 0 4 1
A batch() 0 16 4
A escape() 0 14 4
A e() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Template often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Template, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace League\Plates\Template;
4
5
use Exception;
6
use League\Plates\Engine;
7
use LogicException;
8
use Throwable;
9
10
/**
11
 * Container which holds template data and provides access to template functions.
12
 */
13
class Template
14
{
15
    const SECTION_MODE_REWRITE = 1;
16
    const SECTION_MODE_PREPEND = 2;
17
    const SECTION_MODE_APPEND = 3;
18
19
    /**
20
     * Set section content mode: rewrite/append/prepend
21
     * @var int
22
     */
23
    protected $sectionMode = self::SECTION_MODE_REWRITE;
24
25
    /**
26
     * Instance of the template engine.
27
     * @var Engine
28
     */
29
    protected $engine;
30
31
    /**
32
     * The name of the template.
33
     * @var Name
34
     */
35
    protected $name;
36
37
    /**
38
     * The data assigned to the template.
39
     * @var array
40
     */
41
    protected $data = array();
42
43
    /**
44
     * An array of section content.
45
     * @var array
46
     */
47
    protected $sections = array();
48
49
    /**
50
     * The name of the section currently being rendered.
51
     * @var string
52
     */
53
    protected $sectionName;
54
55
    /**
56
     * Whether the section should be appended or not.
57
     * @deprecated stayed for backward compatibility, use $sectionMode instead
58
     * @var boolean
59
     */
60
    protected $appendSection;
61
62
    /**
63
     * The name of the template layout.
64
     * @var string
65
     */
66
    protected $layoutName;
67
68
    /**
69
     * The data assigned to the template layout.
70
     * @var array
71
     */
72
    protected $layoutData;
73
74
    /**
75
     * Create new Template instance.
76
     * @param Engine $engine
77
     * @param string $name
78
     */
79
    public function __construct(Engine $engine, $name)
80
    {
81
        $this->engine = $engine;
82
        $this->name = new Name($engine, $name);
83
84
        $this->data($this->engine->getData($name));
85
    }
86
87
    /**
88
     * Magic method used to call extension functions.
89
     * @param  string $name
90
     * @param  array  $arguments
91
     * @return mixed
92
     */
93
    public function __call($name, $arguments)
94
    {
95
        return $this->engine->getFunction($name)->call($this, $arguments);
96
    }
97
98
    /**
99
     * Alias for render() method.
100
     * @throws \Throwable
101
     * @throws \Exception
102
     * @return string
103
     */
104
    public function __toString()
105
    {
106
        return $this->render();
107
    }
108
109
    /**
110
     * Assign or get template data.
111
     * @param  array $data
112
     * @return mixed
113
     */
114
    public function data(array $data = null)
115
    {
116
        if (is_null($data)) {
117
            return $this->data;
118
        }
119
120
        $this->data = array_merge($this->data, $data);
121
    }
122
123
    /**
124
     * Check if the template exists.
125
     * @return boolean
126
     */
127
    public function exists()
128
    {
129
        return $this->name->doesPathExist();
130
    }
131
132
    /**
133
     * Get the template path.
134
     * @return string
135
     */
136
    public function path()
137
    {
138
        return $this->name->getPath();
139
    }
140
141
    /**
142
     * Render the template and layout.
143
     * @param  array  $data
144
     * @throws \Throwable
145
     * @throws \Exception
146
     * @return string
147
     */
148
    public function render(array $data = array())
149
    {
150
        $this->data($data);
151
        unset($data);
152
        extract($this->data);
153
154
        if (!$this->exists()) {
155
            throw new LogicException(
156
                'The template "' . $this->name->getName() . '" could not be found at "' . $this->path() . '".'
157
            );
158
        }
159
160
        try {
161
            $level = ob_get_level();
162
            ob_start();
163
164
            include $this->path();
165
166
            $content = ob_get_clean();
167
168
            if (isset($this->layoutName)) {
169
                $layout = $this->engine->make($this->layoutName);
170
                $layout->sections = array_merge($this->sections, array('content' => $content));
171
                $content = $layout->render($this->layoutData);
172
            }
173
174
            return $content;
175
        } catch (Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
176
            while (ob_get_level() > $level) {
177
                ob_end_clean();
178
            }
179
180
            throw $e;
181
        } catch (Exception $e) {
182
            while (ob_get_level() > $level) {
183
                ob_end_clean();
184
            }
185
186
            throw $e;
187
        }
188
    }
189
190
    /**
191
     * Set the template's layout.
192
     * @param  string $name
193
     * @param  array  $data
194
     * @return null
195
     */
196
    public function layout($name, array $data = array())
197
    {
198
        $this->layoutName = $name;
199
        $this->layoutData = $data;
200
    }
201
202
    /**
203
     * Start a new section block.
204
     * @param  string  $name
205
     * @return null
206
     */
207
    public function start($name)
208
    {
209
        if ($name === 'content') {
210
            throw new LogicException(
211
                '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
219
        $this->sectionName = $name;
220
221
        ob_start();
222
    }
223
224
    /**
225
     * Start a new section block in APPEND mode.
226
     * @param  string $name
227
     * @return null
228
     */
229
    public function push($name)
230
    {
231
        $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
        $this->sectionMode = self::SECTION_MODE_APPEND;
233
        $this->start($name);
234
    }
235
236
    /**
237
     * Start a new section block in PREPEND mode.
238
     * @param  string $name
239
     * @return null
240
     */
241
    public function unshift($name)
242
    {
243
        $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
        $this->sectionMode = self::SECTION_MODE_PREPEND;
245
        $this->start($name);
246
    }
247
248
    /**
249
     * Stop the current section block.
250
     * @return null
251
     */
252
    public function stop()
253
    {
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
            $this->sections[$this->sectionName] = '';
262
        }
263
264
        switch ($this->sectionMode) {
265
266
            case self::SECTION_MODE_REWRITE:
267
                $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
                break;
277
278
        }
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
     */
288
    public function end()
289
    {
290
        $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
     */
299
    public function section($name, $default = null)
300
    {
301
        if (!isset($this->sections[$name])) {
302
            return $default;
303
        }
304
305
        return $this->sections[$name];
306
    }
307
308
    /**
309
     * Fetch a rendered template.
310
     * @param  string $name
311
     * @param  array  $data
312
     * @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
     * @param  string $name
322
     * @param  array  $data
323
     * @return null
324
     */
325
    public function insert($name, array $data = array())
326
    {
327
        echo $this->engine->render($name, $data);
328
    }
329
330
    /**
331
     * Apply multiple functions to variable.
332
     * @param  mixed  $var
333
     * @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
                $var = call_user_func($function, $var);
343
            } else {
344
                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