問題描述

作為我為客户加快主題工作的一部分,我通過一個動態的 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 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。