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: