PHP php php: // đầu vào cộng với $ _POST


243

Tôi đã được hướng dẫn sử dụng phương thức php://inputthay vì $_POSTkhi tương tác với các yêu cầu Ajax từ JQuery. Điều tôi không hiểu là lợi ích của việc sử dụng phương pháp này so với phương pháp toàn cầu $_POSThoặc $_GET.


2
Tôi đã từng sử dụng "hack" để nhận các cuộc gọi ajax ở phía PHP trước khi tình cờ thấy bài đăng này và đọc các câu trả lời tuyệt vời của nó! Đối với những người khác có cùng một vấn đề trong tương lai, tôi hy vọng các công cụ tìm kiếm cũng sẽ đọc bình luận của tôi! :)
aderchox

Câu trả lời:


484

Lý do là php://inputtrả về tất cả dữ liệu thô sau các tiêu đề HTTP của yêu cầu, bất kể loại nội dung.

Siêu lớp PHP $_POST, chỉ được cho là bao bọc dữ liệu

  • application/x-www-form-urlencoded (loại nội dung tiêu chuẩn cho bài viết mẫu đơn giản) hoặc
  • multipart/form-data (chủ yếu được sử dụng để tải lên tập tin)

Điều này là do đây là các loại nội dung duy nhất phải được hỗ trợ bởi các tác nhân người dùng . Vì vậy, máy chủ và PHP theo truyền thống không mong đợi nhận được bất kỳ loại nội dung nào khác (điều đó không có nghĩa là chúng không thể).

Vì vậy, nếu bạn chỉ cần POST một HTML cũ tốt form, yêu cầu sẽ trông giống như thế này:

POST /page.php HTTP/1.1

key1=value1&key2=value2&key3=value3

Nhưng nếu bạn đang làm việc với Ajax rất nhiều, thì probaby này cũng bao gồm việc trao đổi dữ liệu phức tạp hơn với các kiểu (chuỗi, int, bool) và cấu trúc (mảng, đối tượng), vì vậy, trong hầu hết các trường hợp, JSON là lựa chọn tốt nhất. Nhưng một yêu cầu có tải trọng JSON sẽ trông giống như thế này:

POST /page.php HTTP/1.1

{"key1":"value1","key2":"value2","key3":"value3"}

Nội dung bây giờ sẽ là application/json(hoặc ít nhất là không có nội dung nào được đề cập ở trên), vì vậy trình biên dịch của PHP $_POSTkhông biết cách xử lý (chưa).

Dữ liệu vẫn còn đó, bạn không thể truy cập nó thông qua trình bao bọc. Vì vậy, bạn cần phải tự tìm nạp nó ở định dạng thô với file_get_contents('php://input')( miễn là nó không được multipart/form-datamã hóa ).

Đây cũng là cách bạn sẽ truy cập dữ liệu XML hoặc bất kỳ loại nội dung không chuẩn nào khác.


40
+1 cho "Đây cũng là cách bạn sẽ truy cập dữ liệu XML hoặc bất kỳ loại nội dung không chuẩn nào khác"
mandza

@Quasdank Tôi đang gửi JSON từ ứng dụng Android đến máy chủ php xampp trên đám mây ( stackoverflow.com/questions/36558261/ mẹo ) nhưng tôi không thể làm cho nó hoạt động khi tôi thử file_get_contents ('php: // input'), mà chỉ đơn giản trả về chuỗi (0). Cái này được sử dụng để làm việc trong máy cục bộ của tôi nhưng nó không hoạt động khi tôi triển khai nó lên đám mây. Bạn có thể vui lòng giúp tôi không?
The_Martian

1
Điều đáng chú ý là việc sử dụng đối tượng XMLHttpRequest trong yêu cầu AJAX cho PHP không có nghĩa là người ta phải đăng JSON. Đó là chi phí phụ, nhưng JavaScript phía máy khách của bạn có thể chuyển đổi sang định dạng ứng dụng / x-www-form-urlencoding. Tuy nhiên, bản dịch có thể không phải là kiểu dữ liệu thuần túy .
Anthony Rutledge

