Facebook的串流(stream)是一個存取你Facebook塗鴉牆上訊息的簡單方法,並且可以用程式來做很好的使用,它跟Twitter 的feed有點類似。
這個範例是一個應用程式的範例,用來拉出串流然後使用PHPExcel將它放進一個格式良好的開放式xml試算表,這個試算表重新安排訊息為一個時間的紀錄每一列有人的照片、他們的名字(有一個連結到個人檔案)、訊息的內容以及發布的日期時間。
隱私權的考慮
在串流內的資料來自你的朋友那邊;他給你權限來看他們的塗鴉牆但卻未必有意讓大眾這樣做,因為這樣,以及Facebook的商業模式運作的方式,會有嚴格的認證要求;這個要求比twitter feed還嚴,因此這範例不適合給你權限在我的實作中 – 你需要建構自己的應用範例!
步驟1:註冊Facebook應用程式
要開發Facebook的應用程式你需要在你的Facebook頁面http://facebook.com/developers 先建立新的應用程式,然後按‘建立新的應用程式’按鈕並給它一個名稱,這會給你一組金鑰:API金鑰跟應用程式密鑰,這個稍後在你的程式碼中會用到。
此處的說明http://developers.facebook.com/get_started.php對設定有很大的幫忙,當然你也可以看ㄚ琪的中文說明Facebook PHP教學。
到你的應用程式設定頁面,從http://www.facebook.com/developers/ => 在‘我的應用程式’ => 點擊你應用程式的名稱 => 點擊 ‘編輯設定’ => 點擊’Facebook集成’=>‘Canvas’
勾選‘Canvas Type’的Iframe按鈕,然後在‘Canvas Page’填上應用程式名稱以及在’Canvas URL’填上你的應用程式網址,這樣可以讓canvas轉向到你的頁面去,允許你透過Facebook的應用程式頁例如http://apps.facebook.com/reallysnazzyapp/到你的應用程式頁面來存取你的應用程式。
步驟2:託管你的應用程式
當你在Facebook註冊應用程式的時候,它看起來像是Facebook的一部分,可是你需要自己託管程式碼 – 這個程式碼會放在這個公用網站讓Facebook可以顯露這個程式。
有很多免費的php主機供應商你可以試試看,有些不錯有些就很難用,你會需要php5.2的版本並且開放–enable-zip使用(這樣PHPExcel函式庫才能運作)
你可以提出一個頁面用下面的指令碼來檢查這些細節:
<?php
echo phpinfo();
這將顯示你的PHP安裝的設定資訊。
假如你有這個資源,我建議有你自己的web伺服器來開發,透過這些方法控制環境會讓開發程序不會那麼複雜。
步驟3:撰寫應用程式
這支應用程式不只在Facebook上顯示,程式碼還會存取Facebook的資料,對於一個存取Facebook資料的應用程式來說安全設定是需要的。
為了驗證Facebook,你需要更改標誌及session金鑰;這個就是你的API金鑰跟應用程式密鑰(你在步驟1產生的),這個部份大多數是由PHP Facebook API函式庫來處理:http://svn.facebook.com/svnroot/platform/clients/packages/facebook-platform.tar.gz(註:這個連結已移除,取而代之的是下面的連結,但是因為函式庫已經有變更,新的函式庫不支援這裡的程式,所以你只能從這裡的程式碼使用舊的函式庫)
https://github.com/facebook/php-sdk
程式碼有四個部份:
· Facebook的驗證
· 從Facebook拉出資料
· 透過資料來處理/檢索
· 新增資料到有時尚格式的試算表中
Facebook的驗證
在你的程式碼中要include Facebook API函式庫:
include_once ‘../php/facebook.php’;
接著增加程式碼來提供驗證。
你會需要用你自己的金鑰、應用程式密鑰來替代,以及驗證後要轉向的網址,或是使用者要取消:
$appapikey = “xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx’;
$appsecret = ‘yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy’;
$next = “http://SomeServer.com/spike/index.php”; //Where to redirect after authentication
$next_cancel = “http://SomeServer.com/spike/canceled.php”; // Where to redirect if the user cancels the authentication
//—–Login, authenticate and get permission to read the user’s stream—–
$facebook = new Facebook($appapikey, $appsecret);//Create a new facebook object
//Sometimes the session token has expired or is invalid, this throws an execption. If this happens catch it and reset the user and try again
try
{
$user_id = $facebook->require_login();//get the user to login
if (!$facebook->api_client->users_hasAppPermission(‘read_stream’,$user_id))
{
$facebook->redirect(“http://www.facebook.com/authorize.php?api_key=” . $appapikey . “&v=1.0&ext_perm=read_stream&next=” . $next . “&next_cancel=” . $next_cancel );
}
}
catch (Exception $ex)
{
//If the session token has expired or is invalid, this will effectivly reset it and then redirect to try again.
$facebook->set_user(null, null);
$facebook->redirect($_SERVER[‘SCRIPT_URI’]);
exit;
}
這段程式碼需要使用者登錄並確認他們是否有這應用程式的權限可以讀取他們的塗鴉牆訊息,假如沒有,會轉向到授權頁,你需要新增一個位址來讓使用者授權後可以轉到哪去,以及他們想要去取消可以到哪,DARCY THOMAS選擇轉回目前的頁面這個最簡單的方案,他也準備好了一個普通簡單的頁面讓使用者在他們要取消時可以轉移到那裡。
canceled.php :
<?php
echo “No love?”;
有時候你會發現你應用程式的session金鑰會過期,這就是為什麼要用try catch,假如有問題,程式會捕捉到然後重新設定,並讓使用者重試。
在下載檔案之前顯示歡迎的訊息
一旦認證完成,我們要確認是否第一次載入,假如是我們就給使用者一個訊息說選擇下載到試算表,點擊submit按鈕會讓資料拉出來,並且轉換到試算表中。
//the first time the user arives at this page $_REQUEST[‘mode’] will != 1 so just display the intro
// if the user clicks the submit then $_REQUEST[‘mode’] == 1 and the file will be created and presented to the user
if ($_REQUEST[‘mode’] != 1)
{
echo ‘<form action=”index.php” method=”get”>’;
echo ‘Download posts from your wall, as an Open XML Spreadsheet <br/>’;
echo ‘<input type=”hidden” name=”mode” value=”1″/><input type=”submit” /></form>’;
}
else
{
// …create file and present to the user
這是必須的因為假如你轉向到另一頁,這個session金鑰會改變然後認證會失敗,對於更強大的解決方案,你可以用switch case來判斷mode的值以含括不同的PHP檔。
從Facebook拉下資料:
拉下結果相當地容易
$feed = $facebook->api_client->stream_get();
這預設會拉下你朋友最新的30條塗鴉牆訊息。
$feed 是‘posts’、‘profiles’及‘albums’的巢狀陣列。
在範例中,DARCY THOMAS只有使用‘posts’跟‘profiles’兩個欄位。
你也可以藉著設定一些參數來擴充stream_get() 傳回的結果
$facebook->api_client->stream_get(viewer_id, source_ids, start_time, end_time, limit, filter_key);
見http://developers.facebook.com/docs/reference/rest/stream.get/有更多的資訊。
處理/搜尋資料
接下來在放資訊到陣列之前我們使用這資訊流($feed)來擷取我們想要的資訊,我們可以使用:
// get the revelent infomation out the stream ($feed) and put into an array
$posts = $feed[‘posts’];
$profiles = $feed[‘profiles’];
//Put in the names for the row headings
$listMessageDetails = array
(
“0”=>array
(
“image”=>”Image”,
“name”=>”Name”,
“message”=>”Message”,
“time”=>”Time created”
)
);
// Put in the rest of the details for each message
$i =1;
foreach ($posts as $post)
{
$message = $post[‘message’];
//If the post is auto generated by some other Facebook app (i.e., xyz quiz), it normally doesnt have a message
//so we dont add the details of these posts
if( !empty($message))
{
$userId = $post[‘actor_id’];
$created_time = $post[‘created_time’]; // this is in’unix time’ format
$details = getDetails($userId, $profiles);
$image = $details[‘pic_square’];
$name = $details[‘name’];
$url = $details[‘url’];
$MessageDetails[‘name’] = $name;
$MessageDetails[‘message’] = $message;
$MessageDetails[‘time’] = date(“g:i a, F j, Y”, $created_time ); // convert ‘unix time’ to 5:16 pm, March 10, 2001
$MessageDetails[‘id’] = $userId;
$MessageDetails[‘image’] = $image;
$MessageDetails[‘url’] = $url;
$listMessageDetails[$i] = $MessageDetails;
$i++;
}
}
在‘post’陣列中的資料有很多的資訊,其中大部份跟我們在這應用程式中感興趣的不相干(例如‘post_id’、‘permalink’等等),這會拉出我們感興趣的資料並放進陣列中,(誰說過什麼、他們的資訊以及他們說什麼)。
一個post只有post的user id(例如 actor_id)而不是他們的名字,連結到他們的個人資訊等等,所以getDetails()函式會從資訊流($feed)的‘profile’陣列中拉出資料。
試算表的欄位也需要一些標題這樣就能容易地在這時候放進這個陣列。
使用樣式格式將資料放入試算表
透過PHPExcel函式庫你可以使用處理過的資料並放進試算表中然後應用一些格式,你會需要從http://phpexcel.codeplex.com/ (註有可能你無法下載,很幸運地這個範例也這函式庫也有了)下載PHPExcel函式庫,放這個函式庫資料在伺服器上然後在你的指令碼的最前面含括起來。
include ‘PHPExcel/IOFactory.php’;
PHPExcel的文件很有用而且有很多的例子;最好確認你有沒有看過。
接著建立一個新的PHPExcel物件然後載入資料。
//—–Take the data in $listMessageDetails and put into a spreadsheet using the PHPExcel library
$objPHPExcel = new PHPExcel(); //create an new PHPExcel spreadsheet object
$sheet = $objPHPExcel->getActiveSheet();
foreach ($listMessageDetails as $row => $MessageDetails)
{
//put the values from each message into a new row, in the correct columns
//note: columns are 0-based indexed but rows are 1-based. So ‘A1’ and an index of (0,1) would refer to the same cell.
$sheet->getColumnDimension(‘A’)->setWidth(12);
$sheet->getColumnDimension(‘B’)->setWidth(20);
$sheet->getCellByColumnAndRow(1 , $row + 1)->setValueExplicit($MessageDetails[‘name’], PHPExcel_Cell_DataType::TYPE_STRING);
$sheet->getColumnDimension(‘C’)->setWidth(100);
$sheet->getCellByColumnAndRow(2 , $row + 1)->setValueExplicit($MessageDetails[‘message’], PHPExcel_Cell_DataType::TYPE_STRING);
$sheet->getStyle(‘C’ . $row)->getAlignment()->setWrapText(true);
$sheet->getColumnDimension(‘D’)->setWidth(20);
$sheet->getCellByColumnAndRow(3 , $row + 1)->setValueExplicit($MessageDetails[‘time’], PHPExcel_Cell_DataType::TYPE_STRING);
}
這段程式碼會使用$listMessageDetails陣列的所有東西然後載入到$objPHPExcel準備好格式化。
這是設定欄位寬度很方便的地方,所以同時可以這樣做,這裡有一個問題你需要注意,就是:欄位是以0為基礎做索引而列是以1為基礎,所以‘A1’跟(0,1)的索引都是參考相同的儲存格。
大部分這裡使用的格式都是從較早DARCY THOMAS在http://openxmldeveloper.org/archive/2009/05/06/4606.aspx 所寫的的PHP/Open XML 文章中複製來的,參考這篇文章有關如何使用PHPExcel函式庫格式化更多的資訊,這裡所使用的很多部份都是不用說明就可以懂的;所以DARCY THOMAS在這裡沒有做太多的說明。
有一些基本的版面配置屬性:
//Set Print properties
$objPHPExcel->getActiveSheet()->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE); //Set printing orentation
// set autofilter on the columns
$objPHPExcel->getActiveSheet()->setAutoFilter(‘A1:’ . $objPHPExcel->getActiveSheet()->getHighestColumn() . $objPHPExcel->getActiveSheet()->getHighestRow() );
Make the headings bold, put in some borders and alternating row colors for visual clarity:
//—–Put in some formatting to the table data to make it easer to read—–
$highestRow = $objPHPExcel->getActiveSheet()->getHighestRow();
$highestColumn = $objPHPExcel->getActiveSheet()->getHighestColumn();
$objPHPExcel->getActiveSheet()->insertNewRowBefore($highestRow + 1, 1);//Add one more row as a footer to the table
//put a border on the top and bottom rows and make the title row bold
$objPHPExcel->getActiveSheet()->getStyle(‘A1’)->getFont()->setBold(true);
$objPHPExcel->getActiveSheet()->getStyle(‘A1’)->getBorders()->getTop()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN);
$objPHPExcel->getActiveSheet()->getStyle(‘A’ . ($highestRow + 1) )->getBorders()->getBottom()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN);
//Loop through all of the rows and put in fill and borders on the edges
for($row =1; $row<$highestRow + 2; $row++) //remember that
{
//Set the colors, mid blue/grey for the top and bottom rows, with alternating white and light blue/grey
if ($row == 1 || $row ==$highestRow + 1) $color = ‘FFCFDAE7’;
else if ($row%2==0) $color = ‘FFFFFFFF’;
else $color = ‘FFE7EDF5’;
// set the fill type and apply the color
$objPHPExcel->getActiveSheet()->getStyle(‘A’ . $row)->getFill()->setFillType(PHPExcel_Style_Fill::FILL_SOLID);
$objPHPExcel->getActiveSheet()->getStyle(‘A’ . $row)->getFill()->getStartColor()->setARGB($color);
//duplcate the first cells style (fill plus the top and bottom borders) across the whole row
$objPHPExcel->getActiveSheet()->duplicateStyle( $objPHPExcel->getActiveSheet()->getStyle(‘A’ . $row), ‘B’ . $row . ‘:’. $highestColumn . $row); //copy style set in first column to the rest of the row
//Put some borders on the far left and right cells of the row
$objPHPExcel->getActiveSheet()->getStyle(‘A’ . $row )->getBorders()->getLeft()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN);
$objPHPExcel->getActiveSheet()->getStyle($highestColumn . $row )->getBorders()->getRight()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN);
}
藉著擷取影像將個人檔案的大頭貼照放進試算表中然後存到一個暫存檔中,接著使用PHPExcel將大頭貼照插入試算表中:
//—–put the profile image into the streadsheet in column ‘A’ —–
$highestRow = $objPHPExcel->getActiveSheet()->getHighestRow();
for($row =1; $row<$highestRow; $row++)
{
//set the row height to 50 for all but the first row
if ($row !=1) $objPHPExcel->getActiveSheet()->getRowDimension($row)->setRowHeight(50);
$objDrawing = new PHPExcel_Worksheet_Drawing();
$objDrawing->setName(‘Profile Image’);
$objDrawing->setDescription(‘Profile Image’);
$pathURL = $listMessageDetails[$row – 1][‘image’];
$path = tempnam(sys_get_temp_dir() , ‘path’ ); //creates a temp file to put the image in
if( !empty($pathURL) && $row != 1) // if the image is inaccessable or doesnt exist (e.g., the first row) then skip
{
copy($pathURL, $path); // Copy the image from its url location to the temporary file, ready to be loaded in the the spreadsheet
$objDrawing->setPath($path);// the path of where to find the image to insert
$objDrawing->setHeight(50); //size you want the image to be displayed as
$objDrawing->setWorksheet($objPHPExcel->getActiveSheet());
$objDrawing->setCoordinates(‘A’ . $row);// set where you want to put the image
}
}
現在我們有每個訊息作者的Facebook個人檔案的url,我們可以讓他們的名字有一個可點擊的超連結:
// Put in a link on the profile name back to the users Facebook page
for($row =2; $row<$highestRow ; $row++)
{
$url = $listMessageDetails[$row – 1][‘url’];
$objPHPExcel->getActiveSheet()->getCell(‘B’ . $row)->getHyperlink()->setUrl($url);
}
最後,我們可以將試算表的檔案傳給使用者,要建立檔案給瀏覽器你會需要含括表頭:
header(‘Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet’);
header(‘Content-Disposition: attachment;filename=”myfile.xlsx”‘);
header(‘Cache-Control: max-age=0’);
現在我們可以輸出串流,這會用我們使用到的PHPExcel物件然後在寫到輸出前放到一個writer:
$objWriter2007 = PHPExcel_IOFactory::createWriter($objPHPExcel, ‘Excel2007’);
$objWriter2007->save(‘php://output’);
你可以擁有它了,從Facebook認證、取用資料,轉換資料然後用格式化的試算表來表現。
完整的PHP應用程式附在這FacebookStreamToOpenXMLSpreadsheet.zip,好好享受吧!