PHP “php: // input” проти $ _POST


243

Мене спрямовують використовувати метод php://inputзамість того, $_POSTщоб взаємодіяти з запитами Ajax від JQuery. Що я не розумію, це переваги використання цього проти глобального методу $_POSTабо $_GET.


2
Раніше я використовував "хаки" для прийому дзвінків Ajax на стороні PHP, перш ніж натрапляти на цей пост і читати його приголомшливі відповіді! Для інших людей, які матимуть те саме питання у майбутньому, я сподіваюся, що пошукові системи теж прочитають мій коментар! :)
aderchox

Відповіді:


484

Причина полягає в тому, що 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- $_POSTwrapper не знає, як з цим впоратися.

Дані все ще є, ви просто не можете отримати доступ до них через обгортку. Тож вам потрібно його самостійно отримати в сирому форматі file_get_contents('php://input')(до тих пір, поки це не multipart/form-data-кодовано ).

Це також спосіб доступу до XML-даних або будь-якого іншого нестандартного типу вмісту.


40
+1 для "Це також, як ви отримаєте доступ до XML-даних або будь-якого іншого нестандартного типу вмісту"
мандза

@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/…
Nagaraju K

53

php://inputможе дати вам необроблені байти даних. Це корисно, якщо дані POSTed - це кодована 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. Це працює лише в тому випадку, якщо дані POSTed зазвичай є розпізнаним форматом 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: // матиме неправильно сформовану рядок.

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


22

По-перше, основна правда про PHP.

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

Однак $_POST, $_GETі $_FILES суперглобального , а функція filter_input_array()дуже корисні для потреб середньої людини / непрофесіонала.

Прихованою перевагою номер 1 $_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-адреси різні форми кодування urlencoding: звичайні, необроблені тощо).

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

Кодування URL-адрес? Якого біса!!!???

Як правило, якщо ви робите звичайні, синхронні (коли вся сторінка перемальовує) запити HTTP із формою HTML, користувальницький агент (веб-браузер) перекодує ваші дані форми для вас. Якщо ви хочете робити асинхронні запити HTTP за допомогою XmlHttpRequestоб'єкта, вам слід створити рядок, що кодується urlencoded, і надіслати його, якщо ви хочете, щоб ці дані відображалися в $_POSTнадглобальному .

Як на зв’язку ви маєте JavaScript? :-)

Перетворення з масиву JavaScript або об'єкта в рядок, що кодується URL, турбує багатьох розробників (навіть з новими 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 Content .
  3. Визначте, чи є значення типу Content-Type потрібним типом медіа.
  4. Визначте, чи добре надіслані дані XML / JSON / YMAL / тощо.
  5. При необхідності перетворіть дані у тип даних PHP: масив чи об’єкт.
  6. Якщо будь-яка з цих основних перевірок чи конверсій не вдається, киньте виняток !

А як щодо кодування символів?

AH, HA! Так, можливо, ви хочете, щоб потік даних, що надсилається у вашу заявку, був закодований UTF-8, але як ви можете дізнатися, є він чи ні?

Дві критичні проблеми.

  1. Ви не знаєте, скільки даних надходить php://input.
  2. Ви точно не знаєте поточного кодування потоку даних.

Чи збираєтесь ви намагатися обробляти дані потоку, не знаючи, скільки спочатку? Це жахлива ідея . Ви не можете покладатися виключно на HTTP- Content-Lengthзаголовок для вказівки щодо розміру потокового входу, оскільки він може бути підробленим.

Вам знадобиться:

  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) Провал / кидок та виняток).

Ви не можете покладатися виключно на HTTP- Content-Encodingзаголовок, оскільки це може означати щось на зразок стиснення, як у наступному. Це не те, про що ви хочете прийняти рішення щодо ікони.

Content-Encoding: gzip

Тому загальними кроками можуть бути ...

Частина I: Пов’язаний із запитом HTTP

  1. Визначте, чи вказано правильний метод HTTP (GET, POST, PUT, PATCH, DELETE, ...)
  2. Визначте, чи передано заголовок типу HTTP Content Content .
  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
Ух ти! Ця відповідь повинна мати набагато вищий рейтинг. Дякую вам велике за те, що ви принесли світло темного потоку.
Локс

Зрештою, 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;  }  
   ?>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.