読者です 読者をやめる 読者になる 読者になる

ハウテレビジョン開発者ブログ

『外資就活ドットコム』を日夜開発している技術陣がプログラミングネタ・業務改善ネタ・よしなしごとについて記していきます。

CakePHP で実装したメール送信フォームで、文字化けしがちな全角チルダ「〜」やハイフンを文字化けしないようにフィルターする

こんにちは、エンジニアの@soyanaです。前回記事CakePHP2 で、phpass でハッシュ化されたパスワードが保存されたユーザーテーブルを扱えるようにするに引き続き、CakePHPのTipsをお送りします。

前提として、

  • メールフォームの文字エンコーディングは UTF-8
  • メール件名、本文の文字エンコーディングは ISO-2022-JP

という環境とします。

Windows 環境から入力された文字列が、機種依存文字の1つである全角チルダ「〜」や全角ハイフン「−」を含んでいた場合に、上記のエンコーディングされると、届くメールが文字化けしてしまうことがよく起こります。 この問題は、波ダッシュ問題や全角チルダ問題といわれており、ハマリがちな問題の1つです。

CakePHP で実装されたフォームで、入力するユーザが意識せずとも、文字化けが起こらないように行った方法をご紹介します。

WordPress では同様のプラグイン Force Wave Dash が公開されており、これを参考に実装を行いました。

まず、変換は、以下のように行うものとします。

見た目の文字 変換前 変換後
U+FF5E FULLWIDTH TILD U+301C
U+2014 EM DASH U+2015
U+FF0D FULLWIDTH HYPHEN-MINUS U+2212
U+FFE0 FULLWIDTH CENT SIGN U+00A2
U+FFE1 FULLWIDTH POUND SIGN U+00A3
U+FFE2 FULLWIDTH NOT SIGN U+00AC

コードで行うことは、以下の2点です

  • 文字化けしないように変換する beahvior を実装
  • model の before save で beahvior で定義した変換メソッドを呼ぶ

テーブル定義

フォームから入力されたメールを保存するテーブル定義は、以下とします。

CREATE TABLE IF NOT EXISTS emails (
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  subject VARCHAR(255) NOT NULL COMMENT 'メール件名',
  body TEXT NOT NULL COMMENT 'メール本文',
  created DATETIME DEFAULT NULL COMMENT '作成日時',
  modified DATETIME DEFAULT NULL COMMENT '編集日時',
) CHARACTER SET 'utf8';

behavior の実装

まずは、behaviror を作成し、問題の文字を対応表にしたがって変換するメソッドを定義します。

<?php
// app/Model/Behavior/ForceWavedashBehavior.php
/*
  Thanks to WordPress Plugin Force Wave Dash
  http://wordpress.org/extend/plugins/force-wave-dash/
*/
class ForceWavedashBehavior extends ModelBehavior {
    public $bad_chars_pat = array(
        "/\x{ff5e}/u", // U+FF5E FULLWIDTH TILD -> U+301C
        "/\x{2014}/u", // U+2014 EM DASH -> U+2015
        "/\x{ff0d}/u", // U+FF0D FULLWIDTH HYPHEN-MINUS -> U+2212
        "/\x{ffe0}/u", // U+FFE0 FULLWIDTH CENT SIGN -> U+00A2
        "/\x{ffe1}/u", // U+FFE1 FULLWIDTH POUND SIGN -> U+00A3
        "/\x{ffe2}/u", // U+FFE2 FULLWIDTH NOT SIGN -> U+00AC
    );
    public $good_chars = array(
        "\xe3\x80\x9c", // U+301C WAVE DASH
        "\xe2\x80\x95", // U+2015 HORIZONTAL BAR
        "\xe2\x88\x92", // U+2212 MINUS SIGN
        "\x00\xc2\xa2", // U+00A2 CENT SIGN
        "\x00\xc2\xa3", // U+00A3 POUND SIGN
        "\x00\xc2\xac", // U+00AC NOT SIGN
    );

    public function tild2wave (Model $Model, $text) {
        return preg_replace($this->bad_chars_pat, $this->good_chars, $text);
    }
}

モデルでデータ保存時に自動で文字を変換する

次に、post されたデータが保存時に自動で変換されるように、Email model の beforeSave メソッドを以下のように定義します。

<?php
// app/Model/Email.php
class Email extends AppModel {
    public $name = ‘Email';
    public $useTable = 'emails';
    public $actsAs = array('ForceWavedash’);

    public function beforeSave($options = array()) {
        if (!empty($this->data[‘Email']['subject'])) {
            $this->data['Email']['subject'] = $this->tild2wave($this->data['Email']['subject']);
        }
        if (!empty($this->data['Email']['body'])) {
            $this->data['Email']['body'] = $this->tild2wave($this->data['Email']['body']);
        }
        return true;
    }

これだけで、何も意識せずにメールで文字化けしないように変換されます。 他の機種依存文字などについても同様に定義してやれば、矯正的に文字を変換して保存することができます。