ayasamind.com

福岡のWebエンジニア、よしまさのブログです。Fusic.Co.,Ltd所属。

Laravelでサロゲートペアを許容しないカスタムバリデーションをつくる

はじめに

お仕事でよくLaravelを使っています。

今回絵文字(😇とか👀とか)や「𠮷」みたいな、1文字が4バイトの「サロゲートペア」を

システムから除きたい。という要件でした。

デフォルトののバリデーションで簡単に省けるかなーと思っていたら、

意外と時間がかかったので、備忘録として。



結論

先に結論を。

以下のようにカスタムバリデーターを作成しました。

// サロゲートペアを通さない
Validator::extend('noSurrogate', function ($attribute, $value, $parameters, $validator) {
return mb_strlen($value) === strlen(mb_convert_encoding($value, 'UTF-16')) / 2;
});


まずはJSで

JavaScriptでサロゲートペアを弾くロジックを、プロジェクトメンバーが教えてくれました。

return !(string.length === Array.from(string).length);


Array.from()でサロゲートペアを配列に変換し、文字数をカウント。

JavaScriptのArray.from()では、サロゲートペアの文字列も一文字として扱ってくれるみたいです。

console.log(Array.from('😇').length)
1 // 出力結果


string.lengthではbyte数が表示されます。

console.log(('😇').length)
2 // 出力結果

この出力が異なる場合はサロゲートペアということですね。

これをphpで記述すればいいわけです。



phpで書く

まず、文字数を数えるのはmb_strlen($value)を使います。

echo mb_strlen('😇');
1 // 出力結果


次にbyte数を数えます。

JSのstring.lengthをphpでどう書くか、いろいろ調べてみると、こんな記事が

JavaScriptのString.lengthをPHPで実装する



この記事を参考に以下のように実装しました。

echo strlen(mb_convert_encoding('😇', 'UTF-16')) / 2;
2 // 出力結果

byte数が出力されましたね。



この二つの値を比較すると、チェックが可能です。

これで、最終的なコードができました

return mb_strlen($value) === strlen(mb_convert_encoding($value, 'UTF-16')) / 2;

サロゲートペアの場合、falseを返します。



最後に

あとは、Laravelのカスタムバリデーションを用いて実装するだけですね。

以上、サロゲートペアのバリデーションに関する記事でした。

最後まで読んでいただき、ありがとうございました!








株式会社Fusicでは一緒に働くエンジニアを募集しています!

福岡での就職・転職に少しでも興味のある方は、

ayasamind宛のDMFusic公式サイトのお問い合わせなど、お気軽にご相談ください。