PHP“php : // input”vs $ _POST


243

JQuery의 Ajax 요청과 상호 작용할 때 php://input대신 메소드를 사용하라는 지시를 $_POST받았습니다. 내가 이해하지 못하는 것은 이것을 사용하는 것 $_POST또는 의 전역 방법의 이점입니다 $_GET.


2
필자는이 포스트를 우연히 다루고 멋진 답변을 읽기 전에 PHP 측에서 아약스 호출을 받기 위해 "해킹"을 사용했습니다! 앞으로 같은 문제가있는 다른 사람들을 위해 검색 엔진에서도 내 의견을 읽어 보시기 바랍니다. :)
aderchox

답변:


484

그 이유는 php://input컨텐츠 유형에 관계없이 요청의 HTTP 헤더 다음에 모든 원시 데이터 를 리턴하기 때문입니다 .

PHP superglobal 은 다음 중 하나 인 데이터 $_POST만 랩핑 해야 합니다.

  • application/x-www-form-urlencoded (간단한 양식 게시물의 표준 콘텐츠 유형) 또는
  • multipart/form-data (주로 파일 업로드에 사용됨)

이는 사용자 에이전트가 지원 해야하는 유일한 컨텐츠 유형이기 때문 입니다. 따라서 서버와 PHP는 전통적으로 다른 콘텐츠 유형을 기대하지 않습니다 (그렇지 않다는 의미는 아님).

따라서 좋은 오래된 HTML을 POST form하면 요청은 다음과 같습니다.

POST /page.php HTTP/1.1

key1=value1&key2=value2&key3=value3

그러나 Ajax를 많이 사용하는 경우이 probaby에는 유형 (문자열, 정수, 부울) 및 구조 (배열, 객체) 로보 다 복잡한 데이터를 교환하는 것이 포함되므로 대부분의 경우 JSON이 최선의 선택입니다. 그러나 JSON 페이로드가있는 요청은 다음과 같습니다.

POST /page.php HTTP/1.1

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

내용은 이제 application/json(또는 위에서 언급 한 것 중 적어도 하나는 아니므로) PHP의 $_POST-wrapper는 (아직) 처리 방법을 모릅니다.

데이터는 여전히 존재하므로 래퍼를 통해 액세스 할 수 없습니다. 당신은 원시 형식으로 직접 가져올 필요가 그래서 file_get_contents('php://input')( 이 아니에요 한로 multipart/form-data인코딩 된 ).

XML 데이터 또는 기타 비표준 컨텐츠 유형에 액세스하는 방법도 있습니다.


40
"이것은 또한 XML 데이터 또는 기타 비표준 컨텐츠 유형에 액세스하는 방법입니다"에 +1
mandza

@Quasdank 클라우드에서 안드로이드 앱에서 PHP xampp 서버로 JSON을 보내고 있지만 ( stackoverflow.com/questions/36558261/… ) file_get_contents ( 'php : // input')을 시도했을 때 작동하지 못했습니다. 단순히 string (0)을 반환합니다. 이것은 내 로컬 컴퓨터에서 작동했지만 클라우드에 배포했을 때 작동하지 않습니다. 제발 좀 도와 줄래?
The_Martian

1
PHP에 대한 AJAX 요청에서 XMLHttpRequest 객체를 사용한다고해서 JSON을 게시해야한다는 것은 아닙니다. 추가 오버 헤드이지만 클라이언트 측 JavaScript는 application / x-www-form-urlencoded 형식으로 변환 할 수 있습니다. 그러나 변환이 datatype pure 가 아닐 수 있습니다 .
Anthony Rutledge

인식되는 두 가지 컨텐츠 유형의 한계는 대체로 역사적이라고 할 필요가 있습니다. PHP가 배열의 application/json유효한 데이터 소스로 인식하는 것을 막는 것은 없습니다 $_POST. 또한 해당 지원에 대한 게시 된 요청도 있습니다.
AnrDaemon


