Completed
Push — master ( 0844ab...297f50 )
by Fran
07:25
created

Template   F

Complexity

Total Complexity 80

Size/Duplication

Total Lines 542
Duplicated Lines 2.95 %

Coupling/Cohesion

Components 1
Dependencies 14

Importance

Changes 46
Bugs 3 Features 0
Metric Value
c 46
b 3
f 0
dl 16
loc 542
rs 2.6956
wmc 80
lcom 1
cbo 14

38 Methods

Rating   Name   Duplication   Size   Complexity  
A getLoader() 0 3 1
A setPublicZone() 0 4 1
A output() 0 17 4
A addPath() 0 4 1
A addAssetFunction() 0 3 1
A addFormsFunction() 0 3 1
A addFormWidgetFunction() 0 4 1
A addFormButtonFunction() 0 3 1
A addConfigFunction() 0 3 1
A addRouteFunction() 0 3 1
A dumpResource() 0 3 1
A __construct() 0 6 1
B setStatus() 0 13 7
A render() 0 6 1
A setReponseHeaders() 0 13 2
A closeRender() 0 8 1
A renderCache() 0 10 2
A dump() 0 13 2
A addTemplateFunction() 0 5 1
A regenerateTemplates() 0 7 1
A generateTemplate() 0 14 4
A extractPath() 0 8 2
B copyr() 0 14 6
A getDomains() 0 11 4
B setCookieHeaders() 0 14 9
A setAuthHeaders() 0 9 2
A setStatusHeader() 0 5 2
A setDebugHeaders() 0 14 2
A useSessionVars() 8 8 1
A existsFlash() 8 8 1
A useFlash() 0 10 1
A addTemplateFunctions() 0 14 1
A getTemplateEngine() 0 3 1
A setup() 0 11 2
A addTemplateTokens() 0 5 1
A optimizeTemplates() 0 4 1
B parsePathTranslations() 0 14 5
A generateTemplatesCache() 0 9 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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 PSFS\base;
4
5
6
use PSFS\base\config\Config;
7
use PSFS\base\exception\ConfigException;
8
use PSFS\base\extension\AssetsTokenParser;
9
use PSFS\base\extension\TemplateFunctions;
10
use PSFS\base\types\SingletonTrait;
11
use PSFS\Dispatcher;
12
13
14
class Template {
15
16
    use SingletonTrait;
17
    /**
18
     * @var \Twig_Environment tpl
19
     */
20
    protected $tpl;
21
    protected $filters = array();
22
23
    protected $debug = false;
24
    protected $public_zone = true;
25
    private $status_code = 200;
26
27
    /**
28
     * @var \PSFS\base\Security $security
29
     */
30
    protected $security;
31
32
    /**
33
     * @var \PSFS\base\Cache $cache
34
     */
35
    protected $cache;
36
37
    /**
38
     * Constructor por defecto
39
     */
40
    public function __construct() {
41
        $this->setup();
42
        $this->addTemplateFunctions();
43
        $this->addTemplateTokens();
44
        $this->optimizeTemplates();
45
    }
46
47
    /**
48
     * Método que devuelve el loader del Template
49
     * @return \Twig_LoaderInterface
50
     */
51
    public function getLoader() {
52
        return $this->tpl->getLoader();
53
    }
54
55
    /**
56
     * Método que activa la zona pública
57
     * @param bool $public
58
     *
59
     * @return Template
60
     */
61
    public function setPublicZone($public = true) {
62
        $this->public_zone = $public;
63
        return $this;
64
    }
65
66
    /**
67
     * Método que establece un header de http status code
68
     * @param string $status
69
     *
70
     * @return Template
71
     */
72
    public function setStatus($status = null) {
73
        switch ($status)
74
        {
75
            //TODO implement all status codes
76
            case '500': $this->status_code = "HTTP/1.0 500 Internal Server Error"; break;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
77
            case '404': $this->status_code = "HTTP/1.0 404 Not Found"; break;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
78
            case '403': $this->status_code = "HTTP/1.0 403 Forbidden"; break;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
79
            case '402': $this->status_code = "HTTP/1.0 402 Payment Required"; break;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
80
            case '401': $this->status_code = "HTTP/1.0 401 Unauthorized"; break;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
81
            case '400': $this->status_code = "HTTP/1.0 400 Bad Request"; break;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
82
        }
83
        return $this;
84
    }
85
86
    /**
87
     * Método que procesa la plantilla
88
     *
89
     * @param string $tpl
90
     * @param array $vars
91
     * @param array $cookies
92
     *
93
     * @return string HTML
94
     */
95
    public function render($tpl, array $vars = array(), array $cookies = array()) {
96
        $vars = $this->setDebugHeaders($vars);
97
        $output = $this->dump($tpl, $vars);
98
99
        return $this->output($output, 'text/html', $cookies);
100
    }
101
102
    /**
103
     * Servicio que establece las cabeceras de la respuesta
104
     * @param string $contentType
105
     * @param array $cookies
106
     */
107
    private function setReponseHeaders($contentType = 'text/html', array $cookies = array()) {
108
        $config = Config::getInstance();
109
        $powered = $config->get("poweredBy");
110
        if (empty($powered)) {
111
            $powered = "@c15k0";
112
        }
113
        header("X-Powered-By: $powered");
114
        $this->setStatusHeader();
115
        $this->setAuthHeaders();
116
        $this->setCookieHeaders($cookies);
117
        header('Content-type: '.$contentType);
118
119
    }
120
121
    /**
122
     * Servicio que devuelve el output
123
     * @param string $output
124
     * @param string $contentType
125
     * @param array $cookies
126
     * @return string HTML
127
     */
128
    public function output($output = '', $contentType = 'text/html', array $cookies = array()) {
129
        ob_start();
130
        $this->setReponseHeaders($contentType, $cookies);
131
        header('Content-length: '.strlen($output));
132
133
        $cache = Cache::needCache();
134
        if (false !== $cache && $this->status_code === 200 && $this->debug === false) {
135
            $cacheName = $this->cache->getRequestCacheHash();
136
            $this->cache->storeData("json".DIRECTORY_SEPARATOR.$cacheName, $output);
137
            $this->cache->storeData("json".DIRECTORY_SEPARATOR.$cacheName.".headers", headers_list(), Cache::JSON);
138
        }
139
        echo $output;
140
141
        ob_flush();
142
        ob_end_clean();
143
        $this->closeRender();
144
    }
145
146
    /**
147
     * Método que cierra y limpia los buffers de salida
148
     */
149
    public function closeRender() {
150
        $this->security->setSessionKey("lastRequest", array(
151
            "url" => Request::getInstance()->getRootUrl().Request::requestUri(),
152
            "ts" => microtime(true),
153
        ));
154
        $this->security->updateSession();
155
        exit;
156
    }
157
158
    /**
159
     * Método que devuelve los datos cacheados con las cabeceras que tenía por entonces
160
     * @param string $data
161
     * @param string|null $headers
0 ignored issues
show
Documentation introduced by
Should the type for parameter $headers not be string|null|array? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
162
     */
163
    public function renderCache($data, $headers = array()) {
164
        ob_start();
165
        for ($i = 0, $ct = count($headers); $i < $ct; $i++) {
166
            header($headers[$i]);
167
        }
168
        echo $data;
169
        ob_flush();
170
        ob_end_clean();
171
        $this->closeRender();
172
    }
173
174
    /**
175
     * Método que añade una nueva ruta al path de Twig
176
     * @param $path
177
     * @param $domain
178
     *
179
     * @return Template
180
     */
181
    public function addPath($path, $domain = '') {
182
        $this->tpl->getLoader()->addPath($path, $domain);
183
        return $this;
184
    }
185
186
    /**
187
     * Método que devuelve el contenido de una plantilla
188
     * @param string $tpl
189
     * @param array $vars
190
     * @return string
191
     */
192
    public function dump($tpl, array $vars = array()) {
193
        $vars["__user__"] = $this->security->getUser();
194
        $vars["__admin__"] = $this->security->getAdmin();
195
        $vars["__profiles__"] = Security::getCleanProfiles();
196
        $vars["__flash__"] = Security::getInstance()->getFlashes();
197
        $dump = '';
198
        try {
199
            $dump = $this->tpl->render($tpl, $vars);
200
        }catch (\Exception $e) {
201
            echo $e->getMessage()."<pre>".$e->getTraceAsString()."</pre>";
202
        }
203
        return $dump;
204
    }
205
206
    /**
207
     * Método que añade una función al motor de plantillas
208
     * @param string $templateFunction
209
     * @param $functionName
210
     *
211
     * @return Template
212
     */
213
    protected function addTemplateFunction($templateFunction, $functionName) {
214
        $function = new \Twig_SimpleFunction($templateFunction, $functionName);
215
        $this->tpl->addFunction($function);
216
        return $this;
217
    }
218
219
    /**
220
     * Funcion Twig para los assets en las plantillas
221
     * @return Template
222
     */
223
    private function addAssetFunction() {
224
        return $this->addTemplateFunction("asset", TemplateFunctions::ASSETS_FUNCTION);
225
    }
226
227
    /**
228
     * Función que pinta un formulario
229
     * @return Template
230
     */
231
    private function addFormsFunction() {
232
        return $this->addTemplateFunction("form", TemplateFunctions::FORM_FUNCTION);
233
    }
234
235
    /**
236
     * Función que pinta un campo de un formulario
237
     * @return Template
238
     */
239
    private function addFormWidgetFunction()
240
    {
241
        return $this->addTemplateFunction("form_widget", TemplateFunctions::WIDGET_FUNCTION);
242
    }
243
244
    /**
245
     * Función que pinta un botón de un formulario
246
     * @return Template
247
     */
248
    private function addFormButtonFunction() {
249
        return $this->addTemplateFunction("form_button", TemplateFunctions::BUTTON_FUNCTION);
250
    }
251
252
    /**
253
     * Método que devuelve un parámetro de configuración en la plantilla
254
     * @return Template
255
     */
256
    private function addConfigFunction() {
257
        return $this->addTemplateFunction("get_config", TemplateFunctions::CONFIG_FUNCTION);
258
    }
259
260
    /**
261
     * Método que añade la función path a Twig
262
     * @return Template
263
     */
264
    private function addRouteFunction() {
265
        return $this->addTemplateFunction("path", TemplateFunctions::ROUTE_FUNCTION);
266
    }
267
268
    /**
269
     * Método que copia directamente el recurso solicitado a la carpeta pública
270
     * @return Template
271
     */
272
    private function dumpResource() {
273
        return $this->addTemplateFunction("resource", TemplateFunctions::RESOURCE_FUNCTION);
274
    }
275
276
    /**
277
     * Servicio que regenera todas las plantillas
278
     * @return array
279
     */
280
    public function regenerateTemplates() {
281
        $this->generateTemplatesCache();
282
        $domains = Cache::getInstance()->getDataFromFile(CONFIG_DIR.DIRECTORY_SEPARATOR."domains.json", Cache::JSON, true);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 123 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
283
        $translations = $this->parsePathTranslations($domains);
0 ignored issues
show
Bug introduced by
It seems like $domains defined by \PSFS\base\Cache::getIns...base\Cache::JSON, true) on line 282 can also be of type string; however, PSFS\base\Template::parsePathTranslations() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
284
        $translations[] = _("Plantillas regeneradas correctamente");
285
        return $translations;
286
    }
