Laravelでガラケー対応する場合の文字コード変換に関するTips - Laravel5編

スマートフォン全盛の時代ですが、サービスによってはまだフィーチャーフォン（ガラケー）からのアクセスがあり、そのユーザーのためにフィーチャーフォンを考慮したシステムを作るケースもあるかもしれません。

その場合の課題の1つとして UTF8 <-> ShiftJIS の文字コード変換があるわけですが、以前 Laravel4 でのやり方を[自分のブログ](http://blog.songs-inside.com/php-696/)に書きました。今回は同じ課題を Laravel5 でやるにはどうするか書いてみます。

やることとしては、フィーチャーフォンからのアクセスなら ShiftJIS のリクエストを処理の最初で UTF8 に変換し、レスポンスを UTF8 から ShiftJIS に変換してフィーチャーフォンに返してやるというものです。こうすることで内部処理やデータを UTF8 に統一させることができます。

Laravel4 ではフィルタという機能を使っていたのですが、Laravel5 ではこれをミドルウェアを使って行うことになります。

# ミドルウェアの作成と登録

ミドルウェアの作成は以下のコマンドで行います

```
php artisan make:middleware SupportFeaturephone
```

上記のコマンドを実行すると、app/Http/Middleware/SupportFeaturephone.php が生成されます。ミドルウェアとして使用するためには app/Http/Kernel.php に登録しておきます。

```Kernel.php
...
    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \App\Http\Middleware\SupportFeaturephone::class, // これを追加
    ];
...
```

ミドルウェアをシステムの全HTTPリクエストに対して実行したい場合は `$middleware` に登録し、ミドルウェアを特定のルートに対して実行したい場合は、`$routeMiddleware` にキーと共に登録します。今回のケースでは全リクエストに対してガラケー対応したいので、`$middleware` の方に追記します。

# 事前処理と事後処理として文字列変換処理を書く

`make:middleware` を実行した直後、`SupportFeaturephone.php` の `handle()` メソッドには `return $next($request);` しか書かれていません。これを分解し、$nextコールバックの前に事前処理を、コールバックから返されたオブジェクトに対して事後処理を実施します。

実際のロジックは以下のようになります。

```SupportFeaturephone.php
<?php

namespace App\Http\Middleware;

use Closure;

class SupportFeaturephone
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $agent = $request->server('HTTP_USER_AGENT');
        if (preg_match("/^DoCoMo/i", $agent) || 
            preg_match("/^(J\-PHONE|Vodafone|MOT\-[CV]|SoftBank)/i", $agent) || 
            preg_match("/^KDDI\-/i", $agent) || preg_match("/UP\.Browser/i", $agent))
        {
            // フィーチャーフォンからのリクエストをUTF-8に変換
            $request->merge($this->encodeString($request->input()));

            // $nextコールバックに渡して以降の処理を実施
            $response = $next($request);

            // 処理から戻ってきた$responseに対して文字コード変換を実施
            $response->header('Content-Type', 'text/html; charset=Shift_JIS');
            $response->setContent($this->encodeString($response->getContent(), 'SJIS', 'UTF-8'));
        }
        else
        {
            // フィーチャーフォン以外の場合はそのまま$nextへ
            $response = $next($request);
        }
        return $response;
    }

    // 文字列または配列の文字コード変換処理。
    private function encodeString($str, $to = 'UTF-8', $from = 'SJIS')
    {
        if (is_array($str))
        {
            $result = array();
            foreach ($str as $key => $value)
            {
                // リクエストが配列になってる場合はさらに潜って変換
                if (is_array($value))
                {
                    $result[$key] = $this->encodeRequest($value, $to, $from);
                }
                else
                {
                    $result[$key] = mb_convert_encoding($value, $to, $from);
                }
            }
            return $result;
        }
        else
        {
            return (mb_convert_encoding($str, $to, $from));
        }
    }
}
```

上のサンプルでは、事前処理でまずユーザーエージェントを判断し、ガラケーであれば入力された文字を ShiftJIS に変換しています。また `$next` コールバックから返ってきた `$response` に対してヘッダ定義（デフォルトではUTF8が指定されてしまう）と文字コード変換（ShiftJISへ）を行っています（リクエストやレスポンスの内容に画像などを含む場合は変換処理のところを適宜書き換えてください）。

以上ですー。