ky による AJAX通信での POSTメソッド のデータ送信と PHP filter_input の仕様

経緯

JavaScript の AJAX通信 用パッケージ ky から content-type: application/json を指定(デフォルト) して HTTP の POSTメソッド でデータを投げたところ、 PHP の filter_input で受け取れなかったため嵌まりました。

コード

例えばフロントエンド (React, TypeScript) で以下のように ky を使用して AJAX通信 させようとします。

import ky from 'ky-universal';

type Props = {
    apibaseurl: string,
};
type Data = {
    id?: number,
    name?: string,
};
type ResWrite = {
    statusCode: number,
    data: Data[],
};

const SomeComponent = ({ apibaseurl, }: Props) => {
    const res: ResWrite = await ky.post(
            `${apibaseurl}write/user/id/`,
            {
                json: { user_id: userId, }
            }
        ).json();
    if(res.data.length > 0 && typeof res.data[0].name === 'string') {
        // some processing ...
    }

    return (
        <>
            ...
        </>
    );
};

export default SomeComponent;

対応するバックエンド(PHP, ルータは FastRoute 使用で自前コード)は以下のような感じ。

class RepositoryUser
{
    // 略

    /**
     * @return array
     */
    public function write(): array
    {
        $userId = filter_input(INPUT_POST, 'user_id', FILTER_VALIDATE_INT);
        if($userId === $some_value) {
            // some proccessing...
        }

        return $someArray;
    }
}

ご覧の通り、 filter_input を使用して POSTメソッド で投げられてくるであろうキー user_id の値を受け取ろうとしたのですが、一向に入ってくる気配がなく嵌まってしまいました……。

原因・結果

PHP の公式リファレンスに答えが記されていました。

Content-Type に application/x-www-form-urlencoded あるいは multipart/form-data を用いた HTTP リクエストで、 HTTP POST メソッドから現在のスクリプトに渡された変数の連想配列です。

PHP: $_POST – Manual

……つまり、 content-type: application/json が指定されたデータは $_POSTfilter_input では受け取れないということが分かりました。正しい仕様の挙動だったのですね……。

これを踏まえて、 file_get_contents を使用して値を受け取るように PHP コードを以下のように改修しました。

改修後のコード

class RepositoryUser
{
    // 略

    /**
     * @return array
     */
    public function write(): array
    {
        // ここを改修
        // NG: $userId = filter_input(INPUT_POST, 'user_id', FILTER_VALIDATE_INT);
        $jsonStr = file_get_contents('php://input');
        $jsonArray = $this->helper->jsonDecode($jsonStr);
        if(array_key_exists('user_id', $jsonArray)) {
            $userId = (int)$jsonArray['user_id'];
        }
        if($userId === $some_value) {
            // some proccessing...
        }

        return $someArray;
    }
}

これで無事値を受け取ることができました。

分かるまで結構長い時間を取られてしまいましたが、まさか仕様通りだったとは……。

参考

この記事を書いた人

アルム=バンド

フロントエンド・バックエンド・サーバエンジニア。LAMPやNodeからWP、Gulpを使ってejs,Scss,JSのコーディングまで一通り。たまにRasPiで遊んだり、趣味で開発したり。