PageRenderTime 54ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Milk/Utils/Compress.php

https://github.com/geekbuntu/milk
PHP | 393 lines | 331 code | 48 blank | 14 comment | 76 complexity | 25a61b6dcb3f1b25c6a0e93e896b73c8 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. namespace Milk\Utils;
  3. use \F3;
  4. use \Milk\Utils\Compress\ClosureCompiler,
  5. \Milk\Utils\Compress\CSSTidy;
  6. use \Milk\WebService\Consumers\Amazon;
  7. require_once(LIB_PATH.'/CSSTidy/class.csstidy.php');
  8. class Compress {
  9. static $js,
  10. $css,
  11. $cli = false,
  12. $js_include = array(),
  13. $css_include = array(),
  14. $uploadFiles = array();
  15. protected $method,
  16. $request,
  17. $query;
  18. public function __construct() {
  19. $compress = F3::get('COMPRESS');
  20. self::$js = $compress['JS'];
  21. self::$css = $compress['CSS'];
  22. F3::set('compress_js', new Compress_Links('js'));
  23. F3::set('compress_css', new Compress_Links('css'));
  24. if (PHP_SAPI == 'cli')
  25. self::$cli = true;
  26. if (!self::$cli) {
  27. $this->method = $_SERVER['REQUEST_METHOD'];
  28. $uri = explode('/', $_SERVER['REQUEST_URI']);
  29. $file = $uri[count($uri)-1];
  30. if ($file == "") {
  31. $file = $uri[count($uri)-2];
  32. }
  33. $this->group = substr($file, 0, strpos($file, '.'));
  34. } else {
  35. $argv = (array)F3::get('argv');
  36. $this->group = $argv[0];
  37. }
  38. }
  39. public function getJS() {
  40. self::outputJS($this->group);
  41. }
  42. public function getCSS() {
  43. self::outputCSS($this->group);
  44. }
  45. public static function generateLinks($type, $group) {
  46. $links = '';
  47. $urls = F3::get('urls');
  48. $gzip = strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') ? '.gz' : '';
  49. if ($type == 'js') {
  50. if (!isset(self::$js[$group]))
  51. return false;
  52. $remote = false;
  53. $release = F3::get('RELEASE');
  54. foreach (self::$js[$group] as $file) {
  55. // check for url
  56. if (strpos($file, '//') > -1) {
  57. $remote = true;
  58. $links .= "<script src=\"$file\"></script>\n\t";
  59. } else if (!$release) {
  60. $links .= "<script src=\"{$urls->static}/js/$file\"></script>\n\t";
  61. }
  62. }
  63. if ($release && !$remote) {
  64. $output = STATIC_PATH.'/js/'.$group.'.min.js';
  65. if (is_readable($output)) {
  66. //$crc = md5_file($output);
  67. $links .= "<script src=\"{$urls->cdn}/js/$group.min$gzip.js\"></script>\n\t";
  68. } else {
  69. self::generateJS($group);
  70. }
  71. }
  72. return $links;
  73. } elseif ($type == 'css') {
  74. if (!isset(self::$css[$group]))
  75. return false;
  76. $media = "all";
  77. $remote = false;
  78. $release = F3::get('RELEASE');
  79. foreach (self::$css[$group] as $file) {
  80. // check for url
  81. if (strpos($file, '//') > -1) {
  82. $remote = true;
  83. $m2 = substr($file, strpos($file, '@'));
  84. if (empty($m2))
  85. $media = $m2;
  86. $links .= "<link href=\"$file\" rel=\"stylesheet\" type=\"text/css\" media=\"$media\" />\n\t";
  87. }
  88. if (!$release) {
  89. $links .= "<link href=\"{$urls->static}/css/$file\" rel=\"stylesheet\" type=\"text/css\" media=\"$media\" />\n\t";
  90. }
  91. }
  92. if ($release && !$remote) {
  93. $output = STATIC_PATH.'/css/'.$group.'.min.css';
  94. if (is_readable($output)) {
  95. //$crc = md5(filemtime($output));
  96. $links .= "<link href=\"{$urls->cdn}/css/$group.min$gzip.css\" rel=\"stylesheet\" type=\"text/css\" media=\"$media\" />\n\t";
  97. } else {
  98. self::generateCSS($group);
  99. }
  100. }
  101. return $links;
  102. }
  103. }
  104. public static function outputJS($group) {
  105. $js = self::$js;
  106. $recheck = F3::get('PARAMS[crc]') == 'recheck' ? true : false;
  107. $output = STATIC_PATH.'/js/'.$group.'.min.js';
  108. if (is_readable($output) && !$recheck) {
  109. if(array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER)) {
  110. $modified_since = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
  111. $last_modified = filemtime($output);
  112. if($modified_since >= $last_modified) {
  113. header("HTTP/1.0 304 Not Modified");
  114. exit;
  115. }
  116. }
  117. } else {
  118. self::generateJS($group);
  119. }
  120. header('Last-Modified: '.gmdate("D, d M Y H:i:s", filemtime($output)).' GMT');
  121. header('Content-Type: application/x-javascript; charset: UTF-8');
  122. readfile($output);
  123. ob_end_flush();
  124. }
  125. public static function outputCSS($group) {
  126. $css = self::$css;
  127. $recheck = F3::get('PARAMS[crc]') == 'recheck' ? true : false;
  128. $output = STATIC_PATH.'/css/'.$group.'.min.css';
  129. if (is_readable($output) && !$recheck) {
  130. if(array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER)) {
  131. $modified_since = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
  132. $last_modified = filemtime($output);
  133. if($modified_since >= $last_modified) {
  134. header("HTTP/1.0 304 Not Modified");
  135. exit;
  136. }
  137. }
  138. } else {
  139. self::generateCSS($group);
  140. }
  141. header('Last-Modified: '.gmdate("D, d M Y H:i:s", filemtime($output)).' GMT');
  142. header('Content-Type: text/css; charset: UTF-8');
  143. readfile($output);
  144. ob_end_flush();
  145. }
  146. public static function generateJS($group) {
  147. set_time_limit(480);
  148. $js = self::$js;
  149. $output = STATIC_PATH.'/js/'.$group.'.min.js';
  150. if (self::$cli)
  151. echo "Target: $output\n";
  152. if ($packed = self::js($js[$group]))
  153. file_put_contents($output, $packed);
  154. // save packed file
  155. self::uploadToS3($output, 'js/'.$group.'.min.js', 'text/javascript');
  156. // create gzipped file as well
  157. $output_gz = STATIC_PATH.'/js/'.$group.'.min.gz.js';
  158. shell_exec("gzip -c $output > $output_gz");
  159. self::uploadToS3($output_gz, 'js/'.$group.'.min.gz.js', 'text/javascript');
  160. }
  161. public static function generateCSS($group) {
  162. set_time_limit(480);
  163. $css = self::$css;
  164. $output = STATIC_PATH.'/css/'.$group.'.min.css';
  165. if (self::$cli)
  166. echo "Target: $output\n";
  167. if ($packed = self::css($css[$group])) {
  168. $urls = F3::get('urls');
  169. preg_match_all("/static(?:-stage)?.maklarpaket.se\/(.[^)]+)/", $packed, $matches);
  170. $images = array();
  171. foreach ($matches[1] as $m) {
  172. if (isset($images[$m]) || !file_exists(STATIC_PATH."/$m"))
  173. continue;
  174. $images[$m] = true;
  175. if (self::$cli)
  176. echo "Processing image: $m\n";
  177. self::uploadToS3(STATIC_PATH."/$m", $m);
  178. }
  179. // save packed file
  180. $packed = str_replace(array('static.maklarpaket.se', 'static-stage.maklarpaket.se'), 'cdn.maklarpaket.se', $packed);
  181. file_put_contents($output, $packed);
  182. self::uploadToS3($output, 'css/'.$group.'.min.css', 'text/css');
  183. // create gzipped file as well
  184. $output_gz = STATIC_PATH.'/css/'.$group.'.min.gz.css';
  185. shell_exec("gzip -c $output > $output_gz");
  186. self::uploadToS3($output_gz, 'css/'.$group.'.min.gz.css', 'text/css');
  187. }
  188. }
  189. public static function executeUpload(){
  190. $ibatch = array();
  191. foreach (self::$uploadFiles as $file) {
  192. $upload = false;
  193. $source = $file['source'];
  194. $target = $file['target'];
  195. $mime = $file['mime'];
  196. if (empty($mime))
  197. $mime = Files::getMimeType($source);
  198. // Lets check if it's on S3 and compare with local file
  199. if (self::$cli)
  200. echo "Checking remote file: $target";
  201. if (($info = S3::getObjectInfo(F3::get('MILK.S3.BUCKET'), $target)) !== false) {
  202. if (md5_file($source) === $info['hash']) {
  203. if (self::$cli)
  204. echo " [IDENTICAL]\n";
  205. } else {
  206. if (self::$cli)
  207. echo " [UPDATING]\n";
  208. $upload = true;
  209. $ibatch[] = $target;
  210. }
  211. } else {
  212. if (self::$cli)
  213. echo " [NOT FOUND]\n";
  214. $upload = true;
  215. }
  216. // Upload file to S3
  217. if ($upload) {
  218. $expires = strtotime("+3 month");
  219. $max_age = $expires-time();
  220. $requestHeaders = array(
  221. F3::HTTP_Content => $mime,
  222. "Cache-Control" => "max-age=$max_age, no-transform, public",
  223. "Expires" => gmdate("D, d M Y H:i:s T", $expires)
  224. );
  225. // Check if target is a gzip file
  226. if (strpos($target, ".gz")) {
  227. $requestHeaders['Content-Encoding'] = 'gzip';
  228. $requestHeaders['Vary'] = 'Accept-Encoding';
  229. }
  230. if (self::$cli)
  231. echo "Uploading: $source => $target";
  232. if (!S3::putObject(
  233. S3::inputFile($source),
  234. F3::get('MILK.S3.BUCKET'),
  235. $target,
  236. S3::ACL_PUBLIC_READ,
  237. array(),
  238. $requestHeaders
  239. )) {
  240. //! Upload failed
  241. if (self::$cli)
  242. echo " [FAILED]\n";
  243. else
  244. trigger_error("Upload to S3 failed!");
  245. return false;
  246. }
  247. if (self::$cli)
  248. echo " [OK]\n";
  249. }
  250. }
  251. if (count($ibatch) > 0) {
  252. if (self::$cli)
  253. echo "Invalidating updated files...";
  254. if ($dist = S3::getDistribution(F3::get('MILK.CLOUDFRONT.DIST_ID')))
  255. S3::invalidatePaths($dist, $ibatch);
  256. if (self::$cli)
  257. echo " Done!\n";
  258. } else {
  259. echo "No files to invalidate.\n";
  260. }
  261. }
  262. public static function uploadToS3($source, $target, $mime=null) {
  263. self::$uploadFiles[] = array(
  264. 'source' => $source,
  265. 'target' => $target,
  266. 'mime' => $mime
  267. );
  268. }
  269. public static function js($files) {
  270. if (!is_array($files))
  271. $files = array($files);
  272. $compiler = new ClosureCompiler();
  273. foreach ($files as $file) {
  274. if (self::$cli)
  275. echo "Adding file $file\n";
  276. $file = STATIC_PATH."/js/$file";
  277. $compiler->add($file);
  278. }
  279. $compiler->simpleMode()
  280. ->useClosureLibrary();
  281. if (F3::get('RELEASE'))
  282. $compiler->hideDebugInfo();
  283. if (self::$cli)
  284. echo "Compiling and merging...";
  285. $compiled = $compiler->_compile();
  286. if (self::$cli)
  287. echo " Done!\n";
  288. return $compiled;
  289. }
  290. public static function css($files) {
  291. if (!is_array($files))
  292. $files = array($files);
  293. $packed = '';
  294. $css_code = '';
  295. foreach ($files as $file) {
  296. if (strpos($file, '://'))
  297. continue;
  298. if (self::$cli)
  299. echo "Adding file $file\n";
  300. $filename = STATIC_PATH."/css/$file";
  301. $content = file_get_contents($filename);
  302. $css_code .= $content;
  303. }
  304. $css = new \csstidy();
  305. $css->set_cfg('preserve_css', FALSE);
  306. $css->set_cfg('remove_bslash', TRUE);
  307. $css->set_cfg('remove_last_;', TRUE);
  308. $css->set_cfg('lowercase_s', TRUE);
  309. $css->set_cfg('merge_selectors', 2);
  310. $css->set_cfg('optimise_shorthands', 1);
  311. $css->set_cfg('replace_colors', TRUE);
  312. $css->set_cfg('case_properties', 1);
  313. $css->set_cfg('compress_font-weight', TRUE);
  314. $css->load_template(LIB_PATH.'/CSSTidy/maklarpaket.tpl');
  315. if (self::$cli)
  316. echo "Compiling and merging...";
  317. $css->parse($css_code);
  318. if (self::$cli)
  319. echo " Done!\n";
  320. $packed = $css->print->plain();
  321. return $packed;
  322. }
  323. }
  324. class Compress_Links {
  325. private $type;
  326. private $links = array();
  327. public function __construct($type) {
  328. $this->type = $type;
  329. }
  330. public function __get($name) {
  331. return $this->links[$name];
  332. }
  333. public function __isset($name) {
  334. // Does not work :(
  335. if (F3::cached('_compress_'.$this->type.$name)) {
  336. return F3::get('_compress_'.$this->type.$name);
  337. }
  338. if (!isset($this->links[$name])) {
  339. $this->links[$name] = Compress::generateLinks($this->type, $name);
  340. //F3::set('_compress_'.$this->type.$name, $this->links[$name], true);
  341. return $this->links[$name];
  342. }
  343. return (bool)$this->links[$name];
  344. }
  345. }