PHP «php: // input» против $ _POST

243

Мне было приказано использовать метод php://inputвместо того, чтобы $_POSTвзаимодействовать с Ajax-запросами из JQuery. То, что я не понимаю, это преимущества использования этого по сравнению с глобальным методом $_POSTили $_GET.

подветренный
источник
2
Раньше я использовал «хаки» для получения вызовов ajax на стороне PHP, прежде чем наткнуться на этот пост и прочитать его удивительные ответы! Я надеюсь, что для других людей, имеющих такую ​​же проблему в будущем, поисковые системы тоже прочтут мой комментарий! :)
aderchox

Ответы:

485

Причина в том, что php://inputвозвращает все необработанные данные после HTTP-заголовков запроса, независимо от типа контента.

PHP суперглобальный $_POST, только предполагается, что обернуть данные, которые либо

  • application/x-www-form-urlencoded (стандартный тип контента для простых форм-постов) или
  • multipart/form-data (в основном используется для загрузки файлов)

Это потому, что это единственные типы контента, которые должны поддерживаться пользовательскими агентами. . Таким образом, сервер и PHP традиционно не ожидают получения какого-либо другого типа контента (что не означает, что они не могли).

Итак, если вы просто поместите старый добрый HTML form, запрос будет выглядеть примерно так:

POST /page.php HTTP/1.1

key1=value1&key2=value2&key3=value3

Но если вы много работаете с Ajax, эта проба также включает в себя обмен более сложными данными с типами (string, int, bool) и структурами (массивами, объектами), поэтому в большинстве случаев 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-данным или любому другому нестандартному типу контента.

Quasdunk
источник
40
+1 за «Это также, как вы бы
получили
@Quasdank Я отправляю JSON из приложения Android на сервер php xampp в облаке ( stackoverflow.com/questions/36558261/… ), но не могу заставить его работать, когда я пытаюсь file_get_contents ('php: // input'), который просто возвращает строку (0). Раньше это работало на моей локальной машине, но не работало, когда я развертывал его в облаке. Не могли бы вы мне помочь, пожалуйста?
The_Martian
1
Стоит отметить, что использование объекта XMLHttpRequest в AJAX-запросе к PHP не означает, что нужно публиковать JSON. Это дополнительные затраты, но ваш клиентский JavaScript может преобразовываться в формат application / x-www-form-urlencoded. Тем не менее, перевод может быть не чисто тип данных .
Энтони Ратледж,
Надо сказать, что предел двух признанных типов контента во многом исторический. Ничто не мешает PHP распознавать то есть application/jsonкак действительный источник данных для $_POSTмассива. И есть даже опубликованные запросы специально для этой поддержки.
AnrDaemon
Здравствуйте, @quasdunk. Не могли бы вы помочь мне в этом magento.stackexchange.com/questions/296960/…
К
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, он вернет ассоциативный массив.
Вахид Амири
28

Если данные поста искажены, $ _POST ничего не будет содержать. Тем не менее, php: // input будет иметь некорректную строку.

Например, есть некоторые ajax-приложения, которые не формируют правильную последовательность ключ-значение поста для загрузки файла, а просто выгружают весь файл как данные поста, без имен переменных или чего-либо еще. $ _POST будет пустым, $ _FILES также пустым, а вход php: // будет содержать точный файл, записанный в виде строки.

безымянный
источник
22

Во-первых, основная истина о PHP.

PHP не был разработан для того, чтобы явно предоставлять вам чистый интерфейс типа REST (GET, POST, PUT, PATCH, DELETE) для обработки HTTP-запросов .

Однако $_POST, $_GETи $_FILES Суперглобальные , а функция filter_input_array()очень полезны для нужд среднего человека / непрофессионала.

Скрытое преимущество номер один $_POST$_GET) заключается в том, что ваши входные данные автоматически кодируются PHP . Вы даже не задумываетесь об этом, особенно для параметров строки запроса в стандартном запросе GET.

Тем не менее, тогда вы узнаете больше ...

При этом, когда вы продвигаетесь в своих знаниях по программированию и хотите использовать XmlHttpRequestобъект JavaScript (для некоторых jQuery), вы видите ограничение этой схемы.

$_POSTограничивает вас использованием двух типов медиа в Content-Typeзаголовке HTTP :

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

Таким образом, если вы хотите отправить значения данных в PHP на сервере и показать их в $_POSTсуперглобальном коде , то вы должны urlencode их на стороне клиента и отправить эти данные в виде пар ключ / значение - неудобный шаг для новичков (особенно при попытке выяснить, требуются ли разные части URL-адреса для разных форм кодирования: обычный, необработанный и т. д.).

Для всех пользователей jQuery этот $.ajax()метод преобразует ваш JSON в пары ключ / значение, закодированные в URL, перед их передачей на сервер. Вы можете изменить это поведение, установив processData: false. Просто прочитайте документацию $ .ajax () и не забудьте отправить правильный тип носителя в заголовке Content-Type.

Кодировка URL? Какого черта!!!???

Как правило, если вы выполняете обычные синхронные (когда перерисовывается вся страница) HTTP-запросы с HTML-формой, пользовательский агент (веб-браузер) будет кодировать данные вашей формы для вас. Если вы хотите выполнять асинхронные HTTP-запросы с использованием XmlHttpRequestобъекта, вы должны сформировать строку с кодировкой urlen и отправить ее, если хотите, чтобы эти данные отображались в $_POSTсуперглобальном элементе .

Как вы общаетесь с JavaScript? :-)

Преобразование из массива или объекта JavaScript в строку с кодировкой urlen беспокоит многих разработчиков (даже с такими новыми API, как Form Data ). Они бы предпочли просто отправить JSON, и для клиентского кода это было бы более эффективно .

Помните (подмигивание, подмигивание), обычный веб-разработчик не учится использовать XmlHttpRequestобъект напрямую, глобальные функции, строковые функции, функции массива и регулярные выражения, такие как вы и я ;-). Урленкодинг для них - это кошмар. ;-)

PHP, что дает?

Отсутствие в PHP интуитивной обработки XML и JSON отвлекает многих людей. Вы могли бы подумать, что это будет частью PHP сейчас (вздох).

Так много типов медиа (MIME-типы в прошлом)

XML, JSON и YAML имеют типы мультимедиа, которые можно поместить в Content-Typeзаголовок HTTP .

  • Приложение / XML
  • applicaiton / JSON
  • application / yaml (хотя IANA не имеет официального обозначения в списке)

Посмотрите, сколько медиа-типов (ранее MIME-типов) определены IANA.

Посмотрите, сколько там HTTP-заголовков .

php: // вход или перебор

Использование 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) Fail / throw и исключения).

Вы не можете полагаться исключительно на Content-Encodingзаголовок HTTP , так как это может указывать на что-то вроде сжатия, как показано ниже. Это не то, что вы хотите принять решение в отношении iconv.

Content-Encoding: gzip

Поэтому общие шаги могут быть ...

Часть I: HTTP-запрос, связанный

  1. Определите, был ли указан правильный метод HTTP (GET, POST, PUT, PATCH, DELETE, ...)
  2. Определите, был ли передан заголовок HTTP Content-Type .
  3. Определите, является ли значение для Content-Type желаемым типом носителя.

Часть II: Потоковые данные, связанные

  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, вы видите, что можете стать жертвой мнений создателей / сопровождающих языка.
Энтони Ратледж
0

Поэтому я написал функцию, которая будет получать данные POST из потока ввода php: // .

Таким образом, проблема здесь заключалась в том, чтобы переключиться на метод запроса PUT, DELETE OR 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;  }  
   ?>
Достонбек Орипжонов
источник