PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/w3-total-cache/lib/Minify/Minify/CSS/UriRewriter.php

https://bitbucket.org/adatux_/uakami
PHP | 381 lines | 171 code | 58 blank | 152 comment | 26 complexity | f6f8fe900d659d5eb5dc0ac3833982d5 MD5 | raw file
  1. <?php
  2. /**
  3. * Class Minify_CSS_UriRewriter
  4. * @package Minify
  5. */
  6. /**
  7. * Rewrite file-relative URIs as root-relative in CSS files
  8. *
  9. * @package Minify
  10. * @author Stephen Clay <steve@mrclay.org>
  11. */
  12. class Minify_CSS_UriRewriter {
  13. /**
  14. * Defines which class to call as part of callbacks, change this
  15. * if you extend Minify_CSS_UriRewriter
  16. * @var string
  17. */
  18. protected static $className = 'Minify_CSS_UriRewriter';
  19. /**
  20. * rewrite() and rewriteRelative() append debugging information here
  21. * @var string
  22. */
  23. public static $debugText = '';
  24. /**
  25. * Does CSS rewrite
  26. *
  27. * @static
  28. * @param string $css
  29. * @param array $options
  30. * @return string
  31. */
  32. public static function rewrite($css, $options) {
  33. if (isset($options['prependRelativePath']) || isset($options['currentDir'])) {
  34. $browsercache_id = (isset($options['browserCacheId']) ? $options['browserCacheId'] : 0);
  35. $browsercache_extensions = (isset($options['browserCacheExtensions']) ? $options['browserCacheExtensions'] : array());
  36. if (isset($options['currentDir'])) {
  37. $document_root = (isset($options['docRoot']) ? $options['docRoot'] : $_SERVER['DOCUMENT_ROOT']);
  38. $symlinks = (isset($options['symlinks']) ? $options['symlinks'] : array());
  39. $prependAbsolutePath = (isset($options['prependAbsolutePath']) ? $options['prependAbsolutePath'] : '');
  40. $prependAbsolutePathCallback = (isset($options['prependAbsolutePathCallback']) ? $options['prependAbsolutePathCallback'] : '');
  41. $css = self::_rewrite(
  42. $css,
  43. $options['currentDir'],
  44. $prependAbsolutePath,
  45. $prependAbsolutePathCallback,
  46. $document_root,
  47. $symlinks,
  48. $browsercache_id,
  49. $browsercache_extensions
  50. );
  51. } elseif (isset($options['prependRelativePath'])) {
  52. $css = self::_prepend(
  53. $css,
  54. $options['prependRelativePath'],
  55. $browsercache_id,
  56. $browsercache_extensions
  57. );
  58. }
  59. }
  60. return $css;
  61. }
  62. /**
  63. * Rewrite file relative URIs as root relative in CSS files
  64. *
  65. * @param string $css
  66. *
  67. * @param string $currentDir The directory of the current CSS file.
  68. *
  69. * @param string $prependAbsolutePath
  70. *
  71. * @param string $prependAbsolutePathCallback
  72. *
  73. * @param string $docRoot The document root of the web site in which
  74. * the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']).
  75. *
  76. * @param array $symlinks (default = array()) If the CSS file is stored in
  77. * a symlink-ed directory, provide an array of link paths to
  78. * target paths, where the link paths are within the document root. Because
  79. * paths need to be normalized for this to work, use "//" to substitute
  80. * the doc root in the link paths (the array keys). E.g.:
  81. * <code>
  82. * array('//symlink' => '/real/target/path') // unix
  83. * array('//static' => 'D:\\staticStorage') // Windows
  84. * </code>
  85. *
  86. * @param int $browserCacheId
  87. *
  88. * @param array $browserCacheExtensions
  89. *
  90. * @return string
  91. */
  92. private static function _rewrite($css, $currentDir, $prependAbsolutePath = null, $prependAbsolutePathCallback = null, $docRoot = null, $symlinks = array(), $browserCacheId = 0, $browserCacheExtensions = array()) {
  93. self::$_docRoot = self::_realpath($docRoot ? $docRoot : $_SERVER['DOCUMENT_ROOT']);
  94. self::$_currentDir = self::_realpath($currentDir);
  95. self::$_prependAbsolutePath = $prependAbsolutePath;
  96. self::$_prependAbsolutePathCallback = $prependAbsolutePathCallback;
  97. self::$_symlinks = array();
  98. self::$_browserCacheId = $browserCacheId;
  99. self::$_browserCacheExtensions = $browserCacheExtensions;
  100. // normalize symlinks
  101. foreach ($symlinks as $link => $target) {
  102. $link = ($link === '//') ? self::$_docRoot : str_replace('//', self::$_docRoot . '/', $link);
  103. $link = strtr($link, '/', DIRECTORY_SEPARATOR);
  104. self::$_symlinks[$link] = self::_realpath($target);
  105. }
  106. self::$debugText .= "docRoot : " . self::$_docRoot . "\n" . "currentDir : " . self::$_currentDir . "\n";
  107. if (self::$_symlinks) {
  108. self::$debugText .= "symlinks : " . implode(', ', self::$_symlinks) . "\n";
  109. }
  110. self::$debugText .= "\n";
  111. $css = self::_trimUrls($css);
  112. // rewrite
  113. $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/', array(self::$className, '_processUriCB'), $css);
  114. $css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/', array(self::$className, '_processUriCB'), $css);
  115. return $css;
  116. }
  117. /**
  118. * Prepend a path to relative URIs in CSS files
  119. *
  120. * @param string $css
  121. * @param string $path The path to prepend.
  122. * @param integer $browserCacheId
  123. * @param array $browserCacheExtensions
  124. *
  125. * @return string
  126. */
  127. private static function _prepend($css, $path, $browserCacheId = 0, $browserCacheExtensions = array()) {
  128. self::$_prependRelativePath = $path;
  129. self::$_browserCacheId = $browserCacheId;
  130. self::$_browserCacheExtensions = $browserCacheExtensions;
  131. $css = self::_trimUrls($css);
  132. // append
  133. $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/', array(self::$className, '_processUriCB'), $css);
  134. $css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/', array(self::$className, '_processUriCB'), $css);
  135. return $css;
  136. }
  137. /**
  138. * @var string directory of this stylesheet
  139. */
  140. private static $_currentDir = '';
  141. /**
  142. * @var string
  143. */
  144. private static $_prependAbsolutePath = null;
  145. /**
  146. * @var string
  147. */
  148. private static $_prependAbsolutePathCallback = null;
  149. /**
  150. * @var string DOC_ROOT
  151. */
  152. private static $_docRoot = '';
  153. /**
  154. * @var array directory replacements to map symlink targets back to their
  155. * source (within the document root) E.g. '/var/www/symlink' => '/var/realpath'
  156. */
  157. private static $_symlinks = array();
  158. /**
  159. * @var int
  160. */
  161. private static $_browserCacheId = 0;
  162. /**
  163. * @var array
  164. */
  165. private static $_browserCacheExtensions = array();
  166. /**
  167. * @var string path to prepend
  168. */
  169. private static $_prependRelativePath = null;
  170. private static function _trimUrls($css) {
  171. return preg_replace('/
  172. url\\( # url(
  173. \\s*
  174. ([^\\)]+?) # 1 = URI (assuming does not contain ")")
  175. \\s*
  176. \\) # )
  177. /x', 'url($1)', $css);
  178. }
  179. private static function _processUriCB($m) {
  180. // $m matched either '/@import\\s+([\'"])(.*?)[\'"]/' or '/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
  181. $isImport = ($m[0][0] === '@');
  182. // determine URI and the quote character (if any)
  183. if ($isImport) {
  184. $quoteChar = $m[1];
  185. $uri = $m[2];
  186. } else {
  187. // $m[1] is either quoted or not
  188. $quoteChar = ($m[1][0] === "'" || $m[1][0] === '"') ? $m[1][0] : '';
  189. $uri = ($quoteChar === '') ? $m[1] : substr($m[1], 1, strlen($m[1]) - 2);
  190. }
  191. // analyze URI
  192. if (false === strpos($uri, '//') && 0 !== strpos($uri, 'data:')) {
  193. // prepend
  194. if (self::$_prependRelativePath) {
  195. if (w3_is_url(self::$_prependRelativePath)) {
  196. $parse_url = @parse_url(self::$_prependRelativePath);
  197. if ($parse_url && isset($parse_url['host'])) {
  198. $scheme = $parse_url['scheme'];
  199. $host = $parse_url['host'];
  200. $port = (isset($parse_url['port']) && $parse_url['port'] != 80 ? ':' . (int) $parse_url['port'] : '');
  201. $path = (!empty($parse_url['path']) ? $parse_url['path'] : '/');
  202. $dir_css = preg_replace('~[^/]+$~', '', $path);
  203. $dir_obj = preg_replace('~[^/]+$~', '', $uri);
  204. $dir = (ltrim((strpos($dir_obj, '/') === 0 ? w3_realpath($dir_obj) : w3_realpath($dir_css . $dir_obj)), '/'));
  205. $file = basename($uri);
  206. $uri = sprintf('%s://%s%s/%s/%s', $scheme, $host, $port, $dir, $file);
  207. }
  208. } else {
  209. $uri = self::$_prependRelativePath . $uri;
  210. }
  211. } else {
  212. $uri = self::_rewriteRelative($uri, self::$_currentDir, self::$_docRoot, self::$_symlinks);
  213. if (self::$_prependAbsolutePath) {
  214. $prependAbsolutePath = self::$_prependAbsolutePath;
  215. } elseif (self::$_prependAbsolutePathCallback) {
  216. $prependAbsolutePath = call_user_func(self::$_prependAbsolutePathCallback, $uri);
  217. } else {
  218. $prependAbsolutePath = '';
  219. }
  220. if ($prependAbsolutePath) {
  221. $uri = rtrim($prependAbsolutePath, '/') . $uri;
  222. }
  223. }
  224. if (self::$_browserCacheId && count(self::$_browserCacheExtensions)) {
  225. $matches = null;
  226. if (preg_match('~\.([a-z-_]+)(\?.*)?$~', $uri, $matches)) {
  227. $extension = $matches[1];
  228. $query = (isset($matches[2]) ? $matches[2] : '');
  229. if ($extension && in_array($extension, self::$_browserCacheExtensions)) {
  230. $uri = w3_remove_query($uri);
  231. $uri .= ($query ? '&' : '?') . self::$_browserCacheId;
  232. }
  233. }
  234. }
  235. }
  236. return $isImport ? "@import {$quoteChar}{$uri}{$quoteChar}" : "url({$quoteChar}{$uri}{$quoteChar})";
  237. }
  238. /**
  239. * Rewrite a file relative URI as root relative
  240. *
  241. * <code>
  242. * Minify_CSS_UriRewriter::rewriteRelative(
  243. * '../img/hello.gif'
  244. * , '/home/user/www/css' // path of CSS file
  245. * , '/home/user/www' // doc root
  246. * );
  247. * // returns '/img/hello.gif'
  248. *
  249. * // example where static files are stored in a symlinked directory
  250. * Minify_CSS_UriRewriter::rewriteRelative(
  251. * 'hello.gif'
  252. * , '/var/staticFiles/theme'
  253. * , '/home/user/www'
  254. * , array('/home/user/www/static' => '/var/staticFiles')
  255. * );
  256. * // returns '/static/theme/hello.gif'
  257. * </code>
  258. *
  259. * @param string $uri file relative URI
  260. *
  261. * @param string $realCurrentDir realpath of the current file's directory.
  262. *
  263. * @param string $realDocRoot realpath of the site document root.
  264. *
  265. * @param array $symlinks (default = array()) If the file is stored in
  266. * a symlink-ed directory, provide an array of link paths to
  267. * real target paths, where the link paths "appear" to be within the document
  268. * root. E.g.:
  269. * <code>
  270. * array('/home/foo/www/not/real/path' => '/real/target/path') // unix
  271. * array('C:\\htdocs\\not\\real' => 'D:\\real\\target\\path') // Windows
  272. * </code>
  273. *
  274. * @return string
  275. */
  276. private static function _rewriteRelative($uri, $realCurrentDir, $realDocRoot, $symlinks = array()) {
  277. if ('/' === $uri[0]) { // root-relative
  278. return $uri;
  279. }
  280. // prepend path with current dir separator (OS-independent)
  281. $path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR);
  282. self::$debugText .= "file-relative URI : {$uri}\n" . "path prepended : {$path}\n";
  283. // "unresolve" a symlink back to doc root
  284. foreach ($symlinks as $link => $target) {
  285. if (0 === strpos($path, $target)) {
  286. // replace $target with $link
  287. $path = $link . substr($path, strlen($target));
  288. self::$debugText .= "symlink unresolved : {$path}\n";
  289. break;
  290. }
  291. }
  292. // strip doc root
  293. $path = substr($path, strlen($realDocRoot));
  294. self::$debugText .= "docroot stripped : {$path}\n";
  295. // fix to root-relative URI
  296. $uri = strtr($path, '/\\', '//');
  297. // remove /./ and /../ where possible
  298. $uri = str_replace('/./', '/', $uri);
  299. // inspired by patch from Oleg Cherniy
  300. do {
  301. $uri = preg_replace('@/[^/]+/\\.\\./@', '/', $uri, 1, $changed);
  302. } while ($changed);
  303. self::$debugText .= "traversals removed : {$uri}\n\n";
  304. $uri = preg_replace('~^' . w3_preg_quote(w3_get_base_path()) . '~', w3_get_site_path(), $uri);
  305. return $uri;
  306. }
  307. /**
  308. * Get realpath with any trailing slash removed. If realpath() fails,
  309. * just remove the trailing slash.
  310. *
  311. * @param string $path
  312. *
  313. * @return mixed path with no trailing slash
  314. */
  315. protected static function _realpath($path) {
  316. $realPath = realpath($path);
  317. if ($realPath !== false) {
  318. $path = $realPath;
  319. }
  320. return rtrim($path, '/\\');
  321. }
  322. }