PUT request with json content-type - php

I have a switch based on $_SERVER['REQUEST_METHOD'] and something is going wrong in the PUT case.
The plausible way to read PUT is to use php://input and read it with fopen or file_get_contents.
The data that gets sent to PUT is of Content-type: application/json
Currently, this is the case I have got:
case "PUT":
parse_str(file_get_contents("php://input"), $putData);
var_dump($putData);
if(isset($_GET['id'])){
putData($_GET['id'], $putData);
} else {
print json_encode(["message" => "Missing parameter `id`."]);
http_response_code(400);
}
break;
The great thing is that my cURL request with key/value pairs work perfectly fine. The data gets filled and my putData() handles everything just fine.
The problem is that I need to accept JSON in this case, how do I go about?
My REST client throws an empty array when I var_dump($putData).

Try using json_decode instead of parse_str
case "PUT":
$rawInput = file_get_contents("php://input");
$putData = json_decode($rawInput);
if (is_null($putData)) {
http_response_code(400);
print json_encode(["message" => "Couldn't decode submission", "invalid_json_input" => $rawInput]);
} else {
if(isset($_GET['id'])){
putData($_GET['id'], $putData);
} else {
http_response_code(400);
print json_encode(["message" => "Missing parameter `id`."]);
}
}
break;

Just guessing here, but if your REST client accepts JSON for this request, it will choke on var_dump() which outputs a string which isn't JSON right back onto the response. Try removing var_dump()
Also, I'm pretty sure you must call http_response_code() before any output is sent to the client.

Related

Problem parsing the body of a POST request

So what I'm trying to do is: with a link similar to: http://localhost/API-REST/Endpoints/ajoutDeMessage.php?contenu='Hello'&idUser=4. For now, I'm only testing my request with Postman. So I'm putting this URL directly in Postman, and press send.
When I'm doing a request without a body, it works fine.
So, I need to send a string and an id through the URL, and with a function, I'm inserting these data in my database.
With the php://input, I expect to have the variables contenu and idUser in the body for a SQL request.
What I want to do next is a React app which will communicate with the API (but that's not related for now).
So here is my code:
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
require_once('../Configuration/database.php');
require_once('../Users/messages.php');
$database = new Database();
$db = $database->getConnection();
$json = json_decode(file_get_contents("php://input"),true);
var_dump($json);
if(!empty($json->contenu) && !empty($json->idUser)){
$contenu = strip_tags($json->contenu);
$idUser = strip_tags($json->idUser);
$message = new Messages($db);
if ($message->addMessage($contenu,$idUser)){
http_response_code(201);
$response['message'] = 'GOOD ENTRY';
}else{
http_response_code(400);
$response['message'] = 'BAD ENTRY';
}
}else{
http_response_code(400);
$response['message'] = 'INCOMPREHENSIBLE DATA';
}
echo json_encode($response);
} else {
http_response_code(405);
$response['error_code'] = 405;
$response['message'] = 'BAD REQUEST TYPE';
echo json_encode($response);
}
There is no post body
i'm putting this url directly in postman, and press send.
I don't use postman myself, but doing this will generate a post request with no data it's the equivalent of:
curl -X POST http://example.com
That's not passing a post body at all. The intent is more like:
curl http://example.com
-H 'Content-Type: application/json'
-d '{"contenu":"Hello","idUser":4}'
This is why file_get_contents("php://input") doesn't return anything.
Note that html form data is available via $_POST - but only for urlencoded POST bodies (which I understand not to be the intent of the question).
Where is the data?
i'm putting this url directly in postman, and press send.
Returning to this quote, the only place for the data is in the url - that is not the normal way to pass data with a post request.
Url arguments are available via $_GET, with the url in the question this code:
<?php
var_dump($_GET);
will output:
array(2) {
["contenu"]=>
string(7) "'Hello'"
["idUser"]=>
string(1) "4"
}
A detail, but note the string includes the single quotes which are in the url (that are probably not desired).
What's the right way to do this?
With a request being made like this:
curl http://example.com
-H 'Content-Type: application/json'
-d '{"contenu":"Hello","idUser":4}'
That data can be accessed like so:
<?php
$body = file_get_contents('php://input');
$data = json_decode($body, true);
$jsonError = json_last_error();
if ($jsonError) {
print_r(['input' => $body, 'error' => json_last_error_msg()]);
exit;
}
echo $data['contenu']; // Hello
echo $data['idUser']; // 4
...
This is very similar to the code in the question, the error appears to primarily be how the request is being made rather than the php logic.

Slack Events PHP Response

