307 votes

PHP "php://input" vs $_POST

On m'a demandé d'utiliser la méthode php://input au lieu de $_POST lors de l'interaction avec les demandes Ajax de JQuery. Ce que je ne comprends pas, ce sont les avantages d'utiliser cette méthode par rapport à la méthode globale de $_POST o $_GET .

5 votes

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

0 votes

Vous n'auriez besoin d'utiliser php://input que si votre requête AJAX jQuery envoyait du JSON, au lieu de paires clé/valeur codées par URL. Par défaut, c'est ce que fait jQuery. Si c'est le cas (vos données sont en JSON), alors oui, vous devez utiliser php://input .

584voto

Quasdunk Points 4194

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

Le super-global PHP $_POST seulement est censé envelopper les données qui sont soit

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

En effet, il s'agit des seuls types de contenu que l'on peut utiliser dans le cadre d'un projet de recherche. debe être pris en charge par les agents utilisateurs . Le serveur et PHP ne s'attendent donc traditionnellement pas à recevoir un autre type de contenu (ce qui ne veut pas dire qu'ils ne le pourraient pas).

Donc, si vous POST simplement un bon vieux HTML form la demande ressemble à quelque chose comme ceci :

POST /page.php HTTP/1.1

key1=value1&key2=value2&key3=value3

Mais si vous travaillez beaucoup avec Ajax, cela inclut probablement aussi l'échange de données plus complexes avec des types (string, int, bool) et des structures (arrays, objets), donc dans la plupart des cas JSON est le meilleur choix. Mais une requête avec une charge utile JSON ressemblerait à quelque chose comme 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 la fonction PHP $_POST -wrapper ne sait pas (encore) comment gérer cela.

Les données sont toujours là, mais vous ne pouvez pas y accéder via le wrapper. Vous devez donc les récupérer vous-même au format brut à l'aide de la commande file_get_contents('php://input') ( tant que ce n'est pas multipart/form-data -encodé ).

C'est également de cette manière que vous accédez aux données XML ou à tout autre type de contenu non standard.

51 votes

+1 pour "C'est également de cette manière que vous accédez aux données XML ou à tout autre type de contenu non standard".

0 votes

@Quasdank Je suis en train d'envoyer JSON de l'application Android vers le serveur php xampp dans le cloud( stackoverflow.com/questions/36558261/ ) mais je n'ai pas réussi à le faire fonctionner lorsque j'ai essayé file_get_contents('php://input'), qui renvoie simplement string(0). Cela fonctionnait sur mon ordinateur local, mais pas lorsque je l'ai déployé sur le cloud. Pouvez-vous m'aider ?

1 votes

Il convient de noter que l'utilisation de l'objet XMLHttpRequest dans une requête AJAX vers PHP ne signifie pas qu'il faille envoyer du JSON. C'est une surcharge supplémentaire, mais votre JavaScript côté client peut convertir au format application/x-www-form-urlencoded. Cependant, la traduction peut ne pas être de type de données pur .

80voto

Anthony Rutledge Points 280

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 (GET, POST, PUT, PATCH, DELETE) pour gérer les requêtes HTTP. .

Toutefois, le $_SERVER , $_COOKIE , $_POST , $_GET et $_FILES superglobales et la fonction filter_input_array() sont très utiles pour les besoins de la personne moyenne / du profane.

L'avantage caché numéro un de $_POST (et $_GET ) est que vos données d'entrée sont url-décodé automatiquement par PHP . On ne pense même pas à devoir le faire, surtout pour les paramètres de chaîne de requête dans un système standard. GET ou les données du corps HTTP soumises avec une requête POST demande.

Autres méthodes de requête HTTP

Ceux qui étudient le protocole HTTP sous-jacent et ses différentes méthodes de requête comprennent qu'il existe de nombreuses méthodes de requête HTTP, y compris la méthode souvent citée en référence. PUT , PATCH (non utilisé dans Apigee de Google), et DELETE .

En PHP, il n'y a pas de superglobales ou de fonctions de filtre d'entrée pour obtenir les données du corps de la requête HTTP lorsque POST n'est pas utilisé. Que doivent faire les disciples de Roy Fielding ? ;-)

Cependant, lorsque vous en apprenez davantage ...

Ceci étant dit, lorsque vous avancez dans votre connaissance de la programmation PHP et que vous souhaitez utiliser les fonctionnalités de JavaScript XmlHttpRequest (jQuery pour certains), vous verrez les limites de ce système.

$_POST vous limite à l'utilisation de deux types de médias dans le protocole HTTP. Content-Type en-tête :

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

_Ainsi, si vous souhaitez envoyer des valeurs de données à PHP sur le serveur, et qu'elles apparaissent dans le fichier $_POST superglobal_ alors vous devez urlencode du côté client et envoyer ces données sous forme de paires clé/valeur - une étape peu pratique pour les novices (surtout lorsqu'il s'agit de déterminer si les différentes parties de l'URL nécessitent différentes formes de codage url : normal, brut, etc ).

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

