MediaWiki
REL1_22
|
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 00098 public static function encodeImageAsDataURI( $file, $type = null, $sizeLimit = self::EMBED_SIZE_LIMIT ) { 00099 if ( $sizeLimit !== false && filesize( $file ) >= $sizeLimit ) { 00100 return false; 00101 } 00102 if ( $type === null ) { 00103 $type = self::getMimeType( $file ); 00104 } 00105 if ( !$type ) { 00106 return false; 00107 } 00108 $data = base64_encode( file_get_contents( $file ) ); 00109 return 'data:' . $type . ';base64,' . $data; 00110 } 00111 00116 public static function getMimeType( $file ) { 00117 $realpath = realpath( $file ); 00118 // Try a couple of different ways to get the mime-type of a file, in order of 00119 // preference 00120 if ( 00121 $realpath 00122 && function_exists( 'finfo_file' ) 00123 && function_exists( 'finfo_open' ) 00124 && defined( 'FILEINFO_MIME_TYPE' ) 00125 ) { 00126 // As of PHP 5.3, this is how you get the mime-type of a file; it uses the Fileinfo 00127 // PECL extension 00128 return finfo_file( finfo_open( FILEINFO_MIME_TYPE ), $realpath ); 00129 } elseif ( function_exists( 'mime_content_type' ) ) { 00130 // Before this was deprecated in PHP 5.3, this was how you got the mime-type of a file 00131 return mime_content_type( $file ); 00132 } else { 00133 // Worst-case scenario has happened, use the file extension to infer the mime-type 00134 $ext = strtolower( pathinfo( $file, PATHINFO_EXTENSION ) ); 00135 if ( isset( self::$mimeTypes[$ext] ) ) { 00136 return self::$mimeTypes[$ext]; 00137 } 00138 } 00139 return false; 00140 } 00141 00152 public static function remap( $source, $local, $remote, $embedData = true ) { 00153 $pattern = '/((?P<embed>\s*\/\*\s*\@embed\s*\*\/)(?P<pre>[^\;\}]*))?' . 00154 self::URL_REGEX . '(?P<post>[^;]*)[\;]?/'; 00155 $offset = 0; 00156 while ( preg_match( $pattern, $source, $match, PREG_OFFSET_CAPTURE, $offset ) ) { 00157 // Skip fully-qualified URLs and data URIs 00158 $urlScheme = parse_url( $match['file'][0], PHP_URL_SCHEME ); 00159 if ( $urlScheme ) { 00160 // Move the offset to the end of the match, leaving it alone 00161 $offset = $match[0][1] + strlen( $match[0][0] ); 00162 continue; 00163 } 00164 // URLs with absolute paths like /w/index.php need to be expanded 00165 // to absolute URLs but otherwise left alone 00166 if ( $match['file'][0] !== '' && $match['file'][0][0] === '/' ) { 00167 // Replace the file path with an expanded (possibly protocol-relative) URL 00168 // ...but only if wfExpandUrl() is even available. 00169 // This will not be the case if we're running outside of MW 00170 $lengthIncrease = 0; 00171 if ( function_exists( 'wfExpandUrl' ) ) { 00172 $expanded = wfExpandUrl( $match['file'][0], PROTO_RELATIVE ); 00173 $origLength = strlen( $match['file'][0] ); 00174 $lengthIncrease = strlen( $expanded ) - $origLength; 00175 $source = substr_replace( $source, $expanded, 00176 $match['file'][1], $origLength 00177 ); 00178 } 00179 // Move the offset to the end of the match, leaving it alone 00180 $offset = $match[0][1] + strlen( $match[0][0] ) + $lengthIncrease; 00181 continue; 00182 } 00183 00184 // Guard against double slashes, because "some/remote/../foo.png" 00185 // resolves to "some/remote/foo.png" on (some?) clients (bug 27052). 00186 if ( substr( $remote, -1 ) == '/' ) { 00187 $remote = substr( $remote, 0, -1 ); 00188 } 00189 00190 // Shortcuts 00191 $embed = $match['embed'][0]; 00192 $pre = $match['pre'][0]; 00193 $post = $match['post'][0]; 00194 $query = $match['query'][0]; 00195 $url = "{$remote}/{$match['file'][0]}"; 00196 $file = "{$local}/{$match['file'][0]}"; 00197 00198 $replacement = false; 00199 00200 if ( $local !== false && file_exists( $file ) ) { 00201 // Add version parameter as a time-stamp in ISO 8601 format, 00202 // using Z for the timezone, meaning GMT 00203 $url .= '?' . gmdate( 'Y-m-d\TH:i:s\Z', round( filemtime( $file ), -2 ) ); 00204 // Embedding requires a bit of extra processing, so let's skip that if we can 00205 if ( $embedData && $embed && $match['embed'][1] > 0 ) { 00206 $data = self::encodeImageAsDataURI( $file ); 00207 if ( $data !== false ) { 00208 // Build 2 CSS properties; one which uses a base64 encoded data URI in place 00209 // of the @embed comment to try and retain line-number integrity, and the 00210 // other with a remapped an versioned URL and an Internet Explorer hack 00211 // making it ignored in all browsers that support data URIs 00212 $replacement = "{$pre}url({$data}){$post};{$pre}url({$url}){$post}!ie;"; 00213 } 00214 } 00215 if ( $replacement === false ) { 00216 // Assume that all paths are relative to $remote, and make them absolute 00217 $replacement = "{$embed}{$pre}url({$url}){$post};"; 00218 } 00219 } elseif ( $local === false ) { 00220 // Assume that all paths are relative to $remote, and make them absolute 00221 $replacement = "{$embed}{$pre}url({$url}{$query}){$post};"; 00222 } 00223 if ( $replacement !== false ) { 00224 // Perform replacement on the source 00225 $source = substr_replace( 00226 $source, $replacement, $match[0][1], strlen( $match[0][0] ) 00227 ); 00228 // Move the offset to the end of the replacement in the source 00229 $offset = $match[0][1] + strlen( $replacement ); 00230 continue; 00231 } 00232 // Move the offset to the end of the match, leaving it alone 00233 $offset = $match[0][1] + strlen( $match[0][0] ); 00234 } 00235 return $source; 00236 } 00237 00244 public static function minify( $css ) { 00245 return trim( 00246 str_replace( 00247 array( '; ', ': ', ' {', '{ ', ', ', '} ', ';}' ), 00248 array( ';', ':', '{', '{', ',', '}', '}' ), 00249 preg_replace( array( '/\s+/', '/\/\*.*?\*\//s' ), array( ' ', '' ), $css ) 00250 ) 00251 ); 00252 } 00253 }