2010-06-24 35 views
15

Tài liệu PHP states rằng chỉ có thể đọc php://input một lần.Tại sao php: // đầu vào có thể đọc nhiều hơn một lần mặc dù tài liệu nói khác?

Trong ứng dụng của tôi, tôi cần phải đọc nó hai lần, một lần cho mục đích xác thực và một lần để thực sự xử lý nội dung, và cả hai chức năng đều được xử lý bởi các mô-đun độc lập khác nhau. Điều điên rồ là: nó hoạt động.

Tôi có thể dựa vào tính năng này hoạt động ở mọi nơi, hoặc đây có phải là một thông dịch trong phiên bản PHP của tôi (5.2.10) không? Các tài liệu duy nhất tôi có thể tìm thấy về điều này là một trong đó nói rằng nó không nên làm việc, không có giới hạn phiên bản được đề cập.


Tiếp theo linh cảm Dennis', tôi đã làm bài kiểm tra này:

$in = fopen('php://input', 'r'); 
echo fread($in, 1024) . "\n"; 
fseek($in, 0); 
echo fread($in, 1024) . "\n"; 
fclose($in); 
echo file_get_contents('php://input') . "\n"; 

Curling:

$ curl http://localhost:8888/tests/test.php -d "This is a test" 
This is a test 

This is a test 

Rõ ràng đó là giới hạn trong một đọc mỗi tay cầm mở.


hơn một ít đào tiết lộ rằng thực sự php://input chỉ có thể được đọc một lần, bao giờ hết, cho PUT yêu cầu. Ví dụ trên đã sử dụng yêu cầu POST.

+3

