PHP «php: // input» vs $ _POST

243

J'ai été invité à utiliser la méthode php://inputau lieu d' $_POSTinteragir avec les demandes Ajax de JQuery. Ce que je ne comprends pas, c'est les avantages de l'utilisation de cette méthode par rapport à la méthode globale de $_POSTou $_GET.

Lee
la source
2
J'avais l'habitude d'utiliser des "hacks" pour recevoir des appels ajax côté PHP avant de tomber sur ce post et de lire ses réponses géniales! Pour d'autres personnes ayant le même problème à l'avenir, j'espère que les moteurs de recherche liront aussi mon commentaire! :)
aderchox

Réponses:

485

La raison en est que php://inputrenvoie toutes les données brutes après les en-têtes HTTP de la demande, quel que soit le type de contenu.

Le PHP superglobal $_POST, seul est censé envelopper les données qui sont soit

  • application/x-www-form-urlencoded (type de contenu standard pour les messages de formulaire simples) ou
  • multipart/form-data (principalement utilisé pour les téléchargements de fichiers)

En effet, ce sont les seuls types de contenu qui doivent être pris en charge par les agents utilisateurs . Ainsi, le serveur et PHP ne s'attendent généralement pas à recevoir un autre type de contenu (ce qui ne signifie pas qu'ils ne le pourraient pas).

Donc, si vous POSTEZ simplement un bon vieux HTML form, la demande ressemble à ceci:

POST /page.php HTTP/1.1

key1=value1&key2=value2&key3=value3

Mais si vous travaillez beaucoup avec Ajax, ce probaby comprend également l'échange de données plus complexes avec des types (chaîne, int, bool) et des structures (tableaux, objets), donc dans la plupart des cas, JSON est le meilleur choix. Mais une demande avec une charge utile JSON ressemblerait à ceci:

POST /page.php HTTP/1.1

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

Le contenu serait maintenant application/json(ou du moins aucun des éléments mentionnés ci-dessus), donc $_POST-wrapper de PHP ne sait pas (encore) comment gérer cela.

Les données sont toujours là, vous ne pouvez tout simplement pas y accéder via le wrapper. Vous devez donc le récupérer vous-même au format brut avec file_get_contents('php://input')( tant qu'il n'est pas multipart/form-datacodé ).

C'est également ainsi que vous accéderez aux données XML ou à tout autre type de contenu non standard.

Quasdunk
la source
40
+1 pour "C'est également ainsi que vous accéderez aux données XML ou à tout autre type de contenu non standard"
mandza
@Quasdank J'envoie du JSON depuis une application Android vers un serveur php xampp dans le cloud ( stackoverflow.com/questions/36558261/… ) mais je n'ai pas pu le faire fonctionner lorsque j'ai essayé file_get_contents ('php: // input'), qui renvoie simplement la chaîne (0). Cela fonctionnait auparavant sur ma machine locale, mais cela ne fonctionne pas lorsque je l'ai déployé dans le cloud. Pourrais-tu m'aider s'il te plait?
The_Martian
1
Il est à noter que l'utilisation de l'objet XMLHttpRequest dans une requête AJAX vers PHP ne signifie pas que l'on doit publier JSON. C'est une surcharge supplémentaire, mais votre JavaScript côté client peut être converti au format application / x-www-form-urlencoded. Cependant, la traduction peut ne pas être un type de données pur .
Anthony Rutledge
Il faut dire que la limite de deux types de contenu reconnus est largement historique. Rien n'empêche PHP de reconnaître, c'est- application/jsonà- dire comme une source de données valide pour le $_POSTtableau. Et il y a même des demandes publiées pour spécifiquement ce support.
AnrDaemon
Salut @quasdunk pouvez-vous m'aider s'il vous plaît sur ce magento.stackexchange.com/questions/296960/…
Nagaraju K
53

php://inputpeut vous donner les octets bruts des données. Ceci est utile si les données POSTed sont une structure encodée JSON, ce qui est souvent le cas pour une requête AJAX POST.

Voici une fonction pour cela:

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

Le $_POSTtableau est plus utile lorsque vous manipulez des données de valeur-clé d'un formulaire, soumises par un POST traditionnel. Cela ne fonctionne que si les données POSTed sont dans un format reconnu, généralement application/x-www-form-urlencoded(voir http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 pour plus de détails).

Rob Agar
la source
7
Il convient de noter que si vous passez en truetant que second paramètre à json_decode, il retournera un tableau associatif.
Vahid Amiri
28

Si les données de publication sont mal formées, $ _POST ne contiendra rien. Pourtant, l'entrée php: // aura la chaîne mal formée.

Par exemple, il existe des applications ajax qui ne forment pas la séquence de valeurs-clés de publication correcte pour télécharger un fichier et qui vident simplement tout le fichier en tant que données de publication, sans nom de variable ou quoi que ce soit. $ _POST sera vide, $ _FILES sera également vide et l'entrée php: // contiendra le fichier exact, écrit sous forme de chaîne.

