How do you use XMLHttpRequest with POST method? - php

I have a central PHP script I use for handling many requests from my page, using XMLHttpRequest() calls from JavaScript, using the GET method. My server PHP is currently 5.6, and yes, I'm avoiding synchronous calls.
It all works well, however there are cases where I'd prefer NOT to have the request remain in browser history, or even be displayed in the URL. So based on the MDN reference on this call, I thought I could simply switch to the POST method, keeping my existing requests (such as "http://myscript.php?cmd=dothis&data=somedata"). then all I'd have to do is add some code to my PHP script, so I could gather the passed data either way...
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
if(isset($_GET['cmd'])) $cmd = $_GET['cmd'];
if(isset($_GET['data'])) $data = $_GET['data'];
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if(isset($_POST['cmd'])) $cmd = $_POST['cmd'];
if(isset($_POST['data'])) $data = $_POST['data'];
}
Well obviously i was dead wrong. What I found was...
Neither of the variables shown in this example are seen by the script within the $_POST array, when I switch to the POST method.
Switching from the $_POST array to the $_REQUEST array works fine (eg: "if(isset($_REQUEST['cmd'])) $cmd = $_REQUEST['cmd']);" ), BUT....
Just the act of issuing my XMLHttpRequest() calls with the POST method, results in errors like this in my log:
PHP Deprecated: Automatically populating $HTTP_RAW_POST_DATA is
deprecated and will be removed in a future version. To avoid this
warning set 'always_populate_raw_post_data' to '-1' in php.ini and use
the php://input stream instead. in Unknown on line 0
So obviously This is a dead end. Doing my best to research what is going on here, in the PHP manual reference to POST I read this right on top...
$_POST $HTTP_POST_VARS [deprecated]
So if I'm understanding this right, the reason I can only obtain the POST variables using the $_REQUEST array is because my requests are still formatted as if I were still using GET, which (apparently?) is a deprecated method. Therefore, "coaxing" it to work is just moving the problem to the future.
Incidentally, I can see by calling the phpinfo.php on my server that despite the ERROR I see in my logs, "always_populate_raw_post_data" is indeed already set to "-1". So all the "solutions" I've found recommending I "JUST" make this setting in my PHP.INI file do not help.
And even if it DID "hide" the error, doing so seems irrelevant to me. Am I wrong in saying that even if I simply gather my passed PHP vars from the $_REQUEST array when using the POST method, AND figure out a way to suppressing the "deprecated" warnings in my LOG, the whole scheme would still fall apart (and make my POST requests fail), the day I finally migrate to PHP 7.x.?
I apologize for asking here, but I have really tried to search every reference I could find for both doing XMLHttpRequests using POST method, AND the deprecated error messages I get in my logs. There is so much obsolete info out there, I can't find any clear discussion on how to PROPERLY alter or re-format my XMLHttpRequests so that I can use the POST method. In fact, I can't even positively determine whether doing so will be possible beyond PHP.5.6. Any help will be very much appreciated.

PHP made a mistake of naming $_GET and $_POST after the default place that a web browser will put data in an HTML form where the method attribute is set to GET or POST.
$_GET gets data from the query string, even if the request method was POST.
The request body is determined by the argument you pass to send. Data you put in the query string remains in the query string.
const url = 'http://myscript.php'
const form_data = new FormData();
form_data.append("cmd", "dothis");
form_data.append("data", "somedata");
cost xhr = new XMLHttpRequest;
xhr.open("POST", url);
xhr.addEventListener('load', function () { console.log(this.response); });
xhr.send(form_data);

Related

file_get_contents('php://input') returning empty string with a PATCH request

My environment is PHP 5.5.9, Nginx 1.4.6-1ubuntu3.2, localhost.
I'm trying to get data from PATCH method, but that just return an empty string...
With POST method that's work fine, this is a piece of my script :
case 'POST':
case 'PATCH':
$this->data = file_get_contents("php://input");
$this->data is empty when PATCH method is used and complete when POST, i use the POSTMAN chrome extension and i push RAW data (not multipart/form-data)
I think Nginx was in fault...but nothing into the log file...
Any help will be much appreciated !
I recently ran into a similar issue trying to access php://input for a PUT request. The issue was simply that I had already accessed it once previously (in a logging function that ran prior to the code in question).
POST acts differently than the other methods, which explains the discrepancy:
Note: Prior to PHP 5.6, a stream opened with php://input could only be read once; the stream did not support seek operations. However, depending on the SAPI implementation, it may be possible to open another php://input stream and restart reading. This is only possible if the request body data has been saved. Typically, this is the case for POST requests, but not other request methods, such as PUT or PROPFIND.
source: http://php.net/manual/en/wrappers.php.php
The solution was simple: Store the value of php://input from the initial grab to a PHP variable, and don't try to run file_get_contents("php://input") more than once for a non-POST request.

