1

我正在尝试从外部 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 服务器上创建了一些域,因此无法共享实时链接。

4

5 回答 5

2

我不相信您可以file_exists在 URL 上使用,这个答案可能会阐明您可以用来正确检查向您返回文件的 URL 的代码。

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

于 2013-07-08T17:46:37.190 回答
1

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

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

如果 CDN 失败,如何回退到本地样式表

于 2013-07-08T17:50:04.063 回答
1

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

确保常量(基本 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;
}
于 2013-07-08T17:50:56.583 回答
1

在我看来,这个过程是错误的——如果本地副本存在(并且不是陈旧的,需要定义的机制),您应该提供本地副本并回退到 CDN。这意味着你的行为就像一个缓存。

但是要直接回答这个问题,最好的办法是使用HEAD对 CDN 的请求来查看它是否存在。这样做很诱人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致命错误。

于 2013-07-08T18:00:36.843 回答
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'; 
    }
}

正如马克 B所建议的那样

文档中所述file_get_contents(),如果找不到指定的资源,将发出警告。

这是使用@错误抑制算子可能是合理的少数情况之一,例如

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

以防止警告破坏输出。

于 2013-07-10T16:24:35.227 回答