53

php://input데이터의 원시 바이트를 제공 할 수 있습니다. POST 된 데이터가 JSON으로 인코딩 된 구조 인 경우에 유용하며, 이는 종종 AJAX POST 요청의 경우입니다.

이를 수행하는 기능은 다음과 같습니다.

  /**
   * 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);
  }

$_POST배열은 기존 POST에서 제출 한 양식의 키-값 데이터를 처리 할 때 더 유용합니다. POST 된 데이터가 인식 가능한 형식 인 경우에만 작동합니다 application/x-www-form-urlencoded( 일반적으로 http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 참조).


7
true두 번째 매개 변수로 전달 json_decode하면 연관 배열이 반환됩니다.
Vahid Amiri 5

28

게시물 데이터의 형식이 잘못된 경우 $ _POST에 아무것도 포함되지 않습니다. 그러나 php : // input에는 잘못된 문자열이 있습니다.

예를 들어 파일을 업로드하기위한 올바른 사후 키-값 시퀀스를 형성하지 않고 변수 이름이나 아무것도없이 모든 파일을 사후 데이터로 덤프하는 일부 ajax 애플리케이션이 있습니다. $ _POST는 비어 있고 $ _FILES도 비어 있으며 php : // input은 문자열로 작성된 정확한 파일을 포함합니다.


22

첫째, PHP에 대한 기본 진실.

PHP는 HTTP 요청을 처리하기위한 인터페이스와 같은 순수한 REST (GET, POST, PUT, PATCH, DELETE)를 명시 적으로 제공하도록 설계되지 않았습니다 .

그러나 $_POST, $_GET$_FILES 자동 전역 , 그리고 기능은 filter_input_array()보통 사람의 / 평신도의 요구에 매우 유용합니다.

$_POST(및 $_GET)의 가장 큰 장점은 입력 데이터가 PHP에 의해 자동으로 URL 디코딩 된다는 것 입니다. 표준 GET 요청 내의 쿼리 문자열 매개 변수에 대해서는 그렇게 할 필요조차 없습니다.

그러나, 당신은 더 많은 것을 배우고 ...

프로그래밍 지식이 발전하고 JavaScript XmlHttpRequest객체 (일부는 jQuery) 를 사용하려는 경우이 체계의 한계를 알게됩니다.

$_POSTHTTP Content-Type헤더 에서 두 가지 미디어 유형을 사용하도록 제한합니다 .

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

서버에 PHP로 데이터 값을 전송하고,이에 표시해야 할 경우에 따라서 $_POST자동 전역 , 당신은해야한다 를 urlencode 키 / 값 쌍으로 데이터를 클라이언트 측에 및 전송했다 - 초보자를위한 불편한 단계를 (특히 URL의 다른 부분에 다른 형식의 urlencoding (일반, 원시 등)이 필요한지 알아낼 때).

모든 jQuery 사용자에게이 $.ajax()방법은 JSON을 URL 인코딩 된 키 / 값 쌍으로 변환 한 후 서버로 전송합니다. 을 설정하여이 동작을 무시할 수 있습니다 processData: false. $ .ajax () documentation을 읽고 Content-Type 헤더에 올바른 미디어 유형을 보내는 것을 잊지 마십시오.

URL 인코딩? 이런 젠장!!!???

일반적으로 HTML 양식을 사용하여 일반적인 동기식 (전체 페이지를 다시 그릴 때) HTTP 요청을 수행하는 경우 사용자 에이전트 (웹 브라우저)가 양식 데이터를 표시합니다. XmlHttpRequest객체를 사용하여 비동기 HTTP 요청을 수행하려면 해당 데이터를 $_POSTsuperglobal 에 표시하려면 urlencoded 문자열을 작성하여 보내야합니다 .

JavaScript와 어떻게 연락하십니까? :-)

JavaScript 배열 또는 객체를 urlencoded 문자열로 변환하면 많은 개발자가 방해를 받습니다 ( Form Data 와 같은 새로운 API를 사용하더라도 ). 오히려 JSON을 보낼 수있을뿐 아니라 클라이언트 코드에서 보다 효율적 으로 처리 할 수 ​​있습니다.

평균 웹 개발자는 XmlHttpRequest객체, 전역 함수, 문자열 함수, 배열 함수 및 정규 표현식과 같은 사용자 를 사용하는 법을 배우지 않습니다 . 그들을위한 Urlencoding은 악몽입니다. ;-)

PHP, 무엇을 제공합니까?

직관적 인 XML과 JSON 처리가 부족한 PHP는 많은 사람들을 혼란시킵니다. 지금까지는 PHP의 일부라고 생각할 것입니다.

너무 많은 미디어 유형 (과거의 MIME 유형)

XML, JSON 및 YAML에는 모두 HTTP Content-Type헤더에 넣을 수있는 미디어 유형이 있습니다 .

  • 응용 프로그램 / xml
  • 응용 프로그램 / json
  • 응용 프로그램 / yaml (IANA에는 공식 명칭이 없지만)

얼마나 많은 봐 미디어 유형 (구, MIME 유형이) IANA에 의해 정의된다.

얼마나 많은 HTTP 헤더 가 있는지보십시오 .

php : // 입력 또는 버스트

은 Using php://input스트림하면 PHP가 세계에 강요했다고 추상화 아기 돌보기 / 손을 잡고 레벨을 우회 할 수 있습니다. :-) 큰 힘에는 큰 책임이 따른다!

이제를 통해 스트리밍되는 데이터 값을 처리하기 전에 php://input몇 가지 작업을 수행해야합니다.

  1. 올바른 HTTP 메소드 가 표시되었는지 판별하십시오 (GET, POST, PUT, PATCH, DELETE, ...).
  2. HTTP Content-Type 헤더가 전송되었는지 확인하십시오 .
  3. Content-Type 의 이 원하는 매체 유형 인지 판별하십시오 .
  4. 전송 된 데이터가 XML / JSON / YMAL 등으로 구성되어 있는지 확인하십시오 .
  5. 필요한 경우 데이터 를 PHP 데이터 유형 (배열 또는 객체)으로 변환하십시오 .
  6. 이러한 기본 점검 또는 변환 중 하나라도 실패하면 예외를 처리하십시오 !

문자 인코딩은 어떻습니까?

아하! 예, 응용 프로그램으로 전송되는 데이터 스트림이 UTF-8로 인코딩되기를 원할 수 있지만 이것이 있는지 여부를 어떻게 알 수 있습니까?

두 가지 중요한 문제.

  1. 얼마나 많은 데이터가 전달되고 있는지 모릅니다 php://input.
  2. 데이터 스트림의 현재 인코딩을 확실하게 알 수 없습니다.

먼저 얼마가 있는지 모르면서 스트림 데이터를 처리하려고합니까? 그건 끔찍한 생각 입니다. Content-Length스푸핑 될 수 있으므로 스트리밍 된 입력 크기에 대한 지침으로 HTTP 헤더 에만 의존 할 수 없습니다 .

당신은 필요합니다 :

  1. 스트림 크기 감지 알고리즘.
  2. 응용 프로그램 정의 스트림 크기 제한 (Apache / Nginx / PHP 제한이 너무 넓을 수 있음)

스트림의 현재 인코딩을 모르고 스트림 데이터를 UTF-8로 변환하려고합니까? 어떻게? iconv 스트림 필터 ( iconv 스트림 필터 예 )는 이와 같이 시작 및 종료 인코딩을 원하는 것 같습니다.

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

따라서 양심적이라면 다음이 필요합니다.

  1. 스트림 인코딩 감지 알고리즘.
  2. 동적 / 런타임 스트림 필터 정의 알고리즘 (선별 인코딩 시작을 알 수 없기 때문에)

( 업데이트 : 'convert.iconv.UTF-8/UTF-8'모든 것을 UTF-8로 강제하지만, iconv 라이브러리가 번역 방법을 알지 못하는 문자를 고려해야합니다. 즉, 문자를 번역 할 수 없을 때 수행 할 조치를 정의해야합니다. : 1) 더미 문자를 삽입합니다. 2) 실패 / 던지기 및 예외).

Content-Encoding다음과 같이 압축과 같은 것을 나타낼 수 있으므로 HTTP 헤더 에만 의존 할 수는 없습니다 . iconv와 관련하여 결정을 내리려는 것은 아닙니다.

Content-Encoding: gzip

따라서 일반적인 단계는 다음과 같습니다.

1 부 : HTTP 요청 관련

  1. 올바른 HTTP 메소드 가 표시되었는지 판별하십시오 (GET, POST, PUT, PATCH, DELETE, ...).
  2. HTTP Content-Type 헤더가 전송되었는지 확인하십시오 .
  3. Content-Type 의 이 원하는 매체 유형 인지 판별하십시오 .

2 부 : 스트림 데이터 관련

  1. 입력 스트림의 크기를 결정하십시오 (선택 사항이지만 권장 됨).
  2. 입력 스트림의 인코딩을 결정하십시오.
  3. 필요한 경우 입력 스트림을 원하는 문자 인코딩 (UTF-8)으로 변환하십시오.
  4. 필요한 경우 응용 프로그램 수준 압축 또는 암호화를 되돌리고 4, 5 및 6 단계를 반복하십시오.

III 부 : 데이터 유형 관련

  1. 전송 된 데이터가 XML / JSON / YMAL 등으로 구성되어 있는지 확인하십시오 .

데이터는 여전히 URL 인코딩 된 문자열 일 수 있으며 구문 분석하고 URL 디코딩해야합니다.

  1. 필요한 경우 데이터 를 PHP 데이터 유형 (배열 또는 객체)으로 변환하십시오 .

IV 부 : 데이터 가치 관련

  1. 입력 데이터를 필터링하십시오.

  2. 입력 데이터를 확인하십시오.

이제 보입니까?

$_POST입력에 대한 제한을위한 php.ini의 설정에 따라 자동 전역은 평신도를위한 간단합니다. 그러나 문자 인코딩을 처리하는 것은 스트림을 사용할 때 훨씬 직관적이고 효율적이며, 적절한 인코딩을 위해 입력 값을 확인하기 위해 슈퍼 글로벌 (또는 일반적으로 배열)을 반복 할 필요가 없기 때문입니다.


1
오 와우! 이 답변의 등급은 훨씬 높아야합니다. 플러드 라이트를 어둠에 가져다 주셔서 감사합니다.
Lox

최종 분석에서 PHP는 기본 기본값을 업데이트하는 것이 좋습니다. 그러나 HTTP 요청 메소드를 동일한 이름 ($ _GET, $ _POST)의 데이터 구조와 결합하는 것은 논리적으로 잘못된 것입니다. 중요한 것은 (1) 원하는 HTTP 요청 방법이며, (2) 해당 요청에 대한 요청 데이터가 있습니까 (Content-Type). 따라서 Perl과 마찬가지로 언어 작성자 / 관리자의 의견에 기꺼이 희생 될 수 있음을 알 수 있습니다.
Anthony Rutledge

0

그래서 php : // input stream 에서 POST 데이터를 가져 오는 함수를 작성했습니다 .

따라서 여기서의 과제는 PUT, DELETE 또는 PATCH 요청 방법으로 전환하고 해당 요청과 함께 전송 된 포스트 데이터를 얻는 것입니다.

나는 비슷한 도전을 가진 사람을 위해 이것을 공유하고 있습니다. 아래 기능은 내가 생각해 낸 것입니다. 도움이 되길 바랍니다!

    /**
     * @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

사용 방법의 간단한 예

 <?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;  }  
   ?>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.