經過在 Day 27 介紹 WordPress 的 Hook,之後,想必對於 WordPres 的事件處理機制有了進一步的認識,接下來就是利用 Hook 來變魔術的時間囉。非常簡單的程式碼,輕鬆完成 WordPress 的快取外掛,真心不騙!
這次的進度主要實作以下邏輯:
完成以上三個項目,讓外掛可以正常運作。
今日的檔案結構如下:
.
├── LICENSE
├── README.md
├── README.txt
├── cache-master.php
├── composer.json
├── inc
│ ├── admin
│ │ ├── menu.php
│ │ ├── register.php
│ │ ├── setting-update.php
│ │ └── setting.php
│ ├── autoload.php
│ ├── class-cache-master.php
│ └── helpers.php
├── languages
├── phpcs.xml
└── vendor
對比 Day 27 的檔案結構多了以下檔案:
而在 Day 27 時檔案還是空的「class-cache-master.php」已經寫好主功能。也是今天變出快取功能的主角!
以下是新增的檔案結構說明:
檔案路徑 | 說明 |
---|---|
inc/admin/setting-update.php | 在設定頁面時按下送出按紐,觸發的事件處理。 |
inc/helpers.php | 函式集,包含檢查 Driver、一個產出 SimpleCache 物件的簡單工廠。 |
範例:/day-29/cache-master/cache-master.php
(圖 A:cache-master.php 程式碼截圖)
和 Day 27 比較起來,多了以下的改變:
第 7 行:載入需要用的函式集。
第 10 行:在 WordPress 管理介面的外掛列表中,啟用外掛後,會觸發的 Hook。
第 11 行:在 WordPress 管理介面的外掛列表中,移除外掛後,會觸發的 Hook。
第 19 行:儲存設定值時的事件處理。
第 23-25 行:實例化主功能類別 Cache_Master
。
WordPress 外掛在存放自己產生的檔案,都是在 wp-content/uploads
這個目錄裡。因此,這個作品要產生的檔案也在這裡。
(圖 B:uploads 目錄截圖)
由上圖可以看到,以年份作為名稱的目錄是 WordPress 自身的媒體庫,專門放上傳的圖片、影片等等。而 sites
這個目錄是啟用多站點後,各部落格自身的媒體庫。
除了以上兩者外,都是其它外掛產生的檔案、日誌等等。因為我們也要產生一個和外掛名稱 (slug) 同名的目錄,「cache-master」。
基於不同的網站伺服器 (web server) 的設定值不同。建議要考慮到目錄若沒有 index.html
檔案,可能是可以被瀏覽目錄的情況。為了防偷窺,我們會產生空白的 index.html
檔案,和 .htaccess
檔案。
這個功能實作在 register.php
這個檔案中。
(圖 C:register.php 下半部程式碼截圖)
而這支建立目錄用的函式由那裡呼叫呢?
見下圖的第 19 行。
(圖 D:register.php 最上方程式碼截圖)
scm_activation
這個函式會在啟用外掛時觸發。
在初始化檔案中:
register_activation_hook( __FILE__, 'scm_activation' );
而 scm_activation
函式使用 add_option 在啟用外掛時先寫入一些預設的設定值。
結果:
(圖 E:cache-master 目錄截圖)
然後再把資料放在下一層目錄,此層為空。因為要考慮到多站點放不同目錄的情況。在這個範例,筆者以開頭為 blog_id
和 8 字元雜湊碼作目錄名稱。
快取主功能,就是把整個 WordPress 網頁 HTML 存起來,然後就不在從資料庫撈資料,直接從快取拿出來吐給網站訪客。這樣的主功能,含註解,只要 106 行。
(圖 F:class-cache-master.php 檔案截圖)
第 28 行:在實例化這個類別時,從 helper.php
的簡單工廠函式拿到 Simple Cache 物件。
第 28 行:每個網站的快取資料的 key 值就是它的路徑的 md5 值。
第 35-39 行:這個 init
方法,就是我們從初始化檔案呼叫這個主功能程式的地方。(圖 A 第 24 行)它分別把 ob_start
、ob_stop
及 get_post_data
這三個方法注入到三個不同的 Hook 執行,這就是產生魔法的地方唷!
還記得 Day 28 介紹的 WordPress Hook 生命週期吧?
(圖:Action Hook 的生命週期圖表)
Cache_Master 類別的 ob_start 方法,注入到 init
這個很早期的 Hook。
開始產生頁面的 Hook 是 template_redirect
,距離 init
很遠呀!但筆者要求這個外掛越早輸出快取,略過越多的流程,節省的 CPU 和記憶體效率越高。
(圖 G:方法 ob_start 程式碼截圖)
第 7 行:從快取讀取頁面的 key 值,有的話呢....
第 10 行:筆者要留一行 debug 的 HTML 註解訊息在原始碼最尾端,來辨別是不是快取網頁。當然,一般人看不到。因為它是 HTML 註解語法。
第 11-12 行:有快取就輸出,然後離開程式啦。
第 15 行:當沒有快取的時候,使用 PHP 原生函式 ob_start
來收集緩衝區資料。
(圖 H:方法 ob_stop 程式碼截圖)
這個方法是注入到 shotdown
這個 Hook,是 WordPress Hook 生命週期中的最後一個。
第 8 行:這一行是判斷該頁面是否可以被快取喔!條件決定在 get_post_data (圖 F 第46-71行),不是每一頁都會加進快取,本外掛只存放首頁、文章 (post) 和頁面 (page)。
第 9 行:將緩衝區資料存到 $content
變數。
第 12 行:scm_option_ttl
是在設定頁面中讓使用者自己設定的快取存活時間,快取如果過期就自動清除唷!
第 14 行:接著將資料放進快取中。
如果該頁面是不支援快取,就什麼事都不會發生。讀 WordPress 原本的網頁。
(圖 I:方法 get_post_data 程式碼截圖)
預設 $this->cache
是 false
。
第 8 行:這是外掛設定值中,使用者自己設定那幾個頁面類型要快取。
第 11 行:設定是 yes 且目前頁面在首頁。
第 15 行:設定是 yes 且目前頁面在文章。
第 19 行:設定是 yes 且目前頁面在頁面。
以上是判斷頁面。不過有以下情況,則不能快取喔!
第 24 行:404 頁面不能快取啦。
第 28 行:使用者登入不能快取啦,因為快取是存整張頁面,包含登入的使用者可以看到的所有東西,網頁上方的 admin bar 會被沒登入的一般訪客看到哦。
這個方法是注入到 pre_get_posts
這個 Hook,但其實注入到更早的 wp
就可以使用 is_home
、is_single
和 is_page
這三個 WordPress 函式了。不過日後應該還會新加功能,直接注入到 pre_get_posts
就可以取文章資料,不需要再換 Hook 囉。
(圖 J:Cache Master 外掛設定頁面截圖)
(圖 J:setting-update.php 程式碼截圖)
WordPress 很多這種 update_option_{option} 後面加上動態 ID 的 Hook。點進去看官方文件,說明長 update_option_{option}
的就屬於動態 Hook。
以這個例子來說。scm_option_driver
是設定頁面的一個選項。會產生一個動態 Hook,update_option_
加上 scm_option_driver
變成:
update_option_scm_option_driver
然後筆者注入函式到裡面,當這個選項有更新時,快取的 Driver 會重建資料表。
scm_option_post_types
這個也是一個設定頁面選項,讓使用者選擇想快取的類型。這邊的注入函式的作用是,當這個選項更新時。快取全部清除。(因為要還要寫今天的文章,所以先全清掉啦。以後再說?)
就這樣,一個快取外掛完成了。
範例原始碼
鐵人賽終於剩最後一天了,我們明天見。不對。兩小時候見。因為筆者要一股作氣完成鐵人賽。終於解脫了。><||