Passed
Push — devel-3.0 ( 218a48...a00b1f )
by Rubén
03:54
created

Minify::checkZlib()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
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\Html;
26
27
use Klein\Klein;
28
use SP\Core\Exceptions\SPException;
29
use SP\Http\Request;
30
use SP\Util\Util;
31
32
defined('APP_ROOT') || die();
33
34
/**
35
 * Class Minify para la gestión de archivos JS y CSS
36
 *
37
 * @package SP
38
 */
39
final class Minify
40
{
41
    /**
42
     * Constantes para tipos de archivos
43
     */
44
    const FILETYPE_JS = 1;
45
    const FILETYPE_CSS = 2;
46
    const OFFSET = 3600 * 24 * 30;
47
    /**
48
     * @var Klein
49
     */
50
    protected $router;
51
52
    /**
53
     * Array con los archivos a procesar
54
     *
55
     * @var array
56
     */
57
    private $files = [];
58
    /**
59
     * Tipos de archivos a procesar
60
     *
61
     * @var int
62
     */
63
    private $type = 0;
64
    /**
65
     * Base relativa de búsqueda de los archivos
66
     *
67
     * @var string
68
     */
69
    private $base = '';
70
71
    /**
72
     * Minify constructor.
73
     *
74
     * @param Klein $router
75
     */
76
    public function __construct(Klein $router)
77
    {
78
        $this->router = $router;
79
    }
80
81
    /**
82
     * @param string $path
83
     * @param bool   $checkPath
84
     *
85
     * @return $this
86
     */
87
    public function setBase($path, $checkPath = false)
88
    {
89
        $this->base = $checkPath === true ? Request::getSecureAppPath($path) : $path;
90
91
        return $this;
92
    }
93
94
    /**
95
     * Devolver al navegador archivos CSS y JS comprimidos
96
     * Método que devuelve un recurso CSS o JS comprimido. Si coincide el ETAG se
97
     * devuelve el código HTTP/304
98
     *
99
     * @param bool $disableMinify Deshabilitar minimizar
100
     *
101
     * @throws \Psr\Container\ContainerExceptionInterface
102
     * @throws \Psr\Container\NotFoundExceptionInterface
103
     */
104
    public function getMinified($disableMinify = false)
105
    {
106
        if (count($this->files) === 0) {
107
            return;
108
        }
109
110
        $this->setHeaders();
111
112
        $data = '';
113
114
        foreach ($this->files as $file) {
115
            $filePath = $file['base'] . DIRECTORY_SEPARATOR . $file['name'];
116
117
            // Obtener el recurso desde una URL
118
            if ($file['type'] === 'url') {
119
                try {
120
                    $data .= '/* URL: ' . $file['name'] . ' */' . PHP_EOL . Util::getDataFromUrl($file['name']);
121
                } catch (SPException $e) {
122
                    processException($e);
123
                }
124
            } else {
125
                if ($file['min'] === true && $disableMinify === false) {
126
                    $data .= '/* MINIFIED FILE: ' . $file['name'] . ' */' . PHP_EOL;
127
                    
128
                    if ($this->type === self::FILETYPE_JS) {
129
                        $data .= $this->jsCompress(file_get_contents($filePath));
130
                    }
131
                } else {
132
                    $data .= '/* FILE: ' . $file['name'] . ' */' . PHP_EOL . file_get_contents($filePath);
133
                }
134
            }
135
        }
136
137
        $this->router->response()->body($data);
138
    }
139
140
    /**
141
     * Sets HTTP headers
142
     */
143
    protected function setHeaders()
144
    {
145
        $response = $this->router->response();
146
        $headers = $this->router->request()->headers();
147
148
        $etag = $this->getEtag();
149
150
        // Devolver código 304 si la versión es la misma y no se solicita refrescar
151
        if ($etag === $headers->get('If-None-Match')
152
            && !($headers->get('Cache-Control') === 'no-cache'
153
                || $headers->get('Cache-Control') === 'max-age=0'
154
                || $headers->get('Pragma') === 'no-cache')
155
        ) {
156
            $response->header($_SERVER['SERVER_PROTOCOL'], '304 Not Modified');
157
            $response->send();
158
            exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
159
        }
160
161
        $response->header('Etag', $etag);
162
        $response->header('Cache-Control', 'public, max-age={' . self::OFFSET . '}, must-revalidate');
163
        $response->header('Pragma', 'public; maxage={' . self::OFFSET . '}');
164
        $response->header('Expires', gmdate('D, d M Y H:i:s \G\M\T', time() + self::OFFSET));
165
166
        switch ($this->type) {
167
            case self::FILETYPE_JS;
168
                $response->header('Content-type', 'application/javascript; charset: UTF-8');
169
                break;
170
            case self::FILETYPE_CSS:
171
                $response->header('Content-type', 'text/css; charset: UTF-8');
172
                break;
173
        }
174
    }
175
176
    /**
177
     * Calcular el hash MD5 de varios archivos.
178
     *
179
     * @return string Con el hash
180
     */
181
    private function getEtag()
182
    {
183
        $md5Sum = '';
184
185
        foreach ($this->files as $file) {
186
            $md5Sum .= $file['md5'];
187
        }
188
189
        return md5($md5Sum);
190
    }
191
192
    /**
193
     * Comprimir código javascript.
194
     *
195
     * @param string $buffer código a comprimir
196
     *
197
     * @return string
198
     */
199
    private function jsCompress($buffer)
200
    {
201
        $regexReplace = [
202
            '#/\*[^*]*\*+([^/][^*]*\*+)*/#',
203
            '#^[\s\t]*//.*$#m',
204
            '#[\s\t]+$#m',
205
            '#^[\s\t]+#m',
206
            '#\s*//\s.*$#m'
207
        ];
208
209
        return str_replace(["\r\n", "\r", "\n", "\t"], '', preg_replace($regexReplace, '', $buffer));
210
    }
211
212
    /**
213
     * @param      $files
214
     * @param bool $minify
215
     *
216
     * @return Minify
217
     */
218
    public function addFilesFromString($files, $minify = true)
219
    {
220
        if (strrpos($files, ',')) {
221
            $files = explode(',', $files);
222
223
            foreach ($files as $filename) {
224
                $this->addFile($filename, $minify);
225
            }
226
        } else {
227
            throw new \RuntimeException('Invalid string format');
228
        }
229
230
        return $this;
231
    }
232
233
    /**
234
     * Añadir un archivo
235
     *
236
     * @param string $file
237
     * @param bool   $minify Si es necesario reducir
238
     *
239
     * @return $this
240
     */
241
    public function addFile($file, $minify = true)
242
    {
243
        $filePath = $this->base . DIRECTORY_SEPARATOR . $file;
244
245
        if (file_exists($filePath)) {
246
            $this->files[] = [
247
                'type' => 'file',
248
                'base' => $this->base,
249
                'name' => Request::getSecureAppFile($file, $this->base),
250
                'min' => $minify === true && $this->needsMinify($file),
251
                'md5' => md5_file($filePath)
252
            ];
253
        } else {
254
            logger('File not found: ' . $filePath);
255
        }
256
257
        return $this;
258
    }
259
260
    /**
261
     * Comprobar si es necesario reducir
262
     *
263
     * @param string $file El nombre del archivo
264
     *
265
     * @return bool
266
     */
267
    private function needsMinify($file)
268
    {
269
        return !preg_match('/\.min|pack\.css|js/', $file);
270
    }
271
272
    /**
273
     * @param array $files
274
     * @param bool  $minify
275
     *
276
     * @return Minify
277
     */
278
    public function addFiles(array $files, $minify = true)
279
    {
280
        foreach ($files as $filename) {
281
            $this->processFile($filename, $minify);
282
        }
283
284
        return $this;
285
    }
286
287
    /**
288
     * @param      $file
289
     * @param bool $minify
290
     */
291
    protected function processFile($file, $minify = true)
292
    {
293
        $filePath = $this->base . DIRECTORY_SEPARATOR . $file;
294
295
        if (file_exists($filePath)) {
296
            $this->files[] = array(
297
                'type' => 'file',
298
                'base' => $this->base,
299
                'name' => Request::getSecureAppFile($file, $this->base),
300
                'min' => $minify === true && $this->needsMinify($file),
301
                'md5' => md5_file($filePath)
302
            );
303
        } else {
304
            logger('File not found: ' . $filePath);
305
        }
306
    }
307
308
    /**
309
     * Añadir un recurso desde URL
310
     *
311
     * @param $url
312
     *
313
     * @return $this
314
     */
315
    public function addUrl($url)
316
    {
317
        $this->files[] = array(
318
            'type' => 'url',
319
            'base' => $this->base,
320
            'name' => $url,
321
            'min' => false,
322
            'md5' => ''
323
        );
324
325
        return $this;
326
    }
327
328
    /**
329
     * Establecer el tipo de recurso a procesar
330
     *
331
     * @param int $type
332
     *
333
     * @return $this
334
     */
335
    public function setType($type)
336
    {
337
        $this->type = (int)$type;
338
339
        return $this;
340
    }
341
}