1: <?php
2: namespace RM;
3:
4: use Nette\Object,
5: Nette\FileNotFoundException,
6: Nette\InvalidArgumentException,
7: Nette\Utils\Finder;
8:
9: 10: 11: 12: 13: 14: 15:
16: class AssetsCollector extends Object
17: {
18:
19: const CSS = "css";
20:
21:
22: const JS = "js";
23:
24:
25: protected $css = array();
26:
27:
28: public $cssPath;
29:
30:
31: protected $js = array();
32:
33:
34: public $jsPath;
35:
36:
37: public $webTemp;
38:
39:
40: public $removeOld;
41:
42:
43: public $cssCompiler = array();
44:
45:
46: public $jsCompiler = array();
47:
48:
49: private $packages;
50:
51:
52: public $enabledCompilers;
53:
54:
55: private $usedPackages;
56:
57:
58: public $mergeFiles;
59:
60: 61: 62: 63: 64: 65:
66: public function addCss($file,$dir=null)
67: {
68: if (is_string($file)) {
69: $f = self::findFile($file,array($this->cssPath,$dir));
70: $fileNameOutput = $this->getTempFromFile($f,self::CSS);
71: if (!in_array($fileNameOutput,$this->css))
72: $this->css[] = $fileNameOutput;
73: } elseif (is_array($file)) {
74: foreach ($file as $item) {
75: $f = self::findFile($item,array($this->cssPath,$dir));
76: $fileNameOutput = $this->getTempFromFile($f,self::CSS);
77: if (!in_array($fileNameOutput,$this->css))
78: $this->css[] = $fileNameOutput;
79: }
80: }
81: return $this;
82: }
83:
84: 85: 86: 87: 88: 89:
90: public function addJs($file,$dir=null)
91: {
92: if (is_string($file)) {
93: $f = self::findFile($file,array($this->jsPath,$dir));
94: $fileNameOutput = $this->getTempFromFile($f,self::JS);
95: if (!in_array($fileNameOutput,$this->js))
96: $this->js[] = $fileNameOutput;
97: } elseif (is_array($file)) {
98: foreach ($file as $item) {
99: $f = self::findFile($item,array($this->jsPath,$dir));
100: $fileNameOutput = $this->getTempFromFile($f,self::JS);
101: if (!in_array($fileNameOutput,$this->js))
102: $this->js[] = $fileNameOutput;
103: }
104: }
105: return $this;
106: }
107:
108: 109: 110: 111: 112:
113: public function addCssContent($content,$dir=null)
114: {
115: $fileNameOutput = $this->getTempFromContent($content,$dir=null,self::CSS);
116: if (!in_array($fileNameOutput,$this->css))
117: $this->css[] = $fileNameOutput;
118: return $this;
119: }
120:
121: 122: 123: 124: 125: 126:
127: public function addJsContent($content,$dir=null)
128: {
129: $fileNameOutput = $this->getTempFromContent($content,$dir=null,self::JS);
130: if (!in_array($fileNameOutput,$this->js))
131: $this->js[] = $fileNameOutput;
132: return $this;
133: }
134:
135: 136: 137: 138: 139: 140:
141: public static function findFile($filename, array $dirs = NULL)
142: {
143: if (!empty($dirs))
144: foreach ($dirs as $dir)
145: if (file_exists($dir."/".$filename))
146: return realpath($dir."/".$filename);
147: if (file_exists($filename))
148: return realpath($filename);
149: throw new FileNotFoundException("File '" . $filename . "' not found.");
150: }
151:
152: 153: 154: 155: 156: 157:
158: private function addToFileName($filename,$string)
159: {
160: $path = explode("/",$filename);
161: $filename = array_pop($path);
162: $filename = explode(".",$filename);
163: $extension = array_pop($filename);
164: $filename[] = $string;
165: $filename = implode(".",$filename).".".$extension;
166: return implode("/",$path)."/".$filename;
167: }
168:
169: 170: 171: 172: 173: 174:
175: private function getTempFromFile($source,$type)
176: {
177: $content = file_get_contents($source);
178: $compile_function = 'compile'.$type;
179: $content = $this->$compile_function($content,dirname($source));
180:
181: $md5 = md5($content);
182: $filename = explode("/",self::addToFileName($source,$md5));
183: $fileNameOutput = array_pop($filename);
184: if (!file_exists($this->webTemp."/".$fileNameOutput))
185: file_put_contents($this->webTemp."/".$fileNameOutput,$content);
186:
187:
188: $this->removeAllOldFiles($source, $fileNameOutput);
189:
190:
191: return substr($this->webTemp,strlen(WWW_DIR))."/".$fileNameOutput;
192: }
193:
194: 195: 196: 197: 198: 199: 200:
201: private function getTempFromContent($content,$dir=null,$type)
202: {
203: if (strlen($content)===0)
204: throw new InvalidArgumentException("Content of generated file can not be empty.");
205:
206:
207: $compile_function = 'compile'.$type;
208: $content = $this->$compile_function($content,$dir);
209:
210: $md5 = md5($content);
211: $fileNameOutput = $md5.".".$type;
212: if (!file_exists($this->webTemp."/".$fileNameOutput))
213: file_put_contents($this->webTemp."/".$fileNameOutput,$content);
214:
215:
216: return substr($this->webTemp,strlen(WWW_DIR))."/".$fileNameOutput;
217: }
218:
219: 220: 221: 222:
223: public function getCss()
224: {
225: if ($this->mergeFiles)
226: $this->css = array($this->mergeFiles($this->css,self::CSS));
227: $css_output = array();
228: foreach ($this->css as $source => $temp) {
229: $css_output[] = $temp;
230: }
231: return $css_output;
232: }
233:
234: 235: 236: 237:
238: public function getJs()
239: {
240: if ($this->mergeFiles)
241: $this->js = array($this->mergeFiles($this->js,self::JS));
242: $js_output = array();
243: foreach ($this->js as $source => $temp) {
244: $js_output[] = $temp;
245: }
246: return $js_output;
247: }
248:
249: 250: 251: 252: 253:
254: private function removeAllOldFiles($source, $fileNameOutput)
255: {
256: if ($this->removeOld) {
257: $d = explode("/",$source);
258: $f = explode(".",array_pop($d));
259: $ext = array_pop($f);
260: foreach (Finder::findFiles(implode(".",$f).'*.'.$ext)
261: ->exclude($fileNameOutput)->from($this->webTemp) as $file) {
262: unlink($file->getRealPath());
263: }
264: }
265: }
266:
267: 268: 269:
270: public function checkRequirements()
271: {
272: if (is_null($this->webTemp))
273: throw new InvalidArgumentException("Directory for temporary files is not defined.");
274: if (!is_dir($this->webTemp))
275: throw new FileNotFoundException($this->webTemp." is not directory.");
276: if (!is_writable($this->webTemp))
277: throw new InvalidArgumentException("Directory '".$this->webTemp."' is not writeable.");
278: }
279:
280: 281: 282: 283: 284: 285:
286: private function compileCss($content,$dir=null)
287: {
288: foreach ($this->cssCompiler as $name => $compiler) {
289: if (in_array($name,$this->enabledCompilers))
290: $content = $compiler($content,$dir);
291: }
292: return $content;
293: }
294:
295: 296: 297: 298: 299: 300:
301: private function compileJs($content,$dir=null)
302: {
303: foreach ($this->jsCompiler as $compiler) {
304: $content = $compiler($content,$dir);
305: }
306: return $content;
307: }
308:
309: 310: 311: 312:
313: public function addCssCompiler(array $compilers)
314: {
315: foreach ($compilers as $compiler) {
316: $name = explode("\\",get_class($compiler));
317: $name = lcfirst(array_pop($name));
318: if ($compiler instanceof \RM\AssetsCollector\Compilers\IAssetsCompiler)
319: $this->cssCompiler[$name] = callback($compiler, 'compile');
320: else
321: throw new InvalidArgumentException("Compiler must be instance of \RM\AssetsCollector\Compilers\IAssetsCompiler");
322: }
323: }
324:
325: 326: 327: 328:
329: public function addJsCompiler(array $compilers)
330: {
331: foreach ($compilers as $compiler) {
332: $name = explode("\\",get_class($compiler));
333: $name = lcfirst(array_pop($name));
334: if ($compiler instanceof \RM\AssetsCollector\Compilers\IAssetsCompiler)
335: $this->jsCompiler[$name] = callback($compiler, 'compile');
336: else
337: throw new InvalidArgumentException("Compiler must be instance of \RM\AssetsCollector\Compilers\IAssetsCompiler");
338: }
339: }
340:
341: 342: 343: 344: 345: 346: 347:
348: public function setPackage($name, array $extends = null, array $css = null, array $js = null)
349: {
350: $this->packages[$name] = new \RM\AssetsCollector\Package($name, $extends, $css, $js);
351: }
352:
353: 354: 355: 356:
357: public function setPackages(array $packages)
358: {
359: foreach ($packages as $name => $details) {
360: (isset($details['extends']))?$extends=$details['extends']:$extends=null;
361: (isset($details['css']))?$css=$details['css']:$css=null;
362: (isset($details['js']))?$js=$details['js']:$js=null;
363: if ($css===null AND $js===null and $extends===null)
364: throw new InvalidArgumentException("Package $name is empty.");
365: if ($css===null AND $js===null and count($extends)<=1)
366: throw new InvalidArgumentException("Package $name duplicated package $extends[0].");
367: $this->setPackage ($name, $extends, $css, $js);
368: }
369: }
370:
371: 372: 373: 374:
375: public function addPackage($package)
376: {
377: $this->usedPackages[] = $package;
378: $dependecies = $this->getDependecies($package);
379: if(!empty($dependecies['css']))
380: foreach ($dependecies['css'] as $css)
381: $this->addCss($css);
382: if(!empty($dependecies['js']))
383: foreach ($dependecies['js'] as $js)
384: $this->addJs($js);
385: $package = $this->packages[$package];
386: }
387:
388: 389: 390: 391: 392:
393: public function getDependecies($package)
394: {
395: $dependencies = array("css"=>array(),"js"=>array());
396: $package = $this->packages[$package];
397: if(!empty($package->extends))
398: foreach ($package->extends as $extend) {
399: $ex = $this->getDependecies($extend);
400: $dependencies['css'] = array_merge($dependencies['css'],$ex['css']);
401: $dependencies['js'] = array_merge($dependencies['js'],$ex['js']);
402: }
403: if(!empty($package->css))
404: foreach ($package->css as $css)
405: $dependencies['css'][] = $css;
406: if(!empty($package->js))
407: foreach ($package->js as $js)
408: $dependencies['js'][] = $js;
409: $dependencies['css'] = array_unique($dependencies['css']);
410: $dependencies['js'] = array_unique($dependencies['js']);
411: return $dependencies;
412: }
413:
414: 415: 416: 417: 418:
419: public function addPackages($packages)
420: {
421: if (is_string($packages)) {
422: $this->addPackage($packages);
423: } elseif (is_array($packages)) {
424: foreach ($packages as $package) {
425: $this->addPackage($package);
426: }
427: }
428: return $this;
429: }
430:
431: 432: 433: 434: 435: 436:
437: public function mergeFiles(array $files, $type)
438: {
439: $content = "";
440:
441: foreach ($files as $file)
442: $content .= file_get_contents(WWW_DIR.$file);
443:
444: $fileNameOutput = md5($content).".".$type;
445:
446: if (!file_exists($this->webTemp."/".$fileNameOutput))
447: file_put_contents($this->webTemp."/".$fileNameOutput,$content);
448:
449: return substr($this->webTemp,strlen(WWW_DIR))."/".$fileNameOutput;
450: }
451: }
452: