HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

PHPでPOSTする

大昔に書いた記事にウソがあったので、それを訂正しつつ、PHPでPOSTするためのコードを何通りか書いてみるテスト。

下準備

以下のようなAPIを用意しておく。HTMLコードを吐き出す仕様になっているのは気にしない。

<dl>
<?php foreach ($_POST as $key => $value): ?>
    <dt><?php echo htmlspecialchars($key); ?></dt>
    <dd><?php echo htmlspecialchars($value); ?></dd>
<?php endforeach ?>
</dl>
<hr />
<dl>
<?php foreach ($_FILES as $key => $file): ?>
    <dt><?php echo htmlspecialchars($key); ?></dt>
    <dd>
        name: <?php echo htmlspecialchars($file['name']); ?>
        <br />
        type: <?php echo htmlspecialchars($file['type']); ?>
        <br />
        size: <?php echo htmlspecialchars($file['size']); ?>
        <br />
        error: <?php echo htmlspecialchars($file['error']); ?>
        <br />
        tmp_name: <?php echo htmlspecialchars($file['tmp_name']); ?>
        <br />
        <pre><?php var_dump(htmlspecialchars(file_get_contents($file['tmp_name']))); ?></pre>
    </dd>
<?php endforeach ?>
</dl>

本題

以前に書いたコードの焼き直しみたいなものだが。

<?php

$protocol = 'http';
$host = 'localhost';
$port = 80;
$path = '/post-api.php';
$apiUrl = "{$protocol}://{$host}:{$port}/{$path}";

$params = array(
    'title' => 'POST test',
    'content' => 'sample text',
);
$files = array(
    'my_file' => 'sample.txt'
);

function buildRequestData($params, $files = null) {
    $data = '';
    if (!empty($files) && is_array($files)) {
        $boundaryStr = '________' . time() . '________';
        $contentType = "Content-Type: multipart/form-data; boundary={$boundaryStr}";
        foreach ($params as $key => $value) {
            $data .= "--{$boundaryStr}\r\n";
            $data .= "Content-Disposition: form-data; name={$key}\r\n";
            $data .= "\r\n";
            $data .= "{$value}\r\n";
        }
        foreach ($files as $key => $file) {
            $filename = basename($file);
            $data .= "--{$boundaryStr}\r\n";
            $data .= "Content-Disposition: form-data; name={$key}; filename=\"{$filename}\"\r\n";
            $data .= "Content-Type: application/octet-stream\r\n";
            $data .= "\r\n";
            $data .= file_get_contents($file) . "\r\n";
        }
        $data .= "--{$boundaryStr}--\r\n";
    } else {
        $contentType = 'Content-Type: application/x-www-form-urlencoded';
        $data .= http_build_query($params);
    }
    $headers = array(
        $contentType,
        'Content-Length: ' . strlen($data),
    );
    $requestData = array(
        'method' => 'POST',
        'content' => $data,
        'header' => implode("\r\n", $headers),
    );
    return $requestData;
}

function test_file_get_contents($url, $params, $files = null) {
    printf("=== %s ===\n", __FUNCTION__);
    $context = stream_context_create(array('http' => buildRequestData($params, $files)));
    var_dump(file_get_contents($url, false, $context));
}
function test_fopen($url, $params, $files = null) {
    printf("=== %s ===\n", __FUNCTION__);
    $context = stream_context_create(array('http' => buildRequestData($params, $files)));
    $sock = fopen($url, 'rb', false, $context);
    if ($sock) {
        while (($s = fgets($sock))) {
            $tmp .= $s;
        }
        var_dump($tmp);
        fclose($sock);
    }
}
function test_fsockopen($host, $port, $path, $params, $files = null) {
    printf("=== %s ===\n", __FUNCTION__);
    $sock = fsockopen($host, $port);
    if ($sock) {
        $requestData = buildRequestData($params, $files);

        fputs($sock, "{$requestData['method']} {$path} HTTP/1.1\r\n");
        fputs($sock, "Host: {$host}\r\n");
        fputs($sock, "{$requestData['header']}\r\n");
        fputs($sock, "\r\n");
        fputs($sock, "{$requestData['content']}");

        $len = 0;
        while (strlen(trim($s = fgets($sock)))) {
            if (preg_match('/^Content-Length: /', $s)) {
                $len = (int)preg_replace('/^Content-Length: /', '', $s);
            }
            echo "[H] {$s}";
        }
        $s = fread($sock, $len);
        var_dump($s);
        fclose($sock);
    }
}
function test_stream_socket_client($protocol, $host, $port, $path, $params, $files = null) {
    printf("=== %s ===\n", __FUNCTION__);
    $remoteSocket = "{$protocol}://{$host}:{$port}";
    $sock = stream_socket_client($remoteSocket);
    if ($sock) {
        $requestData = buildRequestData($params, $files);

        fputs($sock, "{$requestData['method']} {$path} HTTP/1.1\r\n");
        fputs($sock, "Host: {$host}\r\n");
        fputs($sock, "{$requestData['header']}\r\n");
        fputs($sock, "\r\n");
        fputs($sock, "{$requestData['content']}");

        $len = 0;
        while (strlen(trim($s = fgets($sock)))) {
            if (preg_match('/^Content-Length: /', $s)) {
                $len = (int)preg_replace('/^Content-Length: /', '', $s);
            }
            echo "[H] {$s}";
        }
        $s = fread($sock, $len);
        var_dump($s);
        fclose($sock);
    }
}

test_file_get_contents($apiUrl, $params);
test_file_get_contents($apiUrl, $params, $files);
echo "\n";
test_fopen($apiUrl, $params);
test_fopen($apiUrl, $params, $files);
echo "\n";
test_fsockopen($host, $port, $path, $params);
test_fsockopen($host, $port, $path, $params, $files);
echo "\n";
test_stream_socket_client('tcp', $host, $port, $path, $params);
test_stream_socket_client('tcp', $host, $port, $path, $params, $files);

実行結果は以下のような感じ。

=== test_file_get_contents ===
string(105) "<dl>
    <dt>title</dt>
    <dd>POST test</dd>
    <dt>content</dt>
    <dd>sample text</dd>
</dl>
<hr />
<dl>
</dl>
"
=== test_file_get_contents ===
string(326) "<dl>
    <dt>title</dt>
    <dd>POST test</dd>
    <dt>content</dt>
    <dd>sample text</dd>
</dl>
<hr />
<dl>
    <dt>my_file</dt>
    <dd>
        name: sample.txt        <br />
        type: application/octet-stream      <br />
        size: 21        <br />
        error: 0        <br />
        tmp_name: /tmp/phpTb7Eyx        <br />
        <pre>string(21) "SAMPLE TEXT CONTENTS
"
</pre>
    </dd>
</dl>
"

=== test_fopen ===
string(105) "<dl>
    <dt>title</dt>
    <dd>POST test</dd>
    <dt>content</dt>
    <dd>sample text</dd>
</dl>
<hr />
<dl>
</dl>
"
=== test_fopen ===
string(326) "<dl>
    <dt>title</dt>
    <dd>POST test</dd>
    <dt>content</dt>
    <dd>sample text</dd>
</dl>
<hr />
<dl>
    <dt>my_file</dt>
    <dd>
        name: sample.txt        <br />
        type: application/octet-stream      <br />
        size: 21        <br />
        error: 0        <br />
        tmp_name: /tmp/php2AD6yA        <br />
        <pre>string(21) "SAMPLE TEXT CONTENTS
"
</pre>
    </dd>
</dl>
"

=== test_fsockopen ===
[H] HTTP/1.1 200 OK
[H] Date: Sun, 30 May 2021 11:54:49 GMT
[H] Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/5.6.40
[H] X-Powered-By: PHP/5.6.40
[H] Content-Length: 105
[H] Content-Type: text/html; charset=UTF-8
string(105) "<dl>
    <dt>title</dt>
    <dd>POST test</dd>
    <dt>content</dt>
    <dd>sample text</dd>
</dl>
<hr />
<dl>
</dl>
"
=== test_fsockopen ===
[H] HTTP/1.1 200 OK
[H] Date: Sun, 30 May 2021 11:54:49 GMT
[H] Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/5.6.40
[H] X-Powered-By: PHP/5.6.40
[H] Content-Length: 326
[H] Content-Type: text/html; charset=UTF-8
string(326) "<dl>
    <dt>title</dt>
    <dd>POST test</dd>
    <dt>content</dt>
    <dd>sample text</dd>
</dl>
<hr />
<dl>
    <dt>my_file</dt>
    <dd>
        name: sample.txt        <br />
        type: application/octet-stream      <br />
        size: 21        <br />
        error: 0        <br />
        tmp_name: /tmp/phpssgUsE        <br />
        <pre>string(21) "SAMPLE TEXT CONTENTS
"
</pre>
    </dd>
</dl>
"

=== test_stream_socket_client ===
[H] HTTP/1.1 200 OK
[H] Date: Sun, 30 May 2021 11:54:49 GMT
[H] Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/5.6.40
[H] X-Powered-By: PHP/5.6.40
[H] Content-Length: 105
[H] Content-Type: text/html; charset=UTF-8
string(105) "<dl>
    <dt>title</dt>
    <dd>POST test</dd>
    <dt>content</dt>
    <dd>sample text</dd>
</dl>
<hr />
<dl>
</dl>
"
=== test_stream_socket_client ===
[H] HTTP/1.1 200 OK
[H] Date: Sun, 30 May 2021 11:54:49 GMT
[H] Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/5.6.40
[H] X-Powered-By: PHP/5.6.40
[H] Content-Length: 326
[H] Content-Type: text/html; charset=UTF-8
string(326) "<dl>
    <dt>title</dt>
    <dd>POST test</dd>
    <dt>content</dt>
    <dd>sample text</dd>
</dl>
<hr />
<dl>
    <dt>my_file</dt>
    <dd>
        name: sample.txt        <br />
        type: application/octet-stream      <br />
        size: 21        <br />
        error: 0        <br />
        tmp_name: /tmp/phpPF5VVZ        <br />
        <pre>string(21) "SAMPLE TEXT CONTENTS
"
</pre>
    </dd>
</dl>
"