[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Class Minify_ImportProcessor 4 * @package Minify 5 */ 6 7 /** 8 * Linearize a CSS/JS file by including content specified by CSS import 9 * declarations. In CSS files, relative URIs are fixed. 10 * 11 * @imports will be processed regardless of where they appear in the source 12 * files; i.e. @imports commented out or in string content will still be 13 * processed! 14 * 15 * This has a unit test but should be considered "experimental". 16 * 17 * @package Minify 18 * @author Stephen Clay <steve@mrclay.org> 19 * @author Simon Schick <simonsimcity@gmail.com> 20 */ 21 class Minify_ImportProcessor { 22 23 public static $filesIncluded = array(); 24 25 public static function process($file) 26 { 27 self::$filesIncluded = array(); 28 self::$_isCss = (strtolower(substr($file, -4)) === '.css'); 29 $obj = new Minify_ImportProcessor(dirname($file)); 30 return $obj->_getContent($file); 31 } 32 33 // allows callback funcs to know the current directory 34 private $_currentDir = null; 35 36 // allows callback funcs to know the directory of the file that inherits this one 37 private $_previewsDir = null; 38 39 // allows _importCB to write the fetched content back to the obj 40 private $_importedContent = ''; 41 42 private static $_isCss = null; 43 44 /** 45 * @param String $currentDir 46 * @param String $previewsDir Is only used internally 47 */ 48 private function __construct($currentDir, $previewsDir = "") 49 { 50 $this->_currentDir = $currentDir; 51 $this->_previewsDir = $previewsDir; 52 } 53 54 private function _getContent($file, $is_imported = false) 55 { 56 $file = realpath($file); 57 if (! $file 58 || in_array($file, self::$filesIncluded) 59 || false === ($content = @file_get_contents($file)) 60 ) { 61 // file missing, already included, or failed read 62 return ''; 63 } 64 self::$filesIncluded[] = realpath($file); 65 $this->_currentDir = dirname($file); 66 67 // remove UTF-8 BOM if present 68 if (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3)) { 69 $content = substr($content, 3); 70 } 71 // ensure uniform EOLs 72 $content = str_replace("\r\n", "\n", $content); 73 74 // process @imports 75 $content = preg_replace_callback( 76 '/ 77 @import\\s+ 78 (?:url\\(\\s*)? # maybe url( 79 [\'"]? # maybe quote 80 (.*?) # 1 = URI 81 [\'"]? # maybe end quote 82 (?:\\s*\\))? # maybe ) 83 ([a-zA-Z,\\s]*)? # 2 = media list 84 ; # end token 85 /x' 86 ,array($this, '_importCB') 87 ,$content 88 ); 89 90 // You only need to rework the import-path if the script is imported 91 if (self::$_isCss && $is_imported) { 92 // rewrite remaining relative URIs 93 $content = preg_replace_callback( 94 '/url\\(\\s*([^\\)\\s]+)\\s*\\)/' 95 ,array($this, '_urlCB') 96 ,$content 97 ); 98 } 99 100 return $this->_importedContent . $content; 101 } 102 103 private function _importCB($m) 104 { 105 $url = $m[1]; 106 $mediaList = preg_replace('/\\s+/', '', $m[2]); 107 108 if (strpos($url, '://') > 0) { 109 // protocol, leave in place for CSS, comment for JS 110 return self::$_isCss 111 ? $m[0] 112 : "/* Minify_ImportProcessor will not include remote content */"; 113 } 114 if ('/' === $url[0]) { 115 // protocol-relative or root path 116 $url = ltrim($url, '/'); 117 $file = realpath($_SERVER['DOCUMENT_ROOT']) . DIRECTORY_SEPARATOR 118 . strtr($url, '/', DIRECTORY_SEPARATOR); 119 } else { 120 // relative to current path 121 $file = $this->_currentDir . DIRECTORY_SEPARATOR 122 . strtr($url, '/', DIRECTORY_SEPARATOR); 123 } 124 $obj = new Minify_ImportProcessor(dirname($file), $this->_currentDir); 125 $content = $obj->_getContent($file, true); 126 if ('' === $content) { 127 // failed. leave in place for CSS, comment for JS 128 return self::$_isCss 129 ? $m[0] 130 : "/* Minify_ImportProcessor could not fetch '{$file}' */"; 131 } 132 return (!self::$_isCss || preg_match('@(?:^$|\\ball\\b)@', $mediaList)) 133 ? $content 134 : "@media {$mediaList} {\n{$content}\n}\n"; 135 } 136 137 private function _urlCB($m) 138 { 139 // $m[1] is either quoted or not 140 $quote = ($m[1][0] === "'" || $m[1][0] === '"') 141 ? $m[1][0] 142 : ''; 143 $url = ($quote === '') 144 ? $m[1] 145 : substr($m[1], 1, strlen($m[1]) - 2); 146 if ('/' !== $url[0]) { 147 if (strpos($url, '//') > 0) { 148 // probably starts with protocol, do not alter 149 } else { 150 // prepend path with current dir separator (OS-independent) 151 $path = $this->_currentDir 152 . DIRECTORY_SEPARATOR . strtr($url, '/', DIRECTORY_SEPARATOR); 153 // update the relative path by the directory of the file that imported this one 154 $url = self::getPathDiff(realpath($this->_previewsDir), $path); 155 } 156 } 157 return "url({$quote}{$url}{$quote})"; 158 } 159 160 /** 161 * @param string $from 162 * @param string $to 163 * @param string $ps 164 * @return string 165 */ 166 private function getPathDiff($from, $to, $ps = DIRECTORY_SEPARATOR) 167 { 168 $realFrom = $this->truepath($from); 169 $realTo = $this->truepath($to); 170 171 $arFrom = explode($ps, rtrim($realFrom, $ps)); 172 $arTo = explode($ps, rtrim($realTo, $ps)); 173 while (count($arFrom) && count($arTo) && ($arFrom[0] == $arTo[0])) 174 { 175 array_shift($arFrom); 176 array_shift($arTo); 177 } 178 return str_pad("", count($arFrom) * 3, '..' . $ps) . implode($ps, $arTo); 179 } 180 181 /** 182 * This function is to replace PHP's extremely buggy realpath(). 183 * @param string $path The original path, can be relative etc. 184 * @return string The resolved path, it might not exist. 185 * @see http://stackoverflow.com/questions/4049856/replace-phps-realpath 186 */ 187 function truepath($path) 188 { 189 // whether $path is unix or not 190 $unipath = strlen($path) == 0 || $path{0} != '/'; 191 // attempts to detect if path is relative in which case, add cwd 192 if (strpos($path, ':') === false && $unipath) 193 $path = $this->_currentDir . DIRECTORY_SEPARATOR . $path; 194 195 // resolve path parts (single dot, double dot and double delimiters) 196 $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path); 197 $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen'); 198 $absolutes = array(); 199 foreach ($parts as $part) { 200 if ('.' == $part) 201 continue; 202 if ('..' == $part) { 203 array_pop($absolutes); 204 } else { 205 $absolutes[] = $part; 206 } 207 } 208 $path = implode(DIRECTORY_SEPARATOR, $absolutes); 209 // resolve any symlinks 210 if (file_exists($path) && linkinfo($path) > 0) 211 $path = readlink($path); 212 // put initial separator that could have been lost 213 $path = !$unipath ? '/' . $path : $path; 214 return $path; 215 } 216 }
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 |