PHPでPUTで送られたデータを取得する

目次

概要

PHPでは、送られてきたデータは$_GETや$_POSTで受け取ることができます。

しかし、これらを使ってデータを受け取れるのは、リクエスト時のHTTPメソッドがGETやPOSTの時だけです。

なので、REST APIなどをPHPを使って実現しようした時に、PUTメソッドなどの別のHTTPメソッドでは、ほの方法でデータの取得ができません。

ここでは、GETやPOSTメソッド以外のHTTPメソッドでも適切にデータを取得できる方法を書きます。

テキストデータ

リクエストとして送られてくるデータには、テキストデータと、画像などのバイナリデータがあります。

まず、最初にテキストデータをどのようなHTTPメソッドでも受け取ることができるプログラムを書きます。

このプログラムは、下記のURLで解説されているものを参考にしています。

PHP multipart form data PUT request?

参考のページのプログラムではうまく動かなかったので、少しプログラムをいじっています。

function parse_put()
{
    /* PUT data comes in on the stdin stream */
    $putdata = fopen("php://input", "r");

    /* Open a file for writing */
    // $fp = fopen("myputfile.ext", "w");

    $raw_data = '';

    /* Read the data 1 KB at a time
    and write to the file */
    while ($chunk = fread($putdata, 1024))
    $raw_data .= $chunk;

    /* Close the streams */
    fclose($putdata);

    // Fetch content and determine boundary
    $boundary = substr($raw_data, 0, strpos($raw_data, "\r\n"));

    if(empty($boundary)){
        parse_str($raw_data,$data);
        return;
    }

    // Fetch each part
    $parts = array_slice(explode($boundary, $raw_data), 1);
    $data = array();

    foreach ($parts as $part) {
        // If this is the last part, break
        if ($part == "--\r\n") break;

        // Separate content from headers
        $part = ltrim($part, "\r\n");
        list($raw_headers, $body) = explode("\r\n\r\n", $part, 2);

        // Parse the headers list
        $raw_headers = explode("\r\n", $raw_headers);
        $headers = array();
        foreach ($raw_headers as $header) {
            list($name, $value) = explode(':', $header);
            $headers[strtolower($name)] = ltrim($value, ' ');
        }

        // Parse the Content-Disposition to get the field name, etc.
        if (isset($headers['content-disposition'])) {
            $filename = null;
            $tmp_name = null;
            preg_match(
                '/^(.+); *name="([^"]+)"(; *filename="([^"]+)")?/',
                $headers['content-disposition'],
                $matches
            );
            list(, $type, $name) = $matches;

            //Parse File
            if(isset($matches[4]) )
            {
                //if labeled the same as previous, skip
                if(isset($_FILES[$matches[2]]))
                {
                    continue;
                }

                //get filename
                $filename = $matches[4];

                //get tmp name
                $filename_parts = pathinfo($filename);
                $tmp_name = tempnam(sys_get_temp_dir(), $filename_parts['filename']);

                file_put_contents($tmp_name, $body);

                //populate $_FILES with information, size may be off in multibyte situation

                $_FILES[$matches[2]] = array(
                    'error'=>0,
                    'name'=>$filename,
                    'tmp_name'=>$tmp_name,
                    'size'=>strlen($body),
                    'type'=>$value
                );

                //place in temporary directory
                file_put_contents($tmp_name, $body);
            }
            //Parse Field
            else
            {
                $data[$name] = substr($body, 0, strlen($body) - 2);
            }
        }
    }
    return $data;
}

function get_parameter()
{
    $parameter = array();
    switch ($_SERVER['REQUEST_METHOD']) {
        case 'GET':
            $parameter = $_GET;
            break;
        case 'POST':
            $parameter = $_POST;
            break;
        case 'PUT':
        case 'DELETE':
        default:
            $parameter = parse_put();
            break;
    }
    return $parameter;
}

新たに、parse_put()関数を追加して、その中でバイナリデータなどのファイルを$_FILESに入れています。

使い方は、parse_put()関数を呼び出して、その戻り値にはテキストデータが代入されているので、それを受け取り、ファイルなどのバイナリデータは$_FILESに代入されているので、$_FILESを参照します。

まとめ

PHPで、GETやPOSTメソッド以外の方法でデータを受け取るのがこんなに難しく面倒だとは思いませんでした。