LoginSignup
10
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-02-22

はじめに

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

こんな具合です。

<?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);
{
    "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
$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;
    }
}
{
    "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

おわりに

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

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

おまけ

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

10
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
2