X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Flibs%2FCSSMin.php;h=cd80066558ed09b3efdf2f70716895515fe92788;hb=1b0c9f6098c31d6bf16a00d37a8aa5cd493270e1;hp=b8d9c1d184a7c304d85ae659fbe817e73d15500d;hpb=032f0ce8cb513dd07ad77156a4630a9712654214;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/libs/CSSMin.php b/includes/libs/CSSMin.php index b8d9c1d184..cd80066558 100644 --- a/includes/libs/CSSMin.php +++ b/includes/libs/CSSMin.php @@ -38,7 +38,7 @@ class CSSMin { * Internet Explorer data URI length limit. See encodeImageAsDataURI(). */ const DATA_URI_SIZE_LIMIT = 32768; - const URL_REGEX = 'url\(\s*[\'"]?(?P[^\?\)\'"]*?)(?P\?[^\)\'"]*?|)[\'"]?\s*\)'; + const EMBED_REGEX = '\/\*\s*\@embed\s*\*\/'; const COMMENT_REGEX = '\/\*.*?\*\/'; @@ -72,8 +72,9 @@ class CSSMin { $files = []; $rFlags = PREG_OFFSET_CAPTURE | PREG_SET_ORDER; - if ( preg_match_all( '/' . self::URL_REGEX . '/', $stripped, $matches, $rFlags ) ) { + if ( preg_match_all( '/' . self::getUrlRegex() . '/', $stripped, $matches, $rFlags ) ) { foreach ( $matches as $match ) { + self::processUrlMatch( $match, $rFlags ); $url = $match['file'][0]; // Skip fully-qualified and protocol-relative URLs and data URIs @@ -187,17 +188,7 @@ class CSSMin { return self::$mimeTypes[$ext]; } - $realpath = realpath( $file ); - if ( - $realpath - && function_exists( 'finfo_file' ) - && function_exists( 'finfo_open' ) - && defined( 'FILEINFO_MIME_TYPE' ) - ) { - return finfo_file( finfo_open( FILEINFO_MIME_TYPE ), $realpath ); - } - - return false; + return mime_content_type( realpath( $file ) ); } /** @@ -256,7 +247,7 @@ class CSSMin { // quotation marks (e.g. "foo /* bar"). $comments = []; - $pattern = '/(?!' . CSSMin::EMBED_REGEX . ')(' . CSSMin::COMMENT_REGEX . ')/s'; + $pattern = '/(?!' . self::EMBED_REGEX . ')(' . self::COMMENT_REGEX . ')/s'; $source = preg_replace_callback( $pattern, @@ -271,7 +262,7 @@ class CSSMin { // appears in the rule itself, e.g. in a quoted string. You are advised // not to use such characters in file names. We also match start/end of // the string to be consistent in edge-cases ('@import url(…)'). - $pattern = '/(?:^|[;{])\K[^;{}]*' . CSSMin::URL_REGEX . '[^;}]*(?=[;}]|$)/'; + $pattern = '/(?:^|[;{])\K[^;{}]*' . self::getUrlRegex() . '[^;}]*(?=[;}]|$)/'; $source = preg_replace_callback( $pattern, @@ -295,13 +286,14 @@ class CSSMin { // Build two versions of current rule: with remapped URLs // and with embedded data: URIs (where possible). - $pattern = '/(?P' . CSSMin::EMBED_REGEX . '\s*|)' . CSSMin::URL_REGEX . '/'; + $pattern = '/(?P' . CSSMin::EMBED_REGEX . '\s*|)' . self::getUrlRegex() . '/'; $ruleWithRemapped = preg_replace_callback( $pattern, function ( $match ) use ( $local, $remote ) { - $remapped = CSSMin::remapOne( $match['file'], $match['query'], $local, $remote, false ); + self::processUrlMatch( $match ); + $remapped = CSSMin::remapOne( $match['file'], $match['query'], $local, $remote, false ); return CSSMin::buildUrlValue( $remapped ); }, $rule @@ -314,6 +306,8 @@ class CSSMin { $ruleWithEmbedded = preg_replace_callback( $pattern, function ( $match ) use ( $embedAll, $local, $remote, &$mimeTypes ) { + self::processUrlMatch( $match ); + $embed = $embedAll || $match['embed']; $embedded = CSSMin::remapOne( $match['file'], @@ -356,8 +350,8 @@ class CSSMin { }, $source ); // Re-insert comments - $pattern = '/' . CSSMin::PLACEHOLDER . '(\d+)x/'; - $source = preg_replace_callback( $pattern, function( $match ) use ( &$comments ) { + $pattern = '/' . self::PLACEHOLDER . '(\d+)x/'; + $source = preg_replace_callback( $pattern, function ( $match ) use ( &$comments ) { return $comments[ $match[1] ]; }, $source ); @@ -390,6 +384,72 @@ class CSSMin { return false; } + /** + * @codeCoverageIgnore + */ + private static function getUrlRegex() { + static $urlRegex; + if ( $urlRegex === null ) { + // Match these three variants separately to avoid broken urls when + // e.g. a double quoted url contains a parenthesis, or when a + // single quoted url contains a double quote, etc. + // Note: PCRE doesn't support multiple capture groups with the same name by default. + // - PCRE 6.7 introduced the "J" modifier (PCRE_INFO_JCHANGED for PCRE_DUPNAMES). + // https://secure.php.net/manual/en/reference.pcre.pattern.modifiers.php + // However this isn't useful since it just ignores all but the first one. + // Also, while the modifier was introduced in PCRE 6.7 (PHP 5.2+) it was + // not exposed to public preg_* functions until PHP 5.6.0. + // - PCRE 8.36 fixed this to work as expected (e.g. merge conceptually to + // only return the one matched in the part that actually matched). + // However MediaWiki supports 5.5.9, which has PCRE 8.32 + // Per https://secure.php.net/manual/en/pcre.installation.php: + // - PCRE 8.32 (PHP 5.5.0) + // - PCRE 8.34 (PHP 5.5.10, PHP 5.6.0) + // - PCRE 8.37 (PHP 5.5.26, PHP 5.6.9, PHP 7.0.0) + // Workaround by using different groups and merge via processUrlMatch(). + // - Using string concatenation for class constant or member assignments + // is only supported in PHP 5.6. Use a getter method for now. + $urlRegex = '(' . + // Unquoted url + 'url\(\s*(?P[^\'"][^\?\)]*?)(?P\?[^\)]*?|)\s*\)' . + // Single quoted url + '|url\(\s*\'(?P[^\?\']*?)(?P\?[^\']*?|)\'\s*\)' . + // Double quoted url + '|url\(\s*"(?P[^\?"]*?)(?P\?[^"]*?|)"\s*\)' . + ')'; + } + return $urlRegex; + } + + private static function processUrlMatch( array &$match, $flags = 0 ) { + if ( $flags & PREG_SET_ORDER ) { + // preg_match_all with PREG_SET_ORDER will return each group in each + // match array, and if it didn't match, instead of the sub array + // being an empty array it is `[ '', -1 ]`... + if ( isset( $match['file0'] ) && $match['file0'][1] !== -1 ) { + $match['file'] = $match['file0']; + $match['query'] = $match['query0']; + } elseif ( isset( $match['file1'] ) && $match['file1'][1] !== -1 ) { + $match['file'] = $match['file1']; + $match['query'] = $match['query1']; + } else { + $match['file'] = $match['file2']; + $match['query'] = $match['query2']; + } + } else { + if ( isset( $match['file0'] ) && $match['file0'] !== '' ) { + $match['file'] = $match['file0']; + $match['query'] = $match['query0']; + } elseif ( isset( $match['file1'] ) && $match['file1'] !== '' ) { + $match['file'] = $match['file1']; + $match['query'] = $match['query1']; + } else { + $match['file'] = $match['file2']; + $match['query'] = $match['query2']; + } + } + } + /** * Remap or embed a CSS URL path. *