2015年10月28日 星期三

PHP使用fsockopen() post資料到URL並取得內容

在 php使用curl上傳檔案 中,PHP使用curl去呼叫API,
在ubuntu系統中 curl必須安裝 php5-curl
除了curl還有另一種方法,使用fsockopen()

測試API接口
http://test.localhost/PHP/wget_server.php
echo json_encode($_REQUEST);

stackoverflow的範例
// http://stackoverflow.com/questions/2367458/php-post-data-with-fsockopen
$fp = fsockopen('test.localhost', 80); // 必須寫 test.localhost。不能加protocol、位址和參數 http://test.localhost/PHP/wget_server.php?bear=2

// post的資料
$vars = array(
    'hello' => 'world',
    'bear' => 123
);
$content = http_build_query($vars); // $content:hello=world&bear=123

fputs($fp, "POST /PHP/wget_server.php?bear=2 HTTP/1.1\r\n"); // 位址和參數寫在這,使用POST
fputs($fp, "Host: test.localhost\r\n"); //再寫一次host
fputs($fp, "Content-Type: application/x-www-form-urlencoded\r\n");
fputs($fp, "Content-Length: ".strlen($content)."\r\n");
fputs($fp, "Connection: close\r\n");
fputs($fp, "Referer: https://www.google.com.tw/?gws_rd=ssl\r\n"); //偽造 $_SERVER['HTTP_REFERER']
fputs($fp, "Cookie: test=456;ai=789\r\n"); // 設定$_COOKIE
fputs($fp, "\r\n");

fputs($fp, $content);

header('Content-type: text/plain'); // 設定Content-Type為純文字
while (!feof($fp)) {
    echo fgets($fp, 1024);
}

fclose($fp);
結果:
HTTP/1.1 200 OK
Date: Wed, 28 Oct 2015 07:16:26 GMT
Server: Apache/2.4.12 (Win32) OpenSSL/1.0.1l PHP/5.6.8
X-Powered-By: PHP/5.6.8
Content-Length: 30
Connection: close
Content-Type: text/html; charset=UTF-8

{"bear":"123","hello":"world"}
$_SERVER['HTTP_REFERER']:https://www.google.com.tw/?gws_rd=ssl
$_COOKIE:Array
(
    [test] => 456
    [ai] => 789
)

說明:
Content-Type,内容类型,一般是指网页中存在的Content-Type,用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件
如果未指定 ContentType,默认为TEXT/HTML
指令為text/plain 後,瀏覽器會直接顯示<tag>而不會視為HTML

blog.hsin.tw 的範例
//接收POST參數的URL
$url = "http://test.localhost/PHP/wget_server.php?bear=2";

//POST參數,在這個陣列裡,索引是name,值是value,沒有限定組數
$postdata = array('post_name' => 'post_value', 'acc' => 'hsin', 'nick' => 'joe');

//函式回覆的值就是取得的內容
$result = sendpost($url, $postdata);

echo "\$result:";
print_r($result);

function sendpost($url, $data) {
    
    //先解析url 取得的資訊可以看看http://www.php.net/parse_url
    $url = parse_url($url);
    $url_port = $url['port'] == '' ? (($url['scheme'] == 'https') ? 443 : 80) : $url['port'];
    if (!$url) return "couldn't parse url";
    echo "\$url_port:".$url_port;
    echo "\$url:";
    print_r($url);
    
    //對要傳送的POST參數作處理
    $encoded = "";
    while (list($k, $v) = each($data)) {
        $encoded.= ($encoded ? '&' : '');
        $encoded.= rawurlencode($k) . "=" . rawurlencode($v);
    }

    echo "$encoded:";
    print_r($encoded);
    
    //開啟一個socket
    $fp = fsockopen($url['host'], $url_port);
    if (!$fp) return "Failed to open socket to " . $url['host'];
    
    //header的資訊
    fputs($fp, 'POST ' . $url['path'] . ($url['query'] ? '?' . $url['query'] : '') . " HTTP/1.0\r\n");
    fputs($fp, "Host: " . $url['host'] . "\n");
    fputs($fp, "Content-type: application/x-www-form-urlencoded\n");
    fputs($fp, "Content-length: " . strlen($encoded) . "\n");
    fputs($fp, "Connection: close\n\n");
    fputs($fp, $encoded . "\n");
    
    //取得回應的內容
    $line = fgets($fp, 1024);
    if (!eregi("^HTTP/1.. 200", $line)) return;
    $results = "";
    $inheader = 1;
    while (!feof($fp)) {
        $line = fgets($fp, 2048);
        if ($inheader && ($line == "\n" || $line == "\r\n")) {
            $inheader = 0;
        } 
        elseif (!$inheader) {
            $results.= $line;
        }
    }
    fclose($fp);
    return $results;
}
結果:
$url_port:80
$url:Array
(
    [scheme] => http
    [host] => test.localhost
    [path] => /PHP/wget_server.php
    [query] => bear=2
)
$encoded:post_name=post_value&acc=hsin&nick=joe
$result:{"bear":"2","post_name":"post_value","acc":"hsin","nick":"joe"}

說明:
該網站上的範例有誤,某些換行特殊符號忘記跳脫,如
if($inheader&&($line == "n" || $line == "rn")){
應為
if($inheader&&($line == "\n" || $line == "\r\n")){

\r\n通常是微軟的文件會產生的,在其它的編輯器裡面,會在一行的節尾看到 ^M,那就是\r\n
在php中,字串中的\r\n或\n,要用雙引號才有效。
'\r'是回車,'\n'是換行,前者使光標到行首,後者使光標下移一格。通常用的Enter是兩個加起來

參考資料:
http://blog.hsin.tw/2009/php-post-method-fsockopen/ php 傳送POST到別的URL並取得回應內容 使用fsockopen
http://stackoverflow.com/questions/2367458/php-post-data-with-fsockopen PHP Post data with Fsockopen
http://www.t086.com/code/php/function.php-fsockopen.php 函数:fsockopen()
http://seacatcry.pixnet.net/blog/post/13732061-%E3%80%90%E8%BD%89%E8%B2%BC%E3%80%91%5Cr%5Cn%E5%92%8C%5Cn%E7%9A%84%E5%B7%AE%E7%95%B0 【轉貼】\r\n和\n的差異


沒有留言:

張貼留言