Cần phải nói rằng giới hạn của hai loại nội dung được công nhận phần lớn mang tính lịch sử. Không có gì ngăn PHP nhận ra tức application/jsonlà nguồn dữ liệu hợp lệ cho $_POSTmảng. Và thậm chí còn có các yêu cầu được công bố để hỗ trợ cụ thể.
AnrDaemon

Xin chào @quasdunk, bạn có thể vui lòng giúp tôi về magento.stackexchange.com/questions/296960/ không
Nagaraju K

53

php://inputcó thể cung cấp cho bạn các byte thô của dữ liệu. Điều này hữu ích nếu dữ liệu POST là cấu trúc được mã hóa JSON, thường là trường hợp cho yêu cầu AJAX POST.

Đây là một chức năng để làm việc đó:

  /**
   * Returns the JSON encoded POST data, if any, as an object.
   * 
   * @return Object|null
   */
  private function retrieveJsonPostData()
  {
    // get the raw POST data
    $rawData = file_get_contents("php://input");

    // this returns null if not valid json
    return json_decode($rawData);
  }

Các $_POSTmảng là hữu ích hơn khi bạn đang xử lý dữ liệu quan trọng có giá trị từ một hình thức, được gửi bởi một POST truyền thống. Điều này chỉ hoạt động nếu dữ liệu đã đăng ở định dạng được công nhận, thường application/x-www-form-urlencoded(xem http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 để biết chi tiết).


7
Điều đáng chú ý là nếu bạn truyền truetham số thứ hai vào json_decode, nó sẽ trả về một mảng kết hợp.
Vahid Amiri

28

Nếu dữ liệu bài viết không đúng định dạng, $ _POST sẽ không chứa bất cứ thứ gì. Tuy nhiên, php: // input sẽ có chuỗi không đúng.

Ví dụ, có một số ứng dụng ajax, không tạo thành chuỗi khóa-giá trị bài chính xác để tải lên tệp và chỉ đổ tất cả tệp dưới dạng dữ liệu bài đăng, không có tên biến hoặc bất cứ điều gì. $ _POST sẽ trống, $ _FILES cũng trống và đầu vào php: // sẽ chứa tệp chính xác, được viết dưới dạng chuỗi.


22

Đầu tiên, một sự thật cơ bản về PHP.

PHP không được thiết kế để cung cấp cho bạn một giao diện giống như REST (GET, POST, PUT, PATCH, DELETE) rõ ràng để xử lý các yêu cầu HTTP .

Tuy nhiên, $_POST, $_GET, và $_FILES superglobals , và các chức năng filter_input_array()rất hữu ích cho nhu cầu người bình thường của / layman.

Ưu điểm tiềm ẩn số một của $_POST(và $_GET) là dữ liệu đầu vào của bạn được mã hóa tự động bởi PHP . Bạn thậm chí không bao giờ nghĩ về việc phải làm điều đó, đặc biệt là đối với các tham số chuỗi truy vấn trong yêu cầu GET tiêu chuẩn.

Tuy nhiên, sau đó bạn tìm hiểu thêm ...

Điều đó đang được nói, khi bạn nâng cao kiến ​​thức lập trình và muốn sử dụng XmlHttpRequestđối tượng JavaScript (jQuery cho một số), bạn sẽ thấy giới hạn của sơ đồ này.

$_POSTgiới hạn bạn trong việc sử dụng hai loại phương tiện trong Content-Typetiêu đề HTTP :

  1. application/x-www-form-urlencoded
  2. multipart/form-data

Do đó, nếu bạn muốn gửi các giá trị dữ liệu tới PHP trên máy chủ và để nó hiển thị trong $_POSTsuperglobal , thì bạn phải urlencode nó ở phía máy khách và gửi dữ liệu đã nói dưới dạng cặp khóa / giá trị - một bước bất tiện cho người mới (đặc biệt là khi cố gắng tìm hiểu xem các phần khác nhau của URL có yêu cầu các hình thức mã hóa url khác nhau: bình thường, thô, v.v.).

Đối với tất cả những người dùng jQuery của bạn, $.ajax()phương thức này đang chuyển đổi các cặp khóa / giá trị được mã hóa URL của bạn trước khi truyền chúng đến máy chủ. Bạn có thể ghi đè hành vi này bằng cách cài đặt processData: false. Chỉ cần đọc tài liệu $ .ajax () và đừng quên gửi loại phương tiện chính xác trong tiêu đề Kiểu nội dung.

