35 const PLACEHOLDER =
"\x7fPLACEHOLDER\x7f";
41 const URL_REGEX =
'url\(\s*[\'"]?(?P<file>[^\?\)\'"]*?)(?P<query>\?[^\)\'"]*?|)[\'"]?\s*\)';
50 'jpe' =>
'image/jpeg',
51 'jpeg' =>
'image/jpeg',
52 'jpg' =>
'image/jpeg',
54 'tif' =>
'image/tiff',
55 'tiff' =>
'image/tiff',
56 'xbm' =>
'image/x-xbitmap',
57 'svg' =>
'image/svg+xml',
70 $stripped = preg_replace(
'/' . self::COMMENT_REGEX .
'/s',
'',
$source );
74 $rFlags = PREG_OFFSET_CAPTURE | PREG_SET_ORDER;
75 if ( preg_match_all(
'/' . self::URL_REGEX .
'/', $stripped,
$matches, $rFlags ) ) {
77 $url = $match[
'file'][0];
80 if ( substr( $url, 0, 2 ) ===
'//' || parse_url( $url, PHP_URL_SCHEME ) ) {
84 $files[] =
$path . $url;
107 if ( $ie8Compat && filesize( $file ) >= self::DATA_URI_SIZE_LIMIT ) {
111 if (
$type ===
null ) {
136 if ( preg_match(
'/^[\r\n\t\x20-\x7e]+$/', $contents ) ) {
139 $uri =
'data:' .
$type .
',' . rawurlencode( $contents );
140 if ( !$ie8Compat || strlen( $uri ) < self::DATA_URI_SIZE_LIMIT ) {
146 $uri =
'data:' .
$type .
';base64,' . base64_encode( $contents );
147 if ( !$ie8Compat || strlen( $uri ) < self::DATA_URI_SIZE_LIMIT ) {
164 if ( strstr(
$value,
"\0" ) ) {
165 throw new Exception(
"Invalid character in CSS string" );
167 $value = strtr(
$value, [
'\\' =>
'\\\\',
'"' =>
'\\"' ] );
168 $value = preg_replace_callback(
'/[\x01-\x1f\x7f-\x9f]/',
function ( $match ) {
169 return '\\' . base_convert( ord( $match[0] ), 10, 16 ) .
' ';
171 return '"' .
$value .
'"';
180 $ext = strtolower( pathinfo( $file, PATHINFO_EXTENSION ) );
181 if ( isset( self::$mimeTypes[
$ext] ) ) {
182 return self::$mimeTypes[
$ext];
185 $realpath = realpath( $file );
188 && function_exists(
'finfo_file' )
189 && function_exists(
'finfo_open' )
190 && defined(
'FILEINFO_MIME_TYPE' )
192 return finfo_file( finfo_open( FILEINFO_MIME_TYPE ), $realpath );
211 if ( preg_match(
'!^[\w\d:@/~.%+;,?&=-]+$!', $url ) ) {
214 return 'url("' . strtr( $url, [
'\\' =>
'\\\\',
'"' =>
'\\"' ] ) .
'")';
229 public static function remap(
$source, $local, $remote, $embedData =
true ) {
241 if ( substr( $remote, -1 ) ==
'/' ) {
242 $remote = substr( $remote, 0, -1 );
256 $source = preg_replace_callback(
258 function ( $match )
use ( &$comments ) {
259 $comments[] = $match[ 0 ];
260 return CSSMin::PLACEHOLDER . (
count( $comments ) - 1 ) .
'x';
271 $source = preg_replace_callback(
273 function ( $matchOuter )
use ( $local, $remote, $embedData ) {
274 $rule = $matchOuter[0];
279 $rule = preg_replace(
281 CSSMin::PLACEHOLDER .
295 $ruleWithRemapped = preg_replace_callback(
297 function ( $match )
use ( $local, $remote ) {
298 $remapped =
CSSMin::remapOne( $match[
'file'], $match[
'query'], $local, $remote,
false );
309 $ruleWithEmbedded = preg_replace_callback(
311 function ( $match )
use ( $embedAll, $local, $remote, &
$mimeTypes ) {
312 $embed = $embedAll || $match[
'embed'];
321 $url = $match[
'file'] . $match[
'query'];
322 $file =
"{$local}/{$match['file']}";
324 !self::isRemoteUrl( $url ) && !self::isLocalUrl( $url )
325 && file_exists( $file )
339 if ( !$embedData || $ruleWithEmbedded === $ruleWithRemapped ) {
341 return $ruleWithRemapped;
342 } elseif ( $embedData && $needsEmbedFallback ) {
346 return "$ruleWithEmbedded;$ruleWithRemapped!ie";
349 return $ruleWithEmbedded;
354 $pattern =
'/' . CSSMin::PLACEHOLDER .
'(\d+)x/';
355 $source = preg_replace_callback( $pattern,
function( $match )
use ( &$comments ) {
356 return $comments[ $match[1] ];
369 if ( substr( $maybeUrl, 0, 2 ) ===
'//' || parse_url( $maybeUrl, PHP_URL_SCHEME ) ) {
382 if ( $maybeUrl !==
'' && $maybeUrl[0] ===
'/' && !self::isRemoteUrl( $maybeUrl ) ) {
404 if ( self::isLocalUrl( $url ) && function_exists(
'wfExpandUrl' ) ) {
410 if ( self::isRemoteUrl( $url ) || self::isLocalUrl( $url ) ) {
414 if ( $local ===
false ) {
416 $url = $remote .
'/' . $url;
419 $url =
"{$remote}/{$file}";
421 $localFile =
"{$local}/{$file}";
422 if ( file_exists( $localFile ) ) {
425 if ( $data !==
false ) {
429 if ( method_exists(
'OutputPage',
'transformFilePath' ) ) {
434 $url .=
'?' . substr( md5_file( $localFile ), 0, 5 );
440 if ( function_exists(
'wfRemoveDotSegments' ) ) {
455 [
'; ',
': ',
' {',
'{ ',
', ',
'} ',
';}' ],
456 [
';',
':',
'{',
'{',
',',
'}',
'}' ],
457 preg_replace( [
'/\s+/',
'/\/\*.*?\*\//s' ], [
' ',
'' ],
$css )