繼:我朝特有需求之《英文字元佔 0.5 個,中文字元佔 1 個》

medz發表於2017-06-09

之所以寫繼篇,其實我是來自我檢討的,上一次發表了 《ThinkSNS+ 是如何計算字元顯示長度的(使用 Laravel 自定義驗證規則)》 後,下面有一個網友多方測試告訴我說 str_word_count 是有問題的,但是我的環境下確實成功的,所以一直在想一個相容性更高的方法。

這短時間一直在開發 ThinkSNS+ 的支付功能,所以一直沒時間看,今天上午可能沒有什麼感覺寫程式碼,乾脆就來像一個相容性更好的計算方法。

需求

重新說下需求,在我天朝 PM 經常會提一種要求,就是例如 一個使用者名稱做多輸入 12 個漢字,但是英文可以輸入 24 個,混排也要滿足這個規則,也就是 單位元組字元佔 0.5 多子節字元佔 1這樣的計算。

解決

閒下來的時候看了上篇文字發的計演算法法,其實是沒什麼問題的,就是相容性不好,而且寫那個方法的時候也是想多了,根本沒有那麼複雜,我們看上一篇中計算多子節的方法:

$double = str_word_count(preg_replace('([a-zA-Z0-9_])', '', $value));

計算方法是剔除我們需求中允許的單位元組,然後通過 str_word_count 來獲取多子節的個數,其實這個函式是可以獲取到的,但是部分系統下是不會成功的,其實還有一個函式就可以直接獲取多子節的個數 mb_strlen 修改後如下:

第一想法是,當時傻逼了。

$double = mb_strlen(preg_replace('([a-zA-Z0-9_])', '', $str));

然後我們就可以正確完成這個需求了,最終實現的 Laravel 驗證規則如下:

// 新增長度規則
Validator::extend('display_length', function ($attribute, $value, array $parameters) {
    if (empty($parameters)) {
        throw new \InvalidArgumentException('Parameters must be passed');
    }

    $min = 0;
    if (count($parameters) === 1) {
        list($max) = $parameters;
    } elseif (count($parameters) >= 2) {
        list($min, $max) = $parameters;
    }

    if (! isset($max) || $max < $min) {
        throw new \InvalidArgumentException('The parameters passed are incorrect');
    }

    // 計算單位元組.
    preg_match_all('/[a-zA-Z0-9_]/', $value, $single);
    $single = count($single[0]) / 2;

    // 多子節長度.
    $double = mb_strlen(preg_replace('([a-zA-Z0-9_])', '', $value));

    $length = $single + $double;

    return $length >= $min && $length <= $max;
});

laravel 通過上面的規則,可以在表單驗證規則中直接驗證了,還支援傳入最小值和最大值。


上面的程式碼都是來自於 基於 Laravel 開發的開源程式 ThinkSNS+ 中,ThinkSNS+ 採用 apache-2.0 協議開源,我相信可以作為很多 Laravel 學習者的學習程式之一。

喜歡的話幫忙點個 star ,開源不易,感謝大家的支援。

GitHub: https://github.com/zhiyicx/thinksns-plus

本作品採用《CC 協議》,轉載必須註明作者和本文連結

Seven 的程式碼太渣,歡迎關注我的新擴充包 medz/cors 解決 PHP 專案程式設定跨域需求。

相關文章