php://input, mais ...

Même si vous utilisez php://input au lieu de $_POST pour votre HTTP POST les données du corps de la demande, il ne fonctionne pas avec un HTTP Content-Type de multipart/form-data Il s'agit du type de contenu que vous utilisez sur un formulaire HTML lorsque vous souhaitez autoriser le téléchargement de fichiers !

<form enctype="multipart/form-data" accept-charset="utf-8" action="post">
    <input type="file" name="resume">
</form>

Par conséquent, dans le PHP traditionnel, pour traiter une diversité de types de contenu à partir d'un HTTP POST vous apprendrez à utiliser $_POST o filter_input_array(POST) , $_FILES et php://input . Il n'y a aucun moyen d'utiliser une seule source d'entrée universelle pour HTTP POST en PHP.

Vous ne pouvez pas obtenir de fichiers par $_POST , filter_input_array(POST) ou php://input et vous ne pouvez pas obtenir JSON/XML/YAML dans les deux cas. filter_input_array(POST) o $_POST .

Manuel PHP : php://input

php://input est un flux en lecture seule qui vous permet de lire les données brutes à partir du corps de la demande ...php://input est non disponible avec enctype="multipart/form-data".

Les frameworks PHP à la rescousse ?

Les frameworks PHP comme Codeigniter 4 et Laravel utilisent une façade pour fournir une interface plus propre ( IncomingRequest o Request objets) à ce qui précède. C'est pourquoi les développeurs PHP professionnels utilisent des frameworks plutôt que du PHP brut.

Bien sûr, si vous aimez programmer, vous pouvez concevoir votre propre objet de façade pour fournir ce que font les frameworks. C'est parce que j'ai pris le temps d'étudier cette question que je suis en mesure d'écrire cette réponse.

L'encodage des URL ? Mais qu'est-ce que c'est que ça ?

En règle générale, si vous effectuez des requêtes HTTP normales et synchrones (lorsque la page entière est redessinée) avec un formulaire HTML, l'agent utilisateur (navigateur Web) codera les données du formulaire pour vous. Si vous souhaitez effectuer des requêtes HTTP asynchrones en utilisant la fonction XmlHttpRequest vous devez alors façonner une chaîne de caractères urlencodée et l'envoyer, _si vous voulez que ces données apparaissent dans la base de données de l'UE. $_POST superglobal_ .

Quel est votre niveau de connaissance de JavaScript ? :-)

La conversion d'un tableau ou d'un objet JavaScript en une chaîne de caractères urlencodée dérange de nombreux développeurs (même avec les nouvelles API telles que Données du formulaire ). Ils préfèrent de loin pouvoir simplement envoyer du 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 l'outil de gestion de l'information. XmlHttpRequest directement des objets, des fonctions globales, des fonctions de chaîne, des fonctions de tableau et des expressions régulières comme vous et moi ;-). L'urlencodage pour eux est un cauchemar ;-)

PHP, que se passe-t-il ?

L'absence de gestion intuitive de XML et JSON en PHP rebute beaucoup de personnes. On aurait pu penser que cela ferait partie de PHP depuis le temps (soupir).

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

Les formats XML, JSON et YAML ont tous des types de médias qui peuvent être intégrés dans un protocole HTTP. Content-Type en-tête.

  • application/xml
  • applicaiton/json
  • application/yaml (bien que l'IANA ne dispose d'aucune désignation officielle)

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

Regardez combien de En-têtes HTTP il y en a.

php://input or bust