287
288
    /**
289
     * @param $tplDir
290
     * @param string $domain
291
     *
292
     * @return mixed
293
     */
294
    protected function generateTemplate($tplDir, $domain = '') {
295
        $templatesDir = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($tplDir), \RecursiveIteratorIterator::LEAVES_ONLY);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 138 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
296
        foreach ($templatesDir as $file) {
297
            // force compilation
298
            if ($file->isFile()) {
299
                try {
300
                    $this->tpl->loadTemplate(str_replace($tplDir.'/', '', $file));
301
                } catch (\Exception $e) {
302
                    Logger::getInstance()->errorLog($e->getMessage());
303
                }
304
            }
305
        }
306
        return str_replace("%d", $domain, str_replace("%s", $tplDir, _("Generando plantillas en path '%s' para el dominio '%d'")));
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 131 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
307
    }
308
309
    /**
310
     * Método que extrae el path de un string
311
     * @param $path
312
     *
313
     * @return string
314
     */
315
    public static function extractPath($path) {
316
        $explodePath = explode(DIRECTORY_SEPARATOR, $path);
317
        $realPath = array();
318
        for ($i = 0, $parts = count($explodePath) - 1; $i < $parts; $i++) {
319
            $realPath[] = $explodePath[$i];
320
        }
321
        return implode(DIRECTORY_SEPARATOR, $realPath);
322
    }
323
324
    /**
325
     * Método que copia un recurso
326
     * @param string $src
327
     * @param string $dst
328
     * @throws ConfigException
329
     */
330
    public static function copyr($src, $dst) {
331
        $dir = opendir($src);
332
        Config::createDir($dst);
333
        while (false !== ($file = readdir($dir))) {
334
            if (($file != '.') && ($file != '..')) {
335
                if (is_dir($src.'/'.$file)) {
336
                    self::copyr($src.'/'.$file, $dst.'/'.$file);
337
                } elseif (@copy($src.'/'.$file, $dst.'/'.$file) === false) {
338
                    throw new ConfigException("Can't copy ".$src." to ".$dst);
339
                }
340
            }
341
        }
342
        closedir($dir);
343
    }
344
345
    /**
346
     * Método que devuelve los dominios de una plataforma
347
     * @param bool $append
348
     * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be array|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
349
     */
350
    static public function getDomains($append = false) {
351
        $domains = Router::getInstance()->getDomains();
352
        if ($append) {
353
            foreach ($domains as &$domain) {
354
            foreach ($domain as &$path) {
355
                $path .= DIRECTORY_SEPARATOR;
356
        }
357
            }
358
        }
359
        return $domains;
360
    }
361
362
    /**
363
     * @param $cookies
364
     */
