PageRenderTime 61ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/booster/booster_inc.php

http://github.com/Schepp/CSS-JS-Booster
PHP | 2119 lines | 1171 code | 190 blank | 758 comment | 293 complexity | 297a206ed86b005d1ebf68a6ab344b30 MD5 | raw file
Possible License(s): Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * CSS-JS-BOOSTER
  4. *
  5. * An easy to use PHP-Library that combines, optimizes, dataURI-fies, re-splits,
  6. * compresses and caches your CSS and JS for quicker loading times.
  7. *
  8. * PHP version 5
  9. *
  10. * This program is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU Lesser General Public License as published
  12. * by the Free Software Foundation, either version 3 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Lesser General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Lesser General Public License
  21. * along with this program.
  22. * If not, see <http://www.gnu.org/licenses/lgpl-3.0.txt>
  23. *
  24. * @category PHP
  25. * @package CSS-JS-Booster
  26. * @author Christian Schepp Schaefer <schaepp@gmx.de> <http://twitter.com/derSchepp>
  27. * @copyright 2010 Christian Schepp Schaefer
  28. * @license http://www.gnu.org/copyleft/lesser.html The GNU LESSER GENERAL PUBLIC LICENSE, Version 3.0
  29. * @link http://github.com/Schepp/CSS-JS-Booster
  30. */
  31. // Starting zlib-compressed output
  32. @ini_set('zlib.output_compression',2048);
  33. @ini_set('zlib.output_compression_level',4);
  34. // Starting gzip-compressed output if zlib-compression is turned off
  35. if (
  36. isset($_SERVER['HTTP_ACCEPT_ENCODING'])
  37. && substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')
  38. && function_exists('ob_gzhandler')
  39. && (!ini_get('zlib.output_compression') || ini_get('zlib.output_compression') == '' || strtolower(ini_get('zlib.output_compression')) == 'off' || intval(ini_get('zlib.output_compression')) != 2048)
  40. && !function_exists('booster_wp')
  41. )
  42. {
  43. $booster_use_ob_gzhandler = TRUE;
  44. @ob_start('ob_gzhandler');
  45. }
  46. else
  47. {
  48. @ob_start();
  49. }
  50. /**
  51. * CSS-JS-BOOSTER
  52. */
  53. class Booster {
  54. // Global configuration /////////////////////////////////////////////////////////////////////////////////////////////////////////////
  55. /**
  56. * Defines the markup language to use.
  57. *
  58. * replaces old $css_markuptype
  59. * Defaults to "XHTML".
  60. * @var string
  61. * @access public
  62. */
  63. public $markuptype = 'XHTML';
  64. /**
  65. * Used to store globally the date of last change of newest file
  66. *
  67. * @var integer
  68. * @access public
  69. */
  70. public $filestime = 0;
  71. /**
  72. * Defines the server's root directory
  73. *
  74. * Use this if you changed the root with mod_userdir or something else
  75. * Defaults to $_SERVER['DOCUMENT_ROOT']
  76. * @var string
  77. * @access public
  78. */
  79. public $document_root = '';
  80. /**
  81. * Defines the a base offset if $_SERVER['DOCUMENT_ROOT'] is not equal to http://domain/
  82. * but e.g. points to http://domain/~user/
  83. *
  84. * Use this if you changed the root with mod_userdir or something else
  85. * Defaults to '/';
  86. * Change for example to '/~user/';
  87. * @var string
  88. * @access public
  89. */
  90. public $base_offset = '/';
  91. /**
  92. * Defines the directory to use for caching
  93. *
  94. * The directory is relative to "booster"-folder and should be write-enabled
  95. * Defaults to "booster_cache".
  96. * @var string
  97. * @access public
  98. */
  99. public $booster_cachedir = 'booster_cache';
  100. /**
  101. * Switch cache directory automatic cleanup on sundays on/off
  102. *
  103. * @var boolean
  104. * @access public
  105. */
  106. public $booster_cachedir_autocleanup = TRUE;
  107. /**
  108. * Used to remember if the working-path has already been calculated.
  109. *
  110. * @var boolean
  111. * @access private
  112. * @see setcachedir
  113. */
  114. private $booster_cachedir_transformed = FALSE;
  115. /**
  116. * Switch debug mode on/off for debugging CSS and JS
  117. *
  118. * @var boolean
  119. * @access public
  120. */
  121. public $debug = FALSE;
  122. /**
  123. * Switch debug mode on/off for development of this library
  124. *
  125. * @var boolean
  126. * @access public
  127. */
  128. public $librarydebug = FALSE;
  129. /**
  130. * Defines the file to use for logging in librarydebug-mode
  131. *
  132. * The file is located inside cache-folder
  133. * Starts empty, defaults later to "booster_cache/debug_log".
  134. * @var string
  135. * @access private
  136. */
  137. private $debug_log = '';
  138. /**
  139. * Variable in which to put error messages
  140. *
  141. * @var string
  142. * @access private
  143. */
  144. private $errormessage = '';
  145. /**
  146. * Variable in which we store if mod_rewrite is active (if we can detect it)
  147. *
  148. * @var bool
  149. * @access public
  150. */
  151. public $mod_rewrite = TRUE;
  152. // CSS specific configuration ///////////////////////////////////////////////////////////////////////////////////////////////////////
  153. /**
  154. * Defines source to take the CSS stylesheets from.
  155. *
  156. * It accepts foldernames, filenames, multiple files and folders comma-delimited in strings or as array.
  157. * When passing foldernames, containing files will be processed in alphabetical order.
  158. * The variable also accepts a stylesheet-string if you set css_stringmode to "TRUE"
  159. * Defaults to "css".
  160. * @var mixed
  161. * @access public
  162. * @see $css_stringmode
  163. */
  164. public $css_source = 'css';
  165. /**
  166. * Defines media-attribute for CSS markup output
  167. *
  168. * Specify differing media-types like "print", "handheld", etc.
  169. * Defaults to "all".
  170. * @var string
  171. * @access public
  172. */
  173. public $css_media = 'all';
  174. /**
  175. * Defines rel-attribute for CSS markup output
  176. *
  177. * Specify differing relations like "alternate stylesheet"
  178. * Defaults to "stylesheet".
  179. * @var string
  180. * @access public
  181. */
  182. public $css_rel = 'stylesheet';
  183. /**
  184. * Defines a title-attribute for CSS markup output
  185. *
  186. * If you like to title multiple stylesheets
  187. * Defaults to "".
  188. * @var string
  189. * @access public
  190. */
  191. public $css_title = '';
  192. /**
  193. * Defines in how many parts the CSS output shall be split
  194. *
  195. * As newer browsers support more than 2 concurrent parallel connections
  196. * to a webserver you can decrease loading-time by splitting the output up
  197. * into more than one file.
  198. * Defaults to "2".
  199. * @var number
  200. * @access public
  201. */
  202. public $css_totalparts = 2;
  203. /**
  204. * Defines which part to ouput when retrieving CSS in multiple parts
  205. *
  206. * Used by accompagning script "booster_css.php"
  207. * Defaults to "0".
  208. * @var number
  209. * @access public
  210. */
  211. public $css_part = 0;
  212. /**
  213. * Defines if we want these styles to be lazy loaded
  214. *
  215. * Usefull for CSS with large inlined images to load after the rest
  216. * @var boolean
  217. * @access public
  218. */
  219. public $css_lazyload = FALSE;
  220. /**
  221. * Defines if to switch to a more powerful hosted minifier
  222. *
  223. * You can use the full YUI Compressor included in CSS-JS-Booster instead of the
  224. * included minification functions for stylesheets.
  225. * But be carefull, it will only work on dedicated servers with Java installed.
  226. * @var boolean
  227. * @access public
  228. */
  229. public $css_hosted_minifier = FALSE;
  230. /**
  231. * Defines the path to a hosted minifier
  232. *
  233. * Will store the local CSS minifier path relative to this file
  234. * @var string
  235. * @access private
  236. * @see $css_hosted_minifier
  237. */
  238. private $css_hosted_minifier_path = 'yuicompressor/yuicompressor-2.4.2.jar';
  239. /**
  240. * Defines if source-file retrieval shall be recursive
  241. *
  242. * Only matters when passing folders as source-parameter.
  243. * If set to "TRUE" contents of folders found inside source-folder are also fetched.
  244. * Defaults to "FALSE".
  245. * @var boolean
  246. * @access public
  247. */
  248. public $css_recursive = FALSE;
  249. /**
  250. * Switches on string-mode, when passing styleheet-strings as source
  251. *
  252. * Instead of folders and files to read and parse you can also pass
  253. * stylesheet-code as source. But, this only works if you switch string-mode on.
  254. * Defaults to "FALSE".
  255. * @var boolean
  256. * @access public
  257. * @see $css_source
  258. */
  259. public $css_stringmode = FALSE;
  260. /**
  261. * Defines the base-folder for all files referenced in stylesheet-string
  262. *
  263. * When being in string-mode, the booster prepends this path going out from the caller-location
  264. * in order to find all referenced files.
  265. * Defaults to "./".
  266. * @var string
  267. * @access public
  268. * @see $css_stringmode
  269. */
  270. public $css_stringbase = './';
  271. /**
  272. * Used to store the date of last change of a stylesheet-string
  273. *
  274. * Is set to the file-time of the calling script during construction.
  275. * @var integer
  276. * @access private
  277. * @see $css_stringmode
  278. */
  279. private $css_stringtime = 0;
  280. /**
  281. * Used to store if we are dealing with an older IE
  282. *
  283. * Is set to TRUE if IE < 8.
  284. * @var bool
  285. * @access public
  286. */
  287. public $css_mhtml_enabled_ie = FALSE;
  288. // JavaScript specific configuration ///////////////////////////////////////////////////////////////////////////////////////////////////
  289. /**
  290. * Defines source to take the JS from
  291. *
  292. * It accepts foldernames, filenames, multiple files and folders comma-delimited in strings or as array.
  293. * When passing foldernames, containing files will be processed in alphabetical order.
  294. * The variable also accepts a javascript-string if you set js_stringmode to "TRUE"
  295. * Defaults to "js".
  296. * @var mixed
  297. * @access public
  298. * @see $js_stringmode
  299. */
  300. public $js_source = 'js';
  301. /**
  302. * Defines in how many parts the JS output shall be split [Deprecated, still in there for backward compatibility]
  303. *
  304. * Newer browsers support more than 2 concurrent parallel connections
  305. * but NOT for JS-files. So here one single output-file would be best.
  306. * You can still uppen the number of output-files here if like. [Deprecated, still in there for backward compatibility]
  307. * Defaults to "1".
  308. * @var integer
  309. * @access public
  310. */
  311. public $js_totalparts = 1;
  312. /**
  313. * Defines which part to ouput when retrieving JS in multiple parts [Deprecated, still in there for backward compatibility]
  314. *
  315. * Used by accompagning script "booster_js.php" [Deprecated, still in there for backward compatibility]
  316. * Defaults to "0".
  317. * @var integer
  318. * @access public
  319. */
  320. public $js_part = 0;
  321. /**
  322. * Defines if Google Closure Compiler should be used
  323. *
  324. * Used by accompagning script "booster_js.php"
  325. * Defaults to "TRUE".
  326. * @var boolean
  327. * @access public
  328. */
  329. public $js_minify = TRUE;
  330. /**
  331. * Defines if to switch to more powerful a hosted minifier
  332. *
  333. * You can use the full Google Closure Compiler included in CSS-JS-Booster instead of the
  334. * webservice, in order to minify javascript.
  335. * But be carefull, it will only work on dedicated servers with Java installed.
  336. * @var boolean
  337. * @access public
  338. */
  339. public $js_hosted_minifier = FALSE;
  340. /**
  341. * Defines the path to a hosted minifier
  342. *
  343. * Will store the local Google Closure Compiler path relative to this file
  344. * @var string
  345. * @access private
  346. * @see $js_hosted_minifier
  347. */
  348. private $js_hosted_minifier_path = 'compiler/compiler.jar';
  349. /**
  350. * Defines if source-file retrieval shall be recursive
  351. *
  352. * Only matters when passing folders as source-parameter.
  353. * If set to "TRUE" contents of folders found inside source-folder are also fetched.
  354. * Defaults to "FALSE".
  355. * @var boolean
  356. * @access public
  357. */
  358. public $js_recursive = FALSE;
  359. /**
  360. * Switches on string-mode, when passing javascript-strings as source
  361. *
  362. * Instead of folders and files to read and parse you can also pass
  363. * javascript-code as source. But, this only works if you switch string-mode on.
  364. * Defaults to "FALSE".
  365. * @var boolean
  366. * @access public
  367. * @see $js_source
  368. */
  369. public $js_stringmode = FALSE;
  370. /**
  371. * Defines the base-folder for all files referenced in javascript-string
  372. *
  373. * When being in string-mode, the booster prepends this path going out from the caller-location
  374. * in order to find all referenced files.
  375. * Defaults to "./".
  376. * @var string
  377. * @access public
  378. * @see $js_stringmode
  379. */
  380. public $js_stringbase = './';
  381. /**
  382. * Used to store the date of last change of a javascript-string
  383. *
  384. * Is set to the file-time of the calling script during construction.
  385. * @var integer
  386. * @access private
  387. * @see $js_stringmode
  388. */
  389. private $js_stringtime = 0;
  390. /**
  391. * Define if you want the javacript to get executed async or defered (HTML5)
  392. *
  393. * Default is "", file gets loaded and executed instantly, blocking the HTML parser until done.
  394. *
  395. * Set to "async" if you want the script to load while HTML parser is moving on.
  396. * Will get executed as soon as script is loaded. Automatically gets a "defer"-backup-attribute for IE.
  397. * Will get deactivated when document.write() is detected by Booster
  398. *
  399. * Set to "defer" if you want the script to load and execute after HTML parser has finished parsing the page.
  400. * Will get deactivated when document.write() is detected by Booster
  401. *
  402. * @var string
  403. * @access public
  404. */
  405. public $js_executionmode = '';
  406. /**
  407. * Define which JavaScript function to run once a async or defered has reached execution status (HTML5)
  408. *
  409. * Default is "", no JavaScript function is being executed.
  410. *
  411. * Set to any function contained within the boosted JavaScript, e.g. "initialize();"
  412. *
  413. * @var string
  414. * @access public
  415. */
  416. public $js_onload = '';
  417. // Start of functions ///////////////////////////////////////////////////////////////////////////////////////////////////////
  418. /**
  419. * Constructor
  420. *
  421. * Sets @var $css_stringtime to caller file time
  422. *
  423. * @return void
  424. * @access public
  425. */
  426. public function __construct()
  427. {
  428. $this->filestime = filemtime(__FILE__);
  429. $this->document_root = $_SERVER['DOCUMENT_ROOT'];
  430. $this->css_stringtime = filemtime(realpath($_SERVER['SCRIPT_FILENAME']));
  431. $this->css_hosted_minifier_path = realpath(dirname(__FILE__).'/'.$this->css_hosted_minifier_path);
  432. $this->js_stringtime = filemtime(realpath($_SERVER['SCRIPT_FILENAME']));
  433. $this->js_hosted_minifier_path = realpath(dirname(__FILE__).'/'.$this->js_hosted_minifier_path);
  434. // Checking if Apache runs with mod_rewrite
  435. if(function_exists('apache_get_modules') && $apache_modules = apache_get_modules())
  436. {
  437. if(!in_array('mod_rewrite',$apache_modules)) $this->mod_rewrite = FALSE;
  438. }
  439. }
  440. /**
  441. * Looks after some parameters and outputs an error if applicable
  442. *
  443. * @return void
  444. * @access private
  445. */
  446. private function errorcheck()
  447. {
  448. // Calculate absolute path only for this function
  449. $booster_cachedir = str_replace('\\','/',dirname(__FILE__)).'/'.$this->booster_cachedir;
  450. // Throw a warning and quit if cache-directory doesn't exist or isn't writable
  451. if(!@is_dir($booster_cachedir) && !@mkdir($booster_cachedir,0777))
  452. {
  453. $this->errormessage = "\r\nYou need to create a directory \r\n".$this->get_absolute_path($booster_cachedir)."\r\n with CHMOD 0777 rights.\r\nAfterwards, delete your browser's cache and reload.\r\n";
  454. }
  455. // Also check here for the right PHP version
  456. if(strnatcmp(phpversion(),'5.0.0') < 0)
  457. {
  458. $this->errormessage = "\r\nYou need to upgrade to PHP 5 or higher to have CSS-JS-Booster work. You currently are running on PHP ".phpversion().".\r\n";
  459. }
  460. // Check for incorrect document root (e.g. due to Apache's mod_userdir)
  461. if($this->document_root == $_SERVER['DOCUMENT_ROOT'] && substr($this->getpath(str_replace('\\','/',dirname(__FILE__)),str_replace('\\','/',$_SERVER['DOCUMENT_ROOT'])),0,2) == '..')
  462. {
  463. $this->errormessage = "\r\n".'$_SERVER[\'DOCUMENT_ROOT\'] variable is set to '.$_SERVER['DOCUMENT_ROOT'].'. But you seem to have a differing real document root. Please set the variable $booster->document_root to reflect your real document root'."\r\n";
  464. }
  465. // Check for incorrect base path (e.g. due to Apache's mod_userdir)
  466. if($this->base_offset.ltrim(str_replace($this->document_root,'',$_SERVER['SCRIPT_FILENAME']),'/') != parse_url($_SERVER['SCRIPT_NAME'],PHP_URL_PATH))
  467. {
  468. $this->errormessage = "\r\n".'$booster->base_offset variable is set to '.$this->base_offset.'. But this server\'s document root seems to be differently offsetted. Please set the variable $booster->base_offset to reflect this offset'."\r\n".$this->base_offset.ltrim(str_replace($this->document_root,'',$_SERVER['SCRIPT_FILENAME']),'/')."\r\n".parse_url($_SERVER['SCRIPT_NAME'],PHP_URL_PATH)."\r\n";
  469. }
  470. }
  471. /**
  472. * Setcachedir calculates correct cache-path once and checks directory's writability
  473. * and adjusts things not adjustable while constructing
  474. *
  475. * @return void
  476. * @access public
  477. */
  478. public function setcachedir()
  479. {
  480. // Turn on strict error reporting when library debug is on
  481. if($this->librarydebug)
  482. {
  483. ini_set("display_errors", 1);
  484. error_reporting(E_ALL);
  485. }
  486. // Check if @var $booster_cachedir_transformed is still "FALSE"
  487. if(!$this->booster_cachedir_transformed)
  488. {
  489. $this->booster_cachedir_transformed = TRUE;
  490. $this->booster_cachedir = str_replace('\\','/',dirname(__FILE__)).'/'.$this->booster_cachedir;
  491. $this->debug_log = $this->booster_cachedir.'/debug_log.txt';
  492. // Automatic cleanup of old files in booster_cache folder, if switched on, and only on sundays
  493. $today = getdate();
  494. if($this->booster_cachedir_autocleanup && $today['wday'] == 0)
  495. {
  496. if(is_dir($this->booster_cachedir))
  497. {
  498. $handle=opendir($this->booster_cachedir);
  499. while(false !== ($file = readdir($handle)))
  500. {
  501. // If it is a file and the filetype matches and it isn't the log file
  502. // and last file access time is one week old or older, then delete
  503. if($file[0] != '.' &&
  504. strtolower(pathinfo($this->booster_cachedir.'/'.$file,PATHINFO_EXTENSION)) == 'txt' &&
  505. $this->booster_cachedir.'/'.$file != $this->debug_log &&
  506. fileatime($this->booster_cachedir.'/'.$file) < ($_SERVER['REQUEST_TIME'] - 604800)
  507. )
  508. {
  509. @unlink($this->booster_cachedir.'/'.$file);
  510. }
  511. }
  512. closedir($handle);
  513. }
  514. }
  515. }
  516. }
  517. /**
  518. * Get_absolute_path calculates absolute path of a any path, stripping all . and ..
  519. *
  520. * @param string $path
  521. * @return string absolute $path
  522. * @access public
  523. */
  524. public function get_absolute_path($path)
  525. {
  526. $path = str_replace(array('/', '\\'), '/', $path);
  527. $parts = array_filter(explode('/', $path), 'strlen');
  528. $absolutes = array();
  529. foreach ($parts as $part) {
  530. if ('.' == $part) continue;
  531. if ('..' == $part) {
  532. array_pop($absolutes);
  533. } else {
  534. $absolutes[] = $part;
  535. }
  536. }
  537. return implode('/', $absolutes);
  538. }
  539. /**
  540. * Getpath calculates the relative path between @var $path1 and @var $path2
  541. *
  542. * @param string $path1
  543. * @param string $path2
  544. * @param string $path1_sep Sets the folder-delimiter, defaults to '/'
  545. * @return string relative path between @var $path1 and @var $path2
  546. * @access public
  547. */
  548. public function getpath($path1 = '',$path2 = '',$path1_sep = '/')
  549. {
  550. $path2 = str_replace('\\','/',realpath($path2));
  551. $path1 = str_replace('\\','/',realpath($path1));
  552. $path2 = explode($path1_sep, $path2);
  553. $path1 = explode($path1_sep, $path1);
  554. $path = '.';
  555. $fix = '';
  556. $diff = 0;
  557. for($i = -1; ++$i < max(($rC = count($path2)), ($dC = count($path1)));)
  558. {
  559. if(isset($path2[$i]) and isset($path1[$i]))
  560. {
  561. if($diff)
  562. {
  563. $path .= $path1_sep.'..';
  564. $fix .= $path1_sep.$path1[$i];
  565. continue;
  566. }
  567. if($path2[$i] != $path1[$i])
  568. {
  569. $diff = 1;
  570. $path .= $path1_sep.'..';
  571. $fix .= $path1_sep.$path1[$i];
  572. continue;
  573. }
  574. }
  575. elseif(!isset($path2[$i]) and isset($path1[$i]))
  576. {
  577. for($j = $i-1; ++$j < $dC;)
  578. {
  579. $fix .= $path1_sep.$path1[$j];
  580. }
  581. break;
  582. }
  583. elseif(isset($path2[$i]) and !isset($path1[$i]))
  584. {
  585. for($j = $i-1; ++$j < $rC;)
  586. {
  587. $fix = $path1_sep.'..'.$fix;
  588. }
  589. break;
  590. }
  591. }
  592. $pathfix = $path.$fix;
  593. // Remove any unneccessary "./"
  594. $pathfix = preg_replace('/(?<!\.)\.\//','',$pathfix);
  595. return $pathfix;
  596. }
  597. /**
  598. * Getfiles returns all files of a certain type within a folder
  599. *
  600. * @param string $source folder to look for files
  601. * @param string $type sets file-type/suffix (for security reasons)
  602. * @param boolean $recursive tells the script to scan all subfolders, too
  603. * @param array $files prepopulated array of files to append to and return
  604. * @return array filenames sorted alphabetically
  605. * @access protected
  606. */
  607. public function getfiles($source = '',$type = '',$recursive = FALSE,$files = array())
  608. {
  609. // Remove any trailing slash
  610. $source = rtrim($source,'/');
  611. // Check if @var $source really is a folder
  612. if(is_dir(str_replace('\\','/',dirname(__FILE__)).'/'.$source))
  613. {
  614. // For library debugging purposes we log findings
  615. if($this->librarydebug)
  616. {
  617. file_put_contents($this->debug_log,"-----------------\r\n".date("d.m.Y H:i:s")." getfiles detected a folder:\r\n-----------------\r\n".$source."\r\n-----------------\r\n",FILE_APPEND);
  618. }
  619. $handle=opendir(str_replace('\\','/',dirname(__FILE__)).'/'.$source);
  620. while(false !== ($file = readdir($handle)))
  621. {
  622. if($file[0] != '.')
  623. {
  624. // If it is a folder
  625. if(is_dir(str_replace('\\','/',dirname(__FILE__)).'/'.$source.'/'.$file))
  626. {
  627. // If the @var $recursive is set to "TRUE" start fetching the subfolder
  628. if($recursive) $files = $this->getfiles($source.'/'.$file,$type,$recursive,$files);
  629. }
  630. // If it is a file and if the filetype matches
  631. else if(strtolower(pathinfo(str_replace('\\','/',dirname(__FILE__)).'/'.$source.'/'.$file,PATHINFO_EXTENSION)) == $type)
  632. {
  633. // For library debugging purposes we log findings
  634. if($this->librarydebug)
  635. {
  636. file_put_contents($this->debug_log,"-----------------\r\n".date("d.m.Y H:i:s")." getfiles detected a file inside a folder:\r\n-----------------\r\n".$file.' inside '.$source."\r\n-----------------\r\n",FILE_APPEND);
  637. }
  638. // Add to file-list
  639. array_push($files,$source.'/'.$file);
  640. }
  641. }
  642. }
  643. closedir($handle);
  644. // Sort list alphabetically
  645. array_multisort($files, SORT_ASC, $files);
  646. }
  647. // If @var $source is a file, add it to the file-list
  648. elseif(is_file($source) && strtolower(pathinfo($source,PATHINFO_EXTENSION)) == $type)
  649. {
  650. // For library debugging purposes we log findings
  651. if($this->librarydebug)
  652. {
  653. file_put_contents($this->debug_log,"-----------------\r\n".date("d.m.Y H:i:s")." getfiles detected a file:\r\n-----------------\r\n".$source."\r\n-----------------\r\n",FILE_APPEND);
  654. }
  655. array_push($files,$source);
  656. }
  657. // Return file-list
  658. return $files;
  659. }
  660. /**
  661. * Getfilestime returns the timestamp of the newest file of a certain type within a folder
  662. *
  663. * @param mixed $source single folder or multiple comma-delimited folders or array of folders in which to look for files
  664. * @param string $type sets file-type/suffix (for security reasons)
  665. * @param boolean $recursive tells the script to scan all subfolders, too
  666. * @param integer $this->filestime prepopulated timestamp to also check against
  667. * @return integer timestamp of the newest of all scanned files
  668. * @access public
  669. */
  670. public function getfilestime($source = '',$type = '',$recursive = FALSE)
  671. {
  672. // Load @var $source with an array made form @var $source parameter
  673. if(is_array($source)) $sources = $source;
  674. else $sources = explode(',',$source);
  675. reset($sources);
  676. for($j=0;$j<sizeof($sources);$j++)
  677. {
  678. // For library debugging purposes we log findings
  679. if($this->librarydebug)
  680. {
  681. file_put_contents($this->debug_log,"-----------------\r\n".date("d.m.Y H:i:s")." getfilestime found sources:\r\n-----------------\r\n".implode(', ',$sources)."\r\n-----------------\r\n",FILE_APPEND);
  682. }
  683. $source = current($sources);
  684. // Remove any trailing slash
  685. $source = rtrim($source,'/');
  686. // Check if @var $source really is a folder
  687. if(is_dir(str_replace('\\','/',dirname(__FILE__)).'/'.$source))
  688. {
  689. // For library debugging purposes we log findings
  690. if($this->librarydebug)
  691. {
  692. file_put_contents($this->debug_log,"-----------------\r\n".date("d.m.Y H:i:s")." getfilestime detected a folder:\r\n-----------------\r\n".$source."\r\n-----------------\r\n",FILE_APPEND);
  693. }
  694. // Get a list (array) of all folders and files inside that folder
  695. $files = $this->getfiles($source,$type,$recursive);
  696. // For library debugging purposes we log findings
  697. if($this->librarydebug)
  698. {
  699. file_put_contents($this->debug_log,"-----------------\r\n".date("d.m.Y H:i:s")." getfilestime retrieved folder contents:\r\n-----------------\r\n".implode(', ',$files)."\r\n-----------------\r\n",FILE_APPEND);
  700. }
  701. // Check all list-item's timestamps
  702. for($i=0;$i<count($files);$i++)
  703. {
  704. // In case it is a folder, run this funtion on the folder
  705. if(is_dir(str_replace('\\','/',dirname(__FILE__)).'/'.$files[$i]))
  706. {
  707. if($recursive) $this->filestime = $this->getfilestime($files[$i],$type,$recursive);
  708. }
  709. // In case it is a file, get its timestamp
  710. if(is_file(str_replace('\\','/',dirname(__FILE__)).'/'.$files[$i]))
  711. {
  712. // For library debugging purposes we log findings
  713. if($this->librarydebug)
  714. {
  715. file_put_contents($this->debug_log,"-----------------\r\n".date("d.m.Y H:i:s")." getfilestime detected a file inside a folder:\r\n-----------------\r\n".$files[$i].' inside '.$source."\r\n-----------------\r\n",FILE_APPEND);
  716. }
  717. if(filemtime(str_replace('\\','/',dirname(__FILE__)).'/'.$files[$i]) > $this->filestime) $this->filestime = filemtime(str_replace('\\','/',dirname(__FILE__)).'/'.$files[$i]);
  718. }
  719. }
  720. }
  721. // If @var $source is a file check its file time
  722. elseif(is_file(str_replace('\\','/',dirname(__FILE__)).'/'.$source) && filemtime(str_replace('\\','/',dirname(__FILE__)).'/'.$source) > $this->filestime && strtolower(pathinfo(str_replace('\\','/',dirname(__FILE__)).'/'.$source,PATHINFO_EXTENSION)) == $type)
  723. {
  724. // For library debugging purposes we log findings
  725. if($this->librarydebug)
  726. {
  727. file_put_contents($this->debug_log,"-----------------\r\n".date("d.m.Y H:i:s")." getfilestime detected a file:\r\n-----------------\r\n".$source."\r\n-----------------\r\n",FILE_APPEND);
  728. }
  729. $this->filestime = filemtime(str_replace('\\','/',dirname(__FILE__)).'/'.$source);
  730. }
  731. next($sources);
  732. }
  733. }
  734. /**
  735. * Getfilescontents puts together all contents from files of a certain type within a folder
  736. *
  737. * @param string $source folder to look for files or file or code-string
  738. * @param string $type sets file-type/suffix (for security reasons)
  739. * @param boolean $recursive tells the script to scan all subfolders, too
  740. * @param string $filescontent prepopulated string to append to and return
  741. * @return string Return all file contents
  742. * @access protected
  743. */
  744. protected function getfilescontents($source = '',$type = '',$recursive = FALSE,$filescontent = '')
  745. {
  746. // Remove any trailing slash
  747. $source = rtrim($source,'/');
  748. // Prepare content storage
  749. $currentfilecontent = '';
  750. // If @var $source is a folder, get file-list and call itself on them
  751. if(is_dir($source))
  752. {
  753. $files = $this->getfiles($source,$type,$recursive);
  754. for($i=0;$i<count($files);$i++) $filescontent .= $this->getfilescontents($files[$i],$type,$recursive);
  755. }
  756. // If @var $source is a file
  757. elseif(is_file($source))
  758. {
  759. if(strtolower(pathinfo($source,PATHINFO_EXTENSION)) == $type)
  760. {
  761. if($this->librarydebug) file_put_contents($this->debug_log,"processing file: ".$source."\r\n",FILE_APPEND);
  762. $currentfilecontent = file_get_contents($source);
  763. }
  764. }
  765. // If @var $source is a string and we are in stringmode
  766. elseif(
  767. ($type == 'css' && $this->css_stringmode) ||
  768. ($type == 'js' && $this->js_stringmode)
  769. ) $currentfilecontent = $source;
  770. // If @var $source can't be identified
  771. else
  772. {
  773. if($type == 'css') $currentfilecontent = "\r\n".'/* Could not locate '.$source.' */'."\r\n";
  774. if($type == 'js') $currentfilecontent = "\r\n".'// Could not locate '.$source."\r\n";
  775. }
  776. // Find and resolve import-rules
  777. if($type == 'css')
  778. {
  779. preg_match_all('/@import\surl\([\'"]*?([^\'")]+\.css)[\'"]*?\);/ims',$currentfilecontent,$treffer,PREG_PATTERN_ORDER);
  780. for($i=0;$i<count($treffer[0]);$i++)
  781. {
  782. // Buffer findings
  783. $import = $treffer[1][$i];
  784. // If it is a full URL, extract only the path
  785. if(substr($import,0,strlen($_SERVER['SERVER_NAME']) + 7) == 'http://'.$_SERVER['SERVER_NAME']) $import = parse_url($import,PHP_URL_PATH);
  786. // If it is an absolute path
  787. if(substr($import,0,1) == '/') $importfile = str_replace('\\','/',realpath(rtrim($this->document_root,'/'))).$import;
  788. // Else if it is a relative path
  789. else $importfile = str_replace('\\','/',realpath(dirname($source))).'/'.$import;
  790. if($this->librarydebug) file_put_contents($this->debug_log,"found file in @import-rule: ".$importfile."\r\n",FILE_APPEND);
  791. $diroffset = dirname($treffer[1][$i]);
  792. if(file_exists($importfile))
  793. {
  794. $importfilecontent = $this->getfilescontents($importfile,$type);
  795. $importfilecontent = preg_replace('/(url\([\'"]*)([^\/])/ims','\1'.$diroffset.'/\2',$importfilecontent);
  796. $currentfilecontent = str_replace($treffer[0][$i],$importfilecontent."\r\n",$currentfilecontent);
  797. }
  798. // @todo media-type sensivity
  799. #if(trim($treffer[2][$i]) != '') $mediatype = trim($treffer[2][$i]);
  800. #else $mediatype = 'all';
  801. #if($this->librarydebug) $filescontent .= "/* importfile: ".$importfile." */\r\n";
  802. #if(file_exists($importfile)) $currentfilecontent = str_replace($treffer[0][$i],"@media ".$mediatype." {\r\n".$this->getfilescontents($importfile,$type)."}\r\n",$currentfilecontent);
  803. }
  804. }
  805. // Append to @var $filescontent
  806. $filescontent .= $currentfilecontent."\r\n\r\n";
  807. return $filescontent;
  808. }
  809. /**
  810. * Css_minify does some soft minifications to the stylesheets, avoiding damage by optimizing too much
  811. *
  812. * Replaces CSSTidy 1.3 which in some cases was destroying stylesheets
  813. * Removing unnecessessary whitespaces, tabs and newlines
  814. * Leaving commments in there as they may be browser hacks or needed to fulfill the terms of a license
  815. *
  816. * @param string styles-string
  817. * @return string minified styles-string
  818. * @access protected
  819. */
  820. protected function css_minify($filescontent = '')
  821. {
  822. // For library debugging purposes
  823. if($this->librarydebug)
  824. {
  825. file_put_contents($this->debug_log,"-----------------\r\n".date("d.m.Y H:i:s")." css_minify input:\r\n-----------------\r\n".$filescontent."\r\n-----------------\r\n",FILE_APPEND);
  826. }
  827. // If somebody wants to use the included minifier
  828. if($this->css_hosted_minifier && is_readable($this->css_hosted_minifier_path))
  829. {
  830. // Implementation by Vincent Voyer (http://twitter.com/vvoyer)
  831. // must create tmp files because closure compiler can't work with direct input..
  832. $tmp_file_path = sys_get_temp_dir().'/'.uniqid();
  833. file_put_contents($tmp_file_path, $filescontent);
  834. $filescontent = `java -jar $this->css_hosted_minifier_path $tmp_file_path --type css --charset utf-8`;
  835. unlink($tmp_file_path);
  836. }
  837. // Use own subtle minification implementation
  838. else
  839. {
  840. // Backup any values within single or double quotes
  841. preg_match_all('/(\'[^\']*?\'|"[^"]*?")/ims',$filescontent,$treffer,PREG_PATTERN_ORDER);
  842. for($i=0;$i<count($treffer[1]);$i++)
  843. {
  844. $filescontent = str_replace($treffer[1][$i],'##########'.$i.'##########',$filescontent);
  845. }
  846. // For library debugging purposes
  847. if($this->librarydebug)
  848. {
  849. file_put_contents($this->debug_log,"-----------------\r\n".date("d.m.Y H:i:s")." css_minify after string backup:\r\n-----------------\r\n".$filescontent."\r\n-----------------\r\n",FILE_APPEND);
  850. }
  851. // Remove traling semicolon of selector's last property
  852. $filescontent = preg_replace('/;[\s\r\n\t]*?}[\s\r\n\t]*/ims',"}\r\n",$filescontent);
  853. // Remove any whitespaces/tabs/newlines between semicolon and property-name
  854. $filescontent = preg_replace('/;[\s\r\n\t]*?([\r\n]?[^\s\r\n\t])/ims',';$1',$filescontent);
  855. // Remove any whitespaces/tabs/newlines surrounding property-colon
  856. $filescontent = preg_replace('/[\s\r\n\t]*:[\s\r\n\t]*?([^\s\r\n\t])/ims',':$1',$filescontent);
  857. // Remove any whitespaces/tabs/newlines surrounding selector-comma
  858. $filescontent = preg_replace('/[\s\r\n\t]*,[\s\r\n\t]*?([^\s\r\n\t])/ims',',$1',$filescontent);
  859. // Remove any whitespaces/tabs/newlines surrounding opening parenthesis
  860. $filescontent = preg_replace('/[\s\r\n\t]*{[\s\r\n\t]*?([^\s\r\n\t])/ims','{$1',$filescontent);
  861. // Remove any whitespaces/tabs/newlines between numbers and units
  862. $filescontent = preg_replace('/([\d\.]+)[\s\r\n\t]+(px|em|pt|%)/ims','$1$2',$filescontent);
  863. // Shorten zero-values
  864. $filescontent = preg_replace('/([^\d\.]0)(px|em|pt|%)/ims','$1',$filescontent);
  865. // Constrain multiple newlines
  866. $filescontent = preg_replace('/[\r\n]+/ims',"\n",$filescontent);
  867. // Constrain multiple whitespaces
  868. $filescontent = preg_replace('/\p{Zs}+/ims',' ',$filescontent);
  869. // Restore backupped values within single or double quotes
  870. for($i=0;$i<count($treffer[1]);$i++)
  871. {
  872. $filescontent = str_replace('##########'.$i.'##########',$treffer[1][$i],$filescontent);
  873. }
  874. }
  875. // For library debugging purposes we log minify output
  876. if($this->librarydebug)
  877. {
  878. file_put_contents($this->debug_log,"-----------------\r\n".date("d.m.Y H:i:s")." css_minify output:\r\n-----------------\r\n".$filescontent."\r\n-----------------\r\n",FILE_APPEND);
  879. }
  880. return $filescontent;
  881. }
  882. /**
  883. * Css_datauri embeds external files like images into the stylesheet
  884. *
  885. * Depending on the browser and operating system, this funtion does the following:
  886. * IE 6 and 7 on XP and IE 7 on Vista or higher don't understand data-URIs, but a proprietary format named MHTML.
  887. * So they get served that.
  888. * Any other common browser understands data-URIs, even IE 8 up to a file-size of 24KB, so those get data-URI-embedding
  889. * IE 6 on Vista or higher doesn't understand any of the embeddings so it just gets standard styles.
  890. *
  891. * @param integer $this->filestime timestamp of the last modification of the content following
  892. * @param string $filescontent stylesheet-content
  893. * @return string stylesheet-content with data-URI or MHTML embeddings
  894. * @see function Setcachedir
  895. * @access protected
  896. */
  897. protected function css_datauri($filescontent = '',$dir = '')
  898. {
  899. // For library debugging purposes we log file contents
  900. if($this->librarydebug) file_put_contents($this->debug_log,"-----------------\r\n",FILE_APPEND);
  901. if($this->librarydebug && isset($_SERVER['HTTP_USER_AGENT'])) file_put_contents($this->debug_log,"HTTP_USER_AGENT: ".$_SERVER['HTTP_USER_AGENT']."\r\n",FILE_APPEND);
  902. if($this->librarydebug) file_put_contents($this->debug_log,"Browser->Family: ".$this->browser->family."\r\n",FILE_APPEND);
  903. if($this->librarydebug) file_put_contents($this->debug_log,"Browser->Familyversion: ".floatval($this->browser->familyversion)."\r\n",FILE_APPEND);
  904. if($this->librarydebug) file_put_contents($this->debug_log,"Browser->Platform: ".$this->browser->platform."\r\n",FILE_APPEND);
  905. if($this->librarydebug) file_put_contents($this->debug_log,"Browser->Platformversion: ".floatval($this->browser->platformversion)."\r\n",FILE_APPEND);
  906. if($this->librarydebug) file_put_contents($this->debug_log,"-----------------\r\n",FILE_APPEND);
  907. // Call Setcachedir to make sure, cache-path has been calculated
  908. $this->setcachedir();
  909. // Prepare different RegExes
  910. // Media-files (currently images and fonts)
  911. $regex_embed = '/url\([\'"]*(.+?\.)(gif|png|jpg|otf|ttf|woff)[\'"]*\)/msi';
  912. $regex_embed_ie = '/url\([\'"]*(.+?\.)(gif|png|jpg|eot)[\'"]*\)/msi';
  913. // identifier for the cache-files
  914. $identifier = md5($filescontent);
  915. // --------------------------------------------------------------------------------------
  916. // If any MHTML-capable IE browser
  917. if($this->css_mhtml_enabled_ie)
  918. {
  919. // The @var $mhtmlarray collects references to all processed images so that we can look up if we already have embedded a certain image
  920. $mhtmlarray = array();
  921. // The external absolute path to where "booster_mhtml.php" resides
  922. $mhtmlpath = $this->base_offset.$this->getpath(str_replace('\\','/',dirname(__FILE__)),rtrim($this->document_root,'/'));
  923. // Cachefile for the extra MHTML-data
  924. $mhtmlfile = $this->booster_cachedir.'/'.$identifier.'_datauri_mhtml_'.(($this->debug) ? 'debug_' : '').'cache.txt';
  925. // Get Domainname
  926. if(isset($_SERVER['SCRIPT_URI']))
  927. {
  928. $mhtmlhost = parse_url($_SERVER['SCRIPT_URI'],PHP_URL_HOST);
  929. }
  930. else
  931. {
  932. $mhtmlhost = $_SERVER['HTTP_HOST'];
  933. }
  934. // Start putting together the styles and MHTML
  935. $mhtmlcontent = "Content-Type: multipart/related; boundary=\"_ANY_STRING_WILL_DO_AS_A_SEPARATOR\"\r\n\r\n";
  936. preg_match_all($regex_embed_ie,$filescontent,$treffer,PREG_PATTERN_ORDER);
  937. if(!$this->debug) for($i=0;$i<count($treffer[0]);$i++)
  938. {
  939. // Calculate full image path
  940. // If it is an absolute path
  941. if(substr($treffer[1][$i],0,1) == '/')
  942. {
  943. $imagefile = rtrim($this->document_root,'/').$treffer[1][$i].$treffer[2][$i];
  944. }
  945. // If it is a relative path
  946. else
  947. {
  948. $imagefile = str_replace('\\','/',dirname(__FILE__)).'/'.$dir.'/'.$treffer[1][$i].$treffer[2][$i];
  949. }
  950. // Create a new anchor-tag for the MHTML-file
  951. $imagetag = 'img'.$i;
  952. // If image-file exists and if file-size is lower than 24 KB
  953. if(file_exists($imagefile) && filesize($imagefile) < 24000)
  954. {
  955. // Replace reference to image with reference to MHTML-file with corresponding anchor
  956. ((isset($_GET['cachedir'])) ? $booster_cachedir = str_replace('>','..',rtrim(preg_replace('/[^a-z0-9,\-_\.\/>]/i','',$_GET['cachedir']),'/')) : $booster_cachedir = 'booster_cache');
  957. $filescontent = str_replace($treffer[0][$i],'url(mhtml:http://'.$mhtmlhost.$mhtmlpath.'/booster_mhtml.php?dir='.$identifier.'&cachedir='.$booster_cachedir.'&nocache='.$this->filestime.'!'.$imagetag.')',$filescontent);
  958. // Look up in our list if we did not already process that exact file, if not append it
  959. if(!isset($mhtmlarray[$imagetag]))
  960. {
  961. $mhtmlcontent .= "--_ANY_STRING_WILL_DO_AS_A_SEPARATOR\r\n";
  962. $mhtmlcontent .= "Content-Location:".$imagetag."\r\n";
  963. $mhtmlcontent .= "Content-Transfer-Encoding:base64\r\n\r\n";
  964. $mhtmlcontent .= base64_encode(file_get_contents($imagefile))."==\r\n";
  965. // Put file on our processed-list
  966. $mhtmlarray[$imagetag] = 1;
  967. }
  968. }
  969. }
  970. // Fix the caching problems of IE7, see: http://www.phpied.com/the-proper-mhtml-syntax/
  971. $mhtmlcontent .= "--_ANY_STRING_WILL_DO_AS_A_SEPARATOR--\r\n";
  972. $mhtmlcontent .= "\r\n\r\n";
  973. // Scan for any left file-references and adjust their path
  974. $filescontent = $this->css_datauri_cleanup($filescontent,$dir);
  975. // Store the cache-files
  976. @file_put_contents($mhtmlfile,$mhtmlcontent);
  977. }
  978. // If any modern data-URI-compatible browser
  979. else
  980. {
  981. preg_match_all($regex_embed,$filescontent,$treffer,PREG_PATTERN_ORDER);
  982. if(!$this->debug) for($i=0;$i<count($treffer[0]);$i++)
  983. {
  984. // Calculate full image path
  985. // If it is an absolute path
  986. if(substr($treffer[1][$i],0,1) == '/')
  987. {
  988. $imagefile = rtrim($this->document_root,'/').$treffer[1][$i].$treffer[2][$i];
  989. }
  990. // If it is a relative path
  991. else
  992. {
  993. $imagefile = str_replace('\\','/',dirname(__FILE__)).'/'.$dir.'/'.$treffer[1][$i].$treffer[2][$i];
  994. }
  995. if($this->debug) $filescontent .= "/* embed-file: ".$imagefile." */\r\n";
  996. // Switch to right MIME-type
  997. switch(strtolower($treffer[2][$i]))
  998. {
  999. default:
  1000. case 'gif':
  1001. case 'jpg':
  1002. case 'png':
  1003. $mimetype = 'image/'.strtolower($treffer[2][$i]);
  1004. break;
  1005. case 'eot':
  1006. $mimetype = 'application/vnd.ms-fontobject';
  1007. break;
  1008. case 'otf':
  1009. case 'ttf':
  1010. case 'woff':
  1011. $mimetype = 'application/octet-stream';
  1012. break;
  1013. }
  1014. // If image-file exists and if file-size is lower than 24 KB
  1015. if(file_exists($imagefile) && filesize($imagefile) < 24000) $filescontent = str_replace($treffer[0][$i],'url(data:'.$mimetype.';base64,'.base64_encode(file_get_contents($imagefile)).')',$filescontent);
  1016. }
  1017. // Scan for any left file-references and adjust their path
  1018. $filescontent = $this->css_datauri_cleanup($filescontent,$dir);
  1019. }
  1020. // --------------------------------------------------------------------------------------
  1021. return $filescontent;
  1022. }
  1023. /**
  1024. * Css_datauri_cleanup prepends $dir to the path of all file-references found
  1025. *
  1026. * @param string $filescontent contents to scan
  1027. * @param string $dir folder name to prepend
  1028. * @return string content with adjusted paths
  1029. * @access protected
  1030. */
  1031. protected function css_datauri_cleanup($filescontent = '',$dir = '')
  1032. {
  1033. // Calculate absolute path for booster-folder
  1034. $booster_path = '/'.$this->getpath(str_replace('\\','/',dirname(__FILE__)),str_replace('\\','/',$this->document_root));
  1035. // Scan for any left file-references and adjust their path
  1036. $regex_url = '/(url\([\'"]??)([^\'"\)]+?\.[^\'"\)]+?)([\'"]??\))/msi';
  1037. preg_match_all($regex_url,$filescontent,$treffer,PREG_PATTERN_ORDER);
  1038. for($i=0;$i<count($treffer[0]);$i++)
  1039. {
  1040. $search = $treffer[1][$i].$treffer[2][$i].$treffer[3][$i];
  1041. $replace = $treffer[1][$i].$booster_path.'/';
  1042. if($this->css_stringmode) $path_prefix = $this->css_stringmode;
  1043. else $path_prefix = $dir;
  1044. if($path_prefix != '') $replace .= $path_prefix.'/';
  1045. $replace .= $treffer[2][$i].$treffer[3][$i];
  1046. if(
  1047. substr(str_replace(array('"',"'"),'',$treffer[2][$i]),0,5) != 'http:' &&
  1048. substr(str_replace(array('"',"'"),'',$treffer[2][$i]),0,6) != 'https:' &&
  1049. substr(str_replace(array('"',"'"),'',$treffer[2][$i]),0,5) != 'data:' &&
  1050. substr(str_replace(array('"',"'"),'',$treffer[2][$i]),0,6) != 'mhtml:' &&
  1051. substr(str_replace(array('"',"'"),'',$treffer[2][$i]),0,1) != '/' &&
  1052. substr(str_replace(array('"',"'"),'',$treffer[2][$i]),strlen(str_replace(array('"',"'"),'',$treffer[2][$i])) - 4,4) != '.htc'
  1053. ) $filescontent = str_replace($search,$replace,$filescontent);
  1054. }
  1055. return $filescontent;
  1056. }
  1057. /**
  1058. * Css_split takes a multiline CSS-string and splits it according to @var $css_totalparts and @var $css_part
  1059. *
  1060. * @param string $filescontent contents to split
  1061. * @return string requested part-number of splitted content
  1062. * @access protected
  1063. */
  1064. protected function css_split($filescontent = '')
  1065. {
  1066. // If sum of parts is 1 or requested part-number is 0 return full string
  1067. if($this->css_totalparts == 1 || $this->css_part == 0 || $this->css_stringmode)
  1068. {
  1069. // For library debugging purposes we log file contents
  1070. if($this->librarydebug) file_put_contents($this->debug_log,"-----------------\r\n".date("d.m.Y H:i:s")." css_split input content (solo part ".$this->css_part."):\r\n-----------------\r\n".$filescontent."\r\n-----------------\r\n",FILE_APPEND);
  1071. return $filescontent;
  1072. }
  1073. // Else process string
  1074. else
  1075. {
  1076. // Identifier for split-files
  1077. $identifier = md5($filescontent);
  1078. // If any MHTML-capable IE browser
  1079. if($this->css_mhtml_enabled_ie) $cachefilesuffix = 'datauri_ie';
  1080. // If any modern data-URI-compatible browser
  1081. else $cachefilesuffix = 'datauri';
  1082. // Since split processing consumes a lot of time we also cache here
  1083. $cachefilecontent = $this->booster_cachedir.'/'.$identifier.'_splitcontent_'.$cachefilesuffix.'_cache.txt';
  1084. $cachefiledata = $this->booster_cachedir.'/'.$identifier.'_splitdata_'.$cachefilesuffix.'_cache.txt';
  1085. // For library debugging purposes we log file contents
  1086. if($this->librarydebug) file_put_contents($this->debug_log,"-----------------\r\n".date("d.m.Y H:i:s")." css_split input content (part ".$this->css_part."):\r\n-----------------\r\n".$filescontent."\r\n-----------------\r\n",FILE_APPEND);
  1087. if(file_exists($cachefilecontent) && file_exists($cachefiledata))
  1088. {
  1089. $filescontent = file_get_contents($cachefilecontent);
  1090. $line_infos = unserialize(file_get_contents($cachefiledata));
  1091. // For library debugging purposes we log file contents
  1092. if($this->librarydebug) file_put_contents($this->debug_log,"-----------------\r\n".date("d.m.Y H:i:s")." css_split data files found (part ".$this->css_part."):\r\n-----------------\r\n".$filescontent."\r\n-----------------\r\n",FILE_APPEND);
  1093. }
  1094. else
  1095. {
  1096. // Backup any values within single or double quotes
  1097. preg_match_all('/(\'[^\']*?\'|"[^"]*?")/ims',$filescontent,$treffer,PREG_PATTERN_ORDER);
  1098. for($i=0;$i<count($treffer[1]);$i++)
  1099. {
  1100. $filescontent = str_replace($treffer[1][$i],'##########'.$i.'##########',$filescontent);
  1101. }
  1102. // Insert newline at certain points as preparation for parsing
  1103. $filescontent = str_replace("{","\n{",$filescontent);
  1104. $filescontent = str_replace("}","\n}",$filescontent);
  1105. $filescontent = str_replace("/*","\n/*",$filescontent);
  1106. $filescontent = str_replace("*/","\n*/",$filescontent);
  1107. // Restore backupped values within single or double quotes
  1108. for($i=0;$i<count($treffer[1]);$i++)
  1109. {
  1110. $filescontent = str_replace('##########'.$i.'##########',$treffer[1][$i],$filescontent);
  1111. }
  1112. // For library debugging purposes we log file contents
  1113. if($this->librarydebug) file_put_contents($this->debug_log,"-----------------\r\n".date("d.m.Y H:i:s")." css_split prepared content (part ".$this->css_part."):\r\n-----------------\r\n".$filescontent."\r\n-----------------\r\n",FILE_APPEND);
  1114. // In order for @-rule-blocks like @media-blocks, but also @font-face not to get stupidly ripped apart
  1115. // while splitting the file into multiple parts, we need to parse it take some notes for us later.
  1116. // In this array we will store some information for later when we split
  1117. $line_infos = array();
  1118. // As we split line-based we will take notices by lines
  1119. $currentline = 0;
  1120. // Here we note during the parsing if we are currently inside a block or not, and of which type
  1121. $currentblock = '';
  1122. $currentblockancestors = array();
  1123. // Here we note during the parsing if we are currently inside a comment or not
  1124. $currentcomment = '';
  1125. // Here we note during the parsing if we are currently inside a comment or not
  1126. $comment_on = 0;
  1127. // Here we note during the parsing if we are currently inside a property and if yes save its selector
  1128. $currentselector = '';
  1129. // Here we note during the parsing if we are currently inside a selector's properties
  1130. $property_on = 0;
  1131. // Here we note during the parsing if we are currently inside a singlequote-protected string
  1132. $singlequote_on = 0;
  1133. // Here we note during the parsing if we are currently inside a doublequote-protected string
  1134. $doublequote_on = 0;
  1135. // Now we cycle through every character
  1136. for($i=0;$i<strlen($filescontent);$i++)
  1137. {
  1138. // Here we store current character and do different things depending on what it is
  1139. $currentchar = substr($filescontent,$i,1);
  1140. switch($currentchar)
  1141. {
  1142. // We run into or out of a single-quoted file-reference or generated content, remember that
  1143. case "'":
  1144. if($doublequote_on == 0 && $comment_on == 0) $singlequote_on = 1 - $singlequote_on;
  1145. break;
  1146. // We run into or out of a double-quoted file-reference or generated content, remember that
  1147. case '"':
  1148. if($singlequote_on == 0 && $comment_on == 0) $doublequote_on = 1 - $doublequote_on;
  1149. break;
  1150. // We probably ran into a comment
  1151. case '*':
  1152. if($singlequote_on == 0 && $doublequote_on == 0 && $comment_on == 0)
  1153. {
  1154. if($comment_on == 0 && substr($filescontent,$i - 1,1) == '/')
  1155. {
  1156. // Remember that we are inside some /*-comment
  1157. $comment_on = 1;
  1158. $currentcomment = 'comment';
  1159. // For library debugging purposes we log pre-parser structure findings
  1160. if($this->librarydebug && $this->css_part == 1) file_put_contents($this->debug_log,"* comment start (part ".$this->css_part."): ".$currentcomment."\r\n",FILE_APPEND);
  1161. }
  1162. elseif(preg_match('/\A([a-zA-Z\.#\*:][^\{\}@;\/]+)\{/ims', substr($filescontent,$i), $treffer) == 1)
  1163. {
  1164. // remember in what selector we are
  1165. $currentselector = $treffer[1];
  1166. // remove selector for now (we will put it back in later)
  1167. $filescontent = substr_replace($filescontent,'',$i,strlen($currentselector) + 1);
  1168. // store selector for this line
  1169. $line_infos[$currentline]['selector'] = $currentselector;
  1170. // Remember that we are inside some selector's properties
  1171. $property_on = 1;
  1172. $i--;
  1173. // For library debugging purposes we log pre-parser structure findings
  1174. if($this->librarydebug && $this->css_part == 1) file_put_contents($this->debug_log,"} selector start (part ".$this->css_part."): ".$currentselector." -> ".$line_infos[$currentline]['selector']."\r\n",FILE_APPEND);
  1175. }
  1176. }
  1177. break;
  1178. // We maybe leave a comment
  1179. case '/':
  1180. if($singlequote_on == 0 && $doublequote_on == 0 && $comment_on == 1 && substr($filescontent,$i - 1,1) == '*')
  1181. {
  1182. // Remember that we finished being inside some comment
  1183. $comment_on = 0;
  1184. $currentcomment = '';
  1185. // For library debugging purposes we log pre-parser structure findings
  1186. if($this->librarydebug && $this->css_part == 1) file_put_contents($this->debug_log,"/ comment end (part ".$this->css_part."): ".$currentcomment."\r\n",FILE_APPEND);
  1187. }
  1188. break;
  1189. // Newline
  1190. case "\n":
  1191. if(!isset($line_infos[$currentline])) $line_infos[$currentline] = array();
  1192. // If not yet done: store @-rule for this line
  1193. if(!isset($line_infos[$currentline]['block'])) $line_infos[$currentline]['block'] = $currentblock;
  1194. // Store type of comment for this line
  1195. $line_infos[$currentline]['comment'] = $currentcomment;
  1196. // Store selector for this line
  1197. if(!isset($line_infos[$currentline]['selector'])) $line_infos[$currentline]['selector'] = $currentselector;

Large files files are truncated, but you can click here to view the full file