I'm working on an integration between Slack and Filemaker utilizing PHP. I am successful in having the code create a record in Filemaker based on the json request, and also have no trouble returning the challenge key to Slack.
However, I'm having trouble passing the header response 200 OK to Slack, while passing the challenge back. It looks like it has to be one or the other.
I've tried to move the HTTP header to different areas in the code, but haven't had any success so far.
Here is the current code:
<?php
$data = json_decode(file_get_contents('php://input'), true);
if (!isset($data["challenge"])) {
$body = $_SERVER['HTTP_X_SLACK_RETRY_REASON'];
require_once ('Filemaker.php');
//$body = file_get_contents('php://input');
$fm = new Filemaker();
$fm->setProperty('database', '');
$fm->setProperty('username', '');
$fm->setProperty('password', '');
$command = $fm->newPerformScriptCommand('PHP_RESPONSE', 'script', $body);
$result = $command->execute();
}
else {
header("Content-Type: text/plain");
header('X-PHP-Response-Code: 200', true, 200);
echo $data["challenge"];
}
?>
The result I expect is for the code to return the challenge code for Slack, while also returning an HTTP header of 200 OK.
Currently I can see I am receiving an error of "http_error" from Slack, which is what leads me to believe the problem is that the header is not being passed back successfully.
Any ideas on what is wrong, or suggestions on the right direction to proceed would be greatly appreciated.
The problem was occurring because for events slack doesn't send "challenge" as a parameter when sending events. It looks like echoing "challenge" is only needed when initially setting the URL for the events API.
I enclosed the challenge echo in a if statement that would only trigger if the challenge variable was present. After doing so the 200 OK was successfully passed.
Here is the code I used that solved the problem for me:
$data = json_decode(file_get_contents('php://input'), true);
if (isset($data["challenge"])) {
$message = [
"challenge" => $data["challenge"]
];
header('Content-Type: application/json');
echo json_encode($message);
}
The documentation is actually a bit inconsistent on this topic. It claims you can respond the challenge in plan text, but the example shows it as x-www-form-urlencoded.
To be on the safe side try returning the challenge as JSON. That works perfectly for me. You also do not need to explicitly set the HTTP 200 code.
Example code:
$message = [
"challenge" => $data["challenge"]
];
header('content-type: application/json');
echo json_encode($message);

Setting correct content type for cURL to receive a response

I'm trying to create a relatively simple PHP endpoint for users to send requests to. I know that the endpoint is working because when I accessed it using cURL the parameters I sent to my database we're added. The problem however is that when I use
var_dump($response);
The page returns "NULL".
So the code is working fine, I just want to know how to print an error/success message
This is what I've tried so far on the endpoint
header("HTTP/1.1 200 OK");
header('Content-Type: text/plain');
echo 'Success message';
the full cURL code:
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_URL => $url,
CURLOPT_POSTFIELDS => 'example=this'
);
$resp = curl_exec($curl);
// Close request to clear up some resources
curl_close($curl);
$response = json_decode($resp, true);
var_dump($response);
So how can I get the success message to properly show instead of "NULL"?
Test if your curl code returns something by testing: var_dump($resp). It looks like NULL comes from json_decode. You are not returning valid JSON from the endpoint.
php > var_dump(json_decode("Success message", true));
NULL
Try returning a json string such as:
php > echo json_encode("Success", true);
"Success"
Note the " around it. This encodes a json string. See the JSON spec for a reference on how to encode json. Best practice, if your return json, then run your content through json_encode().
Your curl code seems correct.

GET info from external API/URL using PHP

