问题描述

作为我为客户加快主题工作的一部分,我通过一个动态的 PHP 文件传递我的 CSS 。在调用结束时,例如 my_theme_css.php

这样我可以将 Expiry 标头添加到我的 CSS 和 JavaScript 中,如下所示:

<?php
header("Expires: Thu, 31 Dec 2020 20:00:00 GMT");
header('Content-type: text/css');
?>

然后我想允许用户添加自己的自定义 CSS,所以我已经设置了以下内容:

<?php
header("Expires: Thu, 31 Dec 2020 20:00:00 GMT");
header('Content-type: text/css');
?>

p{color:blue;}

/** Custom CSS **/
<?php
if(get_theme_mod('my_custom_css') != '') {
  $my_custom_css = get_theme_mod('my_custom_css');
  echo $my_custom_css;
}
?>

然后我添加了适用的主题选项。但是我发现 get_theme_mod()由于范围问题而无法工作 (我想象) 。有关如何解决这个问题的任何建议?

我可以添加以下代码:

<?php
if(get_theme_mod('my_custom_css') != '') {
  $my_custom_css = get_theme_mod('my_custom_css');
  echo $my_custom_css;
}
?>

在标题中的<style></style> 标签中,可以正常工作,期望我不希望 CSS 内联,我想要在主要的 CSS 文件与过期标题。

最佳解决方案

Hi @Ash G:

我没有遵循 100%的特定问题,所以我不知道我真的可以回答你的问题 point-by-point,但我可以解释如何从头开始。除非你正在做的是更多的参与,那么你提到它比我想象的要多一些工作,但仍然是完全可行的。即使我覆盖了很多地方,你已经知道,有一个很好的机会,其他人的知识或经验较少,可以通过 Google 找到它,也可以通过它来帮助。

Bootstrap WordPress 与/wp-load.php

我们在 my_theme_css.php 文件中需要做的第一件事是引导 WordPress 的核心库功能。以下代码行加载/wp-load.php 。它使用 $_SERVER['DOCUMENT_ROOT']来定位网站的根目录,所以您不必担心在服务器上存储此文件的位置; 假设 DOCUMENT_ROOT 设置正确,因为它始终应该是 WordPress,那么这将引导 WordPress:

<?php
include_once("{$_SERVER['DOCUMENT_ROOT']}/wp-load.php");

所以这很容易。接下来是棘手的部分…

PHP 脚本必须处理所有缓存逻辑

这里我敢打赌,你可能会偶然发现,因为我确实是在试图找出如何回答你的问题。我们非常习惯于 Apache Web 服务器处理的缓存细节,我们忘记了甚至没有意识到,当我们使用 PHP 加载 CSS 或 JS 时,我们必须做好所有的重大工作。

当我们在中间有一个代理服务器时,我们需要的到期标题可能就是我们需要的,但是如果请求使它到 Web 服务器,而 PHP 脚本只是 willy-nilly 返回内容,”Ok” 状态代码本质上就没有缓存。

返回 “200 Ok” 或 “304 未修改”

更具体地说,返回 CSS 的 PHP 文件需要正确地响应浏览器发送的请求头。我们的 PHP 脚本需要根据这些头包含的内容返回正确的状态码。如果内容需要被提供,因为它是第一次请求,或因为内容已经过期,PHP 脚本应该生成所有的 CSS 内容并返回 “200 Ok” 。

另一方面,如果我们根据 cache-related 请求头确定客户端浏览器已经具有最新的 CSS,那么我们不应该返回任何 CSS,而是返回 “304 Not Modified” 。这个代码太分别 (当然你永远不会一个接一个地使用它们,我只是为了方便而在这里显示出来):

<?php
header('HTTP/1.1 200 Ok');
header('HTTP/1.1 304 Not Modified');

HTTP 缓存的四种风味

接下来,我们需要看看 HTTP 可以处理缓存的不同方式。第一个是你提到的; Expires

  • Expires:这个 Expires 标题提供了一个'D, d M Y H:i:s'(gmdate()功能) 格式的日期,其格式为'D, d M Y H:i:s',附加了一个' GMT'(GMT 代表格林威治标准时间) 。理论上如果该标题被服务,浏览器和下游代理将缓存直到指定的时间过去它将开始再次请求页面。这可能是最着名的缓存头,但显然不是首选使用; Cache-Control being the better one 。有趣的是,我在 Mac OS X 上使用 Safari 5.0 对 localhost 进行了测试,我无法让浏览器遵守 Expires 标题; 它总是再次请求该文件 (如果有人可以解释这一点,我将不胜感激。) 这是上面给出的例子:

