標籤: wordpress

  • WordPress 不用外掛置換網域

    WordPress 不用外掛置換網域

    雖然不是第一次做這件事了,不過還真的不常做。今天有朋友網域需要更換,剛好又是放我這管理,因為滿多人用外掛處理,但這個網站基於種種原因不太好用外掛,於是想乾脆改資料庫好了。 紀錄一下比較少處理的作法,某種程度上 wp 的外掛太方便惹,只要品管做的好,現在已經越來越少動程式。

    * 友情提醒:該操作時是離峰時間,所以沒有在顧忌考慮什麼可能會壞掉的。

    首先要先備份,主要是備份資料庫。如果資料過大的話要注意一下,我建議幾個方向:

    1. 優先把 sql 檔案備份下載。這次是使用這個方式處理。
    2. 如果檔案太大,複製一個新的資料庫。修改的話就只能用命令或網頁 phpMyAdmin / adminer 來處理。

    接下來是幾個系統環境設定的部分:

    1. 確認網域 DNS 是否正確:設定 A 紀錄到指定伺服器 ip
    2. 確認 HTTP Service 設定是否正確,可以新舊網域指向同一個 root path 沒問題。
    3. 確認新網域的 SSL 是否正常設置。建議如果使用 cloudflare 的話要先確認好 SSL 的配置是否和舊網域相同,如果使用「彈性」的選項滿容易遇到問題的。

    這時用新網域打開 wordpress,沒意外瀏覽器應該會跳轉至舊網域,正常展示。

    再來修改線上的資料庫 wp_options.siteurl 和 wp_options.home 欄位1,先改成新的網域,確認 SSL 是否正常/網頁正常展示。 如果正常的話開啟瀏覽器開發人員模式會看到網頁正常開啟,不過圖片等網址都還是指向舊的網域2

    再來要來改資料庫了,因為我這次是把資料庫備份下來的,所以記得複製一份以後來編輯,以下用 old-domain.com 和 new-domain.com 來區分新舊網域。

    首先要先注意 www 的子網域,以及是否有可恨的外掛/布景主題使用 PHP 序列化(參考)來做儲存的行為。如果遇到是序列化的欄位,請特別注意字元的數量。如果有要替換 www 的話,搜尋取代行為先替換比較好。

    1. 記得備份,開啟我們要編輯的 sql 檔案。
    2. 因為現在要改 SSL ,搜尋 http://(www)old-domain.com 替換成 https://(www)new-domain.com
    3. 搜尋 https://(www)old-domain.com 替換成 https://(www)new-domain.com
    4. 可能會有 email 或是單純網域的設定。搜尋 (www)old-domain.com 替換成 (www)new-domain.com

    再次提醒如果有遇到類似長這種的,不是 JSON ,要注意序列化的操作,可以使用線上的序列化(參考)和反序列化(參考)工具。這次沒遇到,就不管啦。3

    資料庫更新好以後覆蓋回去即可。如果會擔心的話,建議創一個新的資料庫匯入,從 wp-config.php 中修改資料庫路徑/名稱和帳號密碼。

    打開來檢查一下確認網域有沒有舊網域以及有沒有什麼錯誤。

    註解

    1. 呵呵看欄位名稱就知道網站多舊了。 ↩︎
    2. 這可能也是因為舊網站的關係。wp 很多設定可能有 CDN 或擴展的需求,資料庫紀錄絕對路徑,移站時,除了檔案路徑會有問題以外,常常網域的路徑也會需要注意。 ↩︎
    3. 我是覺得外掛/布景主題設計上,把網域包在序列化的開發者真的要判死刑 XD ↩︎

    同場加映, 如果是很舊的網站,遇到 css 和 js 沒有設定 https 而導致錯誤,這邊提供一個解法。寫在 function.php 裡面。

  • WordPress 上 Cookie 的 SameSite 問題

    WordPress 上 Cookie 的 SameSite 問題

    參考這篇問題,主要是描述在支付頁面 retun 支付成功之後,跳回網頁會因為 cookie samesite 的問題導致會員登出,主要解法如同文中所敘述,需要把記錄 user 的 cookie samesite 改成 none,文中也提供了 apache 作法,這邊記錄一下其他作法,基本上都是沒成功的:

    Nginx 於 server 的 Location 中填寫,可能是位置沒填寫正確,沒作用:

    proxy_cookie_path ~^/(.+)$ "/$1;Secure;SameSite='None';Partitioned";

    PHP 有兩種作法,一種是在程式中處理,參考這篇,在 session_start() 之前寫入:

    session_set_cookie_params(['samesite' => 'None']);

    不過他沒理我,在 php.ini 中替換:

    session.cookie_samesite = "None"

    注意 None 是字串,需要雙引號起來。 這個修改會對 session_cookie 起作用。

    不過我的目的是為了修改 wordpress 網站,wp 的 auth cookie 是透過程式寫入的,檢查後也沒反應。鬱悶。

    於是跑進去追了一下程式碼,發現他使用的 setcookie 是 php 7.2 以前的工法,可能是為了向下相容吧,但偏偏在 php7.2 寫入的方式和 php 7.3 不同(參考這裡),但因為 wp core 的寫法,導致無法 hack 從 define 設定的 cookiepath 中直接修改,鬱悶,總不能把網站環境再改回兩蔣統治時期,快樂的 php 7.1 or 7.2 ,這樣挺尷尬的,連 wp 升級都不行。

    後來挖到了這則討論串,看起來 wordpress 他們也正在煩惱。於是在討論串中挖到了一個在 github 上的外掛:

    這個外掛看起來沒啥問題,經過安裝啟用檢查過後,看起來是可行的, auth 相關的 cookie 已經正確顯示 samesite 為 none 了。

    這外掛設定的部分,如同 github 說明一樣,請在後台的「設定」選項中第一個「一般」,點進去以後會發現多一個 uthentication SameSite Cookie parameter 的設定,像是金流跳轉的問題,把他設定為 None 就可以嘍。

    另外得提一下如果 goolge 關鍵字 「wordpress SameSite」 會搜到一款還正在架上間諜外掛,同評論所講的,我也不明白作者為什麼要上架這款外掛,有點坑,而且如果 php 有設定之後,安裝啟用這款外掛還會造成 cookie 錯誤。 此時可以體現測試環境的重要性。

  • WordPress Debug Journey – 後台無法開啟編輯器為例

    WordPress Debug Journey – 後台無法開啟編輯器為例

    最近開啟部落格新增文章時,發現新增文章編輯器變成一個空白畫面,於是開啟了 wordpress debug 之旅,記錄一下也分享難得的大型錯誤經歷。

    首先先看一下錯誤內容:

    This feature is only for JavaScript modules shipped with WordPress core. Please do not use it in plugins and themes as the unstable APIs will be removed without a warning. If you ignore this error and depend on unstable features, your product will inevitably break on one of the next WordPress releases.

    看起來是一個外掛程式和主題 hack 了一個內部 API 導致警告 or 錯誤。

    首先我們先做統一的預處理:使用無痕開網頁和清除快取。 快取部分除了 wordpress 的快取外掛之外,還有 Cloudflare 上的快取功能,清除確認一下是否問題還是存在。這裡必須特別提醒,通常產品上會有測試環境,更成熟的產品會有 DEV, UAT, 甚至本地的開發環境以及各層級的備份和版本控制機制,如果非必要,請不要再正式環境上搞。

    小提醒,如果有需要更新外掛的動作,請別忘記做備份。

    在這個案例中,清除完快取問題還是存在的,於是衡量一下機會成本,先鎖定外掛的部分,框列幾個可能的外掛,先停用看看,然後後台刷新編輯頁面。

    在我們外掛的開開停停測試中,發現是「Meta Generator and Version Info Remover」這個外掛造成的問題,把他停用以後後台就開啟正常,關閉後台有問題。於是我們繼續確認是否是設定的問題,同樣也是開開關關操作測試,於是發現是其中一個「Remove version from script (JS files)」選項設定有問題。把他停用即可。

    當然問題還沒完,首先關於開關停用的動作,這個案例中操作了 2 次,分別是外掛的開關操作以及外掛內設定的開關操作。自己的經驗是建議將需要開關的選項來分組,就這次的案例來看,錯誤訊息中可以看出編輯器的 js 有問題,於是我會先將分組為後台編輯器相關的外掛來測試,不會先動像是安全設定、核心功能、前台功能等外掛。這就很吃你的經驗和眼光,雖然有些運氣成分,也不是全然靠猜,而通常這類操作也是很好透過自動化和 AI 來取代的,不過 It’s Not Today,我們還是得靠自己努力先。

    再來是需要根除這個問題,於是我登入 wordpress.org 的這個外掛頁面打算抱怨一下,發現已經有人提出類似的問題,作者的意思大概是說這個鍋我不背,請檢查是否和其他外掛衝突。這樣,恩,我也在下面留言回報一下,當然要改外掛也是可以的,得考量維護成本以及後續更新的操作成本,對於一個不賺錢的小部落格是否值得修改?歡迎來到 wordpress 的外掛地獄。

    最後提供一個關於這個功能的實作解法,如果單就 js 來說,把 ver 的 query string 替換掉就是了。 請於佈景主題 function.php 中實作。另外提醒一點,建議修改操作都在 child 的佈景主題中動作會比較保險,如果不知道這是什麼請參考這裡

    // remove wp version number from scripts and styles
    function remove_css_js_version($src)
    {
        if (strpos($src, '?ver=')) {
            $src = remove_query_arg('ver', $src);
        }
        $src = add_query_arg('ver', 'hello-world', $src);
    
        return $src;
    }
    // add_filter('style_loader_src', 'remove_css_js_version', 9999); // 修改 css
    add_filter('script_loader_src', 'remove_css_js_version', 9999); // 修改 js

    對了,修改 query string 會影響快取,尤其如果 cloudflare 是設定用 query string 辨別快取檔案的話。這塊常常被遺忘了,愛注意。

  • WordPress 核心架構圖/堆棧圖 相關檔案結構說明鏈結

    最近用 wordpress 處理一些 MVP 的實作,沒想到竟然要對他做技術文件,這邊先筆記一下避免以後有相同需求找不到來源。

    注意來源取自官網,有些可能是過時的資訊。

    Modules

    source: https://codex.wordpress.org/File:WP_27_modules.JPG

    Wordrpess Site Architecture

    Code Reference | Functions, Hooks, Classes Methods

    Reference | WordPress Developer Resources
    Want to know what’s going on inside WordPress? Find out more information about its functions, classes, methods, and hooks.

    Database Description

    Database Description
    Srouce: https://codex.wordpress.org/Database_Description
  • WordPress SSL 錯誤修復

    最近使用 wordpress 外掛,透過 curl 可能會發生錯誤,如果遇到這類型的錯誤:

    SSL certificate problem: certificate has expired
    SSL certificate problem: certificate has expired

    是因為許多網站都在使用全球證書,該證書已於 2021 年 9 月 30 日到期。參考:

    解決方法是使用新的 crt 檔案替代 wordpress 目錄:wp-includes/certificates 底下的 ca-bundle.crt 檔案。
    crt 檔案下載網址:https://github.com/WordPress/WordPress/blob/master/wp-includes/certificates/ca-bundle.crt

    而這個問題預估在 wordpress 5.9 版會修復。

    參考:

    SSL certificate problem: certificate has expired – Smart Slider Documentation
    In this article Check the error Error only displays during update Solution on localhost servers (with WampServer example) A lot of websites are using a global c

  • WordPress 之中藍新金流開啟後, Elementor 外掛有時會打不開的問題處理

    WordPress 之中藍新金流開啟後, Elementor 外掛有時會打不開的問題處理

    WordPress 有很多外掛,在藍新金流啟用時,同時有使用 Elementor 這個 page builder 外掛,在某些編輯視窗中,可能會造成 AJAX “/wp-json/elementor/v1/globals” 路徑中抓不到 wc_get_chosen_shipping_method_ids() 這個 function 的錯誤訊息。

    這邊查了一下資料,不只是藍新金流外掛會這樣,應該 woocommerce 外掛和 page builder 都有可能造成類似的錯誤。紀錄一下避免老了忘記。

    最近又遇到一樣的問題,查看 log 發現是 wc_get_chosen_shipping_method_ids() 裡面的問題:

    2024/01/02 19:30:32 [error] 1870242#1870242: *46728 FastCGI sent in stderr: "PHP message: PHP Fatal error:  Uncaught Error: Call to a member function get() on null in /xxx/xxx/wp-content/plugins/woocommerce/includes/wc-cart-functions.php:394
    Stack trace:
    #0 /xxx/xxx/wp-content/plugins/newebpay/class-newebpay.php(1673): wc_get_chosen_shipping_method_ids()
    
    ...

    查看 woocommerce 的程式碼(參考),並且參考有類似問題的 stackoverflow

    function wc_get_chosen_shipping_method_ids() {
    	$method_ids     = array();
    	$chosen_methods = WC()->session->get( 'chosen_shipping_methods', array() );
    	foreach ( $chosen_methods as $chosen_method ) {
    		$chosen_method = explode( ':', $chosen_method );
    		$method_ids[]  = current( $chosen_method );
    	}
    	return $method_ids;
    }

    判斷應該是 WC()->session 取不到 get 參數,實際上就是這樣沒錯, woocommerce 當下這個沒有判斷 session 為空的狀況。
    可以參考原文,於 class-newebpay.php:1676 附近, wc_get_chosen_shipping_method_ids() 呼叫之前判斷 WC()->session 是否為空。這邊於底下原文中添加新的 code 。

    附上錯誤訊息:

    Fatal error: Uncaught Error: Call to undefined function wc_get_chosen_shipping_method_ids() in /xxx/wp-content/plugins/newebpay/class-newebpay.php:1676 Stack trace: #0 /xxx/wp-includes/class-wp-hook.php(303): newebpay_alter_payment_gateways(Array) ...

    附上發現錯誤的版本資訊,wordpress 核心版本為 5.8.1 , Elementor 版本為 3.4.3:

    /**
     * newebpay Payment Gateway
     * Plugin URI: http://www.newebpay.com/
     * Description: 藍新金流收款/物流 模組
     * Version: 1.0.3
     * Author URI: http://www.newebpay.com/
     * Author: 藍新金流 newebpay
     * Plugin Name:   藍新金流
     * @class       newebpay
     * @extends     WC_Payment_Gateway
     * @version
     * @author  Pya2go Libby
     * @author  Pya2go Chael
     * @author  Spgateway Geoff
     * @author  Spgateway_Pay2go Q //20170217 1.0.1
     * @author  Spgateway_Pay2go jack //20170622 1.0.2
     * @author  Spgateway_Pay2go Stally //20180420 1.0.3 20181018 1.0.4 20181222 newebpay 1.0.0 20190417 1.0.1 20190711 1.0.2 20200326 1.0.3
     */

    目前的解決方法是在 class-newebpay.php:1676 的 newebpay_alter_payment_gateways() 內添加判斷,檢查是否有方法,沒方法就直接跳回不動作。
    另外也在 plugin meta 上修改版本號和描述,避免被直接更新。不過這個外掛也沒上版控,所以屆時可能需要也是手動更新嘍。

    附上修改後資訊:

    <?php
    /**
     * newebpay Payment Gateway
     * Plugin URI: http://www.newebpay.com/
     * Description: 藍新金流收款/物流 模組 修復了 elementor 開啟會有 wc_get_chosen_shipping_method_ids 錯誤的問題 
     * Version: 99.99.99
     * Author URI: http://www.newebpay.com/
     * Author: 藍新金流 newebpay 修改過 By Jerry
     * Plugin Name:   藍新金流
     * @class       newebpay
     * @extends     WC_Payment_Gateway
     * @version
     * @author  Pya2go Libby
     * @author  Pya2go Chael
     * @author  Spgateway Geoff
     * @author  Spgateway_Pay2go Q //20170217 1.0.1
     * @author  Spgateway_Pay2go jack //20170622 1.0.2
     * @author  Spgateway_Pay2go Stally //20180420 1.0.3 20181018 1.0.4 20181222 newebpay 1.0.0 20190417 1.0.1 20190711 1.0.2 20200326 1.0.3
     */
    add_action('plugins_loaded', 'newebpay_gateway_init', 0);
    
    function newebpay_gateway_init() {
        if (!class_exists('WC_Payment_Gateway')) {
            return;
        }
    
        class WC_newebpay extends WC_Payment_Gateway {
    
     ...
    
        // 選擇藍新金流超商取貨後 payment只輸出藍新金流
        function newebpay_alter_payment_gateways($list) {
            if(isset($_GET['pay_for_order']) && isset($_GET['key'])) {
                $order_id = wc_get_order_id_by_order_key($_GET['key']);
                $order = wc_get_order($order_id);
                if($order->has_shipping_method('newebpay_cvscom')) {
                    $list = array('WC_newebpay');
                }
            } elseif(!is_admin()) { //後台無wc_get_chosen_shipping_method_ids function
                if ( !function_exists( 'wc_get_chosen_shipping_method_ids' ) ) { 
                    return $list;
                } 
    
                // 2024-01-02 更新判斷
                $session = WC()->session;
                if($session){
                    $chosen_shipping = wc_get_chosen_shipping_method_ids();
                    //判斷購物車內商品是否全為虛擬商品 全為虛擬商品時會無法選擇物流方式 導致session的chosen_shipping會維持上次所選
                    $virtual_count = 0;
                    $cart_items = WC()->cart->get_cart();
                    foreach ($cart_items as $key => $cart_item) {
                        $virtual_count += ($cart_item['data']->is_virtual()) ? 1 : 0;
                    }
                    if (@in_array('newebpay_cvscom', $chosen_shipping) && $virtual_count < count($cart_items)) {
                        $list = array('WC_newebpay');
                    }
                } // end 判斷
            }
    
            return $list;
        }
        add_filter('woocommerce_payment_gateways', 'newebpay_alter_payment_gateways', 100);
    
    ...