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

/plugins/Minify/extlib/minify/min/lib/Minify/CSS/UriRewriter.php

https://gitlab.com/BeS/io.schiessle.org
PHP | 270 lines | 115 code | 32 blank | 123 comment | 7 complexity | 5ab5dfdb489e28a39a4e9d6ad70117d6 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. * Rewrite file relative URIs as root relative in CSS files
  26. *
  27. * @param string $css
  28. *
  29. * @param string $currentDir The directory of the current CSS file.
  30. *
  31. * @param string $docRoot The document root of the web site in which
  32. * the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']).
  33. *
  34. * @param array $symlinks (default = array()) If the CSS file is stored in
  35. * a symlink-ed directory, provide an array of link paths to
  36. * target paths, where the link paths are within the document root. Because
  37. * paths need to be normalized for this to work, use "//" to substitute
  38. * the doc root in the link paths (the array keys). E.g.:
  39. * <code>
  40. * array('//symlink' => '/real/target/path') // unix
  41. * array('//static' => 'D:\\staticStorage') // Windows
  42. * </code>
  43. *
  44. * @return string
  45. */
  46. public static function rewrite($css, $currentDir, $docRoot = null, $symlinks = array())
  47. {
  48. self::$_docRoot = self::_realpath(
  49. $docRoot ? $docRoot : $_SERVER['DOCUMENT_ROOT']
  50. );
  51. self::$_currentDir = self::_realpath($currentDir);
  52. self::$_symlinks = array();
  53. // normalize symlinks
  54. foreach ($symlinks as $link => $target) {
  55. $link = ($link === '//')
  56. ? self::$_docRoot
  57. : str_replace('//', self::$_docRoot . '/', $link);
  58. $link = strtr($link, '/', DIRECTORY_SEPARATOR);
  59. self::$_symlinks[$link] = self::_realpath($target);
  60. }
  61. self::$debugText .= "docRoot : " . self::$_docRoot . "\n"
  62. . "currentDir : " . self::$_currentDir . "\n";
  63. if (self::$_symlinks) {
  64. self::$debugText .= "symlinks : " . var_export(self::$_symlinks, 1) . "\n";
  65. }
  66. self::$debugText .= "\n";
  67. $css = self::_trimUrls($css);
  68. // rewrite
  69. $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
  70. ,array(self::$className, '_processUriCB'), $css);
  71. $css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
  72. ,array(self::$className, '_processUriCB'), $css);
  73. return $css;
  74. }
  75. /**
  76. * Prepend a path to relative URIs in CSS files
  77. *
  78. * @param string $css
  79. *
  80. * @param string $path The path to prepend.
  81. *
  82. * @return string
  83. */
  84. public static function prepend($css, $path)
  85. {
  86. self::$_prependPath = $path;
  87. $css = self::_trimUrls($css);
  88. // append
  89. $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
  90. ,array(self::$className, '_processUriCB'), $css);
  91. $css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
  92. ,array(self::$className, '_processUriCB'), $css);
  93. self::$_prependPath = null;
  94. return $css;
  95. }
  96. /**
  97. * @var string directory of this stylesheet
  98. */
  99. private static $_currentDir = '';
  100. /**
  101. * @var string DOC_ROOT
  102. */
  103. private static $_docRoot = '';
  104. /**
  105. * @var array directory replacements to map symlink targets back to their
  106. * source (within the document root) E.g. '/var/www/symlink' => '/var/realpath'
  107. */
  108. private static $_symlinks = array();
  109. /**
  110. * @var string path to prepend
  111. */
  112. private static $_prependPath = null;
  113. private static function _trimUrls($css)
  114. {
  115. return preg_replace('/
  116. url\\( # url(
  117. \\s*
  118. ([^\\)]+?) # 1 = URI (assuming does not contain ")")
  119. \\s*
  120. \\) # )
  121. /x', 'url($1)', $css);
  122. }
  123. private static function _processUriCB($m)
  124. {
  125. // $m matched either '/@import\\s+([\'"])(.*?)[\'"]/' or '/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
  126. $isImport = ($m[0][0] === '@');
  127. // determine URI and the quote character (if any)
  128. if ($isImport) {
  129. $quoteChar = $m[1];
  130. $uri = $m[2];
  131. } else {
  132. // $m[1] is either quoted or not
  133. $quoteChar = ($m[1][0] === "'" || $m[1][0] === '"')
  134. ? $m[1][0]
  135. : '';
  136. $uri = ($quoteChar === '')
  137. ? $m[1]
  138. : substr($m[1], 1, strlen($m[1]) - 2);
  139. }
  140. // analyze URI
  141. if ('/' !== $uri[0] // root-relative
  142. && false === strpos($uri, '//') // protocol (non-data)
  143. && 0 !== strpos($uri, 'data:') // data protocol
  144. ) {
  145. // URI is file-relative: rewrite depending on options
  146. $uri = (self::$_prependPath !== null)
  147. ? (self::$_prependPath . $uri)
  148. : self::rewriteRelative($uri, self::$_currentDir, self::$_docRoot, self::$_symlinks);
  149. }
  150. return $isImport
  151. ? "@import {$quoteChar}{$uri}{$quoteChar}"
  152. : "url({$quoteChar}{$uri}{$quoteChar})";
  153. }
  154. /**
  155. * Rewrite a file relative URI as root relative
  156. *
  157. * <code>
  158. * Minify_CSS_UriRewriter::rewriteRelative(
  159. * '../img/hello.gif'
  160. * , '/home/user/www/css' // path of CSS file
  161. * , '/home/user/www' // doc root
  162. * );
  163. * // returns '/img/hello.gif'
  164. *
  165. * // example where static files are stored in a symlinked directory
  166. * Minify_CSS_UriRewriter::rewriteRelative(
  167. * 'hello.gif'
  168. * , '/var/staticFiles/theme'
  169. * , '/home/user/www'
  170. * , array('/home/user/www/static' => '/var/staticFiles')
  171. * );
  172. * // returns '/static/theme/hello.gif'
  173. * </code>
  174. *
  175. * @param string $uri file relative URI
  176. *
  177. * @param string $realCurrentDir realpath of the current file's directory.
  178. *
  179. * @param string $realDocRoot realpath of the site document root.
  180. *
  181. * @param array $symlinks (default = array()) If the file is stored in
  182. * a symlink-ed directory, provide an array of link paths to
  183. * real target paths, where the link paths "appear" to be within the document
  184. * root. E.g.:
  185. * <code>
  186. * array('/home/foo/www/not/real/path' => '/real/target/path') // unix
  187. * array('C:\\htdocs\\not\\real' => 'D:\\real\\target\\path') // Windows
  188. * </code>
  189. *
  190. * @return string
  191. */
  192. public static function rewriteRelative($uri, $realCurrentDir, $realDocRoot, $symlinks = array())
  193. {
  194. // prepend path with current dir separator (OS-independent)
  195. $path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR)
  196. . DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR);
  197. self::$debugText .= "file-relative URI : {$uri}\n"
  198. . "path prepended : {$path}\n";
  199. // "unresolve" a symlink back to doc root
  200. foreach ($symlinks as $link => $target) {
  201. if (0 === strpos($path, $target)) {
  202. // replace $target with $link
  203. $path = $link . substr($path, strlen($target));
  204. self::$debugText .= "symlink unresolved : {$path}\n";
  205. break;
  206. }
  207. }
  208. // strip doc root
  209. $path = substr($path, strlen($realDocRoot));
  210. self::$debugText .= "docroot stripped : {$path}\n";
  211. // fix to root-relative URI
  212. $uri = strtr($path, '/\\', '//');
  213. // remove /./ and /../ where possible
  214. $uri = str_replace('/./', '/', $uri);
  215. // inspired by patch from Oleg Cherniy
  216. do {
  217. $uri = preg_replace('@/[^/]+/\\.\\./@', '/', $uri, 1, $changed);
  218. } while ($changed);
  219. self::$debugText .= "traversals removed : {$uri}\n\n";
  220. return $uri;
  221. }
  222. /**
  223. * Get realpath with any trailing slash removed. If realpath() fails,
  224. * just remove the trailing slash.
  225. *
  226. * @param string $path
  227. *
  228. * @return mixed path with no trailing slash
  229. */
  230. protected static function _realpath($path)
  231. {
  232. $realPath = realpath($path);
  233. if ($realPath !== false) {
  234. $path = $realPath;
  235. }
  236. return rtrim($path, '/\\');
  237. }
  238. }