header("Expires: Thu, 31 Dec 2020 20:00:00 GMT");

  • Cache-ControlCache-Control 标题比 Expires 标题更容易使用,因为您只需要指定 max-age 的时间数 (以秒为单位),这意味着您不需要提供字符串形式的确切日期格式,这很容易得到错误。另外 Cache-Control 允许其他几个选项,例如当您想强制缓存正常不可缓存的请求 (即通过 HTTPS 的请求),甚至不缓存时,能够告知客户端始终使用 mustrevalidate 选项和 public 重新验证缓存这就是你需要的 (即你可能想要强制 a 1×1 pixel ad tracking .GIF 不被缓存。) 像 Expires 我也无法让这个工作在测试 (任何帮助?) 以下示例缓存 24 小时 (60 秒由 60 分钟 24 小时):

header("Cache-Control: max-age=".60*60*24.", public, must-revalidate");

  • Last-Modified /If-Modified-Since:然后有 Last-Modified 响应头和 IfModified-Since 请求头对。这些也使用与 Expires 头使用相同的 GMT 日期格式,但它们在客户端和服务器之间进行握手。 PHP 脚本需要发送一个 Last-Modified 标头 (顺便说一下,只有当用户上次更新自定义 CSS 时,应该更新),之后浏览器将继续发送相同的值作为 If-Modified-Since 标头,它是 PHP 脚本的将保存的值与浏览器发送的值进行比较的责任。这里是 PHP 脚本在服务 200 Ok304 Not Modified 之间作出决定的地方。以下是使用当前时间 (这不是我们想要做的) 为 Last-Modified 标题提供服务的示例; 请参阅稍后我们实际需要的示例):

header("Last-Modified: " . gmdate('D, d M Y H:i:s', time()).'GMT');

这里是如何读取浏览器通过 If-Modified-Since 标头返回的 Last-Modified

$last_modified_to_compare = $_SERVER['HTTP_IF_MODIFIED_SINCE'];

  • ETag /If-None-Match:最后还有 ETag 响应头和 If-None-Match 请求头对。 ETag 真的只是一个令牌,我们的 PHP 将其设置为唯一的值 (通常基于当前日期),并发送到浏览器,浏览器返回。它的当前值不同于浏览器返回您的 PHP 脚本应该重新生成服务器 200 Ok 的内容,否则不产生任何内容并提供 304 Not Modified 。以下是使用当前时间设置 ETag 的示例:

header("ETag: " . md5(gmdate('D, d M Y H:i:s', time()).'GMT'));

这里是如何读取浏览器通过 If-None-Match 标头返回的 ETag

$etag_to_match = $_SERVER['HTTP_IF_NONE_MATCH'];

现在我们已经涵盖了所有让我们看看我们需要的实际代码:

通过 initwp_enqueue_style()提供 CSS 文件

你没有显示这个,但我想我会为了别人的利益而展示。这是一个函数调用,它告诉 WordPress 使用 my_theme_css.php 作为它的 CSS 。这可以存储在主题的 functions.php 文件中,甚至可以存储在插件中:

<?php
add_action('init','add_php_powered_css');
function add_php_powered_css() {
  if (!is_admin()) {
    $version = get_theme_mod('my_custom_css_version',"1.00");
    $ss_dir = get_stylesheet_directory_uri();
    wp_enqueue_style('php-powered-css',
        "{$ss_dir}/my_theme_css.php",array(),$version);
  }
}

有几点值得注意:

  • 使用 is_admin()避免在管理员加载 CSS(除非你想要…),

  • get_theme_mod()使用 1.00 的默认版本加载 CSS(稍微多一点),

  • 使用 get_stylesheet_directory_uri()来获取当前主题的正确目录,即使当前的主题是一个子主题,

  • wp_enqueue_style()使用 wp_enqueue_style()来排队 CSS 以允许 WordPress 在适当的时候将它加载到'php-powered-css'作为任意名称,以便稍后引用 (如果需要),而空的 array()意味着此 CSS 没有依赖关系 (尽管在现实世界中经常会有一个或多个),和

  • 使用 $version; 可能是最重要的一个,我们正在告诉 wp_enqueue_style()?ver=1.00 参数添加到/my_theme_css.php URL,以便如果版本更改,浏览器将会将其视为完全不同的 URL(稍微多一点) 。

用户更新 CSS 时设置 $versionLast-Modified

所以这里的技巧。每次用户更新他们的 CSS,您想要提供的内容,而不是等到 2020 为大家的浏览器缓存超时,对不对?这是一个与我的其他代码相结合的功能。每次您存储用户更新的 CSS 时,请使用与以下内容相似的功能或功能:

