preg_replace() 正则表达式匹配 CSS 文件中相对路径的 url()。

7

我正在将一些CSS文件合并,并将它们写入一个单独的目录中的文件。我试图替换相对url()值,使其适用于新的文件位置,忽略任何绝对URL。以下是一些示例CSS:

#TEST {
    background:url(test.jpg);
    background:url( 'test.jpg' );
    background:url("test.jpg"  );
    background:url(http://example.com/test.jpg);
    background:url('https://example.com/test.jpg');
    background:url("http://example.com/test.jpg");
    background:url( '//example.com/test.jpg' );
    background:url( "//example.com/test.jpg" );
    background:url(//example.com/test.jpg);
}

任何不以 http://https://// 开头的内容都应该在其前面注入 $path(只匹配前三个)。

期望输出:

#TEST {
    background:url(/themes/default/css/test.jpg);
    background:url( '/themes/default/css/test.jpg' );
    background:url("/themes/default/css/test.jpg"  );
    background:url(http://example.com/test.jpg);
    background:url('https://example.com/test.jpg');
    background:url("http://example.com/test.jpg");
    background:url( '//example.com/test.jpg' );
    background:url( "//example.com/test.jpg" );
    background:url(//example.com/test.jpg);
}

然而,这段代码却相反地匹配了结果:
$path = '/themes/default/css/';
$search = '#url\(\s*([\'"]?)((http(s)?:)?//)#';
$replace = "url($1{$path}$2";
$css = preg_replace($search, $replace, $css);

我知道你可以使用类似于!^(http)的内容来不匹配以http开头的字符串,但是我尝试过的所有方法都失败了(我很菜,不擅长正则表达式)。我一直在使用在线正则表达式测试工具来解决这个问题,但是我真的卡住了。
这可能不是我用来解决编译CSS中路径问题的方法,但是有人能帮我解决这个正则表达式问题吗?或者有更好的解决方案吗?
4个回答

11

你已经接近成功了 - 你所需要的是来实现"不匹配以...开头的字符串",这叫做"负向先行断言",看起来像是(?!http)

所以对于url\(后面不跟着\s*['"]?(https?:)?//,你可以使用:

url\((?!\s*['"]?(http(s)?:)?//)

(我在那里保留了捕获组(s),以防您想要捕获它,但是您不需要将括号放在(s)周围;如果您不关心捕获,则s?(s)?相同。)

在这里看到它的实际效果here

编辑:

由于您想要在引号标记之后(如果有)放置$path,因此我修改了正则表达式为:

url\((?!\s*['"]?(?:https?:)?//)\s*(['"])?

即删除我不关心的所有捕获括号,并添加了一个\s*(['"])?以捕获引号的类型。

我认为它在codepad 这里,但为了确保起见,这是代码:

$path = '/themes/default/css/';
$search = '#url\((?!\s*[\'"]?(?:https?:)?//)\s*([\'"])?#';
$replace = "url($1{$path}";

我不得不稍微修改正则表达式,只是在末尾添加了\s*(['"])?以捕获引号(如果有的话),以便将$path放在引号内。 - mathematical.coffee
抱歉,该链接似乎已经过期。我喜欢这个 Codepad 工具,但好像用它还不太熟练!我会将代码放在我的答案里。 - mathematical.coffee
好的,已更新答案并附上了代码和(希望)一个新的可工作的 codepad 链接(那是个很酷的网站!)。 - mathematical.coffee
更新了你的链接(它没有被保存),非常感谢,现在可以工作了!我仍然不明白?!中的?是什么意思,或者第一个?:(?:https?:)中是用来干什么的。 - Wesley Murch
一个 (?:正则表达式) 就像 (正则表达式) 一样,只是括号不会被保存。它被称为非捕获括号。如果我使用 (https?),那么 $1 中将包含 httphttps,而 $2 中将包含引号。通过使用 (?:https?),括号中的任何内容都不会被保存,而 $1 中将包含引号。 - mathematical.coffee
显示剩余3条评论

4
这个pregmatch和被选中的答案一样好,但也支持原始数据。
'#url\((?!\s*([\'"]?(((?:https?:)?//)|(?:data\:?:))))\s*([\'"])?#'

这是长正则表达式的构建块。
$absoluteUrl = '((?:https?:)?//)';
$rawData = '(?:data\:?:)';
$relativeUrl = '\s*([\'"]?((' . $absoluteUrl . ')|(' . $rawData . ')))';
$search = '#url\((?!' . $relativeUrl . ')\s*([\'"])?#';
$replace = "url($6{$path}";
echo preg_replace($search, $replace, $css);

Full example

http://codepad.org/NDoya4NC


你能否扩展一下代码,以便修复 "../images/image.jpg" 或者 "../../images/image.jpg" 的路径? - Benn
它正在按预期与../..一起工作。我刚刚更新了一个示例http://codepad.org/NDoya4NC - Mangirdas Skripka

1

我使用以下模式使其工作(基于其他答案):

url\s*\(\s*[\'"]?(?!(((?:https?:)?\/\/)|(?:data\:?:)))([^\'"\)]+)[\'"]?\s*\)

让它运行的PHP代码是:

$path = '/themes/default/css/';
$search = '%url\s*\(\s*[\\\'"]?(?!(((?:https?:)?\/\/)|(?:data:?:)))([^\\\'")]+)[\\\'"]?\s*\)%';
$replace = 'url("' . $path  . '/$3")';
$css = preg_replace($search, $replace, $css);
  • 允许在括号周围添加空格
  • 支持 http、https、data、"//" 的负向先行断言
  • 在替换的资源周围插入引号

测试可在此处进行:https://regex101.com/r/zT2gM9/1


0

搞定了:

$sBaseUrl = 'http://example.com/';
$sCss = preg_replace_callback(
    '|url\s*\(\s*[\'"]?([^\'"\)]+)[\'"]\s*\)|',
    function($aMatches) use ($sBaseUrl) {
        return 'url("'. $sBaseUrl . trim($aMatches[1]). '")';
    },
    $sCss
);
print $sCss;

它在闭引号后面加上一个问号的情况下起作用 - url\s*\(\s*[\'"]?([^\'"\)]+)[\'"]?\s*\) - cornernote

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接