Making POST application/json request with file_get_contents - php

I made a code in PHP. The code works correctly when I run the command:
$payload = file_get_contents ('request.json');
However, I need to create dynamic content for the parameters passed by request.json
I made a routine that generates a string with exactly the same content as in the request.json file. However, when I pass this content to the $payload my function does not work.
$options = array (
'http' => array (
'header' => "Content-type: application / json",
'method' => 'POST',
'content' => $reqjson,
)
);
$context = stream_context_create ($options);
$result = file_get_contents ($url, false, $context);
Why is that? Isn't the type returned by the "file_get_contents" function a common string? How to fix it?

First, each line of header must end with "\r\n". Please append that to the "Content-Type" line. Second, if the function file_get_contents() returns false, it mean the request somehow failed. You should examine $http_response_header for more information:
$options = array(
'http' => array(
'header' => "Content-type: application/json\r\n",
'method' => 'POST',
'content' => $reqjson,
),
);
$context = stream_context_create($options);
if (($result = file_get_contents($url, false, $context)) === false) {
var_dump($http_response_header);
}
If the response header ($http_response_header) starts with HTTP/1.0 400 Bad Request, it means that the server somehow think your request is malformatted. Then,
If you have access to the requested server, try to find relevant log file(s) for more information.
If you have documentations to the requested server / service, please check carefully the accepted request format.
Often request format errors can be:
"appliation/json" is not an accepted request format; or
The content of $reqjson is malformat (e.g. it is supposed to be JSON text, not PHP array).
If you're using 3rd party service and still cannot figure out why it doesn't give you the expected result, seek help from the service provider.

Thanks, Koala Yeung.
As I said, the problem is that I tried to assemble in a string exactly the same content that is inside the request.json file. However, when I use the file_get_contents function to get the contents of the json file, it works. When it is directly in the string variable, the error occurs:
array (8) {[0] => string (24) "HTTP / 1.0 400 Bad Request" [1] => string (79) "x-cloud-trace-context: db4d34cce75495db89a04731bb718b49 / 1272894976542815412; o = 0" [2 ] => string (12) "vary: origin" [3] => string (45) "content-type: application / json; charset = utf-8" [4] => string (23) "cache-control: no-cache "[5] => string (19)" content-length: 229 "[6] => string (35)" Date: Tue, 08 Dec 2020 21:01:45 GMT "[7] => string (15) "Via: 1.1 google"}
bool (false)
I believe it is something related to this type of error:
"The content of $ reqjson is malformat (e.g. it is supposed to be JSON text, not PHP array)."
But the string is identical.

Related

Failed to open stream: HTTP request failed! HTTP/1.1 400 Bad Request - PHP Error

SOLUTION: I had malformed my JSON data for the payload body. The "ttl" => 30 was in the incorrect array() method. This probably won't help anyone in the future, moving the ttl key/value pair made this work correctly as seen below.
$data = array(
"statement" => array(
"actor" => array(
"mbox" => "mailto:test#example.com"
),
),
"ttl" => 30
);
I have checked numerous other StackOverflow questions and cannot find a solution that works. I should note that I am testing this using a local XAMPP server running on port 8080. Not sure if that matters. I have been able to get this working using Postman, but translating it to PHP has been problematic. Am I missing something? I am not all that familiar with PHP, but need this for work.
EDIT: Some more information about what the API is expecting. It's a fairly simple API that requires a JSON body, a Basic Authorization header, and a Content-Type: application/json.
Here is the JSON body I am using in Postman. This is a direct copy/paste from Postman, which is successfully communicating with the API:
{
"statement": {
"actor": {
"mbox": "mailto:test#example.com"
}
},
"ttl": 30
}
Is there a syntax error in my below PHP code for this? Again, I am learning PHP on the fly so I'm unsure if I am properly constructing a JSON payload using the array() method in PHP.
My code below has the $https_user,$https_password, and $url domain changed for obvious security reasons. In my actual PHP code, I have the same credentials and domain used in Postman.
The $randomSessionID serves no real purpose other than an identification number for future requests. Has no affect on the API response failing or succeeding.
<?php
$https_user = 'username';
$https_password = 'password';
$randomSessionID = floor((mt_rand() / mt_getrandmax()) * 10000000);
$url = 'https://www.example.com/session/' . $randomSessionID . '/launch';
$json = json_encode(array(
"statement" => array(
"actor" => array(
"mbox" => "mailto:test#example.com"
),"ttl" => 30
)
));
$options = array(
'http' => array(
'method' => 'POST',
'header' => 'Content-Type: application/json\r\n'.
"Authorization: Basic ".base64_encode("$https_user:$https_password")."\r\n",
'content' => $json
)
);
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === FALSE) { /* Handle error */ }
?>
SOLUTION: I had malformed my JSON data for the payload body. The "ttl" => 30 was in the incorrect array() method. This probably won't help anyone in the future, but moving the ttl key/value pair made this work correctly as seen below.
$data = array(
"statement" => array(
"actor" => array(
"mbox" => "mailto:test#example.com"
),
),
"ttl" => 30
);

