PageRenderTime 42ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/NukeViet3.2/includes/class/optimizer.class.php

http://nuke-viet.googlecode.com/
PHP | 520 lines | 391 code | 37 blank | 92 comment | 28 complexity | f1455b4e56bd1d07dbfa94203536813c MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, GPL-2.0
  1. <?php
  2. /**
  3. * @Project NUKEVIET 3.0
  4. * @Author VINADES.,JSC (contact@vinades.vn)
  5. * @Copyright (C) 2010 VINADES.,JSC. All rights reserved
  6. * @Createdate 27/11/2010, 22:45
  7. */
  8. /**
  9. * optimezer
  10. *
  11. * @package
  12. * @author NUKEVIET 3.0
  13. * @copyright VINADES.,JSC
  14. * @version 2010
  15. * @access public
  16. */
  17. class optimezer
  18. {
  19. private $_content;
  20. private $_conditon = array();
  21. private $_conditonCss = array();
  22. private $_conditonJs = array();
  23. private $_condCount = 0;
  24. private $_meta = array();
  25. private $_title = "<title></title>";
  26. private $_style = array();
  27. private $_links = array();
  28. private $_cssLinks = array();
  29. private $_cssIgnoreLinks = array();
  30. private $_jsLinks = array();
  31. private $_jsInline = array();
  32. private $_jsMatches = array();
  33. private $_jsCount = 0;
  34. private $siteRoot;
  35. private $base_siteurl;
  36. private $cssDir = "files/css";
  37. private $jsDir = "files/js";
  38. private $eol = "\r\n";
  39. private $cssImgNewPath;
  40. private $_tidySupport = false;
  41. /**
  42. * optimezer::__construct()
  43. *
  44. * @param mixed $content
  45. * @return
  46. */
  47. public function __construct( $content, $tidySupport )
  48. {
  49. $this->_content = $content;
  50. $this->siteRoot = preg_replace( "/[\/]+$/", '', str_replace( '\\', '/', realpath( dirname( __file__ ) . '/../../' ) ) );
  51. $base_siteurl = pathinfo( $_SERVER['PHP_SELF'], PATHINFO_DIRNAME );
  52. if ( $base_siteurl == '\\' or $base_siteurl == '/' ) $base_siteurl = '';
  53. if ( ! empty( $base_siteurl ) ) $base_siteurl = str_replace( '\\', '/', $base_siteurl );
  54. if ( ! empty( $base_siteurl ) ) $base_siteurl = preg_replace( "/[\/]+$/", '', $base_siteurl );
  55. if ( ! empty( $base_siteurl ) ) $base_siteurl = preg_replace( "/^[\/]*(.*)$/", '/\\1', $base_siteurl );
  56. if ( defined( 'NV_ADMIN' ) )
  57. {
  58. $base_siteurl = preg_replace( "#/" . NV_ADMINDIR . "(.*)$#", '', $base_siteurl );
  59. }
  60. elseif ( ! empty( $base_siteurl ) )
  61. {
  62. $base_siteurl = preg_replace( "#/index\.php(.*)$#", '', $base_siteurl );
  63. }
  64. $this->base_siteurl = $base_siteurl . '/';
  65. $this->_tidySupport = $tidySupport;
  66. }
  67. /**
  68. * optimezer::process()
  69. *
  70. * @return
  71. */
  72. public function process()
  73. {
  74. $conditionRegex = "/<\!--\[if([^\]]+)\].*?\[endif\]-->/is";
  75. preg_match_all( $conditionRegex, $this->_content, $conditonMatches );
  76. if ( $conditonMatches )
  77. {
  78. $this->_conditon = $conditonMatches[0];
  79. $this->_content = preg_replace_callback( $conditionRegex, array( $this, 'conditionCallback' ), $this->_content );
  80. }
  81. $this->_content = preg_replace( "/<script[^>]+src\s*=\s*[\"|']([^\"']+jquery.min.js)[\"|'][^>]*>[\s\r\n\t]*<\/script>/is", "", $this->_content );
  82. $jsRegex = "/<script[^>]*>[^\<]*<\/script>/is";
  83. preg_match_all( $jsRegex, $this->_content, $jsMatches );
  84. if ( $jsMatches )
  85. {
  86. $this->_jsMatches = $jsMatches[0];
  87. $this->_content = preg_replace_callback( $jsRegex, array( $this, 'jsCallback' ), $this->_content );
  88. }
  89. $this->_meta['http-equiv'] = $this->_meta['name'] = array();
  90. //$regex = "!<meta[^>]+>|<title>[^<]+<\/title>|<link[^>]+>|<style[^>]*>[^\<]*</style>|<script[^>]*>[^\<]*</script>!is";
  91. $regex = "!<meta[^>]+>|<title>[^<]+<\/title>|<link[^>]+>|<style[^>]*>[^\<]*</style>!is";
  92. preg_match_all( $regex, $this->_content, $matches );
  93. if ( $matches )
  94. {
  95. foreach ( $matches[0] as $tag )
  96. {
  97. if ( preg_match( "/^<meta/", $tag ) )
  98. {
  99. preg_match_all( "/([a-zA-Z\-\_]+)\s*=\s*[\"|']([^\"']+)/is", $tag, $matches2 );
  100. if ( ! empty( $matches2 ) )
  101. {
  102. $combine = array_combine( $matches2[1], $matches2[2] );
  103. if ( array_key_exists( 'http-equiv', $combine ) )
  104. {
  105. $this->_meta['http-equiv'][strtolower( $combine['http-equiv'] )] = $combine['content'];
  106. } elseif ( array_key_exists( 'name', $combine ) )
  107. {
  108. $this->_meta['name'][strtolower( $combine['name'] )] = $combine['content'];
  109. }
  110. }
  111. } elseif ( preg_match( "/^<title>[^<]+<\/title>/is", $tag ) )
  112. {
  113. $this->_title = $tag;
  114. } elseif ( preg_match( "/^<style[^>]*>([^<]*)<\/style>/is", $tag, $matches2 ) )
  115. {
  116. $this->_style[] = $matches2[1];
  117. } elseif ( preg_match( "/^<link/", $tag ) )
  118. {
  119. preg_match_all( "/([a-zA-Z]+)\s*=\s*[\"|']([^\"']+)/is", $tag, $matches2 );
  120. $combine = array_combine( $matches2[1], $matches2[2] );
  121. if ( isset( $combine['rel'] ) and preg_match( "/stylesheet/is", $combine['rel'] ) )
  122. {
  123. if ( ! isset( $combine['title'] ) and isset( $combine['href'] ) and preg_match( "/^(?!http(s?)|ftp\:\/\/)(.*?)\.css$/", $combine['href'], $matches3 ) )
  124. {
  125. $media = isset( $combine['media'] ) ? $combine['media'] : "";
  126. $this->_cssLinks[$matches3[0]] = $media;
  127. }
  128. else
  129. {
  130. $this->_cssIgnoreLinks[] = $tag;
  131. }
  132. }
  133. else
  134. {
  135. $this->_links[] = $tag;
  136. }
  137. }
  138. /*elseif ( preg_match( "/^<script[^>]+src\s*=\s*[\"|']((?!http(s?)|ftp\:\/\/)[^\"']+\.js)[\"|'][^>]*>/is", $tag, $matches2 ) )
  139. {
  140. $this->_jsLinks[] = $matches2[1];
  141. } elseif ( preg_match( "/<script[^>]*>([^\<]*)<\/script>/is", $tag, $matches2 ) )
  142. {
  143. $this->_jsInline[] = $matches2[1];
  144. }*/
  145. }
  146. $this->_content = preg_replace( $regex, "", $this->_content );
  147. }
  148. if ( ! empty( $this->_conditon ) )
  149. {
  150. foreach ( $this->_conditon as $key => $value )
  151. {
  152. $this->_content = preg_replace( "/\{\|condition\_" . $key . "\|\}/", $value, $this->_content );
  153. }
  154. }
  155. $meta = array();
  156. if ( ! empty( $this->_meta['http-equiv'] ) )
  157. {
  158. foreach ( $this->_meta['http-equiv'] as $value => $content )
  159. {
  160. $meta[] = "<meta http-equiv=\"" . $value . "\" content=\"" . $content . "\" />";
  161. }
  162. }
  163. if ( ! empty( $this->_meta['name'] ) )
  164. {
  165. foreach ( $this->_meta['name'] as $value => $content )
  166. {
  167. $meta[] = "<meta name=\"" . $value . "\" content=\"" . $content . "\" />";
  168. }
  169. }
  170. if ( ! empty( $this->_jsMatches ) )
  171. {
  172. $_jsSrc = array();
  173. foreach ( $this->_jsMatches as $key => $value )
  174. {
  175. unset( $matches2 );
  176. //Chi cho phep ket noi 1 lan doi voi 1 file JS
  177. if ( preg_match( "/^<script[^>]+src\s*=\s*[\"|']([^\"']+)[\"|'][^>]*>[\s\r\n\t]*<\/script>/is", $value, $matches2 ) )
  178. {
  179. $value = ( ! empty( $matches2[1] ) and ! in_array( $matches2[1], $_jsSrc ) ) ? $value : "";
  180. if ( ! empty( $matches2[1] ) ) $_jsSrc[] = $matches2[1];
  181. } elseif ( ! preg_match( "/<script[^>]+src\s*=([^>]+)>[\s\r\n\t]*<\/script>/is", $value ) and preg_match( "/<script([^>]*)>([^\<]+)<\/script>/is", $value, $matches2 ) )
  182. {
  183. $value = ! preg_match( "/^([^\W]*)$/is", $matches2[1] ) ? $this->minifyJsInline( $matches2 ) : "";
  184. }
  185. else
  186. {
  187. $value = "";
  188. }
  189. $this->_content = preg_replace( "/\{\|js\_" . $key . "\|\}/", $value, $this->_content );
  190. }
  191. }
  192. if ( ! $this->_tidySupport ) $this->_content = $this->minifyHTML( $this->_content );
  193. $head = "<head>" . $this->eol . $this->_title . $this->eol;
  194. if ( ! empty( $meta ) ) $head .= implode( $this->eol, $meta ) . $this->eol;
  195. if ( ! empty( $this->_links ) ) $head .= implode( $this->eol, $this->_links ) . $this->eol;
  196. if ( ! empty( $this->_cssLinks ) ) $head .= "<link rel=\"Stylesheet\" href=\"" . $this->newCssLink() . "\" type=\"text/css\" />" . $this->eol;
  197. if ( ! empty( $this->_cssIgnoreLinks ) ) $head .= implode( $this->eol, $this->_cssIgnoreLinks ) . $this->eol;
  198. if ( ! empty( $this->_style ) ) $head .= "<style type=\"text/css\">" . $this->minifyCss( implode( $this->eol, $this->_style ) ) . "</style>" . $this->eol;
  199. $head .= "<script type=\"text/javascript\" src=\"" . $this->base_siteurl . "js/jquery/jquery.min.js\"></script>" . $this->eol;
  200. if ( ! $this->_tidySupport ) $head = $this->minifyHTML( $head );
  201. $this->_content = preg_replace( '/<head>/i', $head, $this->_content, 1 );
  202. /*$body = "";
  203. if ( ! empty( $this->_jsLinks ) ) $body .= "<script type=\"text/javascript\" src=\"" . $this->newJSLink() . "\"></script>" . $this->eol;
  204. if ( ! empty( $this->_jsInline ) ) $body .= "<script type=\"text/javascript\">" . $this->eol . $this->minifyJsInline( implode( $this->eol, $this->_jsInline ) ) . $this->eol . "</script>" . $this->eol;
  205. $body .= "</body>";
  206. $this->_content = preg_replace( '/<\/body>/i', $body, $this->_content, 1 );*/
  207. return $this->_content;
  208. }
  209. /**
  210. * optimezer::conditionCallback()
  211. *
  212. * @param mixed $matches
  213. * @return
  214. */
  215. private function conditionCallback( $matches )
  216. {
  217. $num = $this->_condCount;
  218. $this->_condCount++;
  219. return "{|condition_" . $num . "|}";
  220. }
  221. /**
  222. * optimezer::jsCallback()
  223. *
  224. * @param mixed $matches
  225. * @return
  226. */
  227. private function jsCallback( $matches )
  228. {
  229. $num = $this->_jsCount;
  230. $this->_jsCount++;
  231. return "{|js_" . $num . "|}";
  232. }
  233. /**
  234. * optimezer::newCssLink()
  235. *
  236. * @return
  237. */
  238. private function newCssLink()
  239. {
  240. $newCSSLink = md5( implode( array_keys( $this->_cssLinks ) ) );
  241. $newCSSLinkPath = $this->siteRoot . '/' . $this->cssDir . '/' . $newCSSLink . '.opt.css';
  242. if ( ! file_exists( $newCSSLinkPath ) )
  243. {
  244. $contents = "";
  245. foreach ( $this->_cssLinks as $link => $media )
  246. {
  247. $link = preg_replace( "#^" . $this->base_siteurl . "#", "", $link );
  248. if ( ! empty( $media ) and ! preg_match( "/all/", $media ) ) $contents .= "@media " . $media . "{" . $this->eol;
  249. $contents .= $this->getCssContent( $link ) . $this->eol;
  250. if ( ! empty( $media ) and ! preg_match( "/all/", $media ) ) $contents .= "}" . $this->eol;
  251. }
  252. $contents = $this->minifyCss( $contents );
  253. file_put_contents( $newCSSLinkPath, $contents );
  254. }
  255. return $this->base_siteurl . $this->cssDir . "/" . $newCSSLink . ".opt.css";
  256. }
  257. /**
  258. * optimezer::newJSLink()
  259. *
  260. * @return
  261. */
  262. private function newJSLink()
  263. {
  264. $newJSLink = md5( implode( $this->_jsLinks ) );
  265. $newJSLinkPath = $this->siteRoot . '/' . $this->jsDir . '/' . $newJSLink . '.opt.js';
  266. if ( ! file_exists( $newJSLinkPath ) )
  267. {
  268. $contents = "";
  269. foreach ( $this->_jsLinks as $link )
  270. {
  271. $link = preg_replace( "#^" . $this->base_siteurl . "#", "", $link );
  272. $contents .= file_get_contents( $this->siteRoot . '/' . $link ) . $this->eol;
  273. }
  274. $contents = $this->minifyJs( $contents );
  275. file_put_contents( $newJSLinkPath, $contents );
  276. }
  277. return $this->base_siteurl . $this->jsDir . "/" . $newJSLink . ".opt.js";
  278. }
  279. /**
  280. * optimezer::minifyJs()
  281. *
  282. * @param mixed $contents
  283. * @return
  284. */
  285. private function minifyJs( $contents )
  286. {
  287. $contents = preg_replace( "/(\r\n)+|(\n|\r)+/", "\r\n", $contents );
  288. return $contents;
  289. }
  290. /**
  291. * optimezer::minifyJsInline()
  292. *
  293. * @param mixed $jsInline
  294. * @return
  295. */
  296. private function minifyJsInline( $matches )
  297. {
  298. $jsInline = preg_replace( '/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $matches[2] );
  299. $jsInline = $this->minifyJs( $jsInline );
  300. $jsInline = preg_replace( '/^\s+|\s+$/m', '', $jsInline );
  301. if ( ! $this->_tidySupport and ! preg_match( "/^\/\/<\!\[CDATA\[/", $jsInline ) )
  302. {
  303. $jsInline = "//<![CDATA[" . $this->eol . $jsInline . $this->eol . "//]]>";
  304. }
  305. $jsInline = "<script" . $matches[1] . ">" . $this->eol . $jsInline . $this->eol . "</script>";
  306. return $jsInline;
  307. }
  308. /**
  309. * optimezer::getCssContent()
  310. *
  311. * @param mixed $link
  312. * @return
  313. */
  314. private function getCssContent( $link )
  315. {
  316. $content = file_get_contents( $this->siteRoot . '/' . $link );
  317. $this->cssImgNewPath = "../../" . str_replace( '\\', '/', dirname( $link ) ) . "/";
  318. $content = preg_replace_callback( "/url[\s]*\([\s]*[\'\"]*([^\'\"\)]+)[\'\"]*[\s]*\)/", array( $this, 'changeCssURL' ), $content );
  319. return $content;
  320. }
  321. /**
  322. * optimezer::changeCssURL()
  323. *
  324. * @param mixed $matches
  325. * @return
  326. */
  327. private function changeCssURL( $matches )
  328. {
  329. $url = $this->cssImgNewPath . $matches[1];
  330. while ( preg_match( "/([^\/(\.\.)]+)\/\.\.\//", $url ) )
  331. {
  332. $url = preg_replace( "/([^\/(\.\.)]+)\/\.\.\//", "", $url );
  333. }
  334. return "url(" . $url . ")";
  335. }
  336. /**
  337. * optimezer::minifyCss()
  338. *
  339. * @param mixed $cssContent
  340. * @return
  341. */
  342. private function minifyCss( $cssContent )
  343. {
  344. $cssContent = preg_replace( '@>/\\*\\s*\\*/@', '>/*keep*/', $cssContent );
  345. $cssContent = preg_replace( '@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $cssContent );
  346. $cssContent = preg_replace( '@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $cssContent );
  347. $cssContent = preg_replace_callback( '@\\s*/\\*([\\s\\S]*?)\\*/\\s*@', array( $this, 'commentCB' ), $cssContent );
  348. $cssContent = preg_replace( '/[\s\t\r\n]+/', ' ', $cssContent );
  349. $cssContent = preg_replace( '/[\s]*(\:|\,|\;|\{|\})[\s]*/', "$1", $cssContent );
  350. $cssContent = preg_replace( "/[\#]+/", "#", $cssContent );
  351. $cssContent = str_replace( array( ' 0px', ':0px', ';}', ':0 0 0 0', ':0.', ' 0.' ), array( ' 0', ':0', '}', ':0', ':.', ' .' ), $cssContent );
  352. $cssContent = preg_replace( '/\\s*([{;])\\s*([\\*_]?[\\w\\-]+)\\s*:\\s*(\\b|[#\'"-])/x', '$1$2:$3', $cssContent );
  353. $cssContent = preg_replace_callback( '/(?:\\s*[^~>+,\\s]+\\s*[,>+~])+\\s*[^~>+,\\s]+{/x', array( $this, 'selectorsCB' ), $cssContent );
  354. $cssContent = preg_replace( '/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i', '$1#$2$3$4$5', $cssContent );
  355. $cssContent = preg_replace_callback( '/font-family:([^;}]+)([;}])/', array( $this, 'fontFamilyCB' ), $cssContent );
  356. $cssContent = preg_replace( '/@import\\s+url/', '@import url', $cssContent );
  357. $cssContent = preg_replace( '/:first-l(etter|ine)\\{/', ':first-l$1 {', $cssContent );
  358. $cssContent = preg_replace( "/[^\}]+\{[\s|\;]*\}[\s]*/", "", $cssContent );
  359. $cssContent = preg_replace( "/[ ]+/", " ", $cssContent );
  360. $cssContent = trim( $cssContent );
  361. return $cssContent;
  362. }
  363. /**
  364. * optimezer::commentCB()
  365. *
  366. * @param mixed $m
  367. * @return
  368. */
  369. private function commentCB( $m )
  370. {
  371. $hasSurroundingWs = ( trim( $m[0] ) !== $m[1] );
  372. $m = $m[1];
  373. if ( $m === 'keep' )
  374. {
  375. return '/**/';
  376. }
  377. if ( $m === '" "' )
  378. {
  379. return '/*" "*/';
  380. }
  381. if ( preg_match( '@";\\}\\s*\\}/\\*\\s+@', $m ) )
  382. {
  383. return '/*";}}/* */';
  384. }
  385. if ( preg_match( '@^/\\s*(\\S[\\s\\S]+?)\\s*/\\*@x', $m, $n ) )
  386. {
  387. return "/*/" . $n[1] . "/**/";
  388. }
  389. if ( substr( $m, -1 ) === '\\' )
  390. {
  391. return '/*\\*/';
  392. }
  393. if ( $m !== '' && $m[0] === '/' )
  394. {
  395. return '/*/*/';
  396. }
  397. return $hasSurroundingWs ? ' ' : '';
  398. }
  399. /**
  400. * optimezer::selectorsCB()
  401. *
  402. * @param mixed $m
  403. * @return
  404. */
  405. private function selectorsCB( $m )
  406. {
  407. return preg_replace( '/\\s*([,>+~])\\s*/', '$1', $m[0] );
  408. }
  409. /**
  410. * optimezer::fontFamilyCB()
  411. *
  412. * @param mixed $m
  413. * @return
  414. */
  415. private function fontFamilyCB( $m )
  416. {
  417. $m[1] = preg_replace( '/\\s*("[^"]+"|\'[^\']+\'|[\\w\\-]+)\\s*/x', '$1', $m[1] );
  418. return 'font-family:' . $m[1] . $m[2];
  419. }
  420. /**
  421. * optimezer::checkImg()
  422. *
  423. * @param mixed $m
  424. * @return
  425. */
  426. private function checkImg( $m )
  427. {
  428. if ( ! preg_match( '/alt=[\'|"](.*?)[\'|"]/i', $m[1] ) )
  429. {
  430. return ( "<img alt=\"\"" . $m[1] . "/>" );
  431. }
  432. return $m[0];
  433. }
  434. /**
  435. * optimezer::minifyHTML()
  436. *
  437. * @param mixed $content
  438. * @return
  439. */
  440. private function minifyHTML( $content )
  441. {
  442. $content = preg_replace_callback( '/<!--([\s\S]*?)-->/', array( $this, 'HTMLCommentCB' ), $content );
  443. $content = preg_replace( '/<([^\>]+)\s+\/\s+\>/', '<$1 />', $content );
  444. $content = preg_replace( '#<(br|hr|input|img|meta)([^>]+)>#', "<\\1 \\2 />", $content );
  445. $content = preg_replace( '#\s*\/\s*\/>#', " />", $content );
  446. $content = preg_replace_callback( '/<img([^>]+)\/>/', array( $this, 'checkImg' ), $content );
  447. $content = preg_replace( '/^\s+/m', '', $content );
  448. return $content;
  449. }
  450. /**
  451. * optimezer::HTMLCommentCB()
  452. *
  453. * @param mixed $m
  454. * @return
  455. */
  456. private function HTMLCommentCB( $m )
  457. {
  458. return ( strpos( $m[1], '[' ) === 0 || strpos( $m[1], '<![' ) !== false ) ? $m[0] : '';
  459. }
  460. /**
  461. * optimezer::HTMLoutsideTagCB()
  462. *
  463. * @param mixed $m
  464. * @return
  465. */
  466. private function HTMLoutsideTagCB( $m )
  467. {
  468. return '>' . preg_replace( '/^\\s+|\\s+$/', ' ', $m[1] ) . '<';
  469. }
  470. }
  471. ?>