|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace BootPress\Asset; |
|
4
|
|
|
|
|
5
|
|
|
use BootPress\Page\Component as Page; |
|
6
|
|
|
use BootPress\SQLite\Component as SQLite; |
|
7
|
|
|
use Symfony\Component\HttpFoundation\Response; |
|
8
|
|
|
use Symfony\Component\HttpFoundation\StreamedResponse; |
|
9
|
|
|
use Symfony\Component\HttpFoundation\BinaryFileResponse; |
|
10
|
|
|
use League\Glide\Responses\SymfonyResponseFactory; |
|
11
|
|
|
use League\Glide\ServerFactory; |
|
12
|
|
|
use MatthiasMullie\Minify; |
|
13
|
|
|
use phpUri; |
|
14
|
|
|
|
|
15
|
|
|
class Component |
|
16
|
|
|
{ |
|
17
|
|
|
const PREG_TYPES = 'jpe?g|gif|png|ico|js|css|pdf|ttf|otf|svg|eot|woff2?|swf|tar|t?gz|g?zip|csv|xls?x?|word|docx?|pptx?|psd|ogg|wav|mp3|mp4|mpeg?|mpg|mov|qt'; |
|
18
|
|
|
public static $instance; // made public to facilitate testing |
|
19
|
|
|
public static $not_found = array(); |
|
20
|
|
|
public static $urls = array(); |
|
21
|
|
|
private $cached = null; |
|
22
|
|
|
private $db = null; |
|
23
|
|
|
|
|
24
|
|
|
/** |
|
25
|
|
|
* Prepares a Symfony Response for you to send. |
|
26
|
|
|
* |
|
27
|
|
|
* @param string $file Either a file location, or the type of file you are sending eg. html, txt, less, scss, json, xml, rdf, rss, atom, js, css |
|
28
|
|
|
* @param array|string $options An array of options if ``$file`` is a location, or the string of data you want to send. The available options are: |
|
29
|
|
|
* |
|
30
|
|
|
* - '**name**' - changes a downloadable asset's file name |
|
31
|
|
|
* - |
|
32
|
|
|
* - |
|
33
|
|
|
* |
|
34
|
|
|
* @return object A Symfony\Component\HttpFoundation\Response |
|
35
|
|
|
*/ |
|
36
|
8 |
|
public static function dispatch($file, $options = array()) |
|
37
|
|
|
{ |
|
38
|
8 |
|
$set = array_merge(array( |
|
39
|
8 |
|
'name' => '', |
|
40
|
8 |
|
'expires' => 0, |
|
41
|
8 |
|
'xsendfile' => false, |
|
42
|
8 |
|
), (array) $options); |
|
43
|
8 |
|
$page = Page::html(); |
|
44
|
8 |
|
if (preg_match('/^(html|txt|less|scss|json|xml|rdf|rss|atom|js|css)$/', $file)) { |
|
45
|
4 |
|
if (is_array($options)) { |
|
46
|
3 |
|
foreach ($options as $updated => $content) { |
|
47
|
3 |
|
if (is_numeric($updated)) { |
|
48
|
3 |
|
break; |
|
49
|
|
|
} |
|
50
|
3 |
|
} |
|
51
|
3 |
|
} else { |
|
52
|
3 |
|
$content = (string) $options; |
|
53
|
|
|
} |
|
54
|
4 |
|
$response = new Response($content, Response::HTTP_OK, array( |
|
|
|
|
|
|
55
|
4 |
|
'Content-Type' => static::mime($file), |
|
56
|
4 |
|
'Content-Length' => mb_strlen($content), |
|
57
|
4 |
|
)); |
|
58
|
4 |
|
if (isset($updated) && (int) $updated > 631152000) { // 01-01-1990 |
|
59
|
3 |
|
$response->setCache(array( |
|
60
|
3 |
|
'public' => true, |
|
61
|
3 |
|
'max_age' => $set['expires'], |
|
62
|
3 |
|
's_maxage' => $set['expires'], |
|
63
|
3 |
|
'last_modified' => \DateTime::createFromFormat('U', (int) $updated), |
|
64
|
3 |
|
))->isNotModified($page->request); |
|
65
|
3 |
|
} |
|
66
|
|
|
|
|
67
|
4 |
|
return $response; |
|
68
|
|
|
} |
|
69
|
5 |
|
$type = strtolower(pathinfo($file, PATHINFO_EXTENSION)); |
|
70
|
5 |
|
if (is_null($file) || !is_file($file)) { |
|
71
|
2 |
|
return new Response('', Response::HTTP_NOT_FOUND); |
|
72
|
|
|
} |
|
73
|
4 |
|
if (null === $mime = static::mime($type)) { |
|
74
|
1 |
|
return new Response('', Response::HTTP_NOT_IMPLEMENTED); |
|
75
|
|
|
} |
|
76
|
4 |
|
if (preg_match('/^('.implode('|', array( |
|
77
|
4 |
|
'(?P<download>tar|t?gz|g?zip|csv|xls?x?|word|docx?|pptx?|psd)', |
|
78
|
4 |
|
'(?P<stream>ogg|wav|mp3|mp4|mpeg?|mpg|mov|qt)', |
|
79
|
4 |
|
)).')$/', $type, $matches)) { |
|
80
|
2 |
|
$response = new BinaryFileResponse($file); |
|
81
|
2 |
|
$response->headers->set('Content-Type', $mime); |
|
82
|
2 |
|
$response->setContentDisposition(isset($matches['download']) ? 'attachment' : 'inline', $set['name']); |
|
83
|
2 |
|
if ($set['xsendfile']) { |
|
84
|
1 |
|
BinaryFileResponse::trustXSendfileTypeHeader(); |
|
85
|
1 |
|
} |
|
86
|
|
|
|
|
87
|
2 |
|
return $response; |
|
88
|
|
|
} |
|
89
|
3 |
|
$file = new \SplFileInfo($file); |
|
90
|
|
|
$response = new StreamedResponse(function () use ($file) { |
|
91
|
1 |
|
if ($fp = fopen($file->getPathname(), 'rb')) { |
|
92
|
1 |
|
rewind($fp); |
|
93
|
1 |
|
fpassthru($fp); |
|
94
|
1 |
|
fclose($fp); |
|
95
|
1 |
|
} |
|
96
|
3 |
|
}, 200, array( |
|
97
|
3 |
|
'Content-Type' => $mime, |
|
98
|
3 |
|
'Content-Length' => $file->getSize(), |
|
99
|
3 |
|
)); |
|
100
|
3 |
|
$response->setCache(array( |
|
101
|
3 |
|
'public' => true, |
|
102
|
3 |
|
'max_age' => $set['expires'], |
|
103
|
3 |
|
's_maxage' => $set['expires'], |
|
104
|
3 |
|
'last_modified' => \DateTime::createFromFormat('U', $file->getMTime()), |
|
105
|
3 |
|
)); |
|
106
|
3 |
|
$response->isNotModified($page->request); |
|
107
|
|
|
|
|
108
|
3 |
|
return $response; |
|
109
|
|
|
} |
|
110
|
|
|
|
|
111
|
6 |
|
public static function cached($dir, array $glide = array()) |
|
112
|
|
|
{ |
|
113
|
6 |
|
$page = Page::html(); |
|
114
|
6 |
|
static::$instance = new static(); |
|
115
|
6 |
|
$asset = static::$instance; |
|
116
|
6 |
|
$asset->cached = $page->dir($dir); |
|
117
|
6 |
|
$type = strtolower($page->url['format']); |
|
118
|
6 |
|
if ($type == 'html') { |
|
119
|
2 |
|
$page->filter('page', array($asset, 'urls'), 'this'); |
|
120
|
|
|
|
|
121
|
2 |
|
return false; |
|
122
|
5 |
|
} elseif (!preg_match('/^'.implode('', array( |
|
123
|
5 |
|
'(?P<paths>([1-9a-z]{5}[0]?)+)', |
|
124
|
5 |
|
'(\/.*)?', |
|
125
|
5 |
|
'\.('.self::PREG_TYPES.')', |
|
126
|
5 |
|
)).'$/i', $page->url['path'], $matches)) { |
|
127
|
1 |
|
return false; |
|
128
|
|
|
} |
|
129
|
5 |
|
$paths = explode('0', rtrim($matches['paths'], '0')); |
|
130
|
5 |
|
foreach ($paths as $key => $value) { |
|
131
|
5 |
|
$paths[$key] = '"'.$value.'"'; |
|
132
|
5 |
|
} |
|
133
|
5 |
|
$minify = array(); |
|
134
|
5 |
|
$image = null; |
|
135
|
5 |
|
$file = null; |
|
136
|
5 |
|
$asset->openDatabase(); |
|
137
|
5 |
|
if ($stmt = $asset->db->query(array( |
|
138
|
5 |
|
'SELECT p.tiny, f.file, f.query', |
|
139
|
5 |
|
'FROM paths as p', |
|
140
|
5 |
|
'INNER JOIN files AS f ON p.file_id = f.id', |
|
141
|
5 |
|
'WHERE p.tiny IN('.implode(', ', $paths).')', |
|
142
|
5 |
|
$asset->db->orderIn('p.tiny', $paths), |
|
143
|
5 |
|
), '', 'assoc')) { |
|
|
|
|
|
|
144
|
5 |
|
while ($row = $asset->db->fetch($stmt)) { |
|
145
|
4 |
|
if ($type == strtolower(pathinfo($row['file'], PATHINFO_EXTENSION))) { |
|
146
|
|
|
switch ($type) { |
|
147
|
4 |
|
case 'js': |
|
148
|
4 |
|
case 'css': |
|
149
|
2 |
|
if (is_file($row['file'])) { |
|
150
|
2 |
|
$minify[$row['file']] = $row; |
|
151
|
2 |
|
} |
|
152
|
2 |
|
break 1; |
|
153
|
2 |
|
case 'jpeg': |
|
154
|
2 |
|
case 'jpg': |
|
155
|
2 |
|
case 'gif': |
|
156
|
2 |
|
case 'png': |
|
157
|
1 |
|
if (!empty($row['query'])) { |
|
158
|
1 |
|
parse_str($row['query'], $params); |
|
159
|
1 |
|
$setup = $glide; |
|
160
|
|
|
$glide = array( |
|
161
|
1 |
|
'cache' => $asset->cached.'glide/', |
|
162
|
1 |
|
'source' => dirname($row['file']), |
|
163
|
1 |
|
'response' => new SymfonyResponseFactory($page->request), |
|
164
|
1 |
|
); |
|
165
|
1 |
|
if (isset($setup['group_cache_in_folders']) && is_bool($setup['group_cache_in_folders'])) { |
|
166
|
1 |
|
$glide['group_cache_in_folders'] = $setup['group_cache_in_folders']; |
|
167
|
1 |
|
} |
|
168
|
1 |
|
if (isset($setup['watermarks'])) { |
|
169
|
1 |
|
$glide['watermarks'] = rtrim(str_replace('\\', '/', $setup['watermarks']), '/'); |
|
170
|
1 |
|
} |
|
171
|
1 |
|
if (isset($setup['driver']) && in_array($setup['driver'], array('gd', 'imagick'))) { |
|
172
|
1 |
|
$glide['driver'] = $setup['driver']; |
|
173
|
1 |
|
} |
|
174
|
1 |
|
if (isset($setup['max_image_size']) && is_numeric($setup['max_image_size'])) { |
|
175
|
1 |
|
$glide['max_image_size'] = (int) $setup['max_image_size']; |
|
176
|
1 |
|
} |
|
177
|
1 |
|
$glide = ServerFactory::create($glide); |
|
178
|
1 |
|
$image = $glide->getImageResponse(basename($row['file']), $params); |
|
|
|
|
|
|
179
|
1 |
|
break 2; |
|
180
|
|
|
} |
|
181
|
1 |
|
default: |
|
182
|
1 |
|
$file = $row['file']; |
|
183
|
1 |
|
break 2; |
|
184
|
1 |
|
} |
|
185
|
2 |
|
} |
|
186
|
2 |
|
} |
|
187
|
5 |
|
$asset->db->close($stmt); |
|
188
|
5 |
|
} |
|
189
|
5 |
|
$asset->closeDatabase(); |
|
190
|
5 |
|
if (!empty($minify)) { |
|
191
|
2 |
|
$paths = array(); |
|
192
|
2 |
|
foreach ($minify as $row) { |
|
193
|
2 |
|
$paths[] = $row['tiny']; |
|
194
|
2 |
|
} |
|
195
|
2 |
|
$file = implode('/', array( |
|
196
|
2 |
|
$asset->cached.'minify', |
|
197
|
2 |
|
md5($page->url['base']), |
|
198
|
2 |
|
implode('0', $paths), |
|
199
|
2 |
|
)).'.'.$type; |
|
200
|
2 |
|
if (!is_dir(dirname($file))) { |
|
201
|
1 |
|
mkdir(dirname($file), 0755, true); |
|
202
|
1 |
|
} |
|
203
|
2 |
|
if (!is_file($file)) { |
|
204
|
|
|
switch ($type) { |
|
205
|
2 |
|
case 'js': |
|
206
|
1 |
|
$minifier = new Minify\JS(); |
|
207
|
1 |
|
foreach ($minify as $js => $row) { |
|
208
|
1 |
|
$minifier->add($js); |
|
209
|
1 |
|
} |
|
210
|
1 |
|
$minifier->minify($file); |
|
211
|
1 |
|
break; |
|
212
|
1 |
|
case 'css': |
|
213
|
1 |
|
$minifier = new Minify\CSS(); |
|
214
|
1 |
|
foreach ($minify as $css => $row) { |
|
215
|
1 |
|
$minifier->add($asset->css($css, $row)); |
|
216
|
1 |
|
} |
|
217
|
1 |
|
$minifier->minify($file); |
|
218
|
1 |
|
break; |
|
219
|
|
|
} |
|
220
|
2 |
|
} |
|
221
|
2 |
|
} |
|
222
|
|
|
|
|
223
|
5 |
|
return ($image) ? $image : static::dispatch($file, array('expires' => 31536000)); |
|
224
|
|
|
} |
|
225
|
|
|
|
|
226
|
2 |
|
public static function urls($html) |
|
227
|
|
|
{ |
|
228
|
2 |
|
if (is_null(static::$instance) || empty($html)) { |
|
229
|
1 |
|
return $html; |
|
230
|
|
|
} |
|
231
|
2 |
|
$asset = static::$instance; |
|
232
|
2 |
|
$page = Page::html(); |
|
233
|
2 |
|
$array = (is_array($html)) ? $html : false; |
|
234
|
2 |
|
if ($array) { |
|
235
|
1 |
|
$html = array(); |
|
236
|
|
|
array_walk_recursive($array, function ($value) use (&$html) { |
|
237
|
1 |
|
$html[] = $value; |
|
238
|
1 |
|
}); |
|
239
|
1 |
|
$html = implode(' ', $html); |
|
240
|
1 |
|
} |
|
241
|
2 |
|
preg_match_all('/'.implode('', array( |
|
242
|
2 |
|
'('.$page->url['preg'].')', |
|
243
|
2 |
|
'(?P<dir>['.$page->url['chars'].']+)\/', |
|
244
|
2 |
|
'(?P<file>['.$page->url['chars'].'.\/]+\.(?P<ext>'.self::PREG_TYPES.'))', |
|
245
|
2 |
|
'(?P<query>\?['.$page->url['chars'].'&;=.\/]+)?', |
|
246
|
2 |
|
'(#(?P<frag>['.$page->url['chars'].'.\/#]+))?', |
|
247
|
2 |
|
)).'/i', $html, $matches, PREG_SET_ORDER); |
|
248
|
2 |
|
if (empty($matches)) { |
|
249
|
1 |
|
return $array ? $array : $html; |
|
250
|
|
|
} |
|
251
|
2 |
|
$asset->openDatabase(); |
|
252
|
2 |
|
$cache = array(); |
|
253
|
2 |
|
$assets = array(); |
|
254
|
2 |
|
$found = $page->dir; // in PHP7 isset($page->dir[$url]) will not work on a private property retrieved via ``__get()`` |
|
255
|
2 |
|
foreach ($matches as $url) { |
|
256
|
2 |
|
$dir = $url['dir']; |
|
257
|
2 |
|
if (!isset($found[$dir])) { |
|
258
|
1 |
|
static::$not_found[] = substr($url[0], strlen($page->url['base'])); |
|
259
|
1 |
|
continue; |
|
260
|
|
|
} |
|
261
|
2 |
|
$ext = strtolower($url['ext']); |
|
262
|
2 |
|
$file = $url['file']; |
|
263
|
2 |
|
if (isset($url['query']) && in_array($ext, array('jpeg', 'jpg', 'gif', 'png'))) { |
|
264
|
2 |
|
$file .= $url['query']; |
|
265
|
2 |
|
} |
|
266
|
2 |
|
$files = array(); |
|
267
|
2 |
|
$files[$file] = pathinfo($url['file'], PATHINFO_FILENAME); |
|
268
|
2 |
|
if (isset($url['frag'])) { |
|
269
|
1 |
|
$frag = explode('#', $url['frag']); |
|
270
|
1 |
|
if ($ext == 'js' || $ext == 'css') { |
|
271
|
1 |
|
$base = phpUri::parse($page->dir[$dir].$file); |
|
272
|
1 |
|
foreach ($frag as $file) { |
|
273
|
1 |
|
$file = $base->join($file); |
|
274
|
1 |
|
$info = pathinfo($file); |
|
275
|
1 |
|
if (is_file($file) && $info['extension'] == $ext) { |
|
276
|
1 |
|
$file = substr($file, strlen($page->dir[$dir])); |
|
277
|
1 |
|
$files[$file] = $info['filename']; |
|
278
|
1 |
|
} |
|
279
|
1 |
|
} |
|
280
|
1 |
|
} else { |
|
281
|
1 |
|
$files[$file] = pathinfo(array_shift($frag), PATHINFO_FILENAME); |
|
282
|
|
|
} |
|
283
|
1 |
|
} |
|
284
|
2 |
|
foreach ($files as $file => $name) { |
|
285
|
2 |
|
$cache[$dir][$file] = array(); |
|
286
|
2 |
|
} |
|
287
|
2 |
|
$assets[$url[0]] = array( |
|
288
|
2 |
|
'dir' => $dir, |
|
289
|
2 |
|
'file' => array_keys($files), |
|
290
|
2 |
|
'name' => implode('-', $files), |
|
291
|
2 |
|
'ext' => '.'.$ext, |
|
292
|
|
|
); |
|
293
|
2 |
|
} |
|
294
|
2 |
|
$asset->paths($cache); |
|
295
|
2 |
|
$asset->closeDatabase(); |
|
296
|
2 |
|
$rnr = array(); |
|
297
|
2 |
|
$base = strlen($page->url['base']); |
|
298
|
2 |
|
foreach ($assets as $match => $url) { |
|
299
|
2 |
|
$cached = array(); |
|
300
|
2 |
|
foreach ($url['file'] as $file) { |
|
301
|
2 |
|
$cached[] = $cache[$url['dir']][$file]; |
|
302
|
2 |
|
} |
|
303
|
2 |
|
$cached = implode(0, $cached); |
|
304
|
2 |
|
if (!is_numeric($url['name'])) { |
|
305
|
2 |
|
$cached .= '/'.$url['name']; |
|
306
|
2 |
|
} |
|
307
|
2 |
|
$cached .= $url['ext']; |
|
308
|
2 |
|
$rnr[$page->url['base'].$cached] = $match; // replace => remove |
|
309
|
2 |
|
static::$urls[substr($match, $base)] = $cached; |
|
310
|
2 |
|
} |
|
311
|
2 |
|
ksort(static::$urls); |
|
312
|
2 |
|
uasort($rnr, function($a, $b) { // ORDER BY strlen(remove) DESC so we don't step on any toes |
|
313
|
2 |
|
return mb_strlen($b) - mb_strlen($a); |
|
314
|
2 |
|
}); |
|
315
|
2 |
|
$rnr = array_flip($rnr); // remove => replace |
|
316
|
2 |
|
return str_replace(array_keys($rnr), array_values($rnr), $array ? $array : $html); |
|
317
|
|
|
} |
|
318
|
|
|
|
|
319
|
8 |
|
public static function mime($type) |
|
320
|
|
|
{ |
|
321
|
8 |
|
$mime = null; |
|
322
|
8 |
|
$single = (is_array($type)) ? false : true; |
|
323
|
8 |
|
if (is_array($type)) { |
|
324
|
1 |
|
$type = array_shift($type); |
|
325
|
1 |
|
} |
|
326
|
8 |
|
switch (strtolower($type)) { |
|
327
|
8 |
|
case 'html': |
|
328
|
3 |
|
$mime = array('text/html', 'application/xhtml+xml', 'text/plain'); |
|
329
|
3 |
|
break; |
|
330
|
8 |
|
case 'txt': |
|
331
|
2 |
|
$mime = array('text/plain'); |
|
332
|
2 |
|
break; |
|
333
|
8 |
|
case 'less': |
|
334
|
1 |
|
$mime = array('text/x-less', 'text/css', 'text/plain', 'application/octet-stream'); |
|
335
|
1 |
|
break; |
|
336
|
8 |
|
case 'scss': |
|
337
|
1 |
|
$mime = array('text/css', 'text/plain', 'application/octet-stream'); |
|
338
|
1 |
|
break; |
|
339
|
8 |
|
case 'json': |
|
340
|
1 |
|
$mime = array('application/json', 'application/x-json', 'text/json', 'text/plain'); |
|
341
|
1 |
|
break; |
|
342
|
8 |
|
case 'xml': |
|
343
|
3 |
|
$mime = array('application/xml', 'application/x-xml', 'text/xml', 'text/plain'); |
|
344
|
3 |
|
break; |
|
345
|
6 |
|
case 'rdf': |
|
346
|
1 |
|
$mime = array('application/rdf+xml'); |
|
347
|
1 |
|
break; |
|
348
|
6 |
|
case 'rss': |
|
349
|
2 |
|
$mime = array('application/rss+xml'); |
|
350
|
2 |
|
break; |
|
351
|
5 |
|
case 'atom': |
|
352
|
1 |
|
$mime = array('application/atom+xml'); |
|
353
|
1 |
|
break; |
|
354
|
5 |
|
case 'jpeg': |
|
355
|
5 |
|
case 'jpg': |
|
356
|
2 |
|
$mime = array('image/jpeg', 'image/pjpeg'); |
|
357
|
2 |
|
break; |
|
358
|
5 |
|
case 'gif': |
|
359
|
1 |
|
$mime = array('image/gif'); |
|
360
|
1 |
|
break; |
|
361
|
5 |
|
case 'png': |
|
362
|
1 |
|
$mime = array('image/png', 'image/x-png'); |
|
363
|
1 |
|
break; |
|
364
|
5 |
|
case 'ico': |
|
365
|
1 |
|
$mime = array('image/x-icon', 'image/vnd.microsoft.icon'); |
|
366
|
1 |
|
break; |
|
367
|
5 |
|
case 'js': |
|
368
|
2 |
|
$mime = array('application/javascript', 'application/x-javascript', 'text/javascript', 'text/plain'); |
|
369
|
2 |
|
break; |
|
370
|
4 |
|
case 'css': |
|
371
|
2 |
|
$mime = array('text/css', 'text/plain'); |
|
372
|
2 |
|
break; |
|
373
|
3 |
|
case 'pdf': |
|
374
|
1 |
|
$mime = array('application/pdf', 'application/force-download', 'application/x-download', 'binary/octet-stream'); |
|
375
|
1 |
|
break; |
|
376
|
3 |
|
case 'ttf': |
|
377
|
1 |
|
$mime = array('application/font-sfnt', 'application/font-ttf', 'application/x-font-ttf', 'font/ttf', 'font/truetype', 'application/octet-stream'); |
|
378
|
1 |
|
break; |
|
379
|
3 |
|
case 'otf': |
|
380
|
1 |
|
$mime = array('application/font-sfnt', 'application/font-otf', 'application/x-font-otf', 'font/opentype', 'application/octet-stream'); |
|
381
|
1 |
|
break; |
|
382
|
3 |
|
case 'svg': |
|
383
|
1 |
|
$mime = array('image/svg+xml', 'application/xml', 'text/xml'); |
|
384
|
1 |
|
break; |
|
385
|
3 |
|
case 'eot': |
|
386
|
1 |
|
$mime = array('application/vnd.ms-fontobject', 'application/octet-stream'); |
|
387
|
1 |
|
break; |
|
388
|
3 |
|
case 'woff': |
|
389
|
1 |
|
$mime = array('application/font-woff', 'application/x-woff', 'application/x-font-woff', 'font/x-woff', 'application/octet-stream'); |
|
390
|
1 |
|
break; |
|
391
|
3 |
|
case 'woff2': |
|
392
|
1 |
|
$mime = array('application/font-woff2', 'font/woff2', 'application/octet-stream'); |
|
393
|
1 |
|
break; |
|
394
|
3 |
|
case 'swf': |
|
395
|
1 |
|
$mime = array('application/x-shockwave-flash'); |
|
396
|
1 |
|
break; |
|
397
|
3 |
|
case 'tar': |
|
398
|
1 |
|
$mime = array('application/x-tar'); |
|
399
|
1 |
|
break; |
|
400
|
3 |
|
case 'tgz': |
|
401
|
1 |
|
$mime = array('application/x-tar', 'application/x-gzip-compressed'); |
|
402
|
1 |
|
break; |
|
403
|
3 |
|
case 'gz': |
|
404
|
3 |
|
case 'gzip': |
|
405
|
1 |
|
$mime = array('application/x-gzip'); |
|
406
|
1 |
|
break; |
|
407
|
3 |
|
case 'zip': |
|
408
|
1 |
|
$mime = array('application/x-zip', 'application/zip', 'application/x-zip-compressed', 'application/s-compressed', 'multipart/x-zip'); |
|
409
|
1 |
|
break; |
|
410
|
3 |
|
case 'csv': |
|
411
|
3 |
|
$mime = array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'application/x-csv', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel', 'text/plain'); |
|
412
|
3 |
|
break; |
|
413
|
2 |
|
case 'xl': |
|
414
|
1 |
|
$mime = array('application/excel'); |
|
415
|
1 |
|
break; |
|
416
|
2 |
|
case 'xls': |
|
417
|
1 |
|
$mime = array('application/vnd.ms-excel', 'application/msexcel', 'application/x-msexcel', 'application/x-ms-excel', 'application/x-excel', 'application/x-dos_ms_excel', 'application/xls', 'application/x-xls', 'application/excel', 'application/download', 'application/vnd.ms-office', 'application/msword'); |
|
418
|
1 |
|
break; |
|
419
|
2 |
|
case 'xlsx': |
|
420
|
1 |
|
$mime = array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip', 'application/vnd.ms-excel', 'application/msword', 'application/x-zip'); |
|
421
|
1 |
|
break; |
|
422
|
2 |
|
case 'word': |
|
423
|
1 |
|
$mime = array('application/msword', 'application/octet-stream'); |
|
424
|
1 |
|
break; |
|
425
|
2 |
|
case 'doc': |
|
426
|
1 |
|
$mime = array('application/msword', 'application/vnd.ms-office'); |
|
427
|
1 |
|
break; |
|
428
|
2 |
|
case 'docx': |
|
429
|
1 |
|
$mime = array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/msword', 'application/x-zip'); |
|
430
|
1 |
|
break; |
|
431
|
2 |
|
case 'ppt': |
|
432
|
1 |
|
$mime = array('application/powerpoint', 'application/vnd.ms-powerpoint', 'application/vnd.ms-office', 'application/msword'); |
|
433
|
1 |
|
break; |
|
434
|
2 |
|
case 'pptx': |
|
435
|
1 |
|
$mime = array('application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/x-zip', 'application/zip'); |
|
436
|
1 |
|
break; |
|
437
|
2 |
|
case 'psd': |
|
438
|
1 |
|
$mime = array('application/x-photoshop', 'image/vnd.adobe.photoshop'); |
|
439
|
1 |
|
break; |
|
440
|
2 |
|
case 'ogg': |
|
441
|
1 |
|
$mime = array('audio/ogg'); |
|
442
|
1 |
|
break; |
|
443
|
2 |
|
case 'wav': |
|
444
|
1 |
|
$mime = array('audio/x-wav', 'audio/wave', 'audio/wav'); |
|
445
|
1 |
|
break; |
|
446
|
2 |
|
case 'mp3': |
|
447
|
1 |
|
$mime = array('audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'); |
|
448
|
1 |
|
break; |
|
449
|
2 |
|
case 'mp4': |
|
450
|
1 |
|
$mime = array('video/mp4'); |
|
451
|
1 |
|
break; |
|
452
|
2 |
|
case 'mpe': |
|
453
|
2 |
|
case 'mpeg': |
|
454
|
2 |
|
case 'mpg': |
|
455
|
1 |
|
$mime = array('video/mpeg'); |
|
456
|
1 |
|
break; |
|
457
|
2 |
|
case 'mov': |
|
458
|
2 |
|
case 'qt': |
|
459
|
1 |
|
$mime = array('video/quicktime'); |
|
460
|
1 |
|
break; |
|
461
|
8 |
|
} |
|
462
|
|
|
|
|
463
|
8 |
|
return ($mime && $single) ? array_shift($mime) : $mime; |
|
464
|
|
|
} |
|
465
|
|
|
|
|
466
|
1 |
|
private function css($file, $row) |
|
467
|
|
|
{ |
|
468
|
1 |
|
$page = Page::html(); |
|
469
|
1 |
|
$css = file_get_contents($file); |
|
470
|
1 |
|
if (substr($css, 0, 3) == "\xef\xbb\xbf") { |
|
471
|
1 |
|
$css = substr($css, 3); // strip BOM, if any |
|
472
|
1 |
|
} |
|
473
|
1 |
|
$matches = array(); |
|
474
|
|
|
foreach (array( |
|
475
|
1 |
|
'/url\(\s*(?P<quotes>["\'])?(?P<path>(?!(\s?["\']?(data:|https?:|\/\/))).+?)(?(quotes)(?P=quotes))\s*\)/ix', // url(xxx) |
|
476
|
1 |
|
'/@import\s+(?P<quotes>["\'])(?P<path>(?!(["\']?(data:|https?:|\/\/))).+?)(?P=quotes)/ix', // @import "xxx" |
|
477
|
1 |
|
) as $regex) { |
|
478
|
1 |
|
if (preg_match_all($regex, $css, $match, PREG_SET_ORDER)) { |
|
479
|
1 |
|
$matches = array_merge($matches, $match); |
|
480
|
1 |
|
} |
|
481
|
1 |
|
} |
|
482
|
1 |
|
$rnr = array(); |
|
483
|
1 |
|
$base = phpUri::parse($row['file']); |
|
484
|
1 |
|
$common = dirname($row['file']) . '/'; |
|
485
|
1 |
|
foreach ($matches as $match) { |
|
486
|
1 |
|
if (preg_match('/(?P<file>[^#\?]*)(?P<extra>.*)/', ltrim($match['path'], '/'), $path)) { |
|
487
|
1 |
|
if (static::mime(pathinfo($path['file'], PATHINFO_EXTENSION))) { |
|
488
|
1 |
|
$file = $base->join($path['file']).$path['extra']; |
|
489
|
1 |
|
if ($dir = $page->commonDir(array($common, $file))) { |
|
490
|
1 |
|
$common = $dir; |
|
491
|
1 |
|
if (strpos($match[0], '@import') === 0) { |
|
492
|
1 |
|
$rnr[$match[0]] = '@import "'.$file.'"'; |
|
493
|
1 |
|
} else { |
|
494
|
1 |
|
$rnr[$match[0]] = 'url("'.$file.'")'; |
|
495
|
|
|
} |
|
496
|
1 |
|
} |
|
497
|
1 |
|
} |
|
498
|
1 |
|
} |
|
499
|
1 |
|
} |
|
500
|
1 |
|
if (!empty($rnr)) { |
|
501
|
1 |
|
$page->dir('set', 'css-dir', $common); |
|
502
|
1 |
|
$url = $page->url['base'].'css-dir/'; |
|
503
|
1 |
|
foreach ($rnr as $remove => $replace) { |
|
504
|
1 |
|
$rnr[$remove] = str_replace($common, $url, $replace); |
|
505
|
1 |
|
} |
|
506
|
1 |
|
$css = static::urls(str_replace(array_keys($rnr), array_values($rnr), $css)); |
|
507
|
1 |
|
} |
|
508
|
|
|
|
|
509
|
1 |
|
return $css; |
|
510
|
|
|
} |
|
511
|
|
|
|
|
512
|
2 |
|
private function paths(&$cache) |
|
513
|
|
|
{ |
|
514
|
2 |
|
$page = Page::html(); |
|
515
|
2 |
|
$count = 0; |
|
516
|
2 |
|
foreach ($cache as $dir => $files) { |
|
517
|
2 |
|
$count += count($files); |
|
518
|
2 |
|
} |
|
519
|
2 |
|
$ids = $this->ids($count); |
|
520
|
2 |
|
$insert = array(); |
|
521
|
2 |
|
$update = array(); |
|
522
|
2 |
|
$stmt = $this->db->prepare(array( |
|
523
|
2 |
|
'SELECT f.id AS file_id, f.updated, p.tiny, p.id AS path_id', |
|
524
|
2 |
|
'FROM files AS f INNER JOIN paths AS p ON f.id = p.file_id', |
|
525
|
2 |
|
'WHERE f.file = ? AND f.query = ?', |
|
526
|
2 |
|
), 'assoc'); |
|
527
|
2 |
|
foreach ($cache as $dir => $files) { |
|
528
|
2 |
|
foreach ($files as $path => $tiny) { |
|
529
|
2 |
|
list($file, $query) = explode('?', $path.'?'); |
|
530
|
2 |
|
$file = $page->dir[$dir].$file; |
|
531
|
2 |
|
$updated = filemtime($file); |
|
532
|
2 |
|
$this->db->execute($stmt, array($file, $query)); |
|
533
|
2 |
|
if ($row = $this->db->fetch($stmt)) { |
|
534
|
2 |
|
if ($row['updated'] == $updated) { |
|
535
|
2 |
|
$tiny = $row['tiny']; |
|
536
|
2 |
|
} else { |
|
537
|
1 |
|
list($path_id, $tiny) = each($ids); |
|
538
|
1 |
|
$update[$row['file_id']] = array($path_id, $updated); |
|
539
|
|
|
} |
|
540
|
2 |
|
} else { |
|
541
|
2 |
|
list($path_id, $tiny) = each($ids); |
|
542
|
2 |
|
$insert[] = array($path_id, $file, $query, $updated); |
|
543
|
|
|
} |
|
544
|
2 |
|
$cache[$dir][$path] = $tiny; |
|
545
|
2 |
|
} |
|
546
|
2 |
|
} |
|
547
|
2 |
|
$this->db->close($stmt); |
|
548
|
2 |
|
if (empty($insert) && empty($update)) { |
|
549
|
1 |
|
return; |
|
550
|
|
|
} |
|
551
|
2 |
|
$this->db->exec('BEGIN IMMEDIATE'); |
|
552
|
2 |
|
$paths = array(); |
|
553
|
2 |
|
if (!empty($insert)) { |
|
554
|
2 |
|
$stmt = $this->db->insert('files', array('path_id', 'file', 'query', 'updated')); |
|
555
|
2 |
|
foreach ($insert as $array) { |
|
556
|
2 |
|
$paths[$array[0]] = $this->db->insert($stmt, $array); |
|
557
|
2 |
|
} |
|
558
|
2 |
|
$this->db->close($stmt); |
|
559
|
2 |
|
} |
|
560
|
2 |
|
if (!empty($update)) { |
|
561
|
1 |
|
$stmt = $this->db->update('files', 'id', array('path_id', 'updated')); |
|
562
|
1 |
|
foreach ($update as $file_id => $array) { |
|
563
|
1 |
|
$this->db->update($stmt, $file_id, $array); |
|
564
|
1 |
|
$paths[$array[0]] = $file_id; |
|
565
|
1 |
|
} |
|
566
|
1 |
|
$this->db->close($stmt); |
|
567
|
1 |
|
} |
|
568
|
2 |
|
if (!empty($paths)) { |
|
569
|
2 |
|
$stmt = $this->db->update('paths', 'id', array('file_id')); |
|
570
|
2 |
|
foreach ($paths as $path_id => $file_id) { |
|
571
|
2 |
|
$this->db->update($stmt, $path_id, array($file_id)); |
|
572
|
2 |
|
} |
|
573
|
2 |
|
$this->db->close($stmt); |
|
574
|
2 |
|
} |
|
575
|
2 |
|
$this->db->exec('COMMIT'); |
|
576
|
|
|
|
|
577
|
2 |
|
return; |
|
578
|
|
|
} |
|
579
|
|
|
|
|
580
|
2 |
|
private function ids($count) |
|
581
|
|
|
{ |
|
582
|
2 |
|
$ids = array(); |
|
583
|
2 |
|
if ($stmt = $this->db->query(array( |
|
584
|
2 |
|
'SELECT id, tiny', |
|
585
|
2 |
|
'FROM paths', |
|
586
|
2 |
|
'WHERE file_id = ?', |
|
587
|
2 |
|
'ORDER BY id DESC LIMIT '.$count, |
|
588
|
2 |
|
), 0, 'row')) { |
|
|
|
|
|
|
589
|
2 |
|
while (list($id, $tiny) = $this->db->fetch($stmt)) { |
|
590
|
2 |
|
$ids[$id] = $tiny; |
|
591
|
2 |
|
} |
|
592
|
2 |
|
$this->db->close($stmt); |
|
593
|
2 |
|
} |
|
594
|
2 |
|
if (count($ids) == $count) { |
|
595
|
2 |
|
return $ids; |
|
596
|
|
|
} |
|
597
|
1 |
|
$this->db->exec('BEGIN IMMEDIATE'); |
|
598
|
1 |
|
$stmt = $this->db->insert('OR IGNORE INTO paths', array('tiny')); |
|
599
|
1 |
|
$string = '123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; |
|
600
|
1 |
|
for ($i = 0; $i < $count + 100; ++$i) { |
|
601
|
1 |
|
$tiny_id = ''; // 60 (characters) ^ 5 (length) gives 777,600,000 possible combinations |
|
602
|
1 |
|
while (strlen($tiny_id) < 5) { |
|
603
|
1 |
|
$tiny_id .= $string[mt_rand(0, 60)]; |
|
604
|
1 |
|
} |
|
605
|
1 |
|
$this->db->insert($stmt, array($tiny_id)); |
|
606
|
1 |
|
} |
|
607
|
1 |
|
$this->db->close($stmt); |
|
608
|
1 |
|
$this->db->exec('COMMIT'); |
|
609
|
|
|
|
|
610
|
1 |
|
return $this->ids($count); |
|
611
|
|
|
} |
|
612
|
|
|
|
|
613
|
6 |
|
private function openDatabase() |
|
614
|
|
|
{ |
|
615
|
6 |
|
if (is_null($this->db)) { |
|
616
|
6 |
|
$this->db = new SQLite($this->cached.'Assets.db'); |
|
617
|
6 |
|
if ($this->db->created) { |
|
618
|
1 |
|
$this->db->create('paths', array( |
|
619
|
1 |
|
'id' => 'INTEGER PRIMARY KEY', |
|
620
|
1 |
|
'tiny' => 'TEXT UNIQUE NOT NULL DEFAULT ""', |
|
621
|
1 |
|
'file_id' => 'INTEGER NOT NULL DEFAULT 0', |
|
622
|
1 |
|
)); |
|
623
|
1 |
|
$this->db->create('files', array( |
|
624
|
1 |
|
'id' => 'INTEGER PRIMARY KEY', |
|
625
|
1 |
|
'path_id' => 'INTEGER NOT NULL DEFAULT 0', |
|
626
|
1 |
|
'file' => 'TEXT NOT NULL DEFAULT ""', |
|
627
|
1 |
|
'query' => 'TEXT NOT NULL DEFAULT ""', |
|
628
|
1 |
|
'updated' => 'INTEGER NOT NULL DEFAULT 0', |
|
629
|
1 |
|
), array('unique' => 'file, query')); |
|
630
|
1 |
|
} |
|
631
|
6 |
|
} |
|
632
|
6 |
|
} |
|
633
|
|
|
|
|
634
|
6 |
|
private function closeDatabase() |
|
635
|
|
|
{ |
|
636
|
6 |
|
if (!is_null($this->db)) { |
|
637
|
6 |
|
$this->db->connection()->close(); |
|
638
|
6 |
|
} |
|
639
|
6 |
|
$this->db = null; |
|
640
|
6 |
|
} |
|
641
|
|
|
|
|
642
|
6 |
|
private function __construct() |
|
643
|
|
|
{ |
|
644
|
6 |
|
} |
|
645
|
|
|
} |
|
646
|
|
|
|
If you define a variable conditionally, it can happen that it is not defined for all execution paths.
Let’s take a look at an example:
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.
Available Fixes
Check for existence of the variable explicitly:
Define a default value for the variable:
Add a value for the missing path: