Facebook PHP SDK for Graph API - php

Is it possible to print to my log files the exact request from Facebook PHP SDK to the Facebook Graphs Server?
Can someone explain me how to modify the Facebook PHP Library https://github.com/facebook/php-sdk
I found:
/**
* Invoke the Graph API.
*
* #param String $path the path (required)
* #param String $method the http method (default 'GET')
* #param Array $params the query/post data
* #return the decoded response object
* #throws FacebookApiException
*/
protected function _graph($path, $method = 'GET', $params = array()) {
if (is_array($method) && empty($params)) {
$params = $method;
$method = 'GET';
}
$params['method'] = $method; // method override as we always do a POST
$result = json_decode($this->_oauthRequest(
$this->getUrl('graph', $path),
$params
), true);
// results are returned, errors are thrown
if (is_array($result) && isset($result['error'])) {
$this->throwAPIException($result);
}
return $result;
}

You should rather have a look at the makeRequest function where the actual http request takes place. Since I wouldn't play around in the api, you could also extend the class and override the method:
class FacebookLogger extends Facebook {
protected function makeRequest($url, $params, $ch=null) {
var_dump($url);
var_dump($params);
parent::makeRequest($url, $params, $ch);
}
}

Related

Php Rest API: problem creating an array separated with ampersands

I have a doubt: An api requests the following structure:
API endpoint
● Production:
https://api-cbt.mercadolibre.com/api/orders/search/?days=<recentdays>&status=<order_status>&page=
<pageno>&access_token=xxx
● HTTP Method: GET
● Content-type header: content-type:application/json
Consequently I created the following structure:
$variables = [];
$variables['days'] = 10;
$variables['page'] = 1;
$query = $meli->get('/api/orders/search/', $variables, array('access_token' => $token));
but for some reason gives HTTPCODE = 401 (this is because $variables and commas are not been considered properly, I know that because access_token work perfect with other API calls).
My main question is what I have to do in order to produce a result like days=&status=&page=.. Im confused because I expected an array to be considered in that way but its not what is going on in my experience.
EDIT------
About GET:
/**
* Execute a GET Request
*
* #param string $path
* #param array $params
* #param boolean $assoc
* #return mixed
*/
public function get($path, $params = null, $assoc = false) {
$exec = $this->execute($path, null, $params, $assoc);
return $exec;
}
the library is provided by MercadoLibre at https://github.com/mercadolibre/php-sdk/blob/master/Meli/meli.php
thanks in advance..
I would threat access_token as a variable to pass within query string:
$variables = [];
$variables['days'] = 10;
$variables['page'] = 1;
$variables['access_token'] => $token;
$query = $meli->get('/api/orders/search/', $variables)
Looking at get() it seems that third argument should be boolean, not an array.

Amazon ElasticSearch service Signature mismatch for PUT Request - Amazon SDK php V2

