MediaWiki
REL1_20
|
00001 <?php 00030 class CSSMin { 00031 00032 /* Constants */ 00033 00040 const EMBED_SIZE_LIMIT = 24576; 00041 const URL_REGEX = 'url\(\s*[\'"]?(?P<file>[^\?\)\'"]*)(?P<query>\??[^\)\'"]*)[\'"]?\s*\)'; 00042 00043 /* Protected Static Members */ 00044 00046 protected static $mimeTypes = array( 00047 'gif' => 'image/gif', 00048 'jpe' => 'image/jpeg', 00049 'jpeg' => 'image/jpeg', 00050 'jpg' => 'image/jpeg', 00051 'png' => 'image/png', 00052 'tif' => 'image/tiff', 00053 'tiff' => 'image/tiff', 00054 'xbm' => 'image/x-xbitmap', 00055 ); 00056 00057 /* Static Methods */ 00058 00066 public static function getLocalFileReferences( $source, $path = null ) { 00067 $files = array(); 00068 $rFlags = PREG_OFFSET_CAPTURE | PREG_SET_ORDER; 00069 if ( preg_match_all( '/' . self::URL_REGEX . '/', $source, $matches, $rFlags ) ) { 00070 foreach ( $matches as $match ) { 00071 $file = ( isset( $path ) 00072 ? rtrim( $path, '/' ) . '/' 00073 : '' ) . "{$match['file'][0]}"; 00074 00075 // Only proceed if we can access the file 00076 if ( !is_null( $path ) && file_exists( $file ) ) { 00077 $files[] = $file; 00078 } 00079 } 00080 } 00081 return $files; 00082 } 00083 00088 protected static function getMimeType( $file ) { 00089 $realpath = realpath( $file ); 00090 // Try a couple of different ways to get the mime-type of a file, in order of 00091 // preference 00092 if ( 00093 $realpath 00094 && function_exists( 'finfo_file' ) 00095 && function_exists( 'finfo_open' ) 00096 && defined( 'FILEINFO_MIME_TYPE' ) 00097 ) { 00098 // As of PHP 5.3, this is how you get the mime-type of a file; it uses the Fileinfo 00099 // PECL extension 00100 return finfo_file( finfo_open( FILEINFO_MIME_TYPE ), $realpath ); 00101 } elseif ( function_exists( 'mime_content_type' ) ) { 00102 // Before this was deprecated in PHP 5.3, this was how you got the mime-type of a file 00103 return mime_content_type( $file ); 00104 } else { 00105 // Worst-case scenario has happened, use the file extension to infer the mime-type 00106 $ext = strtolower( pathinfo( $file, PATHINFO_EXTENSION ) ); 00107 if ( isset( self::$mimeTypes[$ext] ) ) { 00108 return self::$mimeTypes[$ext]; 00109 } 00110 } 00111 return false; 00112 } 00113 00124 public static function remap( $source, $local, $remote, $embedData = true ) { 00125 $pattern = '/((?P<embed>\s*\/\*\s*\@embed\s*\*\/)(?P<pre>[^\;\}]*))?' . 00126 self::URL_REGEX . '(?P<post>[^;]*)[\;]?/'; 00127 $offset = 0; 00128 while ( preg_match( $pattern, $source, $match, PREG_OFFSET_CAPTURE, $offset ) ) { 00129 // Skip fully-qualified URLs and data URIs 00130 $urlScheme = parse_url( $match['file'][0], PHP_URL_SCHEME ); 00131 if ( $urlScheme ) { 00132 // Move the offset to the end of the match, leaving it alone 00133 $offset = $match[0][1] + strlen( $match[0][0] ); 00134 continue; 00135 } 00136 // URLs with absolute paths like /w/index.php need to be expanded 00137 // to absolute URLs but otherwise left alone 00138 if ( $match['file'][0] !== '' && $match['file'][0][0] === '/' ) { 00139 // Replace the file path with an expanded (possibly protocol-relative) URL 00140 // ...but only if wfExpandUrl() is even available. 00141 // This will not be the case if we're running outside of MW 00142 $lengthIncrease = 0; 00143 if ( function_exists( 'wfExpandUrl' ) ) { 00144 $expanded = wfExpandUrl( $match['file'][0], PROTO_RELATIVE ); 00145 $origLength = strlen( $match['file'][0] ); 00146 $lengthIncrease = strlen( $expanded ) - $origLength; 00147 $source = substr_replace( $source, $expanded, 00148 $match['file'][1], $origLength 00149 ); 00150 } 00151 // Move the offset to the end of the match, leaving it alone 00152 $offset = $match[0][1] + strlen( $match[0][0] ) + $lengthIncrease; 00153 continue; 00154 } 00155 00156 // Guard against double slashes, because "some/remote/../foo.png" 00157 // resolves to "some/remote/foo.png" on (some?) clients (bug 27052). 00158 if ( substr( $remote, -1 ) == '/' ) { 00159 $remote = substr( $remote, 0, -1 ); 00160 } 00161 00162 // Shortcuts 00163 $embed = $match['embed'][0]; 00164 $pre = $match['pre'][0]; 00165 $post = $match['post'][0]; 00166 $query = $match['query'][0]; 00167 $url = "{$remote}/{$match['file'][0]}"; 00168 $file = "{$local}/{$match['file'][0]}"; 00169 00170 $replacement = false; 00171 00172 if ( $local !== false && file_exists( $file ) ) { 00173 // Add version parameter as a time-stamp in ISO 8601 format, 00174 // using Z for the timezone, meaning GMT 00175 $url .= '?' . gmdate( 'Y-m-d\TH:i:s\Z', round( filemtime( $file ), -2 ) ); 00176 // Embedding requires a bit of extra processing, so let's skip that if we can 00177 if ( $embedData && $embed ) { 00178 $type = self::getMimeType( $file ); 00179 // Detect when URLs were preceeded with embed tags, and also verify file size is 00180 // below the limit 00181 if ( 00182 $type 00183 && $match['embed'][1] > 0 00184 && filesize( $file ) < self::EMBED_SIZE_LIMIT 00185 ) { 00186 // Strip off any trailing = symbols (makes browsers freak out) 00187 $data = base64_encode( file_get_contents( $file ) ); 00188 // Build 2 CSS properties; one which uses a base64 encoded data URI in place 00189 // of the @embed comment to try and retain line-number integrity, and the 00190 // other with a remapped an versioned URL and an Internet Explorer hack 00191 // making it ignored in all browsers that support data URIs 00192 $replacement = "{$pre}url(data:{$type};base64,{$data}){$post};"; 00193 $replacement .= "{$pre}url({$url}){$post}!ie;"; 00194 } 00195 } 00196 if ( $replacement === false ) { 00197 // Assume that all paths are relative to $remote, and make them absolute 00198 $replacement = "{$embed}{$pre}url({$url}){$post};"; 00199 } 00200 } elseif ( $local === false ) { 00201 // Assume that all paths are relative to $remote, and make them absolute 00202 $replacement = "{$embed}{$pre}url({$url}{$query}){$post};"; 00203 } 00204 if ( $replacement !== false ) { 00205 // Perform replacement on the source 00206 $source = substr_replace( 00207 $source, $replacement, $match[0][1], strlen( $match[0][0] ) 00208 ); 00209 // Move the offset to the end of the replacement in the source 00210 $offset = $match[0][1] + strlen( $replacement ); 00211 continue; 00212 } 00213 // Move the offset to the end of the match, leaving it alone 00214 $offset = $match[0][1] + strlen( $match[0][0] ); 00215 } 00216 return $source; 00217 } 00218 00225 public static function minify( $css ) { 00226 return trim( 00227 str_replace( 00228 array( '; ', ': ', ' {', '{ ', ', ', '} ', ';}' ), 00229 array( ';', ':', '{', '{', ',', '}', '}' ), 00230 preg_replace( array( '/\s+/', '/\/\*.*?\*\//s' ), array( ' ', '' ), $css ) 00231 ) 00232 ); 00233 } 00234 }