从CDN动态获取css并回退到本地副本

时间:2013-07-08 17:43:59

标签: php css function external

我正在尝试从外部CDN服务中获取CSS,让我们说http://cdn.example.com/

此代码用于检查外部CDN上是否存在文件,如果存在,则获取该文件,然后从本地文件夹中获取该文件。

目前,这只是获取本地副本,即使该文件存在于外部CDN上。

请帮我更正此代码。

function getCSSfile($filename)
{
    $externalcss = EXTERNAL_CDN.'css/'.$filename.'.css';
    $localcss = LOCAL_CDN.'css/'.$filename.'.css';

    $ctx = stream_context_create(array(
        'http' => array(
            'method' => 'HEAD'
        )
    ));

    if (file_get_contents($externalcss, false, $ctx) !== false) {
        echo '<link href="' . $externalcss . '" rel="stylesheet" type="text/css" media="all"/>';
    } else if (file_exists($localcss)) {
        echo '<link href="' . $localcss . '" rel="stylesheet" type="text/css" media="all"/>';
    } else {
        echo 'Error loading CSS File'; 
    }
}

更新

根据@ DaveRandom的建议更新了代码。目前代码运行正常,它从外部CDN获取副本,但如果外部CDN不可用,它确实提取本地副本也会引发以下错误:

Warning: file_get_contents(http://cdn.localtest.com/css/core.css): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in D:\xampp\htdocs\localtest2\core\core.php on line 165

并且在第165行中这是代码

if (file_get_contents($externalcss, false, $ctx) !== false) {

我在本地测试并在我的XAMPP服务器上创建了一些域,因此无法共享实时链接。

5 个答案:

答案 0 :(得分:2)

我不相信您可以在网址上使用file_existsthis answer可能会对您可以使用的代码有所了解,以便正确检查向您返回文件的网址。

但总而言之,您要检查返回给您的状态代码,而不是使用file_exists

答案 1 :(得分:1)

检查文件是否可以通过PHP访问的问题是检查是从服务器完成的,而不是加载文件的客户端。仅仅因为您的服务器可以从CDN访问CSS文件并不意味着客户端可以。

如果您打算使用回退进行检查,则使用javascript在客户端进行检查会更安全。

How to fallback to local stylesheet if CDN fails

答案 2 :(得分:1)

另一个潜在的问题(除了@Johannes的回答)将是你的常数。在您将CDN网址结构化为http://cdn.example.com的问题中。将此附加到css/(与您的代码中一样)会生成http://cdn.example.comcss/,这不是有效的网址。

确保常量(基本URL)的结构为http://cdn.example.com/


编辑:要添加@Johannes的回答,我在SO上找到了另一个答案的链接:http://www.php.net/manual/en/function.file-exists.php#75064

将代码放在此处:

$file = 'http://www.domain.com/somefile.jpg';
$file_headers = @get_headers($file);
if($file_headers[0] == 'HTTP/1.1 404 Not Found') {
    $exists = false;
}
else {
    $exists = true;
}

答案 3 :(得分:1)

我看待这个问题的方法是,你的这个过程是错误的 - 你应该服务你的本地副本(如果它存在(并且不是陈旧的,需要定义的机制)并且回退到CDN。这意味着你的行为就像一个缓存。

但要直接回答这个问题,最好的办法是向CDN使用HEAD请求,看看它是否存在。使用get_headers()很有吸引力,但实际上这会导致GET方法请求,除非您更改默认流上下文 - 这会在您的应用程序中创建可能不需要的全局状态。

所以我首选的方法是创建本地化的流上下文,并将其与一个接受它作为参数的函数结合使用。为简单起见,在下面的示例中,我将使用file_get_contents()

function getCSSfile($filename)
{
    $externalcss = EXTERNAL_CDN.'css/'.$filename.'.css';
    $localcss = LOCAL_CDN.'css/'.$filename.'.css';

    $ctx = stream_context_create(array(
        'http' => array(
            'method' => 'HEAD'
        )
    ));

    if (file_get_contents($externalcss, false, $ctx) !== false) {
        echo '<link href="' . $externalcss . '" rel="stylesheet" type="text/css" media="all"/>';
    } else if (file_exists($localcss)) {
        echo '<link href="' . $localcss . '" rel="stylesheet" type="text/css" media="all"/>';
    } else {
        echo 'Error loading CSS File'; 
    }
}

现在,这在很长一段路上并不完美,但我从原始代码中回收了很多机制。还有全局状态(在这样的函数中使用用户定义的常量对我来说是一个很大的禁忌),你仍然有一个直接产生输出而不是将控制返回给调用者的函数。

我更愿意将$externalcss$localcss作为参数传递,并简单地返回所选的URL字符串,而不是将其格式化为HTML并回显它。这样,代码是隔离的,不依赖于任何外部代码,并且通过返回字符串,您可以允许调用者在必要时进一步操作数据。从本质上讲,它使代码无限可重复使用。

附注:建议不要在功能中定义功能。这是因为第二次调用会导致Cannot redeclare function致命错误。

答案 4 :(得分:0)

好的,因为任何人都没有解释错误解决方案......我决定回答我自己的问题。这是我找到的解决方案:

function getCSSfile($filename)
{
    $externalcss = EXTERNAL_CDN.'css/'.$filename.'.css';
    $localcss = LOCAL_CDN.'css/'.$filename.'.css';

    $ctx = stream_context_create(array(
        'http' => array(
            'method' => 'HEAD'
        )
    ));

    if (@file_get_contents($externalcss, false, $ctx) !== false) {
        echo '<link href="' . $externalcss . '" rel="stylesheet" type="text/css" media="all"/>';
    } else if (file_exists($localcss)) {
        echo '<link href="' . $localcss . '" rel="stylesheet" type="text/css" media="all"/>';
    } else {
        echo 'Error loading CSS File'; 
    }
}

根据Mark B

的建议

正如the docs中所述,如果找不到所述资源,file_get_contents()会发出警告。

这是少数使用@错误抑制运算符的情况之一,例如

if (@file_get_contents($externalcss, false, $ctx) !== false)

防止警告破坏输出。