PageRenderTime 56ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Minify/HTML/Helper.php

https://github.com/soitun/minify
PHP | 250 lines | 152 code | 23 blank | 75 comment | 25 complexity | 6b49fc173bf510d39ce1ea6c5c3665ab MD5 | raw file
  1. <?php
  2. /**
  3. * Class Minify_HTML_Helper
  4. * @package Minify
  5. */
  6. /**
  7. * Helpers for writing Minify URIs into HTML
  8. *
  9. * @package Minify
  10. * @author Stephen Clay <steve@mrclay.org>
  11. */
  12. class Minify_HTML_Helper
  13. {
  14. public $rewriteWorks = true;
  15. public $minAppUri = '/min';
  16. public $groupsConfigFile = '';
  17. /**
  18. * Get an HTML-escaped Minify URI for a group or set of files
  19. *
  20. * @param string|array $keyOrFiles a group key or array of filepaths/URIs
  21. * @param array $opts options:
  22. * 'farExpires' : (default true) append a modified timestamp for cache revving
  23. * 'debug' : (default false) append debug flag
  24. * 'charset' : (default 'UTF-8') for htmlspecialchars
  25. * 'minAppUri' : (default '/min') URI of min directory
  26. * 'rewriteWorks' : (default true) does mod_rewrite work in min app?
  27. * 'groupsConfigFile' : specify if different
  28. * @return string
  29. */
  30. public static function getUri($keyOrFiles, $opts = array())
  31. {
  32. $opts = array_merge(array( // default options
  33. 'farExpires' => true,
  34. 'debug' => false,
  35. 'charset' => 'UTF-8',
  36. 'minAppUri' => '/min',
  37. 'rewriteWorks' => true,
  38. 'groupsConfigFile' => self::app()->groupsConfigPath,
  39. ), $opts);
  40. $h = new self;
  41. $h->minAppUri = $opts['minAppUri'];
  42. $h->rewriteWorks = $opts['rewriteWorks'];
  43. $h->groupsConfigFile = $opts['groupsConfigFile'];
  44. if (is_array($keyOrFiles)) {
  45. $h->setFiles($keyOrFiles, $opts['farExpires']);
  46. } else {
  47. $h->setGroup($keyOrFiles, $opts['farExpires']);
  48. }
  49. $uri = $h->getRawUri($opts['farExpires'], $opts['debug']);
  50. return htmlspecialchars($uri, ENT_QUOTES, $opts['charset']);
  51. }
  52. /**
  53. * Get non-HTML-escaped URI to minify the specified files
  54. *
  55. * @param bool $farExpires
  56. * @param bool $debug
  57. * @return string
  58. */
  59. public function getRawUri($farExpires = true, $debug = false)
  60. {
  61. $path = rtrim($this->minAppUri, '/') . '/';
  62. if (! $this->rewriteWorks) {
  63. $path .= '?';
  64. }
  65. if (null === $this->_groupKey) {
  66. // @todo: implement shortest uri
  67. $path = self::_getShortestUri($this->_filePaths, $path);
  68. } else {
  69. $path .= "g=" . $this->_groupKey;
  70. }
  71. if ($debug) {
  72. $path .= "&debug";
  73. } elseif ($farExpires && $this->_lastModified) {
  74. $path .= "&" . $this->_lastModified;
  75. }
  76. return $path;
  77. }
  78. /**
  79. * Set the files that will comprise the URI we're building
  80. *
  81. * @param array $files
  82. * @param bool $checkLastModified
  83. */
  84. public function setFiles($files, $checkLastModified = true)
  85. {
  86. $this->_groupKey = null;
  87. if ($checkLastModified) {
  88. $this->_lastModified = self::getLastModified($files);
  89. }
  90. // normalize paths like in /min/f=<paths>
  91. foreach ($files as $k => $file) {
  92. if (0 === strpos($file, '//')) {
  93. $file = substr($file, 2);
  94. } elseif (0 === strpos($file, '/') || 1 === strpos($file, ':\\')) {
  95. $file = substr($file, strlen(self::app()->env->getDocRoot()) + 1);
  96. }
  97. $file = strtr($file, '\\', '/');
  98. $files[$k] = $file;
  99. }
  100. $this->_filePaths = $files;
  101. }
  102. /**
  103. * Set the group of files that will comprise the URI we're building
  104. *
  105. * @param string $key
  106. * @param bool $checkLastModified
  107. */
  108. public function setGroup($key, $checkLastModified = true)
  109. {
  110. $this->_groupKey = $key;
  111. if ($checkLastModified) {
  112. if (! $this->groupsConfigFile) {
  113. $this->groupsConfigFile = self::app()->groupsConfigPath;
  114. }
  115. if (is_file($this->groupsConfigFile)) {
  116. $gc = (require $this->groupsConfigFile);
  117. $keys = explode(',', $key);
  118. foreach ($keys as $key) {
  119. if (!isset($gc[$key])) {
  120. // this can happen if value is null
  121. // which could be solved with array_filter
  122. continue;
  123. }
  124. $this->_lastModified = self::getLastModified($gc[$key], $this->_lastModified);
  125. }
  126. }
  127. }
  128. }
  129. /**
  130. * Get the max(lastModified) of all files
  131. *
  132. * @param array|string $sources
  133. * @param int $lastModified
  134. * @return int
  135. */
  136. public static function getLastModified($sources, $lastModified = 0)
  137. {
  138. $max = $lastModified;
  139. $factory = self::app()->sourceFactory;
  140. /** @var Minify_Source $source */
  141. foreach ((array)$sources as $source) {
  142. $source = $factory->makeSource($source);
  143. $max = max($max, $source->getLastModified());
  144. }
  145. return $max;
  146. }
  147. /**
  148. * @param \Minify\App $app
  149. * @return \Minify\App
  150. * @internal
  151. */
  152. public static function app(\Minify\App $app = null)
  153. {
  154. static $cached;
  155. if ($app) {
  156. $cached = $app;
  157. return $app;
  158. }
  159. if ($cached === null) {
  160. $cached = (require __DIR__ . '/../../../bootstrap.php');
  161. }
  162. return $cached;
  163. }
  164. protected $_groupKey = null; // if present, URI will be like g=...
  165. protected $_filePaths = array();
  166. protected $_lastModified = null;
  167. /**
  168. * In a given array of strings, find the character they all have at
  169. * a particular index
  170. *
  171. * @param array $arr array of strings
  172. * @param int $pos index to check
  173. * @return mixed a common char or '' if any do not match
  174. */
  175. protected static function _getCommonCharAtPos($arr, $pos)
  176. {
  177. if (!isset($arr[0][$pos])) {
  178. return '';
  179. }
  180. $c = $arr[0][$pos];
  181. $l = count($arr);
  182. if ($l === 1) {
  183. return $c;
  184. }
  185. for ($i = 1; $i < $l; ++$i) {
  186. if ($arr[$i][$pos] !== $c) {
  187. return '';
  188. }
  189. }
  190. return $c;
  191. }
  192. /**
  193. * Get the shortest URI to minify the set of source files
  194. *
  195. * @param array $paths root-relative URIs of files
  196. * @param string $minRoot root-relative URI of the "min" application
  197. * @return string
  198. */
  199. protected static function _getShortestUri($paths, $minRoot = '/min/')
  200. {
  201. $pos = 0;
  202. $base = '';
  203. while (true) {
  204. $c = self::_getCommonCharAtPos($paths, $pos);
  205. if ($c === '') {
  206. break;
  207. } else {
  208. $base .= $c;
  209. }
  210. ++$pos;
  211. }
  212. $base = preg_replace('@[^/]+$@', '', $base);
  213. $uri = $minRoot . 'f=' . implode(',', $paths);
  214. if (substr($base, -1) === '/') {
  215. // we have a base dir!
  216. $basedPaths = $paths;
  217. $l = count($paths);
  218. for ($i = 0; $i < $l; ++$i) {
  219. $basedPaths[$i] = substr($paths[$i], strlen($base));
  220. }
  221. $base = substr($base, 0, strlen($base) - 1);
  222. $bUri = $minRoot . 'b=' . $base . '&f=' . implode(',', $basedPaths);
  223. $uri = strlen($uri) < strlen($bUri) ? $uri : $bUri;
  224. }
  225. return $uri;
  226. }
  227. }