Sans nom
la source
22

Tout d'abord, une vérité de base sur PHP.

PHP n'a pas été conçu pour vous donner explicitement une interface de type REST pur (GET, POST, PUT, PATCH, DELETE) pour gérer les requêtes HTTP .

Cependant, la $_POST, $_GETet$_FILES superglobales , et la fonction filter_input_array()sont très utiles pour les besoins simples / de la personne moyenne.

L'avantage caché numéro un de $_POST (et $_GET) est que vos données d'entrée sont urldécodées automatiquement par PHP . Vous ne pensez même jamais à le faire, en particulier pour les paramètres de chaîne de requête dans une demande GET standard.

Cependant, vous en apprendrez plus ...

Cela étant dit, à mesure que vous progressez dans vos connaissances en programmation et que vous souhaitez utiliser l' XmlHttpRequestobjet JavaScript (jQuery pour certains), vous voyez la limitation de ce schéma.

$_POST vous limite à l'utilisation de deux types de supports dans HTTP Content-Type tête :

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

Ainsi, si vous voulez envoyer des valeurs de données à PHP sur le serveur et les faire apparaître dans le $_POSTsuperglobal , vous devez encoder du côté client et envoyer ces données sous forme de paires clé / valeur - une étape peu pratique pour les novices (en particulier lorsque vous essayez de déterminer si différentes parties de l'URL nécessitent différentes formes de codage d'URL: normal, brut, etc.).

Pour tous les utilisateurs jQuery, la $.ajax()méthode consiste à convertir votre JSON en paires clé / valeur encodées URL avant de les transmettre au serveur. Vous pouvez remplacer ce comportement en définissant processData: false. Il suffit de lire le $ .ajax () et n'oubliez pas d'envoyer le bon type de média dans l'en-tête Content-Type.

Encodage URL? Que diable!!!???

En règle générale, si vous effectuez une requête HTTP normale et synchrone (lorsque toute la page est redessinée) avec un formulaire HTML, l'agent utilisateur (navigateur Web) encodera automatiquement vos données de formulaire pour vous. Si vous souhaitez effectuer une requête HTTP asynchrone à l'aide de l' XmlHttpRequestobjet, vous devez créer une chaîne codée en url et l'envoyer, si vous souhaitez que ces données s'affichent dans le $_POSTsuperglobal .

Comment êtes-vous en contact avec JavaScript? :-)

La conversion d'un tableau ou d'un objet JavaScript en une chaîne encodée en url dérange de nombreux développeurs (même avec de nouvelles API comme Form Data ). Ils préfèreraient de loin envoyer JSON, et ce serait plus efficace pour le code client de le faire.

Rappelez-vous (clin d’œil, clin d’œil), le développeur Web moyen n’apprend pas à utiliser XmlHttpRequest directement objet, les fonctions globales, les fonctions de chaîne, les fonctions de tableau et les expressions régulières comme vous et moi ;-). Le codage url pour eux est un cauchemar. ;-)

PHP, qu'est-ce qui donne?

Le manque de PHP de gestion XML et JSON intuitive décourage de nombreuses personnes. On pourrait penser qu'il ferait désormais partie de PHP (soupir).

Tant de types de médias (types MIME dans le passé)

XML, JSON et YAML ont tous des types de médias qui peuvent être placés dans un en- Content-Typetête HTTP .

  • application / xml
  • applicaiton / json
  • application / yaml (bien que l'IANA n'ait aucune désignation officielle répertoriée)

Regardez combien de types de médias (anciennement, les types MIME) sont définis par l'IANA.

Regardez combien il y a d'en- têtes HTTP .

php: // entrée ou buste

L'utilisation du php://inputflux vous permet de contourner le niveau d'abstraction baby-sitting / hand holding que PHP a imposé au monde. :-) Un grand pouvoir implique de grandes responsabilités!

Maintenant, avant de traiter les valeurs de données transmises en continu php://input, vous devez / devez faire quelques choses.

  1. Déterminez si la bonne méthode HTTP a été indiquée (GET, POST, PUT, PATCH, DELETE, ...)
  2. Déterminez si l'en - tête HTTP Content-Type a été transmis.
  3. Déterminez si la valeur du Content-Type est le type de support souhaité.
  4. Déterminez si les données envoyées sont bien formées XML / JSON / YMAL / etc.
  5. Si nécessaire, convertissez les données en un type de données PHP: tableau ou objet.
  6. Si l'une de ces vérifications ou conversions de base échoue, jetez une exception !

Et l'encodage des caractères?

AH, HA! Oui, vous souhaiterez peut-être que le flux de données envoyé dans votre application soit encodé en UTF-8, mais comment savoir s'il l'est ou non?