HTTP protocol's PUT and DELETE and their usage in PHP

Introduction
I've read the following:
Hypertext Transfer Protocol (HTTP) is the life of the web. It's used every time you transfer a document, or make an AJAX request. But HTTP is surprisingly a relative unknown among some web developers.
The HTTP verbs comprise a major portion of our “uniform interface” constraint and provide us the action counterpart to the noun-based resource. The primary or most-commonly-used HTTP verbs (or methods, as they are properly called) are POST, GET, PUT, and DELETE.
Huh?
Well, we came to the point I lost track of things.
PUT and DELETE, they say. I've only ever heard of POST and GET and never saw something like $_PUT or $_DELETE passing by in any PHP code I've ever viewed.
My question
What are these methods (PUT) and (DELETE) for and if it's possible to use them in PHP, how would I go about this.
Note: I know this is not really a problem but I always grab a learning opportunity if I see one and would very much like to learn to use these methods in PHP if this is possible.
What are these methods (PUT) and (DELETE) for...
There are a lot of words to spend to explain this, and I'm not skilled enough to do it, but as already posted, a quick recap of what the HTTP specification describes.
The protocol basically says this:
use GET when you need to access a resource and retrieve data, and you don't have to modify or alter the state of this data.
use POST when you need to send some data to the server. Ex. from a form to save these data somewhere.
use HEAD when you need to access a resource and retrieve just the Headers from the response, without any resource data.
use PUT when you need to replace the state of some data already existing on that system.
use DELETE when you need to delete a resource (relative to the URI you've sent) on that system.
use OPTIONS when you need to get the communication options from a resource, so for checking allowed methods for that resource. Ex. we use it for CORS request and permissions rules.
You can read about the remaining two methods on that document, sorry I've never used it.
Basically a protocol is a set of rules you should use from your application to adhere to it.
... and if it's possible to
use them in PHP, how would I go about this.
From your php application you can retrieve which method was used by looking into the super global array $_SERVER and check the value of the field REQUEST_METHOD.
So from your php application you're now able to recognize if this is a DELETE or a PUT request, ex. $_SERVER['REQUEST_METHOD'] === 'DELETE' or $_SERVER['REQUEST_METHOD'] === 'PUT'.
* Please be also aware that some applications dealing with browsers that don't support PUT or DELETE methods use the following trick, a hidden field from the html form with the verb specified in its value attribute, ex.:
<input name="_method" type="hidden" value="delete" />
Follow an example with a small description on a possible way to handle those 2 http requests
When you (your browser, your client) request a resource to an HTTP server you must use one of the method that the protocol (HTTP) accepts. So your request needs to pass:
A METHOD
An Uri of the resource
Request Headers, like User-Agent, Host, Content-Length, etc
(Optional body of the request)
Now, while you would be able to get data from POST and GET requests with the respective globals ($_GET, $_POST), in case of PUT and DELETE requests PHP doesn't provide these fast access globals; But you can use the value of $_SERVER['REQUEST_METHOD'] to check the method in the request and handle your logic consequently.
So a PUT request would look like:
PUT /something/index.php
(body) maybe=aparameter
and you can access those data in PHP by reading the php://input stream, ex. with something like:
if ($_SERVER['REQUEST_METHOD'] === 'PUT') {
$myEntireBody = file_get_contents('php://input'); //Be aware that the stream can only be read once
}
and a DELETE request would look like:
DELETE /something/index.php?maybe=aparameter
and again you can build your logic after have checked the method:
if ($_SERVER['REQUEST_METHOD'] === 'DELETE') {
// do something
}
Please pay attention that a DELETE request has no Body and pay very attention to Response Status Code too (ex. if you received a PUT request and you've updated that resource without error you should return a 204 status -No content-).
Way to use PUT data from PHP:
$method = $_SERVER['REQUEST_METHOD'];
if ('PUT' === $method) {
parse_str(file_get_contents('php://input'), $_PUT);
var_dump($_PUT); //$_PUT contains put fields
}
PHP's $_GET and $_POST are poorly named. $_GET is used to access the values of query string parameters, and $_POST lets you access the request body.
Using query string parameters is not limited to GET requests, and other kinds of requests than just POST can come with a request body.
If you want to find out the verb used to request the page, use $_SERVER['REQUEST_METHOD'].
Most suitable place to use these (PUT and DELETE) methods is REST API. Where we use http methods to define the mode of operation for example you want to fetch any resources then you can use following:
GET http://api.example.com/employee/<any_id>
to add a new item:
POST http://api.example.com/employee/
to Update or Edit:
PUT http://api.example.com/employee/
to Delete an existing resource:
DELETE http://api.example.com/employee/1
etc.
Now on PHP side you just need to read what HTTP method used so that you can make an action according to that.
There are lots of libraries available which can do that for you.
What are these methods (PUT) and (DELETE)
There are described in the HTTP spec.
In a nutshell, and simplifying somewhat, PUT is for uploading a file to a URL and DELETE is for deleting a file from a URL.
never sawy something like $_PUT or $_DELETE passing by in any PHP code I've ever viewed
$_POST and $_GET are terribly named superglobals. $_POST is for data parsed from the request body. $_GET is for data parsed from the URL. There's nothing that strictly ties data in either of those places (especially the URL) to a particular request method.
DELETE requests only care about the URL's path, so there is no data to parse.
PUT requests usually care about the entire request body (not a parsed version of it) which you would access with file_get_contents('php://input');.
for and if it's possible to use them in PHP, how would I go about this.
You'd need to map the URL onto a PHP script (e.g. with URL rewriting), test the request method, work out what URL you were actually dealing with, and then write code to do the appropriate action.
$GLOBALS["_PUT"]=null;
if($_SERVER['REQUEST_METHOD'] == 'PUT') {
$form_data= json_encode(file_get_contents("php://input"));
$key_size=52;
$key=substr($form_data, 1, $key_size);
$acc_params=explode($key,$form_data);
array_shift($acc_params);
array_pop($acc_params);
foreach ($acc_params as $item){
$start_key=' name=\"';
$end_key='\"\r\n\r\n';
$start_key_pos=strpos($item,$start_key)+strlen($start_key);
$end_key_pos=strpos($item,$end_key);
$key=substr($item, $start_key_pos, ($end_key_pos-$start_key_pos));
$end_value='\r\n';
$value=substr($item, $end_key_pos+strlen($end_key), -strlen($end_value));
$_PUT[$key]=$value;
}
$GLOBALS["_PUT"]=$_PUT;
}
if (!function_exists("getParameter")){
function getParameter($parameter)
{
$value=null;
if(($_SERVER['REQUEST_METHOD'] == 'POST')&& (isset($_POST[$parameter]))){
$value=$_POST[$parameter];
}
else if(($_SERVER['REQUEST_METHOD'] == 'PUT')&& (isset($GLOBALS["_PUT"][$parameter])))
{
$value=$GLOBALS["_PUT"][$parameter];
}
else if(($_SERVER['REQUEST_METHOD'] == 'DELETE')&& (isset($_DELETE[$parameter]))){
$value=$_DELETE[$parameter];
}
else if(($_SERVER['REQUEST_METHOD'] == 'PATCH')&& (isset($_PATCH[$parameter]))){
$value=$_PATCH[$parameter];
}
else if(isset($_GET[$parameter])){
$value=$_GET[$parameter];
}
return $value;
}
}

Request parameters in PHP on HHVM

I'm running HHVM 3.2.0 and trying to get access to GET and POST request parameters. The problem is, HHVM doesn't support access to PHP superglobals ($_GET, $_POST, $_SERVER, etc).
The only other way I know of getting access to request parameters in PHP is via the filter_input function, but is that really best practice (if I'm just using the raw filter)? It seems as though HHVM should support something cleaner than that. (What about Hack?)
HHVM absolutely supports superglobals in PHP code -- they're a really key part of PHP! The docs page you've linked to is simply wrong, and I've filed a bug to get it fixed.
In strict mode Hack code, superglobals are not supported; this cookbook example shows how you can access them via partial mode.
Seems like they added HH\global_get() to get global variables.
https://docs.hhvm.com/hack/reference/function/HH.global_get/
To put everything together
use namespace \Facebook\TypeSpec;
// get $_GET using global_get
function global_get_get(): darray<arraykey, mixed> {
$spec = TypeSpec\darray(
TypeSpec\arraykey(),
TypeSpec\mixed(),
);
return $spec->assertType(\HH\global_get("_GET"));
}

How to use Request::factory()->execute() to call an script from another library in the same host

I'm using Kohana 3.2, and I want to be able to call another script (unrelated to Kohana, outside of its 'jurisdiction') that returns a application/json response.
When I tried using:
$response = json_decode(Request::factory('/scripts/index.php?id=json')->execute()->body());
It errors out saying there's no route to scripts/index.php. So I tried using Request_Client_External
Request_Client_External::factory()->execute(Request::factory('/scripts/index.php?page=s'))->body();
Gives me Request_Exception [ 0 ]: Error fetching remote /scripts/index.php?page=s [ status 0 ] Could not resolve host: scripts; Host not found. It appears it need a full flagged URL using http/https, but how to avoid the overhead of it doing a real external request?
Doing a
Request::factory(url::site('/scripts/index.php?page=s', 'http'))->execute()
works but is it considered "external"?
The short answer to your question is that the only way to use Request::factory()->execute() to achieve that is to use pass it the full url (with whatever "overhead" that entails, which shouldn't be too much: your server's probably quite good at talking to itself).
Otherwise, ideally you'd put the functionality of scripts into a library and call that from Kohana. However it sounds like that's not an option for you. If you have to leave /scripts/index.php untouched and insist on an 'internal' request, you could use PHP's output buffering, as illustrated below. But there are a bunch of caveats so I wouldn't recommend it: the best way is passing a full url.
// Go one level deeper into output buffering
ob_start();
// Mimic your query string ?id=json (see first caveat below)
$_GET = $_REQUEST = array('id' => 'json');
// Get rid of $_POST and $_FILES
$_POST = $_FILES = array();
// Read the file's contents as $json
include('/scripts/index.php');
$json = ob_get_clean();
$response = json_decode($json);
Some caveats.
Firstly, the code changes $_GLOBALS. You probably don't use these in your Kohana code (you use $this->request->get() like a good HMVCer, right?). But in case you do, you should 'remember' and then restore the values, putting $old_globals = $GLOBALS; etc. before the above code, and $GLOBALS = $old_globals; after.
Sessions: if your /scripts/index.php uses `session_start() this will cause a warning if you've already started a session at this point in Kohana.
Note that all variables set in scripts/index.php will remain set in the context you're in. If you want to avoid possible conflicts with that context, you'd start a new context, i.e. wrap the above into its own function.
Finally, you'd also need to make sure that /scripts/index.php doesn't do anything like Kohana::base_url = 'something_else', or touch any other static attributes, or do something catastrophic using this.

Can PHP detect the status code that it has or is preparing to send to the browser?

I am sending a status code via the header function, such as header('HTTP/1.1 403');, and would like to in another area of my code detect what status code is to be sent (in particular if I have sent an error code of some nature). As has been mentioned elsewhere, headers_list() does not return this particular item, and it's unclear to me if it actually counts as a header, as neither Firebug nor Fiddler2 treat it with the headers. So - how can a PHP script detect which status code is about to be sent to the browser?
I would rather not use a wrapper function (or object) around the header method, or otherwise set some global variable along with the sending of the status code. I'd also rather not call my code from itself using curl or the like, due to performance concerns. Please let me know what you think.
Consider setting a constant:
define('HTTP_STATUS', 403);
and using the defined function later on:
if(defined('HTTP_STATUS') && HTTP_STATUS == 403) // ...or whatever you're looking to do
Actually peeking back at the headers themselves is kind of a hack in itself as it's simply too slow: you're dealing with strings and arrays and all sorts of other messy data. Set for yourself a simple constant: it's blazing fast, it does the same thing, and it doesn't create any "true" global variables.
http_response_code() in PHP 5.4 does this now.
It's another call, but as a last resort you could use this rather than curl. If you have php 5.0, What about get_headers()?

Categories