365
    protected function setCookieHeaders($cookies) {
366
        if (!empty($cookies) && is_array($cookies)) {
367
            foreach ($cookies as $cookie) {
368
            setcookie($cookie["name"],
369
                $cookie["value"],
370
                (array_key_exists('expire', $cookie)) ? $cookie["expire"] : NULL,
371
                (array_key_exists('path', $cookie)) ? $cookie["path"] : "/",
372
                (array_key_exists('domain', $cookie)) ? $cookie["domain"] : Request::getInstance()->getRootUrl(FALSE),
373
                (array_key_exists('secure', $cookie)) ? $cookie["secure"] : FALSE,
374
                (array_key_exists('http', $cookie)) ? $cookie["http"] : FALSE
375
            );
376
        }
377
        }
378
    }
379
380
    /**
381
     * Método que inyecta las cabeceras necesarias para la autenticación
382
     */
383
    protected function setAuthHeaders() {
384
        if ($this->public_zone) {
385
            unset($_SERVER["PHP_AUTH_USER"]);
386
            unset($_SERVER["PHP_AUTH_PW"]);
387
            header_remove("Authorization");
388
        }else {
389
            header('Authorization:');
390
        }
391
    }
392
393
    /**
394
     * Método que establece el status code
395
     */
396
    protected function setStatusHeader() {
397
        if (NULL !== $this->status_code) {
398
            header($this->status_code);
399
        }
400
    }
401
402
    /**
403
     * Método que mete en las variables de las plantillas las cabeceras de debug
404
     * @param array $vars
405
     *
406
     * @return array
407
     */
408
    protected function setDebugHeaders(array $vars)
409
    {
410
        if ($this->debug) {
411
            $vars["__DEBUG__"]["includes"] = get_included_files();
412
            $vars["__DEBUG__"]["trace"] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
413
            header('X-PSFS-DEBUG-TS: '.Dispatcher::getInstance()->getTs().' s');
414
            header('X-PSFS-DEBUG-MEM: '.Dispatcher::getInstance()->getMem('MBytes').' MBytes');
415
            header('X-PSFS-DEBUG-FILES: '.count(get_included_files()).' files opened');
416
417
            return $vars;
418
        }
419
420
        return $vars;
421
    }
422
423
    /**
424
     * Función que devuelve el valor de una variable de sesión
425
     * @return Template
426
     */
427 View Code Duplication
    private function useSessionVars() {
428
        $tpl = $this->tpl;
429
        $function = new \Twig_SimpleFunction('session', function($key = "") {
430
            return Security::getInstance()->getSessionKey($key);
431
        });
432
        $tpl->addFunction($function);
433
        return $this;
434
    }
435
436
    /**
437
     * Función que devuelve el valor de una variable de sesión flash
438
     * @return Template
439
     */
440 View Code Duplication
    private function existsFlash() {
441
        $tpl = $this->tpl;
442
        $function = new \Twig_SimpleFunction('existsFlash', function($key = "") {
443
            return null !== Security::getInstance()->getFlash($key);
444
        });
445
        $tpl->addFunction($function);
446
        return $this;
447
    }
448
449
    /**
450
     * Función que devuelve el valor de una variable de sesión flash
451
     * @return Template
452
     */
453
    private function useFlash() {
454
        $tpl = $this->tpl;
455
        $function = new \Twig_SimpleFunction('getFlash', function($key = "") {
456
            $var = Security::getInstance()->getFlash($key);
457
            Security::getInstance()->setFlash($key, null);
458
            return $var;
459
        });
460
        $tpl->addFunction($function);
461
        return $this;
462
    }
463
464
    /**
465
     * Método que añade todas las funciones de las plantillas
466
     */
467
    private function addTemplateFunctions() {
468
        //Asignamos las funciones especiales
469
        $this->addAssetFunction()
470
            ->addFormsFunction()
471
            ->addFormWidgetFunction()
472
            ->addFormButtonFunction()
473
            ->addConfigFunction()
474
            ->addRouteFunction()
475
            ->dumpResource()
476
            ->useSessionVars()
477
            ->existsFlash()
478
            ->useFlash()
479
        ;
480
    }
481
482
    /**
483
     * Método que devuelve el motod de plantillas
484
     * @return \Twig_Environment
485
     */
486
    public function getTemplateEngine() {
487
        return $this->tpl;
488
    }
489
490
    /**
491
     * Método que inicializa el motor de plantillas
492
     */
493
    private function setup() {
494
        $this->debug = Config::getInstance()->getDebugMode() ?: FALSE;
495
        $this->security = Security::getInstance();
496
        $this->cache = Cache::getInstance();
497
        $loader = new \Twig_Loader_Filesystem(Config::getInstance()->getTemplatePath());
498
        $this->tpl = new \Twig_Environment($loader, array(
499
            'cache'       => Config::getInstance()->getCachePath(),
500
            'debug'       => (bool)$this->debug,
501
            'auto_reload' => TRUE,
502
        ));
503
    }
504
505
    /**
506
     * Método que inyecta los parseadores
507
     */
508
    private function addTemplateTokens() {
509
        //Añadimos las extensiones de los tags
510
        $this->tpl->addTokenParser(new AssetsTokenParser("css"));
511
        $this->tpl->addTokenParser(new AssetsTokenParser("js"));
512
    }
513
514
    /**
515
     * Método que inyecta las optimizaciones al motor de la plantilla
516
     */
517
    private function optimizeTemplates() {
518
        //Optimizamos
519
        $this->tpl->addExtension(new \Twig_Extensions_Extension_I18n());
520
    }
521
522
    /**
523
     * Method that extract all path tag for extracting translations
524
     * @param array $domains
525
     *
526
     * @return array
527
     */
528
    private function parsePathTranslations($domains)
529
    {
530
        $translations = array();
531
        if (!empty($domains)) {
532
            foreach ($domains as $domain => $paths) {
533
                if (strlen($domain) && array_key_exists("template", $paths)) {
534
                    $this->addPath($paths["template"], $domain);
535
                    $translations[] = $this->generateTemplate($paths["template"], $domain);
536
                }
537
            }
538
        }
539
540
        return $translations;
541
    }
542
543
    /**
544
     * Method that generate all template caches
545
     */
546
    private function generateTemplatesCache()
547
    {
548
        $availablePaths = $this->tpl->getLoader()->getPaths();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Twig_LoaderInterface as the method getPaths() does only exist in the following implementations of said interface: Twig_Loader_Filesystem.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
549
        if (!empty($availablePaths)) {
550
            foreach ($availablePaths as $path) {
551
                $this->generateTemplate($path);
552
            }
553
        }
554
    }
555
}
556