I am using Amazon ElasticSearch Service and when i tried to create SignatureV4 Request it is working fine for search operations (GET Requests). But when i tried to do some operations like create indices (Using PUT request), it will trough the Signature mismatch error.
I am using Amazon SDK version 2 SignatureV4 library for signing the requests. Also created a custom Elasticsearch handler to add tokens to the request.
Does anybody have such issue with SignatureV4 library in Amazon SDK php V2.
{"message":"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.\n\nThe Canonical String for this request should have been\n'PUT\n/test_index_2\n\nhost:search-test-gps2gj4zx654muo6a5m3vxm3cy.eu-west-1.es.amazonaws.com\nx-amz-date:XXXXXXXXXXXX\n\nhost;x-amz-date\n271d5ef919251148dc0b5b3f3968c3debc911a41b60ef4e92c55b98057d6cdd4'\n\nThe String-to-Sign should have been\n'AWS4-HMAC-SHA256\XXXXXXXXXXXX\n20170511/eu-west-1/es/aws4_request\n0bd34812e0727fba7c54068b0ae1114db235cfc2f97059b88be43e8b264e1d57'\n"}
This tweak only necessary for the users who are still using Amazon SDK PHP version 2. In version 3, it supported by default.
For signed request i updated the current elsticsearch client handler by adding a middle ware for signing the request.
$elasticConfig = Configure::read('ElasticSearch');
$middleware = new AwsSignatureMiddleware();
$defaultHandler = \Elasticsearch\ClientBuilder::defaultHandler();
$awsHandler = $middleware($defaultHandler);
$clientBuilder = \Elasticsearch\ClientBuilder::create();
$clientBuilder->setHandler($awsHandler)
->setHosts([$elasticConfig['host'].':'.$elasticConfig['port']]);
$client = $clientBuilder->build();
I used the following library for this purpose
use Aws\Common\Credentials\CredentialsInterface;
use Aws\Common\Signature\SignatureInterface;
use Guzzle\Http\Message\Request;
class AwsSignatureMiddleware
{
/**
* #var \Aws\Credentials\CredentialsInterface
*/
protected $credentials;
/**
* #var \Aws\Signature\SignatureInterface
*/
protected $signature;
/**
* #param CredentialsInterface $credentials
* #param SignatureInterface $signature
*/
public function __construct()
{
$amazonConf = Configure::read('AmazonSDK');
$this->credentials = new \Aws\Common\Credentials\Credentials($amazonConf['key'], $amazonConf['secret']);
$this->signature = new \Aws\Common\Signature\SignatureV4('es', 'eu-west-1');
}
/**
* #param $handler
* #return callable
*/
public function __invoke($handler)
{
return function ($request) use ($handler) {
$headers = $request['headers'];
if ($headers['host']) {
if (is_array($headers['host'])) {
$headers['host'] = array_map([$this, 'removePort'], $headers['host']);
} else {
$headers['host'] = $this->removePort($headers['host']);
}
}
if (!empty($request['body'])) {
$headers['x-amz-content-sha256'] = hash('sha256', $request['body']);
}
$psrRequest = new Request($request['http_method'], $request['uri'], $headers);
$this->signature->signRequest($psrRequest, $this->credentials);
$headerObj = $psrRequest->getHeaders();
$allHeaders = $headerObj->getAll();
$signedHeaders = array();
foreach ($allHeaders as $header => $allHeader) {
$signedHeaders[$header] = $allHeader->toArray();
}
$request['headers'] = array_merge($signedHeaders, $request['headers']);
return $handler($request);
};
}
protected function removePort($host)
{
return parse_url($host)['host'];
}
}
The exact line i tweaked for this purpose is
if (!empty($request['body'])) {
$headers['x-amz-content-sha256'] = hash('sha256', $request['body']);
}
For PUT and POST request the payload hash was wrong because i was not considering the request body while generating payload.
Hope this code is beneficial for anyone who is using Amazon SDK PHP version 2 and using the IAM based authentication for Elasticsearch Hosted service in Amazon cloud.

Upgrading Facebook API version

I have recently received a Facebook developer notification:
Graph API v2.1 Upgrade Notice
foobarapplication has been making recent API calls to Graph API v2.0,
which will reach the end of the 2-year deprecation window on Monday,
August 8, 2016. Please migrate all calls to v2.1 or higher in order to
avoid potential broken experiences.
We recommend using our new Graph API Upgrade Tool to see which of your
calls are affected by this change as well as any replacement calls in
newer versions. You can also use our changelog to see the full list of
changes.
A year ago I have upgraded Facebook for the given PHP application, by extracting the PHP SDK and changing source-code usages. The login review was successful and there were no serious problems since then. However, the app needs to upgrade from Facebook API 2.0 soon. I have an idea as of how to achieve this, but am not sure whether I am correct. Let us consider the following functionalities:
FacebookRedirectLoginHelper class:
/**
* Stores CSRF state and returns a URL to which the user should be sent to
* in order to continue the login process with Facebook. The
* provided redirectUrl should invoke the handleRedirect method.
*
* #param array $scope List of permissions to request during login
* #param string $version Optional Graph API version if not default (v2.0)
* #param boolean $displayAsPopup Indicate if the page will be displayed as a popup
*
* #return string
*/
public function getLoginUrl($scope = array(), $version = null, $displayAsPopup = false)
{
$version = ($version ?: FacebookRequest::GRAPH_API_VERSION);
$this->state = $this->random(16);
$this->storeState($this->state);
$params = array(
'client_id' => $this->appId,
'redirect_uri' => $this->redirectUrl,
'state' => $this->state,
'sdk' => 'php-sdk-' . FacebookRequest::VERSION,
'scope' => implode(',', $scope)
);
if ($displayAsPopup)
{
$params['display'] = 'popup';
}
return 'https://www.facebook.com/' . $version . '/dialog/oauth?' .
http_build_query($params, null, '&');
}
/**
* Returns a URL to which the user should be sent to re-request permissions.
*
* #param array $scope List of permissions to re-request
* #param string $version Optional Graph API version if not default (v2.0)
*
* #return string
*/
public function getReRequestUrl($scope = array(), $version = null)
{
$version = ($version ?: FacebookRequest::GRAPH_API_VERSION);
$this->state = $this->random(16);
$this->storeState($this->state);
$params = array(
'client_id' => $this->appId,
'redirect_uri' => $this->redirectUrl,
'state' => $this->state,
'sdk' => 'php-sdk-' . FacebookRequest::VERSION,
'auth_type' => 'rerequest',
'scope' => implode(',', $scope)
);
return 'https://www.facebook.com/' . $version . '/dialog/oauth?' .
http_build_query($params, null, '&');
}
FacebookRequest class:
/**
* FacebookRequest - Returns a new request using the given session. optional
* parameters hash will be sent with the request. This object is
* immutable.
*
* #param FacebookSession $session
* #param string $method
* #param string $path
* #param array|null $parameters
* #param string|null $version
* #param string|null $etag
*/
public function __construct(
FacebookSession $session, $method, $path, $parameters = null, $version = null, $etag = null
)
{
$this->session = $session;
$this->method = $method;
$this->path = $path;
if ($version) {
$this->version = $version;
} else {
$this->version = static::GRAPH_API_VERSION;
}
$this->etag = $etag;
$params = ($parameters ?: array());
if ($session
&& !isset($params["access_token"])) {
$params["access_token"] = $session->getToken();
}
if (FacebookSession::useAppSecretProof()
&& !isset($params["appsecret_proof"])) {
$params["appsecret_proof"] = $this->getAppSecretProof(
$params["access_token"]
);
}
$this->params = $params;
}
FacebookCurlHttpClient class:
/**
* Detect versions of Curl which report incorrect header lengths when
* using Proxies.
*
* #return boolean
*/
private static function needsCurlProxyFix()
{
$ver = self::$facebookCurl->version();
$version = $ver['version_number'];
return $version < self::CURL_PROXY_QUIRK_VER;
}
My idea is as follows:
getLoginUrl is called from the application; a version of 2.6. should be specified from now on
I do not really use getReRequestUrl, so I will not make changes in the code for it
FacebookRequest will be instantiated with a $version of 2.6
needsCurlProxyFix will be left as it is
Basically, I will use the PHP lib released in 2014, but with specifying $version at calls. Is my approach feasible, or should I use a new client-side library?
As things turned out, my version was up to date and I did not need to do any changes for Facebook's upgrade to version 2.1. except changing the version name used.

oAuthRequest returns empty array

I have created a Twitter application for a website. I would like to integrate the possibility of logging in/registering with Twitter. I am using php files as helpers, which are named TwitterOAuth.php and OAuth.php, respectively. When a user clicks on the Twitter button, he/she is redirected to a page called twitter.php, which has the following source-code:
<?php
/* Build TwitterOAuth object with client credentials. */
$connection = Common::twitter();
/* Get temporary credentials. */
$request_token = $connection->getRequestToken(App::env()->get('url'));
/* Save temporary credentials to session. */
$token = $request_token['oauth_token'];
$_SESSION['twitter'] = array('id' => $request_token['oauth_token'], 'token' => $request_token['oauth_token_secret']);
/* If last connection failed don't display authorization link. */
switch ($connection->http_code) {
case 200:
/* Build authorize URL and redirect user to Twitter. */
$url = $connection->getAuthorizeURL($token);
header('Location: ' . $url);
break;
default:
/* Show notification if something went wrong. */
var_dump($request_token);
echo 'Could not connect to Twitter. Refresh the page or try again later.';
}
The Common::twitter() function is as follows:
/**
* Returns the twitter OAuth service.
*
* #return TwitterOAuth
*/
public static function twitter() {
if (!self::$tw) {
if ((User::isLoggedIn()) && (User::current()->hasTwitterAccount())) {
self::$tw = new TwitterOAuth(
App::env()->get('twitter', 'consumerKey'),
App::env()->get('twitter', 'consumerSecret'),
App::CurrentUser()->getTwitterId(),
App::CurrentUser()->getTwitterUserAccessToken()
);
} else {
self::$tw = new TwitterOAuth(
App::env()->get('twitter', 'consumerKey'),
App::env()->get('twitter', 'consumerSecret')
);
}
}
return self::$tw;
}
In the scenario I am testing with, the else branch is executed. However, I get an exception:
Exception 'PHPErrorException' with message 'Notice [8] Undefined
index: oauth_token Error on line 81 in file ...\lib\TwitterOAuth.php
The function where the problem occurs is as follows:
/**
* Get a request_token from Twitter
*
* #returns a key/value array containing oauth_token and oauth_token_secret
*/
function getRequestToken($oauth_callback) {
$parameters = array();
$parameters['oauth_callback'] = $oauth_callback;
$request = $this->oAuthRequest($this->requestTokenURL(), 'GET', $parameters);
$token = OAuthUtil::parse_parameters($request);
$this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
return $token;
}
The problem is that OAuthUtil::parse_parameters($request) returns an empty array. This is happening, because $request is false, however, $this->requestTokenURL is https://api.twitter.com/oauth/request_token, $parameters has an oauth_callback, which holds the callback URL defined in the Twitter application. What could be the cause of this issue?
EDIT:
Source of `$this->oAuthRequest`:
/**
* Format and sign an OAuth / API request
*/
function oAuthRequest($url, $method, $parameters) {
if (strrpos($url, 'https://') !== 0 && strrpos($url, 'http://') !== 0) {
$url = "{$this->host}{$url}.{$this->format}";
}
$request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $parameters);
$request->sign_request($this->sha1_method, $this->consumer, $this->token);
switch ($method) {
case 'GET':
return $this->http($request->to_url(), 'GET');
default:
return $this->http($request->get_normalized_http_url(), $method, $request->to_postdata());
}
}
This method is inside of TwitterOAuth.php.
In OAuth.php there was a code chunk in the get_normalized_http_url method, namely:
$port = #$parts['port'];
This caused some errors, so I have fixed it like this:
$port = (array_key_exists('port', $parts) ? $parts['port'] : 80);
However, apparently port number 80 was the problem and after I changed the chunk to this:
$port = (array_key_exists('port', $parts) ? $parts['port'] : 443);
it worked like a spell.

PHP implementation of Bitstamp API

I am trying to implement a PHP API to bitstamp to do a re-occurring transaction by placing the PHP code in the crontab.
I am trying to cause the API to communicate with bitstamp to buy X amount of BTC per execution (and then control the frequency from the crontab), this should be the very definition of the basic implementation.
Here is the joy, I am absolutely not a PHP coder. The guys at BX Media were nice enough to post their PHP frame on github: (https://github.com/willmoss/bitstamp-php-api).
However, the way I understand what they have done is that I must create the "parent" PHP to then include their API code which would also include my credentials
So I put together the most basic PHP code
<?php
require('bitstamp.php');
$KEY = 'XXXXXXXXXXXXXXXX';
$SECRET = 'XXXXXXXXXXXXXXXX';
$CLIENT_ID = 'XXXXX';
$bs = new Bitstamp("KEY","SECRET","CLIENT_ID");
// print_r($bs->ticker());
$bs->buyBTC(0.01); // buy 0.01 bitcoins at ask price
//$bs->bitstamp_query("buy", array('amount'=>'0.05','price'=>'50'));
?>
note: that "bitstamp.php" is in the same directory and is what is presently on Github.
<?php
/**
* #package Bitstamp API
* #author https://bxmediaus.com - BX MEDIA - PHP + Bitcoin. We are ready to work on your next bitcoin project. Only high quality coding. https://bxmediaus.com
* #version 0.1
* #access public
* #license http://www.opensource.org/licenses/LGPL-3.0
*/
class Bitstamp
{
private $key;
private $secret;
private $client_id;
public $redeemd; // Redeemed code information
public $withdrew; // Withdrawal information
public $info; // Result from getInfo()
public $ticker; // Current ticker (getTicker())
public $eurusd; // Current eur/usd
/**
* Bitstamp::__construct()
* Sets required key and secret to allow the script to function
* #param Bitstamp API Key $key
* #param Bitstamp Secret $secret
* #return
*/
public function __construct($key, $secret, $client_id)
{
if (isset($secret) && isset($key) && isset($client_id))
{
$this->key = $key;
$this->secret = $secret;
$this->client_id = $client_id;
} else
die("NO KEY/SECRET/CLIENT ID");
}
/**
* Bitstamp::bitstamp_query()
*
* #param API Path $path
* #param POST Data $req
* #return Array containing data returned from the API path
*/
public function bitstamp_query($path, array $req = array())
{
// API settings
$key = $this->key;
// generate a nonce as microtime, with as-string handling to avoid problems with 32bits systems
$mt = explode(' ', microtime());
$req['nonce'] = $mt[1] . substr($mt[0], 2, 6);
$req['key'] = $key;
$req['signature'] = $this->get_signature($req['nonce']);
// generate the POST data string
$post_data = http_build_query($req, '', '&');
// any extra headers
$headers = array();
// our curl handle (initialize if required)
static $ch = null;
if (is_null($ch))
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT,
'Mozilla/4.0 (compatible; MtGox PHP client; ' . php_uname('s') . '; PHP/' .
phpversion() . ')');
}
curl_setopt($ch, CURLOPT_URL, 'https://www.bitstamp.net/api/' . $path .'/');
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
// run the query
$res = curl_exec($ch);
if ($res === false)
throw new \Exception('Could not get reply: ' . curl_error($ch));
$dec = json_decode($res, true);
if (!$dec)
throw new \Exception('Invalid data received, please make sure connection is working and requested API exists');
return $dec;
}
/**
* Bitstamp::ticker()
* Returns current ticker from Bitstamp
* #return $ticker
*/
function ticker() {
$ticker = $this->bitstamp_query('ticker');
$this->ticker = $ticker; // Another variable to contain it.
return $ticker;
}
/**
* Bitstamp::eurusd()
* Returns current EUR/USD rate from Bitstamp
* #return $ticker
*/
function eurusd() {
$eurusd = $this->bitstamp_query('eur_usd');
$this->eurusd = $eurusd; // Another variable to contain it.
return $eurusd;
}
/**
* Bitstamp::buyBTC()
*
* #param float $amount
*/
function buyBTC($amount){
if (!isset($ticker))
$this->ticker();
$ticker = $this->ticker;
return $this->bitstamp_query('buy', array('amount' => $amount, 'price' => $ticker['ask']));
}
/**
* Bitstamp::sellBTC()
*
* #param float $amount
* #param float $price
* #param string $currency
*/
function sellBTC($amount){
if (!isset($ticker))
$this->ticker();
$ticker = $this->ticker;
return $this->bitstamp_query('sell', array('amount' => $amount, 'price' => $ticker['bid']));
}
/**
* Bitstamp::get_signature()
* Compute bitstamp signature
* #param float $nonce
*/
private function get_signature($nonce)
{
$message = $nonce.$this->client_id.$this->key;
return strtoupper(hash_hmac('sha256', $message, $this->secret));
}
}
I am getting a failure on the execution. As the author of the Bitstamp API has it apparently working with his clients, I assume the error is on my "parent" PHP code. (Note: I am using the real key and secret on my local version).
Anyone have any experience with this API or in general or suggestions?
I'm not sure if this is just anonymization or the actual code, so let me know if I have this wrong, but you have this line:
$bs = new Bitstamp("KEY","SECRET","CLIENT_ID");
This passes the actual strings "KEY", "SECRET", and "CLIENT_ID" to the function; what you want to do is pass the variables you defined on the lines above, like this:
$bs = new Bitstamp($KEY,$SECRET,$CLIENT_ID);
As the author of this api (I am CEO of Bx Media):
You need to remake the constructor like this:
$bs = new Bitstamp("put your key here","put your secret here","put your client ID here");
Ie. you actually put your key/secret/ID between the speech marks.
In PHP, when you put a $ sign, it sets something as a variable.
See more information here: http://www.w3schools.com/php/php_variables.asp
If you want to use variables, you can also use IMSoP's solution.
Please note, we also updated the library tonight with some new functions. So please update your repo to the latest commit to get the new code.
Cheers!

Categories