2017年3月2日 星期四

[PHP] Facebook Graph API v2.2 升級提醒 - CodeIgniter 2.x 與 Facebook PHP SDK v5

這幾天一堆 fb app 被這種信轟炸:YourFBApp 的新開發人員重要通知,簡言之就是 facebook graph 舊版 api 即將在 2017/03/25 失效,請立即更新。

Facebook 開放平台變更紀錄 - https://developers.facebook.com/docs/apps/changelog

追了一下,主因是很多手上的服務是在 2014 年底開發,當時就是用 v2.2 graph api 沒錯,關鍵之處可以用搜尋:

facebook-php-sdk-v4 $ grep -r "v2.2" *
src/Facebook/FacebookRequest.php:  const GRAPH_API_VERSION = 'v2.2';


非常精準的地發現自己就是這個族群,也感謝 Facebook 賜予工作機會(誤),這樣每兩年就有新工作可以做,非常開心!接下來就是痛苦的開始,還是一口氣從 v4 升級到 v5 吧!

前提:

由於使用 PHP CodeIgniter 2.x 架構,再加上 HA 架構,因此需要處理 SESSION 的儲存機制,在此就用 DB 處理,而對 v4 SDK 中,是直接在 FacebookRedirectLoginHelper 改寫一份對於認證所需資料儲存的東西,但在 v5 架構就更漂亮了,可以把負責資料儲存的東西傳入,不需再改成 FacebookRedirectLoginHelper。若服務仍在單一機器上運行,可以略過此設計。

v4:

<?php
// FacebookRedirectLoginCIHelper
namespace Facebook;
use Facebook\FacebookRedirectLoginHelper;
class FacebookRedirectLoginCIHelper extends \Facebook\FacebookRedirectLoginHelper {
private $sessionPrefix = 'FBRLH_';
public function __construct($redirectUrl, $appId = null, $appSecret = null) {
parent::__construct($redirectUrl, $appId, $appSecret);
$this->ci = & get_instance();
}
protected function storeState($state) {
if ($this->ci->session->set_userdata($this->sessionPrefix . 'state', $state)) {
$this->state = $this->ci->session->set_userdata($this->sessionPrefix . 'state', $state);
return $this->state;
}
return NULL;
}
protected function loadState() {
return $this->state = $this->ci->session->userdata($this->sessionPrefix . 'state');
}
}

v5:

<?php
// FacebookSessionPersistentDataCIHandler
namespace Facebook\PersistentData;
use Facebook\Exceptions\FacebookSDKException;
class FacebookSessionPersistentDataCIHandler implements PersistentDataInterface {
protected $sessionPrefix = 'FBRLH_';
public function __construct($enableSessionCheck = true) {
$this->ci = & get_instance();
}
public function get($key) {
if ($this->ci)
return $this->ci->session->userdata($this->sessionPrefix . $key);
return null;
}
public function set($key, $value) {
if ($this->ci)
$this->ci->session->set_userdata($this->sessionPrefix . $key, $value);
}
}


初始化:

v4:

require 'path/facebook-php-sdk-v4/autoload.php';
use Facebook\FacebookRedirectLoginCIHelper;
use Facebook\FacebookRequest;
use Facebook\FacebookSession;
use Facebook\GraphUser;

FacebookSession::setDefaultApplication($this->config->item("api_key"), $this->config->item("secret_key"));

v5:

require 'path/php-graph-sdk-5.4.4/src/Facebook/autoload.php';
use Facebook\Facebook;
use Facebook\Authentication\AccessToken;
use Facebook\PersistentData\FacebookSessionPersistentDataCIHandler;
use Facebook\FacebookRequest;

$fb = new Facebook([
'app_id' => $this->config->item("api_key"),
'app_secret' => $this->config->item("secret_key"),
'persistent_data_handler' => new FacebookSessionPersistentDataCIHandler(),
//'default_graph_version' => 'v2.8',
]);


取得登入網址:

v4:

$helper = new FacebookRedirectLoginCIHelper($callback_url);
$login_url = $helper->getLoginUrl($this->config->item('fb_scope'));

v5:

$helper = $fb->getRedirectLoginHelper();
$login_url = $helper->getLoginUrl($callback_url, $this->config->item('fb_scope'));


登入完成取得 access_token:

v4:

$fb_session = $helper->getSessionFromRedirect();
$access_token = $fb_session->getAccessToken();

v5:

$accessToken = $helper->getAccessToken();


取得 longlived access_token:

v4:

$long_lived_token = $fb_session->getLongLivedSession()->getToken();

v5:
$long_lived_token = (string) $fb->getOAuth2Client()->getLongLivedAccessToken($access_token);


查詢個人資料:

v4:

$user_profile = (new FacebookRequest($fb_session, 'GET', '/me'))->execute()->getGraphObject(GraphUser::className());
$uid = $user_profile->getProperty('id');
$name = $user_profile->getProperty('name');
$email = $user_profile->getProperty('email');
$profile_url = $user_profile->getProperty('link');
if (empty($profile_url))
$profile_url = https://www.facebook.com/$uid";
$profile_image_link = "https://graph.facebook.com/$uid/picture";

v5:

$user_profile = $fb->get('/me', $accessToken)->getGraphNode();
$uid = $user_profile->getField('id');
$name = $user_profile->getField('name');
$email = $user_profile->getField('email');
$profile_url = $user_profile->getField('link');
if (empty($profile_url))
$profile_url = https://www.facebook.com/$uid";
$profile_image_link = "https://graph.facebook.com/$uid/picture";


從字串初始化並檢查 token 是否過期:

v4:

use Facebook\FacebookSession;

$session = new FacebookSession( $token );
return $session->validate();

v5:

return $fb->getOAuth2Client()->debugToken( $token )->getIsValid() == true;


api 查詢架構:

v4:

function query($token, $method, $api, $array_mode = true) {
if (!empty($token)) {
$session = new FacebookSession($token);
                 if (!$session->validate())
return false;
$response = ( new FacebookRequest( $session, $method, $api ) )->execute();
if ($array_mode)
return $response->getGraphObject()->asArray();
return $response->getGraphObject();
}
return false;
}

v5:

function _node_query($token, $method, $api, $array_mode = true) {
if (!empty($token)) {
try {
if (!strcasecmp($method, 'POST'))
return $array_mode ? $this->fb->post($api, $token)->getGraphNode()->asArray() : $this->fb->post($api, $token)->getGraphNode();

return $array_mode ? $this->fb->get($api, $token)->getGraphNode()->asArray() : $this->fb->get($api, $token)->getGraphNode();
} catch (Exception $e) {
var_dump($e);
}
}
return false;
}

function _edge_query($token, $method, $api, $array_mode = false) {
if (!empty($token)) {
try {
if (!strcasecmp($method, 'POST'))
return $array_mode ? $this->fb->post($api, $token)->getGraphEdge()->asArray() : $this->fb->post($api, $token)->getGraphEdge();

return $array_mode ? $this->fb->get($api, $token)->getGraphEdge()->asArray() : $this->fb->get($api, $token)->getGraphEdge();
} catch (Exception $e) {
var_dump($e);
}
}
return false;
}


取得使用者權限清單:

v4:

query($token, 'GET', '/me/permissions');

v5:

_edge_query($token, 'GET', '/me/permissions');


查詢朋友清單:

v4:

function get_friend_installed_app_list($token, &$have_next_page, $page = 1, $item_per_page = 25) {
$have_next_page = false;
$data = $this->query($token, 'GET', '/me/friends?fields=installed&limit='.($item_per_page).'&offset='.(($page - 1) * $item_per_page));
if(isset($data['data']) && ($cnt = count($data['data'])) > 0) {
if (isset($data['paging']) && is_object($data['paging']) && property_exists($data['paging'], 'next')) {
if ($cnt >= $item_per_page)
$have_next_page = true;
}
$output = array();
foreach($data['data'] as $user)
if (is_object($user) && property_exists($user, 'id'))
array_push($output, $user->id);
return $output;
}
return false;

}

v5:

function get_friend_installed_app_list($token, &$have_next_page, $page = 1, $item_per_page = 25) {
$have_next_page = false;
$data = $this->_edge_query($token, 'GET', '/me/friends?fields=installed&limit='.($item_per_page).'&offset='.(($page - 1) * $item_per_page), false);
$items = $data->asArray();
if(is_array($items) && ($cnt = count($items)) > 0) {
$paging = $data->getMetaData();
if (isset($paging['paging']) && isset($paging['paging']['next']))
if ($cnt >= $item_per_page)
$have_next_page = true;
$output = array();
foreach($items as $user)
if (isset($user['id']))
array_push($output, $user['id']);
return $output;
}
return false;
}


收工!

沒有留言:

張貼留言