I wanted to pass the whole incoming data (that is, $request) to the curl not wanted to post to a particular field in the endpoint as subjectId=>1 as am running this curl request for different endPoint everytime. The below curl request will work if CURLOPT_URL => $url . $subjectId, was given. As my input changes for every end point, i've to pass everything that comes in the input to the curl , i can't pass it as an arary $subjectId. Is there any way to do this?
Currently, dd($Response); returns null
Am giving a postman input like this:
{
"subjectId":"1"
}
Curl
public function getContentqApiPost(Request $request)
{
$token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.ey";
$headers = [
"Accept: application/json",
"Authorization: Bearer " . $token
];
$url="http://127.0.0.1:9000/api/courses/course-per-subject";
$subjectId = "?subjectId=$request->subjectId";
$ch = curl_init();
$curlConfig = array(
// CURLOPT_URL => $url . $subjectId,
CURLOPT_URL => $url . $request,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt_array($ch, $curlConfig);
$result = trim(curl_exec($ch));
$Response = json_decode($result, true);
if (curl_errno($ch)) {
$error_msg = curl_error($ch);
echo $error_msg;
}
curl_close($ch);
return $Response;
}
If you would like to pass all params of $request to curl:
$queryParams = '';
$delimeter = '?';
foreach($request->all() as $k => $v){
$queryParams .= "$delimeter$k=$v";
$delimeter = '&';
}
Also You can only pass the params you want:
foreach($request->only(['subjectId']) as $k => $v){
// code here
}
Finally you have:
CURLOPT_URL => $url . $queryParams,
Answer
Assuming you want to pass the entire GET query string as-is:
$query_string = str_replace($request->url(), "", $request->fullUrl());
$url = "http://localhost:9000/api/courses/course-per-subject" . $query_string;
This works because $request->url() returns the URL without the query string parameters, while $request->fullUrl() returns the URL with all the query string parameters, so we can use str_replace with an empty replacement to remove the non-query part. Note that $query_string will already start with a ? so there is no need to add that yourself.
Other suggestions
Unless your Laravel API is a 1:1 copy of the backend API, I strongly suggest writing a class that interfaces with the backend API, then provide it to your Laravel controllers using dependency injection. E.g.
class CourseCatalogApi {
public function getSubjectsInCourse(String $course){
... // your curl code here
}
}
Finally, since you are already using Laravel, there is no need to write such low level code using curl to make HTTP requests. Consider using guzzlehttp, which is already a dependency of Laravel.
I need to convert a cURL command like this into Guzzle-way:
curl -XPOST "https://api/v1.0/endpoint" -F "file=#img.jpg"
This is what I'm trying so far:
$httpClient = new \GuzzleHttp\Client;
$req = $httpClient->createRequest('POST', $url), []);
$postBody = $req->getBody();
$postBody->addFile(new \GuzzleHttp\Post\PostFile('photo', fopen(storage_path() . '/' . $filename, 'r')));
$response = $httpClient->send($req);
But I'm not getting the same response as with the previous command, which is something like:
{"id":5378678,"url":"ui/54/68/97/24/img.jpg"}
I'm getting a GuzzleHttp\Message\Response object, but I'm not being able to find the id and url attributes in there.
Any help will be appreciated!
Just noticed I could do $response->json() to get what I wanted.
I've made a simple script that posts images on tumblr.
everything is fine, but I've noticed some performance issues right after I've changed the host provider (my new host is limited and cheaper).
now, after debugging the script and after contacting the tumblr api helpdesk, I'm stuck on a problem:
there are 3 functions:
function oauth_gen($method, $url, $iparams, &$headers) {
$iparams['oauth_consumer_key'] = CONSUMER_KEY;
$iparams['oauth_nonce'] = strval(time());
$iparams['oauth_signature_method'] = 'HMAC-SHA1';
$iparams['oauth_timestamp'] = strval(time());
$iparams['oauth_token'] = OAUTH_TOKEN;
$iparams['oauth_version'] = '1.0';
$iparams['oauth_signature'] = oauth_sig($method, $url, $iparams);
$oauth_header = array();
foreach($iparams as $key => $value) {
if (strpos($key, "oauth") !== false) {
$oauth_header []= $key ."=".$value;
}
}
$str = print_r($iparams, true);
file_put_contents('data1-1.txt', $str);
$oauth_header = "OAuth ". implode(",", $oauth_header);
$headers["Authorization"] = $oauth_header;
}
function oauth_sig($method, $uri, $params) {
$parts []= $method;
$parts []= rawurlencode($uri);
$iparams = array();
ksort($params);
foreach($params as $key => $data) {
if(is_array($data)) {
$count = 0;
foreach($data as $val) {
$n = $key . "[". $count . "]";
$iparams []= $n . "=" . rawurlencode($val);
//$iparams []= $n . "=" . $val;
$count++;
}
} else {
$iparams[]= rawurlencode($key) . "=" .rawurlencode($data);
}
}
//debug($iparams,"iparams");
$str = print_r($iparams, true);
file_put_contents('data-1.txt', $str);
//$size = filesize('data.txt');
$parts []= rawurlencode(implode("&", $iparams));
//debug($parts,"parts");
//die();
$sig = implode("&", $parts);
return base64_encode(hash_hmac('sha1', $sig, CONSUMER_SECRET."&". OAUTH_SECRET, true));
}
these 2 functions above comes from an online functional example, they have always worked fine.
this is the function I use to call the APIs and the oAuth:
function posta_array($files,$queue,$tags,$caption,$link,$blog){
$datArr = array();
$photoset_layout = "";
foreach ($files as $sing_file){
$dataArr [] = file_get_contents($sing_file);
$photoset_layout .= "1";
}
$headers = array("Host" => "http://api.tumblr.com/", "Content-type" => "application/x-www-form-urlencoded", "Expect" => "");
$params = array(
"data" => $dataArr,
"type" => "photo",
"state" => $queue,
"tags"=>$tags,
"caption"=>$caption,
"photoset_layout" => $photoset_layout,
"link"=>str_replace("_","",$link)
);
debug($headers,"head");
oauth_gen("POST", "http://api.tumblr.com/v2/blog/$blog/post", $params, $headers);
debug($headers,"head 2");
$ch = curl_init();
curl_setopt($ch, CURLOPT_USERAGENT, "Tumblr v1.0");
curl_setopt($ch, CURLOPT_URL, "http://api.tumblr.com/v2/blog/$blog/post");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
"Authorization: " . $headers['Authorization'],
"Content-type: " . $headers["Content-type"],
"Expect: ")
);
$params = http_build_query($params);
$str = print_r($params, true);
file_put_contents('data_curl1.txt', $str);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
$response = curl_exec($ch);
debug($response,"response");
return $response;
}
this is the function with some problems, I try to explain:
I called the oauth_gen passing the parameters array to it, the oauth_gen creates the oauth header that I later used here: "Authorization: " . $headers['Authorization'],.
As I stated, everything is working smoothly, until I have tried to post a gif photoset of 6 files for a total of 6Mb (tumblr permit 2Mb each file and 10Mb total).
PHP runs out of memory and return an error, here it starts my debugging, after a while I contacted the tumblr api helpdesk, and they answer in this way:
You shouldn't need to include the files in the parameters used for
generating the oauth signature. For an example of how this is done,
checkout one of our official API clients.
This changes everything. Untill now, I passed the entire parameters array to the oauth_gen, which, calling the oauth_sig, will rawencode everything into the array (binary strings of gif files inlcuded), with a result of a binary file of about 1Mb becomes at least 3Mb of rawurlencoded string.
and that's why I had memory issues. Nice, so, as the helpdesk say, I've changed the call to the oauth_gen in this way:
$new_array = array();
oauth_gen("POST", "http://api.tumblr.com/v2/blog/$blog/post", $new_array, $headers);
seams legit to me, I passed a new array to the function, the function then generate the oAuth, the headers are passed back and I can use them into the posting call, the result was:
{"meta":{"status":401,"msg":"Unauthorized"},"response":[]}
asking more to tumblr api helpdesk leads only to more links to their documentation and their "tumblr php client" which I can't use, so it isn't a option.
Does anyone has experience with oAuth and can explain me what I'm doing wrong? as far as I understand, the trick is into the encrypted data the oauth_sig create, but I can't figure out how to proceed.
I really want to understand the oauth, but more I read about it and more the tumblr helpdsek seams right to me, but... the solution doesn't work, and works only if I let the oauth function to encrypt the entire data array (with the images and everything) but I can understand that this is wrong... help me.
UPDATE 1
I've tried a new thing today, first I created the empty array, then passed by reference to the oauth_genand only after generating the signature, I've added to the same array all the other fields about the post itself, but the result is the same.
UPDATE 2
reading here: http://oauth.net/core/1.0a/#signing_process
seems that the parameters of the request must all be used for the signature, but this is not totally clear (if someone could explain it better, I really appreciate).
this is weird, because if it's true, it go against the words of the Tumblr help desk, while if it's not true, there is a little confusion in the whole process.
by the way, at this time, I'm stile struck in the same point.
After digging couple of hours into the issue, debugging, reviewing tumblr api and api client, registering a test account and trying to post some images. The good news is finally I come up with a solution. It is not using a native CURL only, you need guzzle and an OAuth library to sign the requests.
Tumblr guys are correct about signing the request. You don't need to pass image data to sign the request. If you check their official library you can see; https://github.com/tumblr/tumblr.php/blob/master/lib/Tumblr/API/RequestHandler.php#L85
I tried to fix the issue with native CURL library but unfortunately I was not successful, either I was signing the request in a wrong way or missing something in the request header, data etc. I don't know actually, Tumblr api is really bad at informing you what you are doing wrong.
So I cheated a little bit and start to read Tumblr api client code, and I come up with a solution.
Here we go, first you need two packages.
$ composer require "eher/oauth:1.0.*"
$ composer require "guzzle/guzzle:>=3.1.0,<4"
And then the PHP code, just define your keys, tokens, secrets etc. Then it should be good to go.
Since the signing request does not include picture data, it is not exceeding memory limit. After signing the request actually we are not getting the contents of the files into our post data array. We are using addPostFiles method of guzzle, which takes care of file addition to POST request, does the dirty work for you. And here is the result for me;
string(70) "{"meta":{"status":201,"msg":"Created"},"response":{"id":143679527674}}"
And here is the url;
http://blog-transparentcoffeebouquet.tumblr.com/
<?php
ini_set('memory_limit', '64M');
define("CONSUMER_KEY", "");
define("CONSUMER_SECRET", "");
define("OAUTH_TOKEN", "");
define("OAUTH_SECRET", "");
function request($options,$blog) {
// Take off the data param, we'll add it back after signing
$files = isset($options['data']) ? $options['data'] : false;
unset($options['data']);
$url = "https://api.tumblr.com/v2/blog/$blog/post";
$client = new \Guzzle\Http\Client(null, array(
'redirect.disable' => true
));
$consumer = new \Eher\OAuth\Consumer(CONSUMER_KEY, CONSUMER_SECRET);
$token = new \Eher\OAuth\Token(OAUTH_TOKEN, OAUTH_SECRET);
$oauth = \Eher\OAuth\Request::from_consumer_and_token(
$consumer,
$token,
"POST",
$url,
$options
);
$oauth->sign_request(new \Eher\OAuth\HmacSha1(), $consumer, $token);
$authHeader = $oauth->to_header();
$pieces = explode(' ', $authHeader, 2);
$authString = $pieces[1];
// POST requests get the params in the body, with the files added
// and as multipart if appropriate
/** #var \Guzzle\Http\Message\RequestInterface $request */
$request = $client->post($url, null, $options);
$request->addHeader('Authorization', $authString);
if ($files) {
if (is_array($files)) {
$collection = array();
foreach ($files as $idx => $f) {
$collection["data[$idx]"] = $f;
}
$request->addPostFiles($collection);
} else {
$request->addPostFiles(array('data' => $files));
}
}
$request->setHeader('User-Agent', 'tumblr.php/0.1.2');
// Guzzle throws errors, but we collapse them and just grab the
// response, since we deal with this at the \Tumblr\Client level
try {
$response = $request->send();
} catch (\Guzzle\Http\Exception\BadResponseException $e) {
$response = $request->getResponse();
}
// Construct the object that the Client expects to see, and return it
$obj = new \stdClass;
$obj->status = $response->getStatusCode();
$obj->body = $response->getBody();
$obj->headers = $response->getHeaders()->toArray();
return $obj;
}
$files = [
"/photo/1.jpg",
"/photo/2.jpg",
"/photo/3.png",
"/photo/4.jpg",
"/photo/1.jpg",
"/photo/2.jpg",
"/photo/3.png",
"/photo/4.jpg",
"/photo/1.jpg",
"/photo/2.jpg",
];
$params = array(
"type" => "photo",
"state" => "published",
"tags"=> [],
"caption"=>"caption",
"link"=>str_replace("_","","http://stackoverflow.com/questions/36747697/oauth-signature-creation-issue-with-php-posting-photoset-to-tumblr"),
"data" => $files,
);
$response = request($params, "blog-transparentcoffeebouquet.tumblr.com");
var_dump($response->body->__toString());
For some automated tests that I did, I had to record requests from Chrome, and then repeat them in curl commands.
I start checking how to do it...
The way I did it was:
Access websites when the developers tools open.
Issue requests, make sure they are logged in the console.
Right click on the requests, select 'Save as HAR with content', and save to a file.
Then run the following php script to parse the HAR file and output the correct curls:
script:
<?php
$contents=file_get_contents('/home/elyashivl/har.har');
$json = json_decode($contents);
$entries = $json->log->entries;
foreach ($entries as $entry) {
$req = $entry->request;
$curl = 'curl -X '.$req->method;
foreach($req->headers as $header) {
$curl .= " -H '$header->name: $header->value'";
}
if (property_exists($req, 'postData')) {
# Json encode to convert newline to literal '\n'
$data = json_encode((string)$req->postData->text);
$curl .= " -d '$data'";
}
$curl .= " '$req->url'";
echo $curl."\n";
}
Don't know in which version they added this feature, but Chrome now offers a "Save as cURL" option:
You can access this by going under the Network tab of the Developer Tools, and right clicking on a XHR request
Building upon the code by ElyashivLavi, I added a file name argument, error checking when reading from the file, putting curl in verbose mode, and disabling the Accept-encoding request header, which usually results in getting back compressed output that would make it hard to debug, as well as automatic execution of curl commands:
<?php
function bail($msg)
{
fprintf(STDERR, "Fatal error: $msg\n");
exit(1);
}
global $argv;
if (count($argv) < 2)
bail("Missing HAR file name");
$fname = $argv[1];
$contents=file_get_contents($fname);
if ($contents === false)
bail("Could not read file $fname");
$json = json_decode($contents);
$entries = $json->log->entries;
foreach ($entries as $entry)
{
$req = $entry->request;
$curl = 'curl --verbose -X '.$req->method;
foreach($req->headers as $header)
{
if (strtolower($header->name) === "accept-encoding")
continue; // avoid gzip response
$curl .= " -H '$header->name: $header->value'";
}
if (property_exists($req, 'postData'))
{
# Json encode to convert newline to literal '\n'
$data = json_encode((string)$req->postData->text);
$curl .= " -d '$data'";
}
$curl .= " '$req->url'";
echo $curl."\n";
system($curl);
}
I wrote a function that uses YQL to pull stock info as follows:
function quote_func() {
$BASE_URL = "https://query.yahooapis.com/v1/public/yql";
// Form YQL query and build URI to YQL Web service
$yql_query = "select * from yahoo.finance.quotes where symbol in ('AAPL')";
$yql_query_url = $BASE_URL . "?q=" . urlencode($yql_query) . "&format=json&env=http://datatables.org/alltables.env&callback=";
// Make call with cURL
$session = curl_init($yql_query_url);
curl_setopt($session, CURLOPT_RETURNTRANSFER,true);
$json = curl_exec($session);
// Convert JSON to PHP object
$phpObj = json_decode($json);
// Confirm that results were returned before parsing
if(!is_null($phpObj->query->results)){
$quote = $phpObj->query->results->quote;
return $quote;
}
}
I then display the information I want using:
<?php echo quote_func()->DATA-I-WANT; ?>
I know that this is bad because each time I use the function I'm executing a GET request.
Can someone point me in the right direction at least?
I'd suggest that you cache the result. You could store the data in a database but the best place is probably a proper cache like memcached, APC or even just the filesystem.
Here's a dead simple filesystem implementation (untested and just for illustration).
$cacheFile = '/tmp/myCache.txt';
$expirePeriod = 1800; // in seconds
if (file_exists($cacheFile) && time() - filemtime() < $expirePeriod) {
$quoteData = file_get_contents($cacheFile);
} else {
$quoteData = quote_func();
file_put_contents($cacheFile, $quoteData);
}