... và bây giờ, 4.5 năm sau, PHP 5.6 chính thức [hỗ trợ] (http://docs.php.net/manual/en/migration56.new-features.php) đọc từ đầu vào 'php: // 'nhiều hơn một lần, và thậm chí tìm kiếm hoạt động :) –

+1

Đối với những người trong chúng ta chưa có trên PHP 5.6, gói' file_get_contents ('php: // input') 'trong một hàm lưu trữ kết quả và gọi thay vào đó là một công việc khả thi -xung quanh. – Umbrella

Trả lời

21

Kiểm tra một chút về mã nguồn mang lại câu trả lời.

Đầu tiên, vâng, bạn bị giới hạn một đọc mỗi tay cầm vì dòng cơ bản không thực hiện seek handler:

php_stream_ops php_stream_input_ops = { 
    php_stream_input_write, 
    /* ... */ 
    "Input", 
    NULL, /* seek */ 
    /* ... */ 
}; 

Thứ hai, xử lý đọc có hai hành vi khác nhau tùy thuộc vào việc "POST dữ liệu "đã được đọc và lưu trữ trong SG(request_info).raw_post_data.

if (SG(request_info).raw_post_data) { 
    read_bytes = SG(request_info).raw_post_data_length - *position; 
    /* ...*/ 
    if (read_bytes) { 
     memcpy(buf, SG(request_info).raw_post_data + *position, read_bytes); 
    } 
} else if (sapi_module.read_post) { 
    read_bytes = sapi_module.read_post(buf, count TSRMLS_CC); 
    /* ... */ 
} else { 
    stream->eof = 1; 
} 

Vì vậy, chúng tôi có ba khả năng ở đây:

  1. Yêu cầu dữ liệu cơ thể đã được đọc và lưu trữ trong SG(request_info).raw_post_data. Trong trường hợp này, vì dữ liệu được lưu trữ, chúng tôi có thể mở và đọc nhiều chốt cho php://input.
  2. Dữ liệu nội dung yêu cầu đã được đọc, nhưng nội dung của nó không được lưu trữ ở bất cứ đâu. php://input không thể cung cấp cho chúng tôi bất cứ điều gì.
  3. Dữ liệu yêu cầu chưa được đọc. Điều này có nghĩa là chúng tôi có thể mở php://input và chỉ đọc nó một lần.

LƯU Ý: Điều sau là hành vi mặc định. Các SAPI khác nhau hoặc các phần mở rộng bổ sung có thể thay đổi hành vi này.

Trong trường hợp yêu cầu POST, PHP định nghĩa trình đọc POST khác và trình xử lý POST tùy thuộc vào loại nội dung.

Trường hợp 1. này xảy ra khi chúng ta có một yêu cầu POST:

  • Với content-type application/x-www-form-encoded. sapi_activate phát hiện yêu cầu POST có loại nội dung và gọi sapi_read_post_data. Điều này phát hiện loại nội dung và định nghĩa cặp trình đọc/xử lý POST. Trình đọc POST là sapi_read_standard_form_data, được gọi ngay lập tức và chỉ sao chép phần thân yêu cầu vào SG(request_info).post_data. Trình đọc bài mặc định php_default_post_reader sau đó được gọi, điền vào $HTTP_RAW_POST_DATA nếu thiết lập ini always_populate_post_data được đặt và sau đó sao chép SG(request_info).post_data thành SG(request_info).raw_post_data và xóa đầu tiên. Cuộc gọi đến trình xử lý không quan trọng ở đây và được hoãn lại cho đến khi các superglobals được xây dựng (có thể không xảy ra, trong trường hợp JIT được kích hoạt và superglobals không được sử dụng).
  • Với nội dung không được công nhận hoặc không tồn tại. Trong trường hợp này, không có trình đọc và trình xử lý POST được xác định. Cả hai trường hợp đều kết thúc trong php_default_post_reader mà không cần đọc dữ liệu. Vì đây là yêu cầu POST và không có cặp trình đọc/xử lý, sapi_read_standard_form_data sẽ được gọi. Đây là chức năng tương tự như trình xử lý đọc loại nội dung application/x-www-form-encoded, vì vậy tất cả dữ liệu bị nuốt vào SG(request_info).post_data. Sự khác biệt duy nhất từ ​​bây giờ là $HTTP_RAW_POST_DATA luôn được điền vào (không quan trọng giá trị của always_populate_post_data) và không có trình xử lý để xây dựng các superglobals.

Trường hợp 2. Điều này xảy ra khi chúng tôi có yêu cầu biểu mẫu với loại dữ liệu "nhiều phần/biểu mẫu". Trình đọc POST là NULL, do đó trình xử lý là rfc1867_post_handler hoạt động như một hỗn hợp reader/handler. Không có dữ liệu nào được đọc trong giai đoạn sapi_activate. Hàm sapi_handle_post cuối cùng được gọi trong pha sau, trong đó, lần lượt nó gọi trình xử lý POST. rfc1867_post_handler đọc dữ liệu yêu cầu, điền POSTFILES, nhưng không để lại gì trong SG(request_info).raw_post_data.

Trường hợp 3. Trường hợp cuối cùng này diễn ra với các yêu cầu khác với POST (ví dụ: PUT). php_default_post_reader được gọi trực tiếp. Do yêu cầu không phải là yêu cầu POST nên dữ liệu bị nuốt bởi sapi_read_standard_form_data. Vì không có dữ liệu được đọc, không có bất cứ điều gì còn lại để được thực hiện.

+0

Câu trả lời độc đáo tuyệt đối! Nhưng, có lý do chính đáng nào cho hành vi này hay chỉ đơn giản là giám sát? I E. nó hoạt động "như dự định" hay "được mã hóa"? :) – deceze

+1

@deceze Việc triển khai có vẻ hợp lý. dữ liệu mã hóa ứng dụng/x-www-form thường ngắn, do đó, hình phạt bộ nhớ để giữ nó trong bộ nhớ là nhỏ. multipart/form-data thường lớn hơn một cách đáng kể (ví dụ: bao gồm các tệp), nó sẽ trở nên quan trọng hơn để có bộ nhớ. Với các yêu cầu POST khác, tôi không nghĩ rằng đó là một ý tưởng tuyệt vời để giữ mọi thứ trong bộ nhớ, nhưng nó là cần thiết cho khả năng tương thích ngược (các tập lệnh cũ chỉ có $ HTTP_RAW_POST_DATA). Đối với các phương thức yêu cầu khác, việc triển khai cũng có vẻ hợp lý - cho phép người dùng đọc dữ liệu theo kiểu luồng, tiết kiệm bộ nhớ. – Artefacto

+0

Tôi đoán điều đó có ý nghĩa. Cảm ơn! – deceze

3

Có thể chúng có nghĩa là fseek() hoặc tua lại() không khả dụng. Bạn đã thử một trong các chức năng này trên một đầu vào php: // đã mở chưa?

+1

Có vẻ như điều này có thể xảy ra. Tốt linh cảm. Xem câu hỏi được cập nhật. – deceze

+0

Có lẽ tôi đang xem cái gì đó, nhưng .. câu hỏi mới là gì? –

Các vấn đề liên quan