問題描述

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