Deux problèmes critiques.

  1. Vous ne savez pas combien de données transitent php://input.
  2. Vous ne connaissez pas avec certitude l'encodage actuel du flux de données.

Allez-vous essayer de gérer les données de flux sans savoir combien il y a d'abord? C'est une terrible idée . Vous ne pouvez pas vous fier exclusivement à l'en- Content-Lengthtête HTTP pour obtenir des conseils sur la taille de l'entrée diffusée, car elle peut être usurpée.

Vous allez avoir besoin d'un:

  1. Algorithme de détection de la taille du flux.
  2. Limites de taille de flux définies par l'application (les limites Apache / Nginx / PHP peuvent être trop larges).

Allez-vous tenter de convertir des données de flux en UTF-8 sans connaître l'encodage actuel du flux? Comment? Le filtre de flux iconv ( exemple de filtre de flux iconv ) semble vouloir un encodage de début et de fin, comme celui-ci.

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

Ainsi, si vous êtes consciencieux, vous aurez besoin de:

  1. Algorithme de détection de codage de flux.
  2. Algorithme de définition de filtre de flux dynamique / d'exécution (car vous ne pouvez pas connaître le codage de départ a priori).

( Mise à jour : 'convert.iconv.UTF-8/UTF-8'forcera tout à UTF-8, mais vous devez toujours tenir compte des caractères que la bibliothèque iconv peut ne pas savoir traduire. En d'autres termes, vous devez savoir comment définir l'action à entreprendre lorsqu'un caractère ne peut pas être traduit : 1) Insérez un caractère fictif, 2) Échec / lancer et exception).

Vous ne pouvez pas vous fier exclusivement à l'en- Content-Encodingtête HTTP , car cela pourrait indiquer quelque chose comme la compression comme ci-dessous. Ce n'est pas de cela que vous voulez prendre une décision concernant iconv.

Content-Encoding: gzip

Par conséquent, les étapes générales pourraient être ...

Partie I: liée à la demande HTTP

  1. Déterminez si la bonne méthode HTTP a été indiquée (GET, POST, PUT, PATCH, DELETE, ...)
  2. Déterminez si l'en - tête HTTP Content-Type a été transmis.
  3. Déterminez si la valeur du Content-Type est le type de support souhaité.

Partie II: Données liées au flux

  1. Déterminez la taille du flux d'entrée (facultatif, mais recommandé).
  2. Déterminez l'encodage du flux d'entrée.
  3. Si nécessaire, convertissez le flux d'entrée en encodage de caractères souhaité (UTF-8).
  4. Si nécessaire, inversez toute compression ou chiffrement au niveau de l'application, puis répétez les étapes 4, 5 et 6.

Partie III: liée au type de données

  1. Déterminez si les données envoyées sont bien formées XML / JSON / YMAL / etc.

(N'oubliez pas, les données peuvent toujours être une chaîne codée URL que vous devez ensuite analyser et décoder URL).

  1. Si nécessaire, convertissez les données en un type de données PHP: tableau ou objet.

Partie IV: Valeur des données liées

  1. Filtrez les données d'entrée.

  2. Validez les données d'entrée.

Voyez-vous maintenant?

Le $_POSTsuperglobal, ainsi que les paramètres php.ini pour les limites d'entrée, sont plus simples pour le profane. Cependant, le traitement de l'encodage de caractères est beaucoup plus intuitif et efficace lors de l'utilisation de flux car il n'est pas nécessaire de parcourir les superglobales (ou les tableaux, en général) pour vérifier les valeurs d'entrée pour l'encodage approprié.

Anthony Rutledge
la source
1
Oh wow! Cette réponse devrait avoir une note beaucoup plus élevée. Merci beaucoup d'avoir apporté une lumière d'inondation dans l'obscurité.
Lox
En dernière analyse, PHP ferait bien de mettre à jour les valeurs par défaut de base. Cependant, c'est une erreur logique de coupler une méthode de requête HTTP avec une structure de données du même nom ($ _GET, $ _POST). Ce qui importe, c'est (1) la méthode de requête HTTP souhaitée, et (2) y a-t-il des données de requête avec cette requête (Content-Type). Par conséquent, comme avec Perl, vous devez voir que vous pouvez être la victime volontaire des opinions des créateurs / mainteneurs de langage.
Anthony Rutledge
0

J'ai donc écrit une fonction qui obtiendrait les données POST du flux d'entrée php: // .

Donc, le défi ici était de passer à la méthode de demande PUT, DELETE OR PATCH et d'obtenir toujours les données de publication envoyées avec cette demande.

Je partage cela peut-être pour quelqu'un avec un défi similaire. La fonction ci-dessous est ce que j'ai trouvé et cela fonctionne. J'espère que ça aide!

    /**
     * @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;
    }
Ifeanyi Amadi
la source
-5

Exemple simple d'utilisation

 <?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;  }  
   ?>
Dostonbek Oripjonov
la source