PHP中的$_POST和file_get_content('php://input')

PHP中的$_POST和file_get_content('php://input')

故事背景:又是一個激情四射的故事。前段時間咱們在弄咱們本身的開源基礎組件,基礎組件包括咱們常用的一些組件,好比說aliyunMQ,aliyunSearch、request(基於guzzle)等進行封裝處理。就是這個request引起了一些美好的誤會。javascript

$_POST

老規矩,查看官方文檔,全世界都會騙你,可是文檔不會騙你。php

$HTTP_POST_VARS [已棄用]
$_POST -- $HTTP_POST_VARS [已棄用] — HTTP POST 變量
當 HTTP POST 請求的 Content-Type 是 application/x-www-form-urlencoded 或 multipart/form-data 時,會將變量以關聯數組形式傳入當前腳本。

$HTTP_POST_VARS 包含相同的信息,但它不是一個超全局變量。 (注意 $HTTP_POST_VARS 和 $_POST 是不一樣的變量,PHP 處理它們的方式不一樣)

一看文檔就知道所有信息了,$_POST支持的request中的header中的content-type的類型只有application/x-www-form-urlencoded 或 multipart/form-data 。css

當咱們使用guzzle的時候它會根據你傳入的params是不是數組進行判斷,若是不是數組會在body中。可是若是是數組它就會按照json方式進行傳遞,content-type會application/json的方式固然不會被$_POST進行處理。因此,這邊是沒有毛病的。可是,難道由於用了這個組件就不進行這個類型處理了嗎?顯然不行。html

php://input

php:// — 訪問各個輸入/輸出流(I/O streams)

PHP 提供了一些雜項輸入/輸出(IO)流,容許訪問 PHP 的輸入輸出流、標準輸入輸出和錯誤描述符, 內存中、磁盤備份的臨時文件流以及能夠操做其餘讀取寫入文件資源的過濾器。

php://input 是個能夠訪問請求的原始數據的只讀流。 POST 請求的狀況下,最好使用 php://input 來代替 $HTTP_RAW_POST_DATA,由於它不依賴於特定的 php.ini 指令。 並且,這樣的狀況下 $HTTP_RAW_POST_DATA 默認沒有填充, 比激活 always_populate_raw_post_data 潛在須要更少的內存。 enctype="multipart/form-data" 的時候 php://input 是無效的。

Note: 在 PHP 5.6 以前 php://input 打開的數據流只能讀取一次; 數據流不支持 seek 操做。 不過,依賴於 SAPI 的實現,請求體數據被保存的時候, 它能夠打開另外一個 php://input 數據流並從新讀取。 一般狀況下,這種狀況只是針對 POST 請求,而不是其餘請求方式,好比 PUT 或者 PROPFIND。

這個東西說實話,就是$_POST基於這個進行封裝處理了一層。它可以獲取到最原始的數據,無論你是什麼content-type,它都可以獲取到數據。因此,當咱們的post的過來的數據是原始數據的時候,好比說是application/json或者' application/x-json,text/xml, application/xml,application/x-xml這些時候,$_POST都是沒法獲取到數據的。此刻,咱們就須要經過php://input進行獲取原始數據了。可是,原始數據並非咱們想要的最終格式。所以,咱們須要進行封裝一層。java

parseRequest

request類實際上是很好寫的,可是通常的類中對post方式傳遞參數仍是老方式。所以,我改寫了通常的類中獲取post的參數,對於全部post請求方式的方法,針對不一樣的content-type進行數據獲取和原始數據的解析,弄成咱們想要的最終數組。
核心代碼:json

public static function post($key = NULL, $default = NULL)
    {
        $data = [];       if(in_array($_SERVER['CONTENT_TYPE'],self::$formats['json'])){
            $data = file_get_contents('php://input');
            $data = json_decode($data,true);
        }

        if(in_array($_SERVER['CONTENT_TYPE'],self::$formats['xml'])){
            $data = file_get_contents('php://input');
            $data = DataParser::toArray($data);
        }

        if($key==null && !empty($data)){
            return $data;
        }

        if(!empty($data)){
            return isset($data[$key]) ? $data[$key] : $default;
        }

        return static::lookup($_POST, $key, $default);
    }

在類中咱們會定義幾個conten-type的format數組,經過$_SERVER['CONTENT_TYPE']來進行判斷處理,針對性的進行數據獲取和轉換。數組

protected static $formats = array(
        'html' => array('text/html', 'application/xhtml+xml'),
        'txt'  => array('text/plain'),
        'js'   => array('application/javascript',
            'application/x-javascript', 'text/javascript'),
        'css'  => array('text/css'),
        'json' => array('application/json', 'application/x-json'),
        'xml'  => array('text/xml', 'application/xml',
            'application/x-xml'),
        'rdf'  => array('application/rdf+xml'),
        'atom' => array('application/atom+xml'),
        'rss'  => array('application/rss+xml'),
    );

針對xml格式的數據,同時封裝了數據處理類app

public static function toArray($xml)
    {
        if (!$xml) {
            return false;
        }

        // 檢查xml是否合法
        $xml_parser = xml_parser_create();
        if (!xml_parse($xml_parser, $xml, true)) {
            xml_parser_free($xml_parser);
            return false;
        }
        libxml_disable_entity_loader(true);

        $data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);

        return $data;
    }

就是簡單的將xml格式數據轉換成array數據。post

後話

通常$_POST在不少項目中會有使用,基本上用來使用獲取參數數據。一般的請求的content-type不會出現很奇怪的,可是我以爲仍是須要本身去封裝一層進行數據獲取的類,可以省去不少麻煩,或者說對於大家項目的一致性有不少幫助。atom

相關文章
相關標籤/搜索