我正在尝试从外部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服务器上创建了一些域,因此无法共享实时链接。
答案 0 :(得分:2)
我不相信您可以在网址上使用file_exists
,this answer可能会对您可以使用的代码有所了解,以便正确检查向您返回文件的网址。
但总而言之,您要检查返回给您的状态代码,而不是使用file_exists
。
答案 1 :(得分:1)
检查文件是否可以通过PHP访问的问题是检查是从服务器完成的,而不是加载文件的客户端。仅仅因为您的服务器可以从CDN访问CSS文件并不意味着客户端可以。
如果您打算使用回退进行检查,则使用javascript在客户端进行检查会更安全。
答案 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)
防止警告破坏输出。