PHP5.3以下でJSON_PRETTY_PRINTを実現する【車輪の再発明】

# はじめに

PHPの`json_encode()`には、第2引数に`JSON_PRETTY_PRINT`というオプションを与えることで、インデント付きのJSONを取得することが出来ます。

こんな具合です。

```php
<?php
$array = array(
    'Laala' => array(
        'name' => 'Laala Manaka',
        'serif' => 'kashikoma'
    ),
    'Mirei' => array(
        'name' => 'Mirei Minami',
        'serif' => 'puri'
    ),
    'Sophie' => array(
        'name' => 'Sophie Hojo',
        'serif' => 'pushu-'
    )
);
echo json_encode($array, JSON_PRETTY_PRINT);
```

```json
{
    "Laala": {
        "name": "Laala Manaka",
        "serif": "kashikoma"
    },
    "Mirei": {
        "name": "Mirei Minami",
        "serif": "puri"
    },
    "Sophie": {
        "name": "Sophie Hojo",
        "serif": "pushu-"
    }
}
```

何かと便利なこのオプションなのですが、**PHP5.4以上でしか使えません。**

そして筆者はこれを書いている2018年2月現在**PHP5.3**のプロジェクトにいます。
このオプションが必要になったのですが……使えない！
**仕方なく自作した** ので、コード晒しを兼ねて公開してみます。
レッツ車輪の再発明。

# コード

```php
<?php
$array = array(
    'Laala' => array(
        'name' => 'Laala Manaka',
        'serif' => 'kashikoma'
    ),
    'Mirei' => array(
        'name' => 'Mirei Minami',
        'serif' => 'puri'
    ),
    'Sophie' => array(
        'name' => 'Sophie Hojo',
        'serif' => 'pushu-'
    )
);
echo JsonPretty::prettyPrint(json_encode($array));

class JsonPretty
{
    const INDENT = '    ';

    public static function prettyPrint($json)
    {
        if (!is_string($json) || is_null(json_decode($json))) return false;
        $json = self::_lineFeed($json);

        $arrayed = explode("\n", $json);
        $indentLayer = 0;
        foreach ($arrayed as &$line) {
            if (strpos($line, '}') !== false || strpos($line, ']') !== false) {
                $indentLayer--;
            }
            for ($i = 0; $i < $indentLayer; $i++) {
                $line = self::INDENT . $line;
            }
            if (strpos($line, '{') !== false || strpos($line, '[') !== false) {
                $indentLayer++;
            }
        }
        return implode("\n", $arrayed);
    }
    
    private static function _lineFeed($json)
    {
        $json = str_replace('{', "{\n", $json);
        $json = str_replace('[', "[\n", $json);
        $json = str_replace(',', ",\n", $json);
        $json = str_replace(':', ": ", $json);
        $json = str_replace(']', "\n]\n", $json);
        $json = str_replace('}', "\n}", $json);
        return $json;
    }
}
```

```json
{
    "Laala": {
        "name": "Laala Manaka",
        "serif": "kashikoma"
    },
    "Mirei": {
        "name": "Mirei Minami",
        "serif": "puri"
    },
    "Sophie": {
        "name": "Sophie Hojo",
        "serif": "pushu-"
    }
}
```

# 解説

やってることは単純です。

1. 括弧やカンマを基準に改行する。
    * 開き括弧の後で改行する
    * 閉じ括弧の手前で改行する
    * カンマの後で改行する
    * コロンの後にスペースを入れる（ついでに）

2. 各行ごとにインデントを入れる
    * 開き括弧があれば、 **次の行から** インデントを1つ付ける
    * 閉じ括弧があれば **その行から** インデントを1つ無くす

JSONの中にマルチバイト文字が入ることが分かっている場合は、`JSON_UNESCAPED_UNICODE`と併用する必要がありますが。。。
**こいつも例によってPHP5.3では動きません。** おのれ。
ただこっちは既にベストプラクティスがあるので、それを使えば良いでしょう。
[json_encode における JSON_UNESCAPED_UNICODE に PHP5.3 で対応する - Qiita](https://qiita.com/mpyw/items/819974cf6b48ef80fd0f)

# おわりに

PHP5.4以上なら普通に`JSON_PRETTY_PRINT`が使えるので、素直にそっち使った方が良いです。
~~あと`preg_replace()`を多用しているので、パフォーマンス的には良くないんでしょう。恐らく。~~
追記：`str_replace()`で普通に出来たので書き換えました。改行コード扱うから、と思い込みがありました。。

とにかく手間が多いので、 **レガシーPHPは早く滅びて欲しい。**

# おまけ

@alt さんから「RaaraでなくLaalaならSofiでなくSophieなのでは」と編集リクエスト頂いたので採用しました。
調べてみたら海外ファンサイトでは「Sophie」なんですね。初めて知りました←