Passed
Push — master ( d2956c...9cde44 )
by Fran
03:15
created

Template   C

Complexity

Total Complexity 63

Size/Duplication

Total Lines 502
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Test Coverage

Coverage 35.9%

Importance

Changes 0
Metric Value
dl 0
loc 502
ccs 79
cts 220
cp 0.359
rs 5.8023
c 0
b 0
f 0
wmc 63
lcom 1
cbo 13

34 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A getLoader() 0 4 1
A setPublicZone() 0 5 1
C setStatus() 0 25 7
A setReponseHeaders() 0 14 2
A render() 0 8 1
B output() 0 22 4
A closeRender() 0 11 1
A renderCache() 0 12 2
A addPath() 0 5 1
A dump() 0 14 2
A addTemplateFunction() 0 6 1
A addAssetFunction() 0 4 1
A addFormsFunction() 0 4 1
A addFormWidgetFunction() 0 4 1
A addFormButtonFunction() 0 4 1
A addConfigFunction() 0 4 1
A addRouteFunction() 0 4 1
A addResourceFunction() 0 4 1
A addSessionFunction() 0 4 1
A addExistsFlashFunction() 0 4 1
A addGetFlashFunction() 0 4 1
A regenerateTemplates() 0 11 2
A generateTemplate() 0 15 4
A extractPath() 0 9 2
A getDomains() 0 12 4
A addTemplateFunctions() 0 14 1
A getTemplateEngine() 0 4 1
A loadDomains() 0 9 3
A setup() 0 12 2
A addTemplateTokens() 0 6 1
A optimizeTemplates() 0 5 1
B parsePathTranslations() 0 14 5
A generateTemplatesCache() 0 11 3

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 PSFS\base;
4
5
6
use PSFS\base\config\Config;
7
use PSFS\base\extension\AssetsTokenParser;
8
use PSFS\base\extension\TemplateFunctions;
9
use PSFS\base\types\helpers\ResponseHelper;
10
use PSFS\base\types\helpers\SecurityHelper;
11
use PSFS\base\types\helpers\TemplateHelper;
12
use PSFS\base\types\SingletonTrait;
13
use PSFS\Dispatcher;
14
15
16
class Template
17
{
18
    use SingletonTrait;
19
    const STATUS_OK = 'HTTP/1.0 200 OK';
20
    /**
21
     * @var \Twig_Environment tpl
22
     */
23
    protected $tpl;
24
    protected $filters = array();
25
26
    protected $debug = false;
27
    protected $public_zone = true;
28
    private $status_code = Template::STATUS_OK;
29
30
    /**
31
     * @var \PSFS\base\Cache $cache
32
     */
33
    protected $cache;
34
35
    /**
36
     * Constructor por defecto
37
     */
38 1
    public function __construct()
39
    {
40 1
        $this->setup();
41 1
        $this->addTemplateFunctions();
42 1
        $this->addTemplateTokens();
43 1
        $this->optimizeTemplates();
44 1
    }
45
46
    /**
47
     * Método que devuelve el loader del Template
48
     * @return \Twig_LoaderInterface
49
     */
50
    public function getLoader()
51
    {
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
    {
63
        $this->public_zone = $public;
64
        return $this;
65
    }
66
67
    /**
68
     * Método que establece un header de http status code
69
     * @param string $status
70
     *
71
     * @return Template
72
     */
73
    public function setStatus($status = null)
74
    {
75
        switch ($status) {
76
            //TODO implement all status codes
77
            case '500':
78
                $this->status_code = "HTTP/1.0 500 Internal Server Error";
79
                break;
80
            case '404':
81
                $this->status_code = "HTTP/1.0 404 Not Found";
82
                break;
83
            case '403':
84
                $this->status_code = "HTTP/1.0 403 Forbidden";
85
                break;
86
            case '402':
87
                $this->status_code = "HTTP/1.0 402 Payment Required";
88
                break;
89
            case '401':
90
                $this->status_code = "HTTP/1.0 401 Unauthorized";
91
                break;
92
            case '400':
93
                $this->status_code = "HTTP/1.0 400 Bad Request";
94
                break;
95
        }
96
        return $this;
97
    }
98
99
    /**
100
     * Método que procesa la plantilla
101
     *
102
     * @param string $tpl
103
     * @param array $vars
104
     * @param array $cookies
105
     *
106
     * @return string HTML
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

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.

Loading history...
107
     */
108 1
    public function render($tpl, array $vars = array(), array $cookies = array())
109
    {
110 1
        Logger::log('Start render response');
111 1
        $vars = ResponseHelper::setDebugHeaders($vars);
112
        $output = $this->dump($tpl, $vars);
113
114
        return $this->output($output, 'text/html', $cookies);
115
    }
116
117
    /**
118
     * Servicio que establece las cabeceras de la respuesta
119
     * @param string $contentType
120
     * @param array $cookies
121
     */
122 1
    private function setReponseHeaders($contentType = 'text/html', array $cookies = array())
123
    {
124
        $config = Config::getInstance();
125
        $powered = $config->get("poweredBy");
126 1
        if (empty($powered)) {
127 1
            $powered = "@c15k0";
128 1
        }
129 1
        header("X-Powered-By: $powered");
130
        ResponseHelper::setStatusHeader($this->status_code);
131
        ResponseHelper::setAuthHeaders($this->public_zone);
132
        ResponseHelper::setCookieHeaders($cookies);
133
        header('Content-type: ' . $contentType);
134
135
    }
136
137
    /**
138
     * Servicio que devuelve el output
139
     * @param string $output
140
     * @param string $contentType
141
     * @param array $cookies
142
     * @return string HTML
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

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.

Loading history...
143
     */
144
    public function output($output = '', $contentType = 'text/html', array $cookies = array())
145
    {
146
        Logger::log('Start output response');
147
        ob_start();
148
        $this->setReponseHeaders($contentType, $cookies);
149
        header('Content-length: ' . strlen($output));
150
151
        $cache = Cache::needCache();
152
        if (false !== $cache && $this->status_code === Template::STATUS_OK && $this->debug === false) {
153
            Logger::log('Saving output response into cache');
154
            $cacheName = $this->cache->getRequestCacheHash();
155
            $tmpDir = substr($cacheName, 0, 2) . DIRECTORY_SEPARATOR . substr($cacheName, 2, 2) . DIRECTORY_SEPARATOR;
156
            $this->cache->storeData("json" . DIRECTORY_SEPARATOR . $tmpDir . $cacheName, $output);
157
            $this->cache->storeData("json" . DIRECTORY_SEPARATOR . $tmpDir . $cacheName . ".headers", headers_list(), Cache::JSON);
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...
158
        }
159
        echo $output;
160
161
        ob_flush();
162
        ob_end_clean();
163
        Logger::log('End output response');
164
        $this->closeRender();
165
    }
166
167
    /**
168
     * Método que cierra y limpia los buffers de salida
169
     */
170
    public function closeRender()
171
    {
172
        Logger::log('Close template render');
173
        Security::getInstance()->setSessionKey("lastRequest", array(
174
            "url" => Request::getInstance()->getRootUrl() . Request::requestUri(),
175
            "ts" => microtime(true),
176
        ));
177
        Security::getInstance()->updateSession();
178
        Logger::log('End request: ' . Request::requestUri(), LOG_INFO);
179
        exit;
180
    }
181
182
    /**
183
     * Método que devuelve los datos cacheados con las cabeceras que tenía por entonces
184
     * @param string $data
185
     * @param array $headers
186
     */
187
    public function renderCache($data, $headers = array())
188
    {
189
        ob_start();
190
        for ($i = 0, $ct = count($headers); $i < $ct; $i++) {
191
            header($headers[$i]);
192
        }
193
        header('X-PSFS-CACHED: true');
194
        echo $data;
195
        ob_flush();
196
        ob_end_clean();
197
        $this->closeRender();
198
    }
199
200
    /**
201
     * Método que añade una nueva ruta al path de Twig
202
     * @param $path
203
     * @param $domain
204
     *
205
     * @return Template
206
     */
207 1
    public function addPath($path, $domain = '')
208
    {
209 1
        $this->tpl->getLoader()->addPath($path, $domain);
210 1
        return $this;
211
    }
212
213
    /**
214
     * Método que devuelve el contenido de una plantilla
215
     * @param string $tpl
216
     * @param array $vars
217
     * @return string
218
     */
219
    public function dump($tpl, array $vars = array())
220
    {
221
        $vars["__user__"] = Security::getInstance()->getUser();
222
        $vars["__admin__"] = Security::getInstance()->getAdmin();
223
        $vars["__profiles__"] = Security::getCleanProfiles();
224
        $vars["__flash__"] = Security::getInstance()->getFlashes();
225
        $dump = '';
226
        try {
227
            $dump = $this->tpl->render($tpl, $vars);
228
        } catch (\Exception $e) {
229
            Logger::log($e->getMessage(), LOG_ERR);
230
        }
231
        return $dump;
232
    }
233
234
    /**
235
     * Método que añade una función al motor de plantillas
236
     * @param string $templateFunction
237
     * @param $functionName
238
     *
239
     * @return Template
240
     */
241 1
    protected function addTemplateFunction($templateFunction, $functionName)
242
    {
243 1
        $function = new \Twig_SimpleFunction($templateFunction, $functionName);
244 1
        $this->tpl->addFunction($function);
245 1
        return $this;
246
    }
247
248
    /**
249
     * Funcion Twig para los assets en las plantillas
250
     * @return Template
251
     */
252 1
    private function addAssetFunction()
253
    {
254 1
        return $this->addTemplateFunction("asset", TemplateFunctions::ASSETS_FUNCTION);
255
    }
256
257
    /**
258
     * Función que pinta un formulario
259
     * @return Template
260
     */
261 1
    private function addFormsFunction()
262
    {
263 1
        return $this->addTemplateFunction("form", TemplateFunctions::FORM_FUNCTION);
264
    }
265
266
    /**
267
     * Función que pinta un campo de un formulario
268
     * @return Template
269
     */
270 1
    private function addFormWidgetFunction()
271
    {
272 1
        return $this->addTemplateFunction("form_widget", TemplateFunctions::WIDGET_FUNCTION);
273
    }
274
275
    /**
276
     * Función que pinta un botón de un formulario
277
     * @return Template
278
     */
279 1
    private function addFormButtonFunction()
280
    {
281 1
        return $this->addTemplateFunction("form_button", TemplateFunctions::BUTTON_FUNCTION);
282
    }
283
284
    /**
285
     * Método que devuelve un parámetro de configuración en la plantilla
286
     * @return Template
287
     */
288 1
    private function addConfigFunction()
289
    {
290 1
        return $this->addTemplateFunction("get_config", TemplateFunctions::CONFIG_FUNCTION);
291
    }
292
293
    /**
294
     * Método que añade la función path a Twig
295
     * @return Template
296
     */
297 1
    private function addRouteFunction()
298
    {
299 1
        return $this->addTemplateFunction("path", TemplateFunctions::ROUTE_FUNCTION);
300
    }
301
302
    /**
303
     * Método que copia directamente el recurso solicitado a la carpeta pública
304
     * @return Template
305
     */
306 1
    private function addResourceFunction()
307
    {
308 1
        return $this->addTemplateFunction("resource", TemplateFunctions::RESOURCE_FUNCTION);
309
    }
310
311
    /**
312
     * @return Template
313
     */
314 1
    private function addSessionFunction()
315
    {
316 1
        return $this->addTemplateFunction("session", TemplateFunctions::SESSION_FUNCTION);
317
    }
318
319
    /**
320
     * @return Template
321
     */
322 1
    private function addExistsFlashFunction()
323
    {
324 1
        return $this->addTemplateFunction("existsFlash", TemplateFunctions::EXISTS_FLASH_FUNCTION);
325
    }
326
327
    /**
328
     * @return Template
329
     */
330 1
    private function addGetFlashFunction()
331
    {
332 1
        return $this->addTemplateFunction("getFlash", TemplateFunctions::GET_FLASH_FUNCTION);
333
    }
334
335
    /**
336
     * Servicio que regenera todas las plantillas
337
     * @return array
338
     */
339
    public function regenerateTemplates()
340
    {
341
        $this->generateTemplatesCache();
342
        $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 127 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...
343
        $translations = [];
344
        if (is_array($domains)) {
345
            $translations = $this->parsePathTranslations($domains);
346
        }
347
        $translations[] = _("Plantillas regeneradas correctamente");
348
        return $translations;
349
    }
350
351
    /**
352
     * @param $tplDir
353
     * @param string $domain
354
     *
355
     * @return mixed
356
     */
357
    protected function generateTemplate($tplDir, $domain = '')
358
    {
359
        $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...
360
        foreach ($templatesDir as $file) {
361
            // force compilation
362
            if ($file->isFile()) {
363
                try {
364
                    $this->tpl->load(str_replace($tplDir . '/', '', $file));
365
                } catch (\Exception $e) {
366
                    Logger::log($e->getMessage(), LOG_ERR, ['file' => $e->getFile(), 'line' => $e->getLine()]);
367
                }
368
            }
369
        }
370
        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...
371
    }
372
373
    /**
374
     * Método que extrae el path de un string
375
     * @param $path
376
     *
377
     * @return string
378
     */
379
    public static function extractPath($path)
380
    {
381
        $explodePath = explode(DIRECTORY_SEPARATOR, $path);
382
        $realPath = array();
383
        for ($i = 0, $parts = count($explodePath) - 1; $i < $parts; $i++) {
384
            $realPath[] = $explodePath[$i];
385
        }
386
        return implode(DIRECTORY_SEPARATOR, $realPath);
387
    }
388
389
    /**
390
     * Método que devuelve los dominios de una plataforma
391
     * @param bool $append
392
     * @return array
393
     */
394
    static public function getDomains($append = false)
395
    {
396
        $domains = Router::getInstance()->getDomains();
397
        if ($append) {
398
            foreach ($domains as &$domain) {
399
                foreach ($domain as &$path) {
400
                    $path .= DIRECTORY_SEPARATOR;
401
                }
402
            }
403
        }
404
        return $domains;
405
    }
406
407
    /**
408
     * Método que añade todas las funciones de las plantillas
409
     */
410 1
    private function addTemplateFunctions()
411
    {
412
        //Asignamos las funciones especiales
413 1
        $this->addAssetFunction()
414 1
            ->addFormsFunction()
415 1
            ->addFormWidgetFunction()
416 1
            ->addFormButtonFunction()
417 1
            ->addConfigFunction()
418 1
            ->addRouteFunction()
419 1
            ->addSessionFunction()
420 1
            ->addExistsFlashFunction()
421 1
            ->addGetFlashFunction()
422 1
            ->addResourceFunction();
423 1
    }
424
425
    /**
426
     * Método que devuelve el motod de plantillas
427
     * @return \Twig_Environment
428
     */
429
    public function getTemplateEngine()
430
    {
431
        return $this->tpl;
432
    }
433
434
    /**
435
     * Method that extract all domains for using them with the templates
436
     */
437 1
    private function loadDomains()
438
    {
439 1
        $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 127 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...
440 1
        if(null !== $domains) {
441 1
            foreach($domains as $domain => $paths) {
442 1
                $this->addPath($paths['template'], preg_replace('/(@|\/)/', '', $domain));
443 1
            }
444 1
        }
445 1
    }
446
447
    /**
448
     * Método que inicializa el motor de plantillas
449
     */
450 1
    private function setup()
451
    {
452 1
        $this->debug = Config::getInstance()->getDebugMode() ?: FALSE;
453 1
        $this->cache = Cache::getInstance();
454 1
        $loader = new \Twig_Loader_Filesystem(Config::getInstance()->getTemplatePath());
455 1
        $this->tpl = new \Twig_Environment($loader, array(
456 1
            'cache' => Config::getInstance()->getCachePath() . DIRECTORY_SEPARATOR . 'twig',
457 1
            'debug' => (bool)$this->debug,
458 1
            'auto_reload' => Config::getParam('twig.auto_reload', TRUE),
459 1
        ));
460 1
        $this->loadDomains();
461 1
    }
462
463
    /**
464
     * Método que inyecta los parseadores
465
     */
466 1
    private function addTemplateTokens()
467
    {
468
        //Añadimos las extensiones de los tags
469 1
        $this->tpl->addTokenParser(new AssetsTokenParser("css"));
470 1
        $this->tpl->addTokenParser(new AssetsTokenParser("js"));
471 1
    }
472
473
    /**
474
     * Método que inyecta las optimizaciones al motor de la plantilla
475
     */
476 1
    private function optimizeTemplates()
477
    {
478
        //Optimizamos
479 1
        $this->tpl->addExtension(new \Twig_Extensions_Extension_I18n());
480 1
    }
481
482
    /**
483
     * Method that extract all path tag for extracting translations
484
     * @param array $domains
485
     *
486
     * @return array
487
     */
488
    private function parsePathTranslations($domains)
489
    {
490
        $translations = array();
491
        if (!empty($domains)) {
492
            foreach ($domains as $domain => $paths) {
493
                if (strlen($domain) && array_key_exists("template", $paths)) {
494
                    $this->addPath($paths["template"], $domain);
495
                    $translations[] = $this->generateTemplate($paths["template"], $domain);
496
                }
497
            }
498
        }
499
500
        return $translations;
501
    }
502
503
    /**
504
     * Method that generate all template caches
505
     */
506
    private function generateTemplatesCache()
507
    {
508
        /** @var \Twig_Loader_Filesystem $loader */
509
        $loader = $this->tpl->getLoader();
510
        $availablePaths = $loader->getPaths();
511
        if (!empty($availablePaths)) {
512
            foreach ($availablePaths as $path) {
513
                $this->generateTemplate($path);
514
            }
515
        }
516
    }
517
}
518