<?php
function set_my_custom_css($custom_css) {
  $new_version = round(get_theme_mod('my_custom_css_version','1.00',2))+0.01;
  set_theme_mod('my_custom_css_version',$new_version);
  set_theme_mod('my_custom_css_last_modified',gmdate('D, d M Y H:i:s',time()).' GMT');
  set_theme_mod('my_custom_css',$custom_css);
}

set_my_custom_css()功能自动将当前版本增加 0.01(这仅是我选择的任意增量值),并且还将最后修改的日期设置为右,最后存储新的自定义 CSS 。要调用此功能,可能就像这样简单 (new_custom_css 可能会通过用户提交的 $_POST 而不是通过硬编码分配,如您所见)

<?php
$new_custom_css = 'body {background-color:orange;}';
set_my_custom_css($new_custom_css);

这使我们走上了最后一个重要的一步:

从 PHP 脚本生成 CSS

最后我们看到肉,实际的 my_theme_css.php 文件。在高水平上,它测试了 If-Modifed-Since 与保存的 Last-Modified 值和 If-None-Match 相对于从保存的 Last-Modified 值派生的 ETag,如果两者都没有更改,则将标题设置为 304 Not Modifed 并分支到最后。

然而,如果任何一个已经更改,它将生成 ExpiresCache-ControlLast-ModifiedEtag 标头以及 200 Ok,表明内容类型为 text/css 。我们可能不需要所有这些,但是如果使用不同的浏览器和代理,可以使用非常好的缓存,我认为它不会覆盖所有的基础。 (和任何人有更多的经验 HTTP 缓存和 WordPress 请做钟声,如果我有任何细微差错。)

以下代码中还有一些细节,但我认为您可以自己使用它们:

<?php

  $s = $_SERVER;

  include_once("{$s['DOCUMENT_ROOT']}/wp-load.php");

  $max_age = 60*60*24; // 24 hours
  $now = gmdate('D, d M Y H:i:s', time()).'GMT';
  $last_modified = get_theme_mod('my_custom_css_last_modified',$now);
  $etag = md5($last_modified);

  if (strtotime($s['HTTP_IF_MODIFIED_SINCE']) >= strtotime($last_modified) || $s['HTTP_IF_NONE_MATCH']==$etag) {
    header('HTTP/1.1 304 Not Modified');
  } else {
    header('HTTP/1.1 200 Ok');
    header("Expires: " . gmdate('D, d M Y H:i:s', time()+$max_age.'GMT'));
    header("Cache-Control: max-age={$mag_age}, public, must-revalidate");
    header("Last-Modified: {$last_modified}");
    header("ETag: {$etag}");
    header('Content-type: text/css');
    echo_default_css();
    echo_custom_css();
  }
  exit;

function echo_custom_css() {
  $custom_css = get_theme_mod('my_custom_css');
  if (!empty($custom_css))
    echo "n{$custom_css}";
}

function echo_default_css() {
  $default_css =<<<CSS
body {background-color:yellow;}
CSS;
  echo $default_css;
}

所以用这三大代码代码; 1.)init 函数调用的 add_php_powered_css()函数,2.) 任何代码调用的 set_my_custom_css()函数允许用户更新其自定义 CSS,最后是 3.)my_theme_css.php 你应该几乎都有这个舔。

进一步阅读

除了已经链接的那些,我遇到了一些其他的文章,我认为这是非常有用的主题,所以我想我应该链接到这里:

结语:

但是我不会作出闭幕评论而离开这个话题。

过期于 2020?可能太极了

首先,我真的不认为你想将 Expires 设置到 2020 年。任何尊重 Expires 的浏览器或代理,即使您做了很多 CSS 更改,也不会 re-request 。更好地设置合理的 24 小时 (就像我在代码中所做的那样),但即使这样会使用户在硬编码的 CSS 中进行更改,但忘记了服务的版本号。万事皆宜?

这一切都可能过度杀伤!

当我正在阅读各种文章来帮助我回答你的问题,我遇到了以下 Mr. Cache Tutorial 自己,Mark Nottingham

The best way to make a script cache-friendly (as well as perform better) is to dump its content to a plain file whenever it changes. The Web server can then treat it like any other Web page, generating and using validators, which makes your life easier. Remember to only write files that have changed, so the Last-Modified times are preserved.

虽然所有这些代码我写的很酷,很有意思 (是的,我实际上承认了),maybe it’s better just to generate a static CSS file every time the user updates their custom CSS 代替,让 Apache 做出所有的重担,就像出生在做吗?我只是说

希望这可以帮助!

参考文献

注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。