Vimeo API not responding to "If-Modified-Since" header

According to Vimeo API documentation:
The If-Modified-Since header enables you to return only those API resources that have been modified since a particular date and time.
The header looks like this:
If-Modified-Since: {ddd}, {D} {MMM} {YYYY} {HH}:{mm}:{ss} {Z}
NOTE: If your formatting codes are rusty, Tue, 20 Jun 2023 14:42:36 GMT is an example.
If none of the resources have been modified since this date, the API returns an empty response body and the HTTP status 304 Not Modified.
I'm using the Official PHP library for the Vimeo API.
According to GitHub issue #130, the PHP library's request() method accepts an array of headers. And this commit shows how the $headers array is passed and parsed:
public function request($url, $params = array(), $method = 'GET',
$json_body = true, array $headers = array())
// Set the headers
foreach ($headers as $key => $value) {
$curl_opts[CURLOPT_HTTPHEADER][] = sprintf('%s: %s', $key, $value);
}
But when I pass the future date shown in the example, I still receive a full list of videos rather than the "empty response body and the HTTP status 304 Not Modified" specified in the documentation.
What am I doing wrong?
$fields = array(
'created_time',
'modified_time'
);
$params = array(
'page' => $page,
'filter' => 'embeddable',
'filter_embeddable' => true,
'fields' => implode(',',$fields)
);
$headers = array(
'If-Modified-Since' => 'Tue, 20 Jun 2023 14:42:36 GMT'
);
$json_body = true;
$method = 'GET';
$response = $vimeo->request('/me/videos', $params, $method, $json_body, $headers);
Result:
Array
(
[0] => Array
(
[created_time] => 2018-06-05T19:27:18+00:00
[modified_time] => 2018-06-29T19:12:21+00:00
)
[1] => Array
(
[created_time] => 2016-06-02T03:01:40+00:00
[modified_time] => 2019-04-30T06:15:29+00:00
)
[2] => Array
(
[created_time] => 2016-05-29T05:31:46+00:00
[modified_time] => 2019-04-25T07:46:53+00:00
)
....
Edit
Based on this answer (not about Vimeo), it seems that the API might return the entire set of videos if even one of them was modified after the "If-Modified-Since" date.
If anything has changed in the entire response, then it will send the entire response to you.
But I'd still expect the result to be empty if the date is in the future. Am I misunderstanding?
Edit
Tom suggested that the Vimeo API ignores "If-Modified-Since" headers that are set in the future. I tried setting mine in the recent past, but I'm still getting results that were modified before that date:
$vimeo = new \Vimeo\Vimeo(false,false,$access_token);
$fields = array(
'modified_time'
);
$params = array(
'page' => 1,
'fields' => implode(',',$fields)
);
$method = 'GET';
$json_body = true;
$headers = array(
'If-Modified-Since' => 'Fri, 24 May 2019 14:42:36 GMT'
);
$response = $vimeo->request('/me/videos', $params, $method, $json_body, $headers);
echo"<pre>".print_r($response,true)."</pre>";
The result includes:
[21] => Array
(
[modified_time] => 2019-05-16T17:22:58+00:00
)
[22] => Array
(
[modified_time] => 2019-05-12T08:07:30+00:00
)
Edit
I was wrong. As mentioned above, I believe the entire response is returned if any item in the response has been modified since the "If-Modified-Since" timestamp. That made it look like the header wasn't working. But I set the timestamp as close as possible to the current time and I did get a "304 Not Modified" response, as Tom reported in his answer below. Others (content producers) also have access to the Vimeo account I'm testing against and I wasn't aware how recently they had modified content.
It's not documented by Vimeo, but I found out by experiment that:
When If-Modified-Since lies in the future, it is ignored.
Otherwise, the header works as expected. But take care of your timezones and possible clock-skew of a few seconds.
Vimeo's modification time is shown in the API response, in my case:
"modified_time": "2019-05-22T09:52:45+00:00",
If-Modified-Since: Wed, 22 May 2019 09:56:25 GMT returns a 304 Not Modified for my situation.
I've submitted a support request to Vimeo to clarify or change this behaviour.

Async Upload Using Curl in PHP - curl_multi_exec()

I'm trying to figure out for days how would it be possible if at all, to upload multiple files parallel using PHP.
given I have a class called Request with 2 methods register() and executeAll():
class Request
{
protected $curlHandlers = [];
protected $curlMultiHandle = null;
public function register($url , $file = [])
{
if (empty($file)) {
return;
}
// Register the curl multihandle only once.
if (is_null($this->curlMultiHandle)) {
$this->curlMultiHandle = curl_multi_init();
}
$curlHandler = curl_init($url);
$options = [
CURLOPT_ENCODING => 'gzip',
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => $file,
CURLOPT_USERAGENT => 'Curl',
CURLOPT_HTTPHEADER => [
'Content-Type' => 'multipart/form-data; boundry=-------------'.uniqid()
]
];
curl_setopt_array($curlHandler, $options);
$this->curlHandlers[] = $curlHandler;
curl_multi_add_handle($this->curlMultiHandle, $curlHandler);
}
public function executeAll()
{
$responses = [];
$running = null;
do {
curl_multi_exec($this->curlMultiHandle, $running);
} while ($running > 0);
foreach ($this->curlHandlers as $id => $handle) {
$responses[$id] = curl_multi_getcontent($handle);
curl_multi_remove_handle($this->curlMultiHandle, $handle);
}
curl_multi_close($this->curlMultiHandle);
return $responses;
}
}
$request = new Request;
// For this example I will keep it simple uploading only one file.
// that was posted using a regular HTML form multipart
$resource = $_FILES['file'];
$request->register('http://localhost/upload.php', $resource);
$responses = $request->executeAll(); // outputs an empty subset of array(1) { 0 => array(0) { } }
Problem:
Can't figure out why on upload.php (the script which is my endpoint url on the register method) $_FILES is always an empty array:
upload.php:
<?php
var_dump($_FILES); // outputs an empty array(0) { }
Things I've already tried:
prefixing the data with #, like so:
$options = [
CURLOPT_ENCODING => 'gzip',
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => ['file' => '#'.$file['tmp_name']],
CURLOPT_USERAGENT => 'Curl',
CURLOPT_HTTPHEADER => [
'Content-Type' => 'multipart/form-data; boundry=-------------'.uniqid()
]
];
That unfortunately did not work.
What am I doing wrong ?
how could I get the file resource stored in $_FILES global on the posted script (upload.php) ?
Further Debug Information:
on upload.php print_r the headers I get as response the following:
array(1) {
[0]=>
string(260) "Array
(
[Host] => localhost
[User-Agent] => Curl
[Accept] => */*
[Accept-Encoding] => gzip
[Content-Length] => 1106
[Content-Type] => multipart/form-data; boundary=------------------------966fdfac935d2bba
[Expect] => 100-continue
)
"
}
print_r($_POST) on upload.php gives the following response back:
array(1) {
[0]=>
string(290) "Array
(
[name] => example-1.jpg
[encrypted_name] => Nk9pN21IWExiT2VlNnpHU3JRRkZKZz09.jpg
[type] => image/jpeg
[extension] => jpg
[tmp_name] => C:\xampp\tmp\php77D7.tmp
[error] => 0
[size] => 62473
[encryption] => 1
[success] =>
[errorMessage] =>
)
"
}
I appreciate any answer.
Thanks,
Eden
Can't figure out why on upload.php (the script which is my endpoint url on the register method) $_FILES is always an empty array - your first problem is that you override libcurl's boundary string with your own, but you have curl generate the multipart/form-data body automatically, meaning curl generates another random boundary string, different from your own, in the actual request body, meaning the server won't be able to parse the files. remove the custom boundary string from the headers (curl will insert its own if you don't overwrite it). your second problem is that you're using $_FILES wrong, you need to extract the upload name if you wish to give it to curl, and you need to convert the filenames to CURLFile objects to have curl upload them for you. another problem is that your script will for no good reason use 100% cpu while executing the multi handle, you should add a curl_multi_select to prevent choking an entire cpu core. for how to handle $_FILES, see http://php.net/manual/en/features.file-upload.post-method.php
, for how to use CURLFile, see http://php.net/manual/en/curlfile.construct.php and for how to use curl_multi_select, see http://php.net/manual/en/function.curl-multi-select.php

Retrieve authentication token from api via post

Im trying to build an app that uses the simplenote api but I am having trouble with the authentication part. I am getting a 400 error(bad request).
Im guessing this issue is not related to the simplenote api, it's rather my understanding of the documentation.
Here is what the api is saying:
HTTP POST to the following URL:
https://simple-note.appspot.com/api/login
The body of the request should contain this, but base64-encoded:
email=[email-address]&password=[password]
To be clear, you will concatenate email= with the user’s email address (trimmed),
&password=, and the user’s password; then base64 the resulting string and send it as
the HTTP request body. (Do not encode this request using the standard variable
encoding used for POSTed web forms. We basically ignore the Content+Type header
for this request, but text/plain makes the most sense here.)
And here is my code so far:
$url = 'https://simple-note.appspot.com/api/login';
$data = array('email' => base64_encode($email), 'password' => base64_encode($password));
$options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($data),
),
);
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
var_dump($result);
I think what they mean is:
$data = base64_encode(http_build_query(array(
"email" => $email,
"password" => $password
));
So base64 encode the resulting string, not the individual parts.
As for making the request. I can recommend you take a look at cURL, it's much faster than file_get_contents.

POST Request not supported by Google Server

I am getting following error while posting request from my server to google.com:
Warning: file_get_contents(http://accounts.google.com): failed to open stream: HTTP request failed! HTTP/1.0 405 Method Not Allowed in C:\...\index.php on line 23
My code is following:
$postdata = http_build_query(
array(
'code' => '####',
'client_id' => '####',
'client_secret' => '####',
'grant_type' => 'authorization_code'
)
);
$opts = array(
'http' => array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata
)
);
$context = stream_context_create($opts);
$result = file_get_contents('http://accounts.google.com', false, $context);
echo $result;
the method you have chosen is 'PUT' and not 'POST'. if you want to send request as POST then change
'method' => 'PUT'
to
'method' => 'POST'
First of all what you get here is a response code (405) and it is within the error class (400 to 499 or also written as 4xx).
So the server reports back to you an error on the protocol level. The protocol used is named as well: HTTP, more specifically HTTP/1.0.
HTTP/1.0 including the status codes are outlined in RFC1945:
https://www.rfc-editor.org/rfc/rfc1945
The problem here is that the code 405 (as the concrete number) is not defined. So the docs fall back to the general description of 4xx for error codes.
HTTP/1.1 is outlined in RFC2616:
https://www.rfc-editor.org/rfc/rfc2616
It has the 405 error code details however, as the response headers in your code example show:
// ...
$result = file_get_contents('http://accounts.google.com', false, $context);
var_dump($http_response_header);
Output:
array(6) {
[0] => string(31) "HTTP/1.0 405 Method Not Allowed"
[1] => string(38) "Content-Type: text/html; charset=UTF-8"
[2] => string(19) "Content-Length: 958"
[3] => string(35) "Date: Thu, 24 Oct 2013 12:03:09 GMT"
[4] => string(15) "Server: GFE/2.0"
[5] => string(27) "Alternate-Protocol: 80:quic"
}
The required response listing shows another protocol violation as the required Allow header is not part of the response. It would have shown which methods are allowed.
So all you can do is to try if common HTTP methods work instead of PUT, you might be looking for POST. Let's run the example with POST, but no luck:
Warning: file_get_contents(http://accounts.google.com): failed to open stream: HTTP request failed! HTTP/1.0 405 HTTP method POST is not supported by this URL
I suggest you contact Google (the vendor of that online service you send HTTP requests to) and ask about the technical specification you need to match for the request you had in mind. For debugging purposes I suggest you read about PHP's special $http_response_header variable, I have some example code and explanation as well as some hints on error handling with PHP's HTTP Wrapper in:
HEAD first with PHP Streams (Sep 2011; by hakre)
Using the wrong URI, (which is why you get no allowed methods header). Access tokens are retrieved via https://accounts.google.com/o/oauth2/token "https" is required.
Unrelated: Your postdata is missing the required "redirect_uri".
This is quite late, hope it helps someone else like me.
There are two issues. First of all an access token cannot be generated with client_secret. So to have a successful request client_secret must be included.
Secondly google wants to identify the client_id, so it should also be a part of POST parameters.
The correct parameters for generating access tokens should be like
$opts = array(
'http' => array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata,
'client_id' => '######################',
'client_secret' => '##########################'
)
)

Categories