Passed
Push — 3.0 ( 84800e...b611c9 )
by Rubén
03:52
created

Template::includeTemplate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 2
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * sysPass
4
 *
5
 * @author    nuxsmin
6
 * @link      https://syspass.org
7
 * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org
8
 *
9
 * This file is part of sysPass.
10
 *
11
 * sysPass is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation, either version 3 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * sysPass is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 *  along with sysPass.  If not, see <http://www.gnu.org/licenses/>.
23
 */
24
25
namespace SP\Mvc\View;
26
27
defined('APP_ROOT') || die();
28
29
use SP\Bootstrap;
30
use SP\Core\Exceptions\FileNotFoundException;
31
use SP\Core\UI\ThemeInterface;
32
use SP\Http\Uri;
33
34
/**
35
 * Class Template
36
 *
37
 * A very basic template engine...
38
 *
39
 * Idea original de http://www.sitepoint.com/author/agervasio/
40
 * publicada en http://www.sitepoint.com/flexible-view-manipulation-1/
41
 *
42
 */
43
final class Template
44
{
45
    const TEMPLATE_EXTENSION = '.inc';
46
    const PARTIALS_DIR = '_partials';
47
    const LAYOUTS_DIR = '_layouts';
48
49
    /**
50
     * @var ThemeInterface
51
     */
52
    protected $theme;
53
    /**
54
     * @var array List of templates to load into the view
55
     */
56
    private $templates = [];
57
    /**
58
     * @var TemplateVarCollection Template's variables collection
59
     */
60
    private $vars;
61
    /**
62
     * @var string Base path for imcluding templates
63
     */
64
    private $base;
65
    /**
66
     * @var array
67
     */
68
    private $contentTemplates = [];
69
    /**
70
     * @var bool
71
     */
72
    private $upgraded = false;
73
74
    /**
75
     * @param ThemeInterface $theme
76
     */
77
    public function __construct(ThemeInterface $theme)
78
    {
79
        $this->theme = $theme;
80
        $this->vars = new TemplateVarCollection();
81
    }
82
83
    /**
84
     * Añadir una nueva plantilla al array de plantillas de la clase
85
     *
86
     * @param string $name Con el nombre del archivo de plantilla
87
     * @param string $base Directorio base para la plantilla
88
     *
89
     * @return bool
90
     */
91
    public function addContentTemplate($name, $base = null)
92
    {
93
        try {
94
            $template = $this->checkTemplate($name, $base);
95
            $this->setContentTemplate($template, $name);
96
        } catch (FileNotFoundException $e) {
97
            return '';
0 ignored issues
show
Bug Best Practice introduced by
The expression return '' returns the type string which is incompatible with the documented return type boolean.
Loading history...
98
        }
99
100
        return $template;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $template returns the type string which is incompatible with the documented return type boolean.
Loading history...
101
    }
102
103
    /**
104
     * Comprobar si un archivo de plantilla existe y se puede leer
105
     *
106
     * @param string $template Con el nombre del archivo
107
     *
108
     * @return string La ruta al archivo de la plantilla
109
     *
110
     * @param string $base     Directorio base para la plantilla
111
     *
112
     * @throws \SP\Core\Exceptions\FileNotFoundException
113
     */
114
    private function checkTemplate($template, $base = null)
115
    {
116
        $base = null !== $base ? $base : (null !== $this->base ? $this->base : null);
117
118
        if ($base === null) {
119
            $templateFile = $this->theme->getViewsPath() . DIRECTORY_SEPARATOR . $template . self::TEMPLATE_EXTENSION;
120
        } elseif (is_dir($base)) {
121
            $templateFile = $base . DIRECTORY_SEPARATOR . $template . self::TEMPLATE_EXTENSION;
122
        } else {
123
            $templateFile = $this->theme->getViewsPath() . DIRECTORY_SEPARATOR . $base . DIRECTORY_SEPARATOR . $template . self::TEMPLATE_EXTENSION;
124
        }
125
126
        if (!is_readable($templateFile)) {
127
            $msg = sprintf(__('Unable to retrieve "%s" template: %s'), $templateFile, $template);
128
129
            logger($msg);
130
131
            throw new FileNotFoundException($msg);
132
        }
133
134
        return $templateFile;
135
    }
136
137
    /**
138
     * Añadir un nuevo archivo de plantilla al array de plantillas de contenido
139
     *
140
     * @param string $file Con el nombre del archivo
141
     * @param string $name Nombre de la plantilla
142
     */
143
    private function setContentTemplate($file, $name)
144
    {
145
        $this->contentTemplates[$name] = $file;
146
    }
147
148
    /**
149
     * Removes a template from the stack
150
     *
151
     * @param $name
152
     *
153
     * @return Template
154
     */
155
    public function removeTemplate($name)
156
    {
157
        unset($this->templates[$name]);
158
159
        return $this;
160
    }
161
162
    /**
163
     * Removes a template from the stack
164
     *
165
     * @param $name
166
     *
167
     * @return Template
168
     */
169
    public function removeContentTemplate($name)
170
    {
171
        unset($this->contentTemplates[$name]);
172
173
        return $this;
174
    }
175
176
    /**
177
     * Removes a template from the stack
178
     *
179
     * @param string $src Source template
180
     * @param string $dst Destination template
181
     * @param string $base
182
     *
183
     * @return mixed|string
184
     */
185
    public function replaceTemplate($src, $dst, $base)
186
    {
187
        try {
188
            if (isset($this->contentTemplates[$dst])) {
189
                $this->contentTemplates[$dst] = $this->checkTemplate($src, $base);
190
            }
191
        } catch (FileNotFoundException $e) {
192
            return '';
193
        }
194
195
        return $this->contentTemplates[$dst];
196
    }
197
198
    /**
199
     * Add partial template
200
     *
201
     * @param $partial
202
     */
203
    public function addPartial($partial)
204
    {
205
        $this->addTemplate($partial, self::PARTIALS_DIR);
206
    }
207
208
    /**
209
     * Añadir una nueva plantilla al array de plantillas de la clase
210
     *
211
     * @param string $name Con el nombre del archivo de plantilla
212
     * @param string $base Directorio base para la plantilla
213
     *
214
     * @return bool
215
     */
216
    public function addTemplate($name, $base = null)
217
    {
218
        try {
219
            $template = $this->checkTemplate($name, $base);
220
            $this->setTemplate($template, $name);
221
        } catch (FileNotFoundException $e) {
222
            return '';
0 ignored issues
show
Bug Best Practice introduced by
The expression return '' returns the type string which is incompatible with the documented return type boolean.
Loading history...
223
        }
224
225
        return $template;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $template returns the type string which is incompatible with the documented return type boolean.
Loading history...
226
    }
227
228
    /**
229
     * Añadir un nuevo archivo de plantilla al array de plantillas
230
     *
231
     * @param string $file Con el nombre del archivo
232
     * @param string $name Nombre de la plantilla
233
     */
234
    private function setTemplate($file, $name)
235
    {
236
        $this->templates[$name] = $file;
237
    }
238
239
    /**
240
     * Añadir una nueva plantilla dentro de una plantilla
241
     *
242
     * @param string $file Con el nombre del archivo de plantilla
243
     *
244
     * @return bool
245
     */
246
    public function includePartial($file)
247
    {
248
        return $this->includeTemplate($file, self::PARTIALS_DIR);
249
    }
250
251
    /**
252
     * Añadir una nueva plantilla dentro de una plantilla
253
     *
254
     * @param string $file Con el nombre del archivo de plantilla
255
     * @param string $base Directorio base para la plantilla
256
     *
257
     * @return bool
258
     */
259
    public function includeTemplate($file, $base = null)
260
    {
261
        try {
262
            return $this->checkTemplate($file, $base);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->checkTemplate($file, $base) returns the type string which is incompatible with the documented return type boolean.
Loading history...
263
        } catch (FileNotFoundException $e) {
264
            return false;
265
        }
266
    }
267
268
    /**
269
     * Overloading para controlar la devolución de atributos dinámicos.
270
     *
271
     * @param string $name Nombre del atributo
272
     *
273
     * @return null
274
     */
275
    public function __get($name)
276
    {
277
        return $this->get($name);
278
    }
279
280
    /**
281
     * Overloading para añadir nuevas variables en al array de variables dela plantilla
282
     * pasadas como atributos dinámicos de la clase
283
     *
284
     * @param string $name  Nombre del atributo
285
     * @param string $value Valor del atributo
286
     */
287
    public function __set($name, $value)
288
    {
289
        $this->vars->set($name, $value);
290
    }
291
292
    /**
293
     * Returns a variable value
294
     *
295
     * @param $name
296
     *
297
     * @return mixed
298
     */
299
    public function get($name)
300
    {
301
        if (!$this->vars->exists($name)) {
302
            logger(sprintf(__('Unable to retrieve "%s" variable'), $name), 'ERROR');
303
304
            return null;
305
//            throw new InvalidArgumentException(sprintf(__('Unable to retrieve "%s" variable'), $name));
306
        }
307
308
        return $this->vars->get($name);
309
    }
310
311
    /**
312
     * Overloading para comprobar si el atributo solicitado está declarado como variable
313
     * en el array de variables de la plantilla.
314
     *
315
     * @param string $name Nombre del atributo
316
     *
317
     * @return bool
318
     */
319
    public function __isset($name)
320
    {
321
        return $this->vars->exists($name);
322
    }
323
324
    /**
325
     * Overloading para eliminar una variable del array de variables de la plantilla pasado como
326
     * atributo dinámico de la clase
327
     *
328
     * @param string $name Nombre del atributo
329
     *
330
     * @return $this
331
     */
332
    public function __unset($name)
333
    {
334
        if (!$this->vars->exists($name)) {
335
            logger(sprintf(__('Unable to unset "%s" variable'), $name));
336
337
//            throw new InvalidArgumentException(sprintf(__('Unable to unset "%s" variable'), $name));
338
            return $this;
339
        }
340
341
        $this->vars->remove($name);
342
343
        return $this;
344
    }
345
346
    /**
347
     * Mostrar la plantilla solicitada.
348
     * La salida se almacena en buffer y se devuelve el contenido
349
     *
350
     * @return string Con el contenido del buffer de salida
351
     * @throws FileNotFoundException
352
     */
353
    public function render()
354
    {
355
        if (empty($this->templates)) {
356
            throw new FileNotFoundException(__('Template does not contain files'));
357
        }
358
359
        $icons = $this->theme->getIcons();
0 ignored issues
show
Unused Code introduced by
The assignment to $icons is dead and can be removed.
Loading history...
360
        $configData = $this->vars->get('configData');
0 ignored issues
show
Unused Code introduced by
The assignment to $configData is dead and can be removed.
Loading history...
361
        $sk = $this->vars->get('sk');
362
363
        // An anonymous proxy function for handling views variables
364
        $_getvar = function ($key, $default = null) {
0 ignored issues
show
Unused Code introduced by
The assignment to $_getvar is dead and can be removed.
Loading history...
365
            if (DEBUG && !$this->vars->exists($key)) {
366
                logger(sprintf(__('Unable to retrieve "%s" variable'), $key), 'WARN');
367
368
                return $default;
369
            }
370
371
            return $this->vars->get($key, $default);
372
        };
373
374
        $_getRoute = function ($path) use ($sk) {
0 ignored issues
show
Unused Code introduced by
The assignment to $_getRoute is dead and can be removed.
Loading history...
375
            $uri = new Uri(Bootstrap::$WEBROOT . Bootstrap::$SUBURI);
376
            $uri->addParam('r', $path);
377
            $uri->addParam('sk', $sk);
378
379
            return $uri->getUri();
380
        };
381
382
        ob_start();
383
384
        // Añadimos las plantillas
385
        foreach ($this->templates as $template) {
386
            include_once $template;
387
        }
388
389
        return ob_get_clean();
390
    }
391
392
    /**
393
     * Anexar el valor de la variable al array de la misma en el array de variables
394
     *
395
     * @param      $name  string nombre de la variable
396
     * @param      $value mixed valor de la variable
397
     * @param      $index string índice del array
398
     * @param null $scope string ámbito de la variable
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $scope is correct as it would always require null to be passed?
Loading history...
399
     */
400
    public function append($name, $value, $scope = null, $index = null)
401
    {
402
        if (null !== $scope) {
0 ignored issues
show
introduced by
The condition null !== $scope is always false.
Loading history...
403
            $name = $scope . '_' . $name;
404
        }
405
406
        $var = $this->vars->get($name, []);
407
408
        if (null === $index) {
409
            $var[] = $value;
410
        } else {
411
            $var[$index] = $value;
412
        }
413
414
        $this->vars->set($name, $var);
415
    }
416
417
    /**
418
     * Reset de las plantillas añadidas
419
     */
420
    public function resetTemplates()
421
    {
422
        $this->templates = [];
423
424
        return $this;
425
    }
426
427
    /**
428
     * Reset de las plantillas añadidas
429
     */
430
    public function resetContentTemplates()
431
    {
432
        $this->contentTemplates = [];
433
434
        return $this;
435
    }
436
437
    /**
438
     * Reset de las plantillas añadidas
439
     */
440
    public function resetVariables()
441
    {
442
        $this->vars = [];
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type SP\Mvc\View\TemplateVarCollection of property $vars.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
443
    }
444
445
    /**
446
     * @return string
447
     */
448
    public function getBase()
449
    {
450
        return $this->base;
451
    }
452
453
    /**
454
     * @param string $base
455
     */
456
    public function setBase($base)
457
    {
458
        $this->base = $base;
459
    }
460
461
    /**
462
     * @return ThemeInterface
463
     */
464
    public function getTheme()
465
    {
466
        return $this->theme;
467
    }
468
469
    /**
470
     * Dumps current stored vars
471
     */
472
    public function dumpVars()
473
    {
474
        logger($this->vars);
475
    }
476
477
    /**
478
     * @return array
479
     */
480
    public function getContentTemplates()
481
    {
482
        return $this->contentTemplates;
483
    }
484
485
    /**
486
     * @return bool
487
     */
488
    public function hashContentTemplates()
489
    {
490
        return count($this->contentTemplates) > 0;
491
    }
492
493
    /**
494
     * @return array
495
     */
496
    public function getTemplates()
497
    {
498
        return $this->templates;
499
    }
500
501
    /**
502
     * Assigns the current templates to contentTemplates
503
     *
504
     * @return $this
505
     */
506
    public function upgrade()
507
    {
508
        if (count($this->templates) > 0) {
509
            $this->contentTemplates = $this->templates;
510
511
            $this->templates = [];
512
513
            $this->upgraded = true;
514
        }
515
516
        return $this;
517
    }
518
519
    /**
520
     * Crear la variable y asignarle un valor en el array de variables
521
     *
522
     * @param      $name  string nombre de la variable
523
     * @param      $value mixed valor de la variable
524
     * @param null $scope string ámbito de la variable
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $scope is correct as it would always require null to be passed?
Loading history...
525
     */
526
    public function assign($name, $value = '', $scope = null)
527
    {
528
        if (null !== $scope) {
0 ignored issues
show
introduced by
The condition null !== $scope is always false.
Loading history...
529
            $name = $scope . '_' . $name;
530
        }
531
532
        $this->vars->set($name, $value);
533
    }
534
535
    /**
536
     * @return bool
537
     */
538
    public function isUpgraded()
539
    {
540
        return $this->upgraded;
541
    }
542
543
    /**
544
     * When an object is cloned, PHP 5 will perform a shallow copy of all of the object's properties.
545
     * Any properties that are references to other variables, will remain references.
546
     * Once the cloning is complete, if a __clone() method is defined,
547
     * then the newly created object's __clone() method will be called, to allow any necessary properties that need to be changed.
548
     * NOT CALLABLE DIRECTLY.
549
     *
550
     * @link https://php.net/manual/en/language.oop5.cloning.php
551
     */
552
    public function __clone()
553
    {
554
        // Clone TemplateVarCollection to avoid unwanted object references
555
        $this->vars = clone $this->vars;
556
    }
557
558
}
559