I am using the SageOne API PHP Library. It works fine, but I get an error if I try to use get or post.
The error is,
Only variables should be passed by reference sage.api.php on line 130
My get request code is
$client = new SageOne(SAGE_CLIENT_ID, SAGE_CLIENT_SECRET);
$client->setAccessToken("c7c7547xxxxxxxxxxxx8efa4f5df08f750df");
$data = array( );
$result = "";
$client = $client->get('/products', $data);
I don’t know what’s wrong.
Full Code
require 'sage.api.php';
define('SAGE_CLIENT_ID', "fa1e8c1b114347a356d2");
define('SAGE_CLIENT_SECRET', "faaa7b353521f823ba13e3a20e72dd057c3a5fd1");
$client = new SageOne(SAGE_CLIENT_ID, SAGE_CLIENT_SECRET);
$callbackURL = 'xxxxx/addonmodules.php?module=sageone';
// We need to build the authorise url and redirect user to authorise our app
if(!$_GET['code']){
$authoriseURL = $client->getAuthoriseURL($callbackURL);
// redirect user
header("Location: ".$authoriseURL);
exit;
// We now have the authorisation code to retrieve the access token
} else {
$accessToken = $client->getAccessToken($_GET['code'], $callbackURL);
$token= $accessToken['access_token'];
$end = 'public';
$data ='';
$result = $client->get($end, $data);
echo '<pre>';
print_r($result);
Code Snippets from sage.api.php
class SageOne { ...
...
public function get($endpoint, $data=false){
return $this->call($endpoint, 'get', $data);
}
...
// error line 130 from this code
private function buildSignature($method, $url, $params, $nonce){
// uc method and append &
$signature = strtoupper($method).'&';
// percent encode bit of url before ? and append &
$signature .= rawurlencode(array_shift(explode('?', $url))).'&';
// percent encode any params and append &
if (is_array($params)){
// sort params alphabetically
$this->ksortRecursive($params);
// build query string from params, encode it and append &
$signature .= str_replace(
array('%2B'),
array('%2520'),
rawurlencode(http_build_query($params, '', '&'))
).'&';
// params can be string
} else {
// build query string from params, encode it and append &
$signature .= rawurlencode($params).'&';
}
// add 'nonce' - just use an md5
$signature .= $nonce;
// now generate signing key
$signingKey = rawurlencode($this->signingSecret).'&'.rawurlencode($this->accessToken);
// encode using sha 1, then base64 encode
$finalSignature = base64_encode(hash_hmac('sha1', $signature, $signingKey, true));
return $finalSignature;
}
This is the shortest i can make to see all important code
This is due to trying to return the result of a function or method directly to another function or method... the result doesn't have a reference.
So, for example:
$obj->method(doSomething(), 'asdf', 'qwerty');
The error means you should assign the value of doSomething() before passing it.
$result = doSomething();
$obj->method($result, 'asdf', 'qwerty');
Also see: Only variables should be passed by reference
A function (in this case, $client->get()) can be defined to receive its parameters by reference. This means that it can modify those parameters directly. So if you call $client->get($a, $b), the function may alter the values of $a and $b.
Clearly, it can only alter the values of variables, so when a function receives a parameter by reference, you must pass it a variable, not a string, an integer, or a direct call to another function.
So if the function $client->get() receives its first parameter by reference, none of the following can work:
$client->get('string', $data);
$client->get(15, $data); // int
$client->get(other_function_call(), $data);
$client->get(12.5, $data); // float
$client->get(array(), $data);
You have to do this:
$a = 'string';
$client->get($a, $data);
Or $a = whatever, be it a string, an int, a function call. The point is (and this is stated quite clearly in the error message) that you must pass a variable. So save whatever you want to pass as a variable, then pass that.
Related
What is the best way to parse a string containing a function call with parameters in PHP so that I have the function name and the parameters with their correct types. Example:
$string = "ask('Do you want to continue?', ['yes', 'no'])";
I don't want to directly call that function, so eval is not an option. I want to do something based on the function name and use the parameters with their correct types. Is there an easy way in PHP?
I expect something like this as a result:
$name = 'ask';
$parameters = ['Do you want to continue?', ['yes', 'no']];
Assuming that you want the arguments to be parsed to an array structure, you would still need to use eval (with all the precautions taken to ensure that the content is safe).
This code also assumes the format is as expected, i.e. it represents a valid function call, and the closing parenthesis is the final non-blank character:
$string = "ask('Do you want to continue?', ['yes', 'no'])";
$parts = array_map("trim", explode("(", substr(trim($string), 0, -1), 2));
$parts[1] = eval("return [$parts[1]];");
$parts will be:
[
"ask",
[
"Do you want to continue?",
["yes", "no"]
]
]
I think you should use one good library to parse PHP code.
that is some example of that kind of library
use PhpParser\Error;
use PhpParser\NodeDumper;
use PhpParser\ParserFactory;
$code = <<<'CODE'
<?php
function test($foo)
{
var_dump($foo);
}
CODE;
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
try {
$ast = $parser->parse($code);
} catch (Error $error) {
echo "Parse error: {$error->getMessage()}\n";
return;
}
$dumper = new NodeDumper;
echo $dumper->dump($ast) . "\n";
https://github.com/nikic/PHP-Parser
I try to generate temporary URLs for my Openstack ObjectStorage in a PHP application.
I've followed their documentation but even their python example isn't working.
So far, here is my generator :
class TempUrlGenerator implements ITempUrlGenerator
{
//See https://docs.openstack.org/swift/latest/api/temporary_url_middleware.html for details.
public function generate(string $url, int $validity = 5): string
{
$timestamp = time() + ($validity * 60);
return sprintf(
'%s?temp_url_sig=%s&temp_url_expires=%s',
$url,
$this->generateSignature($url, $timestamp),
$timestamp
);
}
private function generateSignature(string $url, int $timestamp): string
{
$body = sprintf(
'%s\n%s\n%s',
'GET',
$timestamp,
$this->getPath($url)
);
return hash_hmac('sha1', $body, trim(getenv('OPENSTACK_TEMPURL_KEY')));
}
private function getPath(string $url): string
{
$exploded = explode('/v1/', $url);
return sprintf('/v1/%s', $exploded[1]);
}
}
URL is the whole, complete URL (https://myserver.host.com/v1/...), validity is the number of minutes I want to keep my URL valid.
Beside that, the env variable is my secret key. I've already checked using a HEAD call on my account and my containers that the key is, indeed, uploaded on them with the good header. (X-Account-Meta-Temp-URL-Key and X-Container-Meta-Temp-URL-Key).
I've also checked multiple examples and implementations.
Yet I keep getting 401 invalid temp url when I try them.
Do you have any clue why or how I can troubleshot that ? Maybe is there some kind of setting on our server to check ?
Regards,
I need to re-generate the URL of my page, removing the additional parameters. For example: when I receive:
/bao1/bao2/?removeMe1=anything&keepMe1=anything&removeMe2=&keepMe2=anything
I want to generate the URL with removeMe query var removed, but with everything else intact. Like this:
/bao1/bao2/?keepMe1=anything&keepMe2=anything
I autowired the request:
public function __construct(RequestStack $httpRequest)
{
$this->httpRequest = $httpRequest;
}
Then I'm playing around like this:
public function getCleanUrl()
{
// HttpFoundation\Request
$currentHttpRequest = $this->httpRequest->getCurrentRequest();
// Trying to remove the parameters
$currentHttpRequest->query->remove("removeMe1");
return $currentHttpRequest->getUri()
}
The query->remove("removeMe1") works, but when I invoke getUri() I still get the full input url, as if remove() was never invoked. I think I'm probably missing to call some kind of $currentHttpRequest->regenerate()->getUri() but I cannot find anything.
To get the modified URL after calling mutator methods on a Request object, you need to call overrideGlobals().
If not, Request methods will give you results accordin to the original superglobals ($_GET, $_POST, $_SERVER). By calling Request::overrideGlobals() you tell the object not to.
E.g.:
if ($request->query->has('amp') && Request::METHOD_GET === $request->getMethod()) {
$request->query->remove('amp');
$request->overrideGlobals();
return new RedirectResponse($request->getUri(), Response::HTTP_MOVED_PERMANENTLY));
}
Or maybe, something more adjusted to your use case (untested, but the general idea should hold):
$queryParams = array_keys($request->query->all());
$goodParams = ['foo', 'bar', 'baz'];
$badParams = array_diff($queryParams, $goodParams);
foreach ($badParams as $badParam) {
$request->query->remove($badParam);
}
$request->overrideGlobals();
// get modified URL
echo $request->getUri();
I had to make this work, so I devised a non-Symfony solution:
$currentHttpRequest = $this->httpRequest->getCurrentRequest();
$arrParams = $currentHttpRequest->query->all();
$arrParams = array_intersect_key($arrParams, array_flip([
"keepMe1", "keepMe2"
]));
$currentUrlNoQs = strtok($currentHttpRequest->getUri(), '?');
if( empty($arrParams) ) {
$canonical = $currentUrlNoQs;
} else {
$queryString = http_build_query($arrParams);
$canonical = $currentUrlNoQs . '?' . $queryString;
}
return $canonical;
I'm not too fond of it, but it got the job done.
Quite sure I'm doing something wrong here, just don't know what or where.
I have a Laravel controller that handles an ajax post request and returns the results of a curl request to that ajax call:
public function store()
{
/** Receive long and lat through ajax **/
$long = Input::get('longitude');
$lat = Input::get('latitude');
$location = $lat . "," . $long;
$url = blablable;
$curl = curl_init();
curl_setopt_array($curl,
array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_URL => $url
));
$result = curl_exec($curl);
return $result;
curl_close($curl);
}
This works: the curl returns a JSON array, which gets passed back to the ajax call.
But now I want to save the incoming data to the database:
$location = new Location();
$location->latitude = Input::get('latitude');
$location->longitude = Input::get('longitude');
$location->save();
I add those 4 lines at the top of the function, the data gets saved to the database but the JSON array get's grabbled, somehow <!-- app/models/Location.php --> gets added to the top of the return, making the JSON array invalid.
No clue as to what is causing this so any hints or suggestions are highly appreciated!
-- Edit 1 --
The result of Input::all(); is
array(2) {
["latitude"]=>
string(10) "50.8809794"
["longitude"]=>
string(9) "4.6920714"
}
This is not intended as a solution, but as a way to clean up your code a bit:
You don't need to use curl and can use Request::create() and Route::dispatch() instead.
You should use the protected $fillable attribute in your model, to clean up the new entry.
Your code can become:
public function store()
{
// This is for saving the entry
$location = new Location();
$location->fill(Input::all());
$location->save();
// This is for the cURL
// (assuming this is a GET requst, but other methods are available)
$request = Request::create('/path/to/url/', 'GET');
$response = Route::dispatch($request);
// Do stuff with the $response or just return it
return $response;
}
How do I execute the transaction(123) function?
The response via API is: transaction(123)
I store this in the $response varible.
<?php
function transaction($orderid) {
return $orderid;
}
//api response
$response = "transaction(123)";
try {
$orderid = call_user_func($response);
echo $orderid;
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
?>
According to the manual page call_user_func() should be called with two parameters in your use case.
$orderid = call_user_func('transaction', 123);
This means you must extract the function and parameter separately from your $response variable:
preg_match('/([\w\_\d]+)\(([\w\W]*)\)/', $response, $matches);
Would result in the $matches array containing the function name at index 1 and the parameter at index 2.
So you would then do:
$orderid = call_user_func($matches[1], $matches[2]);
Obviously you need to be very careful with the values if they are coming from an untrusted source.
The bad way to do it, is to use the eval() function. It's very bad in your use-case because the API may very well return things you don't want to execute.
The good way to do it is to parse your string, validate its contents, and map the call and its arguments accordingly.
You can parse the return string using a regular expression:
preg_match("/^(.+?)\((.*?)\)$/", $answer, $match);
var_dump($match[1]); // method
var_dump(explode(',', $match[2])); // arguments
You must sanitize/validate the above.
Call call_user_func this way:
$orderid = call_user_func('transaction', 123);
Additionally, take a look at http://es.php.net/manual/en/function.call-user-func.php