Template::assign()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 3
dl 0
loc 7
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-2019, 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
     * @param string $base     Directorio base para la plantilla
108
     *
109
     * @return string La ruta al archivo de la plantilla
110
     *
111
     * @throws FileNotFoundException
112
     */
113
    private function checkTemplate($template, $base = null)
114
    {
115
        $base = null !== $base ? $base : (null !== $this->base ? $this->base : null);
116
117
        if ($base === null) {
118
            $templateFile = $this->theme->getViewsPath() . DIRECTORY_SEPARATOR . $template . self::TEMPLATE_EXTENSION;
119
        } elseif (strpos($base, APP_ROOT) === 0
120
            && is_dir($base)
121
        ) {
122
            $templateFile = $base . DIRECTORY_SEPARATOR . $template . self::TEMPLATE_EXTENSION;
123
        } else {
124
            $templateFile = $this->theme->getViewsPath() . DIRECTORY_SEPARATOR . $base . DIRECTORY_SEPARATOR . $template . self::TEMPLATE_EXTENSION;
125
        }
126
127
        if (!is_readable($templateFile)) {
128
            $msg = sprintf(__('Unable to retrieve "%s" template: %s'), $templateFile, $template);
129
130
            logger($msg);
131
132
            throw new FileNotFoundException($msg);
133
        }
134
135
        return $templateFile;
136
    }
137
138
    /**
139
     * Añadir un nuevo archivo de plantilla al array de plantillas de contenido
140
     *
141
     * @param string $file Con el nombre del archivo
142
     * @param string $name Nombre de la plantilla
143
     */
144
    private function setContentTemplate($file, $name)
145
    {
146
        $this->contentTemplates[$name] = $file;
147
    }
148
149
    /**
150
     * Removes a template from the stack
151
     *
152
     * @param $name
153
     *
154
     * @return Template
155
     */
156
    public function removeTemplate($name)
157
    {
158
        unset($this->templates[$name]);
159
160
        return $this;
161
    }
162
163
    /**
164
     * Removes a template from the stack
165
     *
166
     * @param $name
167
     *
168
     * @return Template
169
     */
170
    public function removeContentTemplate($name)
171
    {
172
        unset($this->contentTemplates[$name]);
173
174
        return $this;
175
    }
176
177
    /**
178
     * Removes a template from the stack
179
     *
180
     * @param string $src Source template
181
     * @param string $dst Destination template
182
     * @param string $base
183
     *
184
     * @return mixed|string
185
     */
186
    public function replaceTemplate($src, $dst, $base)
187
    {
188
        try {
189
            if (isset($this->contentTemplates[$dst])) {
190
                $this->contentTemplates[$dst] = $this->checkTemplate($src, $base);
191
            }
192
        } catch (FileNotFoundException $e) {
193
            return '';
194
        }
195
196
        return $this->contentTemplates[$dst];
197
    }
198
199
    /**
200
     * Add partial template
201
     *
202
     * @param $partial
203
     */
204
    public function addPartial($partial)
205
    {
206
        $this->addTemplate($partial, self::PARTIALS_DIR);
207
    }
208
209
    /**
210
     * Añadir una nueva plantilla al array de plantillas de la clase
211
     *
212
     * @param string $name Con el nombre del archivo de plantilla
213
     * @param string $base Directorio base para la plantilla
214
     *
215
     * @return bool
216
     */
217
    public function addTemplate($name, $base = null)
218
    {
219
        try {
220
            $template = $this->checkTemplate($name, $base);
221
            $this->setTemplate($template, $name);
222
        } catch (FileNotFoundException $e) {
223
            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...
224
        }
225
226
        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...
227
    }
228
229
    /**
230
     * Añadir un nuevo archivo de plantilla al array de plantillas
231
     *
232
     * @param string $file Con el nombre del archivo
233
     * @param string $name Nombre de la plantilla
234
     */
235
    private function setTemplate($file, $name)
236
    {
237
        $this->templates[$name] = $file;
238
    }
239
240
    /**
241
     * Añadir una nueva plantilla dentro de una plantilla
242
     *
243
     * @param string $file Con el nombre del archivo de plantilla
244
     *
245
     * @return bool
246
     */
247
    public function includePartial($file)