Utilisation de la php://input stream vous permet de contourner le niveau d'abstraction de baby-sitting / hand holding que PHP a imposé au monde :-) De grands pouvoirs impliquent de grandes responsabilités !

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

  1. Déterminez si le bon Méthode HTTP a été indiqué (GET, POST, PUT, PATCH, DELETE, ...)
  2. Déterminez si le HTTP Content-Type a été transmis.
  3. Déterminer si le valeur pour le Content-Type est le média souhaité souhaité.
  4. Déterminer si les données envoyées sont bien formé XML / JSON / YAML etc.
  5. Si nécessaire, convertir les données vers un type de données PHP : tableau ou objet.
  6. Si l'une de ces vérifications ou conversions de base échoue, une exception est levée. !

Qu'en est-il du codage des caractères ?

AH, HA ! Oui, vous pouvez souhaiter que le flux de données envoyé à 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 passent par le système. php://input .
  2. Vous ne connaissez pas avec certitude l'encodage actuel du flux de données.

Allez-vous tenter de manipuler des données de flux sans savoir d'abord combien il y en a ? C'est une idée terrible . Vous ne pouvez pas compter exclusivement sur le HTTP Content-Length pour obtenir des indications sur la taille de l'entrée en continu, car elle peut être falsifié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 d'Apache / Nginx / PHP peuvent être trop larges).

Allez-vous tenter de convertir les données du 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 ceci.

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

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

  1. Algorithme de détection du codage du flux.
  2. Algorithme de définition de filtre de flux dynamique / runtime (car on ne peut 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 pourrait ne pas savoir comment traduire. En d'autres termes, vous devez définir d'une manière ou d'une autre l'action à entreprendre lorsqu'un caractère ne peut pas être traduit : 1) Insérer un caractère factice, 2) Échouer / lancer une exception).

Vous ne pouvez pas compter exclusivement sur le HTTP Content-Encoding car cela pourrait indiquer quelque chose comme une compression comme dans le cas suivant. Ce n'est pas sur cette base que vous devez prendre une décision en ce qui concerne iconv.

Content-Encoding: gzip

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

Partie I : Requêtes HTTP liées

  1. Déterminez si le bon Méthode HTTP a été indiqué (GET, POST, PUT, PATCH, DELETE, ...)
  2. Déterminez si le HTTP Content-Type a été transmis.
  3. Déterminer si le valeur pour le Content-Type est le média souhaité souhaité.

Partie II : Données de flux liées

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

Partie III : Type de données

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

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

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

Partie IV : La valeur des données

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

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

Vous voyez maintenant ?

Le site $_POST superglobal, ainsi que les paramètres php.ini pour les limites de saisie, sont plus simples pour le profane. Cependant, le traitement de l'encodage des caractères est beaucoup plus intuitif et efficace lorsqu'on utilise des flux, car il n'est pas nécessaire de boucler à travers les superglobales (ou les tableaux, en général) pour vérifier le bon encodage des valeurs d'entrée.

59voto

Rob Agar Points 5793

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

Voici une fonction qui permet de le faire :

  /**
   * 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 site $_POST est plus utile lorsque vous traitez des données de type clé-valeur provenant d'un formulaire, soumis par un POST traditionnel. Cela ne fonctionne que si les données POST 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).

9 votes

Il est intéressant de noter que si vous réussissez true comme deuxième paramètre de json_decode il retournera un tableau associatif.

1 votes

Méfiez-vous de l'extraction de données directement à partir de php://input . Découvrez les filtres de flux et assurez-vous que vous obtenez des données codées UTF-8.

0 votes

Ne faites pas cela sans utiliser un filtre d'entrée pour vous assurer que vous avez affaire à UTF-8. 'convert.iconv.UTF-8/UTF-8'

30voto

Nameless Points 1209

Si les données postales sont mal formées, $_POST ne contiendra rien. Pourtant, php://input aura la chaîne malformée.

Par exemple, il existe des applications ajax qui ne forment pas de séquence clé-valeur correcte pour le téléchargement d'un fichier, et se contentent de déverser tout le fichier en tant que données de post, sans noms de variables ou autre. $_POST sera vide, $_FILES vide également, et php://input contiendra le fichier exact, écrit sous forme de chaîne.

0 votes

$_REQUEST sera également vide.

0voto

Ifeanyi Amadi Points 281

J'ai donc écrit une fonction qui récupère les données POST à partir de la base de données de l'entreprise. php://flux d'entrée .

Le défi consistait donc à passer à la méthode de requête PUT, DELETE ou PATCH, tout en obtenant les données postales envoyées avec cette requête.

Je partage ceci peut-être pour quelqu'un qui a un défi similaire. La fonction ci-dessous est celle que j'ai trouvée et elle fonctionne. J'espère que cela vous aidera !

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X