I have the URL http://api.minetools.eu/ping/play.desnia.net/25565 which outputs statistics of my server.
For example:
{
"description": "A Minecraft Server",
"favicon": null,
"latency": 64.646,
"players": {
"max": 20,
"online": 0,
"sample": []
},
"version": {
"name": "Spigot 1.8.8",
"protocol": 47
}
}
I want to get the value of online player count to display it on my website as: Online Players: online amount
Can anyone help?
I tried to do:
<b> Online players:
<?php
$content = file_get_contents("http://api.minetools.eu/ping/play.desnia.net/25565");
echo ($content, ["online"]);
}
?>
</b>
But it didn't work.
1) Don't use file_get_contents() (If you can help it)
This is because you'd need to enable fopen_wrappers to enable file_get_contents() to work on an external source. Sometimes this is closed (depending on your host; like shared hosting), so your application will break.
Generally a good alternative is curl()
2) Using curl() to perform a GET request
This is pretty straight forward. Issue a GET request with some headers using curl().
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "http://api.minetools.eu/ping/play.desnia.net/25565",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => array(
"cache-control: no-cache"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
3) Using the response
The response comes back in a JSON object. We can use json_decode() to put this into a usable object or array.
$response = json_decode($response, true); //because of true, it's in an array
echo 'Online: '. $response['players']['online'];
Your server is returning a JSON string.
So you should use json_decode() function to convert that into a plain PHP object.
Thereafter you can access any variable of that object.
So, something like this shall help
<?php
$content = file_get_contents("http://api.minetools.eu/ping/play.desnia.net/25565");
$result = json_decode($content);
print_r( $result->players->online );
?>
More details for json_decode can be read here -
http://php.net/manual/en/function.json-decode.php
Your webservice (URL: http://api.minetools.eu/ping/play.desnia.net/25565) returns JSON.
This is a standard format, and PHP (at least since 5.2) supports decoding it natively - you'll get some form of PHP structure back from it.
Your code currently doesn't work (your syntax is meaningless on the echo - and even if it was valid, you're treating a string copy of the raw JSON data as an array - which won't work), you need to have PHP interpret (decode) the JSON data first:
http://php.net/manual/en/function.json-decode.php
<?php
$statisticsJson = file_get_contents("http://api.minetools.eu/ping/play.desnia.net/25565");
$statisticsObj = json_decode($statisticsJson);
Your $statisticsObj will be NULL if an error occurred - and you can get that error using other standard PHP functions:
http://php.net/manual/en/function.json-last-error.php
Assuming it isn't NULL, you can examine the structure of the object with var_dump($statisticsObj) - and then alter your code to print it out appropriately.
In short, something like:
<?php
$statisticsJson = file_get_contents("http://api.minetools.eu/ping/play.desnia.net/25565");
$statisticsObj = json_decode($statisticsJson);
if ($statisticsObj !== null) {
echo $statisticsObj->players->online;
} else {
echo "Unknown";
}
You should also check what comes back from file_get_contents() too - various return values can come back (which would blow up json_decode()) on errors. See the documentation for possibilities:
http://php.net/manual/en/function.file-get-contents.php
I'd also wrap the entire thing in a function or class method to keep your code tidy. A simple "almost complete" solution could look like this:
<?php
function getServerStatistics($url) {
$statisticsJson = file_get_contents($url);
if ($statisticsJson === false) {
return false;
}
$statisticsObj = json_decode($statisticsJson);
if ($statisticsObj !== null) {
return false;
}
return $statisticsObj;
}
// ...
$stats = getServerStatistics($url);
if ($stats !== false) {
print $stats->players->online;
}
If you want better handling over server / HTTP errors etc, I'd look at using curl_*() - http://php.net/manual/en/book.curl.php
Ideally you also should be confirming the structure returned from your webservice is what you expected before blindly making assumptions too. You can do that with something like property_exists().
Happy hacking!
Since it's returning an array you should use print_r or var_dump instead of echo. Or perhaps it threw you an error.

abort curl download if content type is wrong

I'm setting up a site where users will be able to post links, and curl (in php) will crawl the url, and format something based on the metadata, open graph tags, etc. I have it set up to run simultaneous uploads with multi_init and multi_exec. I created a gist for the class here. What it's supposed to do is:
get metadata from multiple urls
return a single json string but only for pages with content-type 'text/html' (so don't bother with direct links to images, js, executables, etc)
The problem seems to be the callback for CURLOPT_HEADERFUNCTION. I thought that having it return -1 when a content-type header exists but isn't an html header would abort the download but it doesn't seem to do anything (although the check appears correct and it seems to be returning -1.) It still seems to allow any content type through.
Here specifically is the callback:
CURLOPT_HEADERFUNCTION => function($ch, $header){
// if they're sending a content-type header, it must be text/html
if(stripos(trim($header), "Content-Type") === 0){
list($key, $val) = explode(":", $header);
if(stripos(trim($val), "text/html") === 0){
return strlen($header);
}
else{
return -1;
}
}
else{
return strlen($header);
}
}
I tried curl_close but got an error about closing curl in a callback. Any suggestions?
Use the callback to set a (global) variable. Skip your curl_exec() call when false.
$htmlheader = true;
function header_callback($ch, $headers)
{
$GLOBALS['htmlheader']=false;
}
$ch = curl_init('http://www.example.com/');
curl_setopt($ch,CURLOPT_HEADERFUNCTION, 'header_callback');
if($htmlheader)
{
$result = curl_exec($ch);
}
curl_close($ch);

Categories