248
    {
249
        return $this->includeTemplate($file, self::PARTIALS_DIR);
250
    }
251
252
    /**
253
     * Añadir una nueva plantilla dentro de una plantilla
254
     *
255
     * @param string $file Con el nombre del archivo de plantilla
256
     * @param string $base Directorio base para la plantilla
257
     *
258
     * @return bool
259
     */
260
    public function includeTemplate($file, $base = null)
261
    {
262
        try {
263
            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...
264
        } catch (FileNotFoundException $e) {
265
            return false;
266
        }
267
    }
268
269
    /**
270
     * Overloading para controlar la devolución de atributos dinámicos.
271
     *
272
     * @param string $name Nombre del atributo
273
     *
274
     * @return null
275
     */
276
    public function __get($name)
277
    {
278
        return $this->get($name);
279
    }
280
281
    /**
282
     * Overloading para añadir nuevas variables en al array de variables dela plantilla
283
     * pasadas como atributos dinámicos de la clase
284
     *
285
     * @param string $name  Nombre del atributo
286
     * @param string $value Valor del atributo
287
     */
288
    public function __set($name, $value)
289
    {
290
        $this->vars->set($name, $value);
291
    }
292
293
    /**
294
     * Returns a variable value
295
     *
296
     * @param $name
297
     *
298
     * @return mixed
299
     */
300
    public function get($name)
301
    {
302
        if (!$this->vars->exists($name)) {
303
            logger(sprintf(__('Unable to retrieve "%s" variable'), $name), 'ERROR');
304
305
            return null;
306
//            throw new InvalidArgumentException(sprintf(__('Unable to retrieve "%s" variable'), $name));
307
        }
308
309
        return $this->vars->get($name);
310
    }
311
312
    /**
313
     * Overloading para comprobar si el atributo solicitado está declarado como variable
314
     * en el array de variables de la plantilla.
315
     *
316
     * @param string $name Nombre del atributo
317
     *
318
     * @return bool
319
     */
320
    public function __isset($name)
321
    {
322
        return $this->vars->exists($name);
323
    }
324
325
    /**
326
     * Overloading para eliminar una variable del array de variables de la plantilla pasado como
327
     * atributo dinámico de la clase
328
     *
329
     * @param string $name Nombre del atributo
330
     *
331
     * @return $this
332
     */
333
    public function __unset($name)
334
    {
335
        if (!$this->vars->exists($name)) {
336
            logger(sprintf(__('Unable to unset "%s" variable'), $name));
337
338
//            throw new InvalidArgumentException(sprintf(__('Unable to unset "%s" variable'), $name));
339
            return $this;
340
        }
341
342
        $this->vars->remove($name);
343
344
        return $this;
345
    }
346
347
    /**
348
     * Mostrar la plantilla solicitada.
349
     * La salida se almacena en buffer y se devuelve el contenido
350
     *
351
     * @return string Con el contenido del buffer de salida
352
     * @throws FileNotFoundException
353
     */
354
    public function render()
355
    {
356
        if (empty($this->templates)) {
357
            throw new FileNotFoundException(__('Template does not contain files'));
358
        }
359
360
        $icons = $this->theme->getIcons();
0 ignored issues
show
Unused Code introduced by
The assignment to $icons is dead and can be removed.
Loading history...
361
        $configData = $this->vars->get('configData');
362
        $sk = $this->vars->get('sk');
363
364
        // An anonymous proxy function for handling views variables
365
        $_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...
366
            if (DEBUG && !$this->vars->exists($key)) {
367
                logger(sprintf(__('Unable to retrieve "%s" variable'), $key), 'WARN');
368
369
                return $default;
370
            }
371
372
            return $this->vars->get($key, $default);
373
        };
374
375
        $_getRoute = function ($path) use ($sk, $configData) {
0 ignored issues
show
Unused Code introduced by
The assignment to $_getRoute is dead and can be removed.
Loading history...
376
            $baseUrl = ($configData->getApplicationUrl() ?: Bootstrap::$WEBURI) . Bootstrap::$SUBURI;
377
378
            $uri = new Uri($baseUrl);
379
            $uri->addParam('r', $path);
380
            $uri->addParam('sk', $sk);
381
382
            return $uri->getUri();
383
        };
384
385
        ob_start();
386
387
        // Añadimos las plantillas
388
        foreach ($this->templates as $template) {
389
            include_once $template;
390
        }
391
392
        return ob_get_clean();
393
    }
394
395
    /**
396
     * Anexar el valor de la variable al array de la misma en el array de variables
397
     *
398
     * @param      $name  string nombre de la variable
399
     * @param      $value mixed valor de la variable
400
     * @param      $index string índice del array
401
     * @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...
402
     */
403
    public function append($name, $value, $scope = null, $index = null)
404
    {
405
        if (null !== $scope) {
0 ignored issues
show
introduced by
The condition null !== $scope is always false.
Loading history...
406
            $name = $scope . '_' . $name;
407
        }
408
409
        $var = $this->vars->get($name, []);
410
411
        if (null === $index) {
412
            $var[] = $value;
413
        } else {
414
            $var[$index] = $value;
415
        }
416
417
        $this->vars->set($name, $var);
418
    }
419
420
    /**
421
     * Reset de las plantillas añadidas
422
     */
423
    public function resetTemplates()
424
    {
425
        $this->templates = [];
426
427
        return $this;
428
    }
429
430
    /**
431
     * Reset de las plantillas añadidas
432
     */
433
    public function resetContentTemplates()
434
    {
435
        $this->contentTemplates = [];
436
437
        return $this;
438
    }
439
440
    /**
441
     * Reset de las plantillas añadidas
442
     */
443
    public function resetVariables()
444
    {
445
        $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...
446
    }
447
448
    /**
449
     * @return string
450
     */
451
    public function getBase()
452
    {
453
        return $this->base;
454
    }
455
456
    /**
457
     * @param string $base
458
     */
459
    public function setBase($base)
460
    {
461
        $this->base = $base;
462
    }
463
464
    /**
465
     * @return ThemeInterface
466
     */
467
    public function getTheme()
468
    {
469
        return $this->theme;
470
    }
471
472
    /**
473
     * Dumps current stored vars
474
     */
475
    public function dumpVars()
476
    {
477
        logger($this->vars);
478
    }
479
480
    /**
481
     * @return array
482
     */
483
    public function getContentTemplates()
484
    {
485
        return $this->contentTemplates;
486
    }
487
488
    /**
489
     * @return bool
490
     */
491
    public function hashContentTemplates()
492
    {
493
        return count($this->contentTemplates) > 0;
494
    }
495
496
    /**
497
     * @return array
498
     */
499
    public function getTemplates()
500
    {
501
        return $this->templates;
502
    }
503
504
    /**
505
     * Assigns the current templates to contentTemplates
506
     *
507
     * @return $this
508
     */
509
    public function upgrade()
510
    {
511
        if (count($this->templates) > 0) {
512
            $this->contentTemplates = $this->templates;
513
514
            $this->templates = [];
515
516
            $this->upgraded = true;
517
        }
518
519
        return $this;
520
    }
521
522
    /**
523
     * Crear la variable y asignarle un valor en el array de variables
524
     *
525
     * @param      $name  string nombre de la variable
526
     * @param      $value mixed valor de la variable
527
     * @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...
528
     */
529
    public function assign($name, $value = '', $scope = null)
530
    {
531
        if (null !== $scope) {
0 ignored issues
show
introduced by
The condition null !== $scope is always false.
Loading history...
532
            $name = $scope . '_' . $name;
533
        }
534
535
        $this->vars->set($name, $value);
536
    }
537
538
    /**
539
     * @return bool
540
     */
541
    public function isUpgraded()
542
    {
543
        return $this->upgraded;
544
    }
545
546
    /**
547
     * When an object is cloned, PHP 5 will perform a shallow copy of all of the object's properties.
548
     * Any properties that are references to other variables, will remain references.
549
     * Once the cloning is complete, if a __clone() method is defined,
550
     * then the newly created object's __clone() method will be called, to allow any necessary properties that need to be changed.
551
     * NOT CALLABLE DIRECTLY.
552
     *
553
     * @link https://php.net/manual/en/language.oop5.cloning.php
554
     */
555
    public function __clone()
556
    {
557
        // Clone TemplateVarCollection to avoid unwanted object references
558
        $this->vars = clone $this->vars;
559
    }
560
561
}
562