1 <?php
2
3 4 5 6 7 8 9 10
11
12 namespace Brickrouge;
13
14 use ICanBoogie\FileCache;
15
16 17 18 19 20
21 class Document extends \ICanBoogie\Object
22 {
23 public $body;
24
25 26 27
28 public $js;
29
30 31 32
33 public $css;
34
35 36 37 38 39
40 public function __construct()
41 {
42 global $core;
43
44 $use_cache = !empty($core->config['cache assets']);
45
46 $this->body = new Element('body');
47 $this->js = new JSCollector($use_cache);
48 $this->css = new CSSCollector($use_cache);
49 }
50
51 52 53 54 55
56 protected function get_assets()
57 {
58 return array
59 (
60 'css' => $this->css->get(),
61 'js' => $this->js->get()
62 );
63 }
64
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
80 protected function set_assets(array $assets)
81 {
82 unset($this->assets);
83 $this->add_assets($assets);
84 }
85
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
102 protected function __unset_assets()
103 {
104 $this->js->clear();
105 $this->css->clear();
106 }
107
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
126 public function add_assets(array $assets)
127 {
128 if (!empty($assets['css']))
129 {
130 foreach ($assets['css'] as $path => $priority)
131 {
132 $this->css->add($path, $priority);
133 }
134 }
135
136 if (!empty($assets['js']))
137 {
138 foreach ($assets['js'] as $path => $priority)
139 {
140 $this->js->add($path, $priority);
141 }
142 }
143 }
144
145 146 147 148 149 150
151 static private function resolve_root()
152 {
153 $stack = debug_backtrace();
154
155 foreach ($stack as $trace)
156 {
157 if (empty($trace['file']) || $trace['file'] == __FILE__)
158 {
159 continue;
160 }
161
162 return dirname($trace['file']);
163 }
164 }
165
166 167 168 169 170 171 172 173 174 175 176 177 178
179 static public function resolve_url($path, $relative=null)
180 {
181 if (strpos($path, 'http://') === 0 || strpos($path, 'https://') === 0)
182 {
183 return $path;
184 }
185 else if (strpos($path, 'phar://') === 0)
186 {
187 if (file_exists($path))
188 {
189 $path = get_accessible_file($path, 'phar');
190 }
191 else
192 {
193 trigger_error(format('Phar file %path does not exists.', array('%path' => $path)));
194
195 return;
196 }
197 }
198
199 $root = DOCUMENT_ROOT;
200
201
202
203 $tried = array($path);
204 $realpath = realpath($path);
205
206
207
208 if (!$realpath)
209 {
210 if (!$relative)
211 {
212 $relative = self::resolve_root() . DIRECTORY_SEPARATOR;
213 }
214
215 $tried[] = $relative . $path;
216 $realpath = realpath($relative . $path);
217 }
218
219
220
221 if (!$realpath)
222 {
223 $tries[] = $root . $path;
224 $realpath = realpath($root . $path);
225 }
226
227
228
229
230
231 if (!$realpath)
232 {
233 trigger_error(format('Unable to resolve path %path to an URL, tried: !tried', array('path' => $path, 'tried' => implode(', ', $tried))));
234
235 return;
236 }
237
238
239
240
241
242 if (strpos($realpath, $root) === false)
243 {
244 $realpath = get_accessible_file($realpath);
245 }
246
247
248
249
250
251 $url = substr($realpath, strlen($root));
252
253 if (DIRECTORY_SEPARATOR == '\\')
254 {
255 $url = strtr($url, '\\', '/');
256 }
257
258 if ($url{0} != '/')
259 {
260 $url = '/' . $url;
261 }
262
263 return $url;
264 }
265 }
266
267 268 269
270 abstract class AssetsCollector
271 {
272 273 274 275 276
277 protected $collected = array();
278
279 280 281 282 283
284 public $use_cache = false;
285
286 287 288
289 public function __construct($use_cache=false)
290 {
291 $this->use_cache = $use_cache;
292 }
293
294 295 296 297 298 299 300 301 302
303 public function add($path, $weight=0, $root=null)
304 {
305 $url = Document::resolve_url($path, $root);
306
307 $this->collected[$url] = $weight;
308
309 return $this;
310 }
311
312 313 314 315 316
317 public function get()
318 {
319 $by_priority = array();
320
321 foreach ($this->collected as $url => $priority)
322 {
323 $by_priority[$priority][] = $url;
324 }
325
326 ksort($by_priority);
327
328 $sorted = array();
329
330 foreach ($by_priority as $urls)
331 {
332 $sorted = array_merge($sorted, $urls);
333 }
334
335 return $sorted;
336 }
337
338 339 340
341 public function clear()
342 {
343 $this->collected = array();
344 }
345
346 abstract public function cache_construct(FileCache $cache, $key, array $userdata);
347 }
348
349 350 351
352 class CSSCollector extends AssetsCollector
353 {
354 public function __toString()
355 {
356 global $core;
357
358 $collected = $this->get();
359
360 try
361 {
362 if ($this->use_cache)
363 {
364 $recent = 0;
365 $root = DOCUMENT_ROOT;
366
367 foreach ($collected as $file)
368 {
369 $recent = max($recent, filemtime($root . $file));
370 }
371
372 $cache = new FileCache
373 (
374 array
375 (
376 FileCache::T_REPOSITORY => $core->config['repository.files'] . '/assets',
377 FileCache::T_MODIFIED_TIME => $recent
378 )
379 );
380
381 $key = sha1(implode(',', $collected)) . '.css';
382
383 $rc = $cache->get($key, array($this, 'cache_construct'), array($collected));
384
385 if ($rc)
386 {
387 $list = json_encode($collected);
388
389 return <<<EOT
390
391 <link type="text/css" href="{$cache->repository}/{$key}" rel="stylesheet" />
392
393 <script type="text/javascript">
394
395 var brickrouge_cached_css_assets = $list;
396
397 </script>
398
399 EOT;
400
401 }
402 }
403 }
404 catch (\Exception $e) { echo render_exception($e); }
405
406
407
408
409
410 $rc = '';
411
412 foreach ($collected as $url)
413 {
414 $rc .= '<link type="text/css" href="' . escape($url) . '" rel="stylesheet" />' . PHP_EOL;
415 }
416
417 return $rc;
418 }
419
420 public function cache_construct(FileCache $cache, $key, array $userdata)
421 {
422 list($collected) = $userdata;
423
424 $rc = '/* Compiled CSS file generated by ' . __CLASS__ . ' */' . PHP_EOL . PHP_EOL;
425
426 foreach ($collected as $url)
427 {
428 $contents = file_get_contents(DOCUMENT_ROOT . $url);
429 $contents = preg_replace('/url\(([^\)]+)/', 'url(' . dirname($url) . '/$1', $contents);
430
431 $rc .= $contents . PHP_EOL;
432 }
433
434 file_put_contents(getcwd() . '/' . $key, $rc);
435
436 return $key;
437 }
438 }
439
440 441 442
443 class JSCollector extends AssetsCollector
444 {
445 public function __toString()
446 {
447 global $core;
448
449 $collected = $this->get();
450
451
452
453
454
455 if (0)
456 {
457 $root = DOCUMENT_ROOT;
458 $repository = $core->config['repository.files'] . '/assets/minified/';
459
460 foreach ($collected as $file)
461 {
462 $minified_key = md5($file);
463
464 if (!file_exists($root . $repository . $minified_key))
465 {
466 echo "<code>create minified ($minified_key) for $file</code><br />";
467
468 $cmd = "java -jar /users/serveurweb/Sites/yuicompressor-2.4.6.jar {$root}{$file} -o {$root}{$repository}{$minified_key}.js --charset utf-8";
469
470 echo "<code><strong>cmd:</strong> $cmd</code>";
471
472 $output = null;
473 $return_var = null;
474
475 exec($cmd, $output, $return_var);
476
477 var_dump($output, $return_var);
478 }
479 }
480 }
481
482
483
484
485
486 try
487 {
488 if ($this->use_cache)
489 {
490 $recent = 0;
491 $root = DOCUMENT_ROOT;
492
493 foreach ($collected as $file)
494 {
495 $recent = max($recent, filemtime($root . $file));
496 }
497
498 $cache = new FileCache
499 (
500 array
501 (
502 FileCache::T_REPOSITORY => $core->config['repository.files'] . '/assets',
503 FileCache::T_MODIFIED_TIME => $recent
504 )
505 );
506
507 $key = sha1(implode(',', $collected)) . '.js';
508
509 $rc = $cache->get($key, array($this, 'cache_construct'), array($collected));
510
511 if ($rc)
512 {
513 return PHP_EOL . PHP_EOL . '<script type="text/javascript" src="' . $cache->repository . '/' . $key . '"></script>' . PHP_EOL . PHP_EOL;
514 }
515 }
516 }
517 catch (\Exception $e) { echo render_exception($e); }
518
519
520
521
522
523 $rc = '';
524
525 foreach ($collected as $url)
526 {
527 $rc .= '<script type="text/javascript" src="' . escape($url) . '"></script>' . PHP_EOL;
528 }
529
530 return $rc;
531 }
532
533 public function cache_construct(FileCache $cache, $key, array $userdata)
534 {
535 list($collected) = $userdata;
536
537 $class = __CLASS__;
538 $date = date('Y-m-d');
539 $list = json_encode($collected);
540
541 $content = <<<EOT
542 /*
543 * Compiled Javascript file generated by $class ($date)
544 */
545
546 var brickrouge_cached_js_assets = $list;
547
548 EOT;
549
550 foreach ($collected as $url)
551 {
552 $content .= file_get_contents(DOCUMENT_ROOT . $url) . PHP_EOL;
553 }
554
555 file_put_contents(getcwd() . '/' . $key, $content);
556
557 return $key;
558 }
559 }