今天來實作臉書登入後,幫使用者建立帳號並登入的步驟
取得資料的方法是:
$fb->setDefaultAccessToken($accessToken);
$response = $fb->get('/me?locale=en_US&fields=id,name,email');
$userNode = $response->getGraphUser();
$email = $userNode->getField('email');
$fb_user_id = $userNode->getField('id');
先來做一些變數整理:
把 app_id, app_secret 與 fb_callback_url 整理到我們的 config 吧
記得改寫成你自己申請的 id 與 secret
config/Config.php
class Config {
const BASE_URL = "http://127.0.0.1/login_register";
const MAIL_FROM = "no_reply@mail.com";
const MAIL_FROM_NAME = "http://127.0.0.1/login_register/";
const MAIL_USER_NAME = "your_gmail_account";
const MAIL_USER_PASSWROD = "your_gmail_password";
const FB_APP_ID = 'your_app_id';
const FB_APP_SECRET = 'your_app_secret';
const FB_CALLBACK_URL = 'http://localhost/game/fb-callback';
}
再來把臉書基本宣告這段放到 init.php 裡面
<?php
date_default_timezone_set('Asia/Taipei');
if(isset($_POST))$_POST = GUMP::xss_clean($_POST);
$route = new Router(Request::uri()); //搭配 .htaccess 排除資料夾名稱後解析 URL
$msg = new \Plasticbrain\FlashMessages\FlashMessages();
$fb = new Facebook\Facebook([
'app_id' => Config::FB_APP_ID, // Replace {app-id} with your app id
'app_secret' => Config::FB_APP_SECRET,
'default_graph_version' => 'v2.2',
]);
接著來修改這支專門接臉書的回傳程式吧,在後方加上檢查是否存在,不存在就自動註冊若存在就自動登入。
controller/fb-callback.php
<?php
$helper = $fb->getRedirectLoginHelper();
try {
$accessToken = $helper->getAccessToken();
} catch(Facebook\Exceptions\FacebookResponseException $e) {
// When Graph returns an error
echo 'Graph returned an error: ' . $e->getMessage();
exit;
} catch(Facebook\Exceptions\FacebookSDKException $e) {
// When validation fails or other local issues
echo 'Facebook SDK returned an error: ' . $e->getMessage();
exit;
}
if (! isset($accessToken)) {
if ($helper->getError()) {
header('HTTP/1.0 401 Unauthorized');
echo "Error: " . $helper->getError() . "\n";
echo "Error Code: " . $helper->getErrorCode() . "\n";
echo "Error Reason: " . $helper->getErrorReason() . "\n";
echo "Error Description: " . $helper->getErrorDescription() . "\n";
} else {
header('HTTP/1.0 400 Bad Request');
echo 'Bad request';
}
exit;
}
// The OAuth 2.0 client handler helps us manage access tokens
$oAuth2Client = $fb->getOAuth2Client();
// Get the access token metadata from /debug_token
$tokenMetadata = $oAuth2Client->debugToken($accessToken);
// Validation (these will throw FacebookSDKException's when they fail)
$tokenMetadata->validateAppId(Config::FB_APP_ID); // Replace {app-id} with your app id
// If you know the user ID this access token belongs to, you can validate it here
//$tokenMetadata->validateUserId('123');
$tokenMetadata->validateExpiration();
if (! $accessToken->isLongLived()) {
// Exchanges a short-lived access token for a long-lived one
try {
$accessToken = $oAuth2Client->getLongLivedAccessToken($accessToken);
} catch (Facebook\Exceptions\FacebookSDKException $e) {
echo "<p>Error getting long-lived access token: " . $helper->getMessage() . "</p>\n\n";
exit;
}
}
$_SESSION['fb_access_token'] = (string) $accessToken;
$fb->setDefaultAccessToken($accessToken);
$response = $fb->get('/me?locale=en_US&fields=id,name,email');
$userNode = $response->getGraphUser();
$email = $userNode->getField('email');
$fb_user_id = $userNode->getField('id');
$userVeridator = new UserVeridator();
$userVeridator->isEmailDuplicate($email);
$error = $userVeridator->getErrorArray();
if(count($error) == 0)
{
// 沒有註冊
$table = 'members';
$data_array = array(
'username' => $fb_user_id,
'password' => '',
'email' => $email,
'active' => 'Yes',
'fb_user_id' => $fb_user_id
);
Database::get()->insert($table, $data_array);
$_SESSION['memberID'] = Database::get()->getLastId();
$_SESSION['username'] = $fb_user_id;
}else{
// 有註冊了
if($userVeridator->fbLoginVerification($email, $fb_user_id)){
$_SESSION['memberID'] = $userVeridator->getMemberIdByFb($fb_user_id);
$_SESSION['username'] = $fb_user_id;
}
}
header('Location: home');
exit;
另外我們需要這兩支新的 function 來驗證
UserVeridator.php
/**
* 驗證臉書帳號是否已存在於資料庫中
*/
public function fbLoginVerification($email, $fb_user_id) {
$result = Database::get()->execute('SELECT memberID FROM members WHERE email = :email AND fb_user_id = :fb_user_id',
array(':email' => $email, ':fb_user_id' => $fb_user_id));
if(isset($result[0]['memberID']) AND !empty($result[0]['memberID'])){
return true;
}
return false;
}
/**
* 用臉書 ID 取得 memberID
*/
public function getMemberIdByFb($fb_user_id) {
$result = Database::get()->execute('SELECT memberID FROM members WHERE fb_user_id = :fb_user_id',
array(':fb_user_id' => $fb_user_id));
if(isset($result[0]['memberID']) AND !empty($result[0]['memberID'])){
return $result[0]['memberID'];
}
return false;
}
把這個臉書登入按鈕抽出來放這裡 當成一個介面的元件
view/_component/fb-login.php
<?php
$helper = $fb->getRedirectLoginHelper();
$loginUrl = $helper->getLoginUrl(Config::FB_CALLBACK_URL, array('email'));
echo '<a href="' . htmlspecialchars($loginUrl) . '">Log in with Facebook!</a>';
使用這個介面元件,放到我們原本 login 頁面
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-8 col-md-6 col-sm-offset-2 col-md-offset-3">
<form role="form" method="post" action="<?=Config::BASE_URL?>do_login" autocomplete="off">
<h2>Please Login</h2>
<p><a href='register'>Register a New Account</a></p>
<hr>
<?php if ($msg->hasMessages()) $msg->display(); ?>
<div class="form-group">
<input type="text" name="username" id="username" class="form-control input-lg" placeholder="User Name" value="<?php if($msg->hasErrors()){ echo htmlspecialchars($_POST['username'], ENT_QUOTES); } ?>" tabindex="1">
</div>
<div class="form-group">
<input type="password" name="password" id="password" class="form-control input-lg" placeholder="Password" tabindex="3">
</div>
<div class="row">
<div class="col-xs-9 col-sm-9 col-md-9">
<a href='forget'>Forgot your Password?</a>
</div>
</div>
<hr>
<div class="row">
<div class="col-xs-6 col-md-6"><input type="submit" name="submit" value="Login" class="btn btn-primary btn-block btn-lg" tabindex="5"></div>
<?php include("view/_component/fb-login.php");?>
</div>
</form>
</div>
</div>
</div>
看起來就會這樣:
按下去之後,自動註冊,若已有帳號就自動登入囉。
因為是臉書已經驗證過的信箱,所以我們就相信他的 email 直接免驗證。
require 通常用在 function 或 class ,include 則是靜態
include 的差異是 require 出錯是會顯示錯誤,會立刻停止不再執行
另外只有 include 可以用在迴圈,require 無法