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