Helper.php 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <?php
  2. /**
  3. * Class Minify_HTML_Helper
  4. * @package Minify
  5. */
  6. /**
  7. * Helpers for writing Minfy URIs into HTML
  8. *
  9. * @package Minify
  10. * @author Stephen Clay <steve@mrclay.org>
  11. */
  12. class Minify_HTML_Helper {
  13. public $rewriteWorks = true;
  14. public $minAppUri = '/min';
  15. public $groupsConfigFile = '';
  16. /*
  17. * Get an HTML-escaped Minify URI for a group or set of files
  18. *
  19. * @param mixed $keyOrFiles a group key or array of filepaths/URIs
  20. * @param array $opts options:
  21. * 'farExpires' : (default true) append a modified timestamp for cache revving
  22. * 'debug' : (default false) append debug flag
  23. * 'charset' : (default 'UTF-8') for htmlspecialchars
  24. * 'minAppUri' : (default '/min') URI of min directory
  25. * 'rewriteWorks' : (default true) does mod_rewrite work in min app?
  26. * 'groupsConfigFile' : specify if different
  27. * @return string
  28. */
  29. public static function getUri($keyOrFiles, $opts = array())
  30. {
  31. $opts = array_merge(array( // default options
  32. 'farExpires' => true
  33. ,'debug' => false
  34. ,'charset' => 'UTF-8'
  35. ,'minAppUri' => '/min'
  36. ,'rewriteWorks' => true
  37. ,'groupsConfigFile' => ''
  38. ), $opts);
  39. $h = new self;
  40. $h->minAppUri = $opts['minAppUri'];
  41. $h->rewriteWorks = $opts['rewriteWorks'];
  42. $h->groupsConfigFile = $opts['groupsConfigFile'];
  43. if (is_array($keyOrFiles)) {
  44. $h->setFiles($keyOrFiles, $opts['farExpires']);
  45. } else {
  46. $h->setGroup($keyOrFiles, $opts['farExpires']);
  47. }
  48. $uri = $h->getRawUri($opts['farExpires'], $opts['debug']);
  49. return htmlspecialchars($uri, ENT_QUOTES, $opts['charset']);
  50. }
  51. /*
  52. * Get non-HTML-escaped URI to minify the specified files
  53. */
  54. public function getRawUri($farExpires = true, $debug = false)
  55. {
  56. $path = rtrim($this->minAppUri, '/') . '/';
  57. if (! $this->rewriteWorks) {
  58. $path .= '?';
  59. }
  60. if (null === $this->_groupKey) {
  61. // @todo: implement shortest uri
  62. $path = self::_getShortestUri($this->_filePaths, $path);
  63. } else {
  64. $path .= "g=" . $this->_groupKey;
  65. }
  66. if ($debug) {
  67. $path .= "&debug";
  68. } elseif ($farExpires && $this->_lastModified) {
  69. $path .= "&" . $this->_lastModified;
  70. }
  71. return $path;
  72. }
  73. public function setFiles($files, $checkLastModified = true)
  74. {
  75. $this->_groupKey = null;
  76. if ($checkLastModified) {
  77. $this->_lastModified = self::getLastModified($files);
  78. }
  79. // normalize paths like in /min/f=<paths>
  80. foreach ($files as $k => $file) {
  81. if (0 === strpos($file, '//')) {
  82. $file = substr($file, 2);
  83. } elseif (0 === strpos($file, '/')
  84. || 1 === strpos($file, ':\\')) {
  85. $file = substr($file, strlen($_SERVER['DOCUMENT_ROOT']) + 1);
  86. }
  87. $file = strtr($file, '\\', '/');
  88. $files[$k] = $file;
  89. }
  90. $this->_filePaths = $files;
  91. }
  92. public function setGroup($key, $checkLastModified = true)
  93. {
  94. $this->_groupKey = $key;
  95. if ($checkLastModified) {
  96. if (! $this->groupsConfigFile) {
  97. $this->groupsConfigFile = dirname(dirname(dirname(__DIR__))) . '/groupsConfig.php';
  98. }
  99. if (is_file($this->groupsConfigFile)) {
  100. $gc = (require $this->groupsConfigFile);
  101. if (isset($gc[$key])) {
  102. $this->_lastModified = self::getLastModified($gc[$key]);
  103. }
  104. }
  105. }
  106. }
  107. public static function getLastModified($sources, $lastModified = 0)
  108. {
  109. $max = $lastModified;
  110. foreach ((array)$sources as $source) {
  111. if (is_object($source) && isset($source->lastModified)) {
  112. $max = max($max, $source->lastModified);
  113. } elseif (is_string($source)) {
  114. if (0 === strpos($source, '//')) {
  115. $source = $_SERVER['DOCUMENT_ROOT'] . substr($source, 1);
  116. }
  117. if (is_file($source)) {
  118. $max = max($max, filemtime($source));
  119. }
  120. }
  121. }
  122. return $max;
  123. }
  124. protected $_groupKey = null; // if present, URI will be like g=...
  125. protected $_filePaths = array();
  126. protected $_lastModified = null;
  127. /**
  128. * In a given array of strings, find the character they all have at
  129. * a particular index
  130. *
  131. * @param array $arr array of strings
  132. * @param int $pos index to check
  133. * @return mixed a common char or '' if any do not match
  134. */
  135. protected static function _getCommonCharAtPos($arr, $pos) {
  136. $l = count($arr);
  137. $c = $arr[0][$pos];
  138. if ($c === '' || $l === 1)
  139. return $c;
  140. for ($i = 1; $i < $l; ++$i)
  141. if ($arr[$i][$pos] !== $c)
  142. return '';
  143. return $c;
  144. }
  145. /**
  146. * Get the shortest URI to minify the set of source files
  147. *
  148. * @param array $paths root-relative URIs of files
  149. * @param string $minRoot root-relative URI of the "min" application
  150. */
  151. protected static function _getShortestUri($paths, $minRoot = '/min/') {
  152. $pos = 0;
  153. $base = '';
  154. $c;
  155. while (true) {
  156. $c = self::_getCommonCharAtPos($paths, $pos);
  157. if ($c === '') {
  158. break;
  159. } else {
  160. $base .= $c;
  161. }
  162. ++$pos;
  163. }
  164. $base = preg_replace('@[^/]+$@', '', $base);
  165. $uri = $minRoot . 'f=' . implode(',', $paths);
  166. if (substr($base, -1) === '/') {
  167. // we have a base dir!
  168. $basedPaths = $paths;
  169. $l = count($paths);
  170. for ($i = 0; $i < $l; ++$i) {
  171. $basedPaths[$i] = substr($paths[$i], strlen($base));
  172. }
  173. $base = substr($base, 0, strlen($base) - 1);
  174. $bUri = $minRoot . 'b=' . $base . '&f=' . implode(',', $basedPaths);
  175. $uri = strlen($uri) < strlen($bUri)
  176. ? $uri
  177. : $bUri;
  178. }
  179. return $uri;
  180. }
  181. }