Passed
Push — master ( b7ce52...453953 )
by Fran
48:08
created

Template::output()   B

Complexity

Conditions 4
Paths 2

Size

Total Lines 22
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

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