Mã hóa URL? Cái quái gì thế !!! ???

Thông thường, nếu bạn đang thực hiện một yêu cầu HTTP bình thường, đồng bộ (khi toàn bộ trang vẽ lại) các biểu mẫu HTML, tác nhân người dùng (trình duyệt web) sẽ urlencode dữ liệu biểu mẫu của bạn cho bạn. Nếu bạn muốn làm một yêu cầu HTTP không đồng bộ bằng cách sử dụng XmlHttpRequestđối tượng, sau đó bạn phải thời trang một chuỗi urlencoded và gửi nó, nếu bạn muốn dữ liệu đó để hiển thị trong $_POSTsuperglobal .

Làm thế nào bạn liên lạc với JavaScript? :-)

Chuyển đổi từ một mảng hoặc đối tượng JavaScript thành một chuỗi urlencoding làm phiền nhiều nhà phát triển (ngay cả với các API mới như Dữ liệu biểu mẫu ). Thay vào đó, họ chỉ có thể gửi JSON và sẽ hiệu quả hơn cho mã máy khách để làm như vậy.

Hãy nhớ (nháy mắt, nháy mắt), nhà phát triển web trung bình không học cách sử dụng XmlHttpRequesttrực tiếp đối tượng, hàm toàn cục, hàm chuỗi, hàm mảng và biểu thức chính quy như bạn và tôi ;-). Urlencoding đối với họ là một cơn ác mộng. ;-)

PHP, những gì cho?

Việc thiếu xử lý XML và JSON trực quan của PHP khiến nhiều người tắt. Bây giờ bạn sẽ nghĩ nó sẽ là một phần của PHP (thở dài).

Vì vậy, nhiều loại phương tiện (loại MIME trong quá khứ)

XML, JSON và YAML đều có các loại phương tiện có thể được đặt vào Content-Typetiêu đề HTTP .

  • ứng dụng / xml
  • táo / json
  • application / yaml (mặc dù IANA không có chỉ định chính thức được liệt kê)

Hãy xem IANA có bao nhiêu loại phương tiện (trước đây là các loại MIME).

Hãy xem có bao nhiêu tiêu đề HTTP .

php: // đầu vào hoặc phá sản

Sử dụng php://inputluồng cho phép bạn vượt qua mức độ trừu tượng của việc ngồi / nắm tay em bé mà PHP đã buộc trên thế giới. :-) Với sức mạnh lớn đến trách nhiệm lớn!

Bây giờ, trước khi bạn xử lý các giá trị dữ liệu được truyền qua php://input, bạn nên / phải làm một vài điều.

  1. Xác định xem phương thức HTTP chính xác đã được chỉ định chưa (GET, POST, PUT, PATCH, DELETE, ...)
  2. Xác định xem tiêu đề Kiểu nội dung HTTP đã được truyền chưa.
  3. Xác định xem giá trị cho Loại Nội dung có phải là loại phương tiện mong muốn không.
  4. Xác định xem dữ liệu được gửi có được định dạng tốt XML / JSON / YMAL / v.v.
  5. Nếu cần, chuyển đổi dữ liệu thành kiểu dữ liệu PHP: mảng hoặc đối tượng.
  6. Nếu bất kỳ kiểm tra hoặc chuyển đổi cơ bản nào không thành công, hãy ném ngoại lệ !

Mã hóa ký tự thì sao?

À, HÀ! Có, bạn có thể muốn luồng dữ liệu được gửi vào ứng dụng của mình được mã hóa UTF-8, nhưng làm thế nào bạn có thể biết liệu nó có hay không?

Hai vấn đề quan trọng.

  1. Bạn không biết có bao nhiêu dữ liệu đi qua php://input.
  2. Bạn không biết chắc chắn mã hóa hiện tại của luồng dữ liệu.

Bạn sẽ cố gắng xử lý dữ liệu truyền phát mà không biết có bao nhiêu đầu tiên? Đó là một ý tưởng khủng khiếp . Bạn không thể chỉ dựa vào Content-Lengthtiêu đề HTTP để được hướng dẫn về kích thước của luồng đầu vào vì nó có thể bị giả mạo.

Bạn sẽ cần một:

  1. Thuật toán phát hiện kích thước luồng.
  2. Ứng dụng giới hạn kích thước luồng được xác định (giới hạn Apache / Nginx / PHP có thể quá rộng).

Bạn có định chuyển đổi dữ liệu luồng thành UTF-8 mà không biết mã hóa hiện tại của luồng không? Làm sao? Bộ lọc luồng iconv ( ví dụ về bộ lọc luồng iconv ) dường như muốn mã hóa bắt đầu và kết thúc, như thế này.

'convert.iconv.ISO-8859-1/UTF-8'

Vì vậy, nếu bạn có lương tâm, bạn sẽ cần:

  1. Thuật toán phát hiện mã hóa dòng.
  2. Thuật toán định nghĩa bộ lọc luồng động / thời gian chạy (vì bạn không thể biết mã hóa bắt đầu).

( Cập nhật : 'convert.iconv.UTF-8/UTF-8'sẽ buộc mọi thứ thành UTF-8, nhưng bạn vẫn phải tính đến các ký tự mà thư viện iconv có thể không biết cách dịch. Nói cách khác, bạn phải xác định cách thực hiện hành động nào khi nhân vật không thể dịch : 1) Chèn một ký tự giả, 2) Thất bại / ném và ngoại lệ).

Bạn không thể chỉ dựa hoàn toàn vào Content-Encodingtiêu đề HTTP , vì điều này có thể chỉ ra điều gì đó như nén như sau. Đây không phải là những gì bạn muốn đưa ra quyết định liên quan đến iconv.

Content-Encoding: gzip

Do đó, các bước chung có thể là ...

Phần I: Yêu cầu HTTP liên quan

  1. Xác định xem phương thức HTTP chính xác đã được chỉ định chưa (GET, POST, PUT, PATCH, DELETE, ...)
  2. Xác định xem tiêu đề Kiểu nội dung HTTP đã được truyền chưa.
  3. Xác định xem giá trị cho Loại Nội dung có phải là loại phương tiện mong muốn không.

Phần II: Truyền dữ liệu liên quan

  1. Xác định kích thước của luồng đầu vào (tùy chọn, nhưng được khuyến nghị).
  2. Xác định mã hóa của luồng đầu vào.
  3. Nếu cần, hãy chuyển đổi luồng đầu vào thành mã hóa ký tự mong muốn (UTF-8).
  4. Nếu cần, đảo ngược bất kỳ nén hoặc mã hóa cấp ứng dụng, sau đó lặp lại các bước 4, 5 và 6.

Phần III: Kiểu dữ liệu liên quan

  1. Xác định xem dữ liệu được gửi có được định dạng tốt XML / JSON / YMAL / v.v.

(Hãy nhớ rằng, dữ liệu vẫn có thể là một chuỗi được mã hóa URL mà sau đó bạn phải phân tích cú pháp và giải mã URL).

  1. Nếu cần, chuyển đổi dữ liệu thành kiểu dữ liệu PHP: mảng hoặc đối tượng.

Phần IV: Giá trị dữ liệu liên quan

  1. Lọc dữ liệu đầu vào.

  2. Xác thực dữ liệu đầu vào.

Bây giờ bạn có thấy không?

Các $_POSTsuperglobal, cùng với các thiết lập php.ini cho giới hạn về đầu vào, là đơn giản hơn cho người không có đạo. Tuy nhiên, việc xử lý mã hóa ký tự sẽ trực quan và hiệu quả hơn nhiều khi sử dụng các luồng vì không cần phải lặp qua các siêu lớp (hay nói chung là mảng) để kiểm tra các giá trị đầu vào cho mã hóa phù hợp.


1
Tuyệt vời! Câu trả lời này nên có đánh giá cao hơn nhiều. Cảm ơn bạn rất nhiều vì đã mang ánh sáng lũ vào bóng tối.
Lox

Trong phân tích cuối cùng, PHP sẽ làm tốt để cập nhật các mặc định cơ sở. Tuy nhiên, việc kết hợp một phương thức yêu cầu HTTP với cấu trúc dữ liệu có cùng tên ($ _GET, $ _POST) là một sai lầm logic. Vấn đề là (1) phương thức yêu cầu HTTP mong muốn và (2) có dữ liệu yêu cầu với yêu cầu đó (Loại nội dung). Do đó, như với Perl, bạn sẽ thấy rằng bạn có thể là nạn nhân sẵn sàng cho ý kiến ​​của những người sáng tạo / duy trì ngôn ngữ.
Anthony Rutledge

0

Vì vậy, tôi đã viết một hàm sẽ lấy dữ liệu POST từ luồng đầu vào php: // .

Vì vậy, thách thức ở đây là chuyển sang phương thức yêu cầu PUT, DELETE OR PATCH và vẫn lấy được dữ liệu bài đăng được gửi cùng với yêu cầu đó.

Tôi đang chia sẻ điều này có lẽ cho một người có thử thách tương tự. Các chức năng dưới đây là những gì tôi đã đưa ra và nó hoạt động. Tôi hy vọng nó sẽ giúp!

    /**
     * @method Post getPostData
     * @return array
     * 
     * Convert Content-Disposition to a post data
     */
    function getPostData() : array
    {
        // @var string $input
        $input = file_get_contents('php://input');

        // continue if $_POST is empty
        if (strlen($input) > 0 && count($_POST) == 0 || count($_POST) > 0) :

            $postsize = "---".sha1(strlen($input))."---";

            preg_match_all('/([-]{2,})([^\s]+)[\n|\s]{0,}/', $input, $match);

            // update input
            if (count($match) > 0) $input = preg_replace('/([-]{2,})([^\s]+)[\n|\s]{0,}/', '', $input);

            // extract the content-disposition
            preg_match_all("/(Content-Disposition: form-data; name=)+(.*)/m", $input, $matches);

            // let's get the keys
            if (count($matches) > 0 && count($matches[0]) > 0)
            {
                $keys = $matches[2];

                foreach ($keys as $index => $key) :
                    $key = trim($key);
                    $key = preg_replace('/^["]/','',$key);
                    $key = preg_replace('/["]$/','',$key);
                    $key = preg_replace('/[\s]/','',$key);
                    $keys[$index] = $key;
                endforeach;

                $input = preg_replace("/(Content-Disposition: form-data; name=)+(.*)/m", $postsize, $input);

                $input = preg_replace("/(Content-Length: )+([^\n]+)/im", '', $input);

                // now let's get key value
                $inputArr = explode($postsize, $input);

                // @var array $values
                $values = [];

                foreach ($inputArr as $index => $val) :
                    $val = preg_replace('/[\n]/','',$val);

                    if (preg_match('/[\S]/', $val)) $values[$index] = trim($val);

                endforeach;

                // now combine the key to the values
                $post = [];

                // @var array $value
                $value = [];

                // update value
                foreach ($values as $i => $val) $value[] = $val;

                // push to post
                foreach ($keys as $x => $key) $post[$key] = isset($value[$x]) ? $value[$x] : '';

                if (is_array($post)) :

                    $newPost = [];

                    foreach ($post as $key => $val) :

                        if (preg_match('/[\[]/', $key)) :

                            $k = substr($key, 0, strpos($key, '['));
                            $child = substr($key, strpos($key, '['));
                            $child = preg_replace('/[\[|\]]/','', $child);
                            $newPost[$k][$child] = $val;

                        else:

                            $newPost[$key] = $val;

                        endif;

                    endforeach;

                    $_POST = count($newPost) > 0 ? $newPost : $post;

                endif;
            }

        endif;

        // return post array
        return $_POST;
    }

-5

Ví dụ đơn giản về cách sử dụng nó

 <?php  
     if(!isset($_POST) || empty($_POST)) { 
     ?> 
        <form name="form1" method="post" action=""> 
          <input type="text" name="textfield"><br /> 
          <input type="submit" name="Submit" value="submit"> 
        </form> 
   <?php  
        } else { 
        $example = file_get_contents("php://input");
        echo $example;  }  
   ?>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.