Php/zend framework/zend oauthとzend service twitterを使ったtwitter投稿
やっていること
- ログイン認証からtwitter投稿までの一連の処理
- 認証時DB保存(botを作りたい際必要、不必要ならTwitterUsersSetLogicクラスを削除
- jqueryプラグイン(charCount.js)で文字数表示
- utf-8で作成
サンプルコード
application/controllers/OauthController.php
<?php
require_once dirname(__FILE__) . '/../models/OauthConsumerModel.php';
require_once dirname(__FILE__) . '/../models/LoginSessionModel.php';
require_once dirname(__FILE__) . '/../models/logic/TwitterOauthCallBackLogic.php';
/**
* Oauth認証コントローラー
*/
class OauthController extends Zend_Controller_Action
{
private $_oauth;
private $_config;
function init()
{
$this->_config = new Zend_Config_Ini(APPLICATION_PATH . '/configs/twitter.ini', APPLICATION_ENV);
$this->_oauth = new OauthConsumerModel($this->_config);
}
function indexAction()
{
// レンダー無し設定
$this->_helper->ViewRenderer->setNoRender();
// AccessTokenリクエスト
$this->_oauth->requestAccessToken();
}
/**
* コールバック
*/
function callbackAction()
{
// レンダー無し設定
$this->_helper->ViewRenderer->setNoRender();
// アプリを拒否された時
if ($this->_request->denied) {
$this->_redirect('/');
}
// twitterOauthコールバックロジックロード
$logic = new TwitterOauthCallBackLogic();
$logic->execLogic($this->_request);
// リダイレクト
$this->_redirect('/twitter/tweet/');
}
/**
* 認証クリア
*/
function clearAction()
{
// レンダー無し設定
$this->_helper->ViewRenderer->setNoRender();
// ログインセッションロード
$session = new LoginSessionModel();
// sessionクリア
$session->delTwitter();
echo 'clearしました';
}
}
application/controllers/TwitterController.php
<?php
require_once APPLICATION_PATH . '/models/AppException.php';
require_once dirname(__FILE__) . '/../models/logic/TweetLogic.php';
require_once dirname(__FILE__) . '/../models/logic/TwitterFriendshipCreateLogic.php';
/**
* Twitterコントローラー
*/
class TwitterController extends Zend_Controller_Action
{
public function init()
{
/* Initialize action controller here */
}
public function preDispatch()
{
// ログインセッション取得
$session = new LoginSessionModel();
$profile = $session->getTwitterProfile();
// プロフィールが存在しないとき
if (!isset($profile['id'])) {
// 認証前のため処理できません。
throw new AppException(AppException::ERR_TWITTER_OAUTH);
}
}
public function indexAction()
{
}
/**
* tweetフォーム
*/
public function tweetAction()
{
}
/**
* tweet実行
*/
public function tweetexecAction()
{
// レンダー無し設定
$this->_helper->ViewRenderer->setNoRender();
// tweetロジックロード
$logic = new TweetLogic();
$ret = $logic->execLogic($this->_request->tweet);
echo '投稿完了';
}
/**
* twitterFollorget実行
*/
public function followAction()
{
// レンダー無し設定
$this->_helper->ViewRenderer->setNoRender();
// tweetロジックロード
$logic = new TwitterFriendshipCreateLogic();
$ret = $logic->execLogic('test');
echo '投稿完了';
}
}
application/models/OauthConsumerModel.php
<?php
require_once dirname(__FILE__) . '/../models/OauthConsumerModel.php';
require_once dirname(__FILE__) . '/../models/LoginSessionModel.php';
require_once dirname(__FILE__) . '/../models/logic/TwitterOauthCallBackLogic.php';
/**
* Oauth認証コントローラー
*/
class OauthController extends Zend_Controller_Action
{
private $_oauth;
private $_config;
function init()
{
$this->_config = new Zend_Config_Ini(APPLICATION_PATH . '/configs/twitter.ini', APPLICATION_ENV);
$this->_oauth = new OauthConsumerModel($this->_config);
}
function indexAction()
{
// レンダー無し設定
$this->_helper->ViewRenderer->setNoRender();
// AccessTokenリクエスト
$this->_oauth->requestAccessToken();
}
/**
* コールバック
*/
function callbackAction()
{
// レンダー無し設定
$this->_helper->ViewRenderer->setNoRender();
// アプリを拒否された時
if ($this->_request->denied) {
$this->_redirect('/');
}
// twitterOauthコールバックロジックロード
$logic = new TwitterOauthCallBackLogic();
$logic->execLogic($this->_request);
// リダイレクト
$this->_redirect('/twitter/tweet/');
}
/**
* 認証クリア
*/
function clearAction()
{
// レンダー無し設定
$this->_helper->ViewRenderer->setNoRender();
// ログインセッションロード
$session = new LoginSessionModel();
// sessionクリア
$session->delTwitter();
echo 'clearしました';
}
}
application/models/OauthTwitterModel.php
<?php
require_once 'Zend/Service/Twitter.php';
require_once 'Zend/Oauth/Token/Access.php';
/**
* Oauth認証Twitterモデルクラス
*/
class OauthTwitterModel extends Zend_Service_Twitter
{
public function __construct(Zend_Oauth_Token_Access $token, array $config)
{
self::setHttpClient($token->getHttpClient($config));
parent::__construct(null);
}
}
application/models/LoginSessionModel.php
<?php
require_once APPLICATION_PATH . '/utils/XmlToArrayUtil.php';
/**
* ログインセッションモデルクラス
*/
class LoginSessionModel
{
const DEFAULT_NAMESPACE = __CLASS__;
private $_session;
function __construct($namespace = self::DEFAULT_NAMESPACE)
{
$this->_session = new Zend_Session_Namespace($namespace);
}
/**
* twitterプロフィール設定(XML
*
* @see http://apiwiki.twitter.com/w/page/22554685/Twitter-REST-API-Method:-account%C2%A0update_profile
*/
public function setTwitterProfileXmlElement($xmlElement)
{
// xmlElementからArray変換ユーティリティロード
$util = new XmlToArrayUtil;
$obj = $util->exec($xmlElement);
$this->_session->twitterProfile = serialize($obj);
}
/**
* twitterプロフィール設定
*/
public function setTwitterProfile($data)
{
$this->_session->twitterProfile = serialize($data);
}
/**
* twitterプロフィール取得
*/
public function getTwitterProfile()
{
if (!isset($this->_session->twitterProfile)) {
return array();
}
return unserialize($this->_session->twitterProfile);
}
/**
* twitterトークン設定(Element
*/
public function setTwitterTokenElement($element)
{
// 連想配列生成
$obj = array(
'oauth_token' => $element->oauth_token,
'oauth_token_secret' => $element->oauth_token_secret,
'screen_name' => $element->screen_name,
'user_id' => $element->user_id,
);
$this->setTwitterToken($obj);
}
/**
* twitterトークン設定
*/
public function setTwitterToken($data)
{
$this->_session->twitterToken = serialize($data);
}
/**
* twitterトークン取得
*/
public function getTwitterToken()
{
if (!isset($this->_session->twitterToken)) {
return array();
}
return unserialize($this->_session->twitterToken);
}
/**
* セッション削除
*/
public function delTwitter()
{
// twitterプロフィールセッションを削除
unset($this->_session->twitterProfile);
// twitterトークンセッションを削除
unset($this->_session->twitterToken);
}
}
application/models/logic/TwitterOauthCallBackLogic.php
<?php
require_once APPLICATION_PATH . '/models/AppException.php';
require_once dirname(__FILE__) . '/../OauthConsumerModel.php';
require_once dirname(__FILE__) . '/../OauthTwitterModel.php';
require_once dirname(__FILE__) . '/../LoginSessionModel.php';
require_once dirname(__FILE__) . '/TwitterUserDbSetLogic.php';
/**
* TwitterOauth認証後のコールバックロジック
*/
class TwitterOauthCallBackLogic
{
/**
* 実行
*/
public function execLogic($request)
{
// アプリを拒否された時
if ($request->denied) {
return false;
}
$config = new Zend_Config_Ini(APPLICATION_PATH . '/configs/twitter.ini', APPLICATION_ENV);
$oauth = new OauthConsumerModel($config);
// AccessToken取得
$token = $oauth->getAccessToken($request);
if (!isset($token)) {
// アクセストークン取得に失敗しました。
throw new AppException(AppException::ERR_TWITTER_ACCESS_TOKEN_GET);
}
// OauthTwitter生成
$twitter = new OauthTwitterModel($token, $config->toArray());
// プロフィール取得
$profile = $twitter->account->verifyCredentials();
// プロフィールが存在しない時
if (!isset($profile->user)) {
// プロフィール取得に失敗しました。
throw new AppException(AppException::ERR_TWITTER_PROFILE_GET, $profile->error);
}
// *Sessionを使う場合
// ログインセッションロード
$session = new LoginSessionModel();
// twitterトークン(Element)を設定
$session->setTwitterTokenElement($token);
// *DBを使う場合
// twitterユーザをDBに設定
$logic = new TwitterUserDbSetLogic();
$logic->execLogic($token, $profile->getIterator());
// ログインセッションロード
$session = new LoginSessionModel();
// twitterプロフィール(SimpleXMLElement)を設定
$session->setTwitterProfileXmlElement($profile->getIterator());
return true;
}
}
application/models/logic/TweetLogic.php
<?php
require_once APPLICATION_PATH . '/models/AppException.php';
require_once dirname(__FILE__) . '/../OauthTwitterModel.php';
require_once dirname(__FILE__) . '/../dao/TwitterUsersDao.php';
require_once dirname(__FILE__) . '/../LoginSessionModel.php';
/**
* twitter投稿ロジック
*/
class TweetLogic
{
/**
* 実行
*/
public function execLogic($text)
{
// twitter設定取得
$config = new Zend_Config_Ini(APPLICATION_PATH . '/configs/twitter.ini', APPLICATION_ENV, true);
// ログインセッション取得
$session = new LoginSessionModel();
// *Sessionを使う場合
$tokenSession = $session->getTwitterToken();
// ZendAccessTokenロード
$token = new Zend_Oauth_Token_Access();
$token->setToken($tokenSession['oauth_token']);
$token->setTokenSecret($tokenSession['oauth_token_secret']);
// *DBを使う場合
// $profile = $loginSession->getTwitterProfile();
// // twitterユーザDaoロード
// $dao = new TwitterUsersDao();
// $twitterUser = $dao->findRowByUserId($profile['id']);
// // ZendAccessTokenロード
// $token = new Zend_Oauth_Token_Access();
// $token->setToken($twitterUser->oauth_token);
// $token->setTokenSecret($twitterUser->oauth_token_secret);
// AccessToken設定
$config->accessToken = $token;
// Oauth認証Twitterモデルロード
$twitter = new OauthTwitterModel($token, $config->toArray());
// twitter投稿
$response = $twitter->status->update($text);
if (isset($response->error)) {
// tweetに失敗しました。
throw new AppException(AppException::ERR_TWITTER_TWEET, $response->error);
}
return true;
}
}
application/models/logic/TwitterUserDbSetLogic.php
<?php
require_once dirname(__FILE__) . '/../dao/TwitterUsersDao.php';
/**
* twitterユーザDB設定ロジック
*/
class TwitterUserDbSetLogic
{
/**
* 実行
*/
public function execLogic($token, $profile)
{
// データないときは戻る
if (is_null($token->user_id)) return;
// TwitterユーザDaoロード
$dao = new TwitterUsersDao();
// twitterユーザ取得
$twitterUser = $dao->findRowByUserId($token->user_id);
// 既に登録済みのときは
if ($twitterUser->user_id) {
// データ更新
$data = array(
'oauth_token' => $token->oauth_token,
'oauth_token_secret' => $token->oauth_token_secret,
'screen_name' => $token->screen_name,
'name' => $profile->name,
);
$dao->updateByUserId($data, $token->user_id);
} else {
// データ挿入
$data = array(
'user_id' => $token->user_id,
'oauth_token' => $token->oauth_token,
'oauth_token_secret' => $token->oauth_token_secret,
'screen_name' => $token->screen_name,
'name' => $profile->name,
);
$dao->insert($data);
}
}
}
application/models/AppException.php
<?php
/**
* アプリエラークラス
*/
class AppException extends Zend_Exception
{
// エラーコード定数
const ERR_TWITTER_ACCESS_TOKEN_GET = 20001; // アクセストークン取得に失敗しました。
const ERR_TWITTER_PROFILE_GET = 20002; // プロフィール取得に失敗しました。
const ERR_TWITTER_OAUTH = 20003; // 認証前のため処理できません。
const ERR_TWITTER_TWEET = 20101; // tweetに失敗しました。
/**
* constructor
*/
public function __construct($errCode, $addMessage = )
{
switch ($errCode)
{
case self::ERR_TWITTER_ACCESS_TOKEN_GET:
$errMessage = 'アクセストークン取得に失敗しました。';
break;
case self::ERR_TWITTER_PROFILE_GET:
$errMessage = 'プロフィール取得に失敗しました。';
break;
case self::ERR_TWITTER_OAUTH:
$errMessage = '認証前のため処理できません。';
break;
case self::ERR_TWITTER_TWEET:
$errMessage = 'tweetに失敗しました。';
break;
default:
$errMessage = 'システムエラー';
}
$errMessage = 'Error:' . $errCode . ' ' . $errMessage;
parent::__construct($errMessage . $addMessage, $errCode);
}
}
application/models/dao/TwitterUsersDao.php
<?php
/**
* twitterユーザDao
*/
class TwitterUsersDao extends Zend_Db_Table_Abstract
{
// テーブル名
protected $_name = 'twitter_users';
// ユニークキー
protected $_primary = 'id';
// 挿入
public function insert($data)
{
$data['created'] = date('Y-m-d H:i:s', time());
$data['updated'] = date('Y-m-d H:i:s', time());
parent::insert($data);
}
// 更新(by UserId
public function update($data, $where)
{
$data['updated'] = date('Y-m-d H:i:s', time());
parent::update($data, $where);
}
// 更新(by UserId
public function updateByUserId($data, $userId)
{
$where = $this->getAdapter()->quoteInto('user_id = ?', $userId);
$this->update($data, $where);
}
// レコード取得(by UserId
public function findRowByUserId($userId)
{
$where = $this->getAdapter()->quoteInto('user_id = ?', $userId);
return $this->fetchRow($where);
}
}
application/utils/XmlToArrayUtil.php
<?php
/**
* xmlElementをArrayへ変換ユーティリティ
*/
class XmlToArrayUtil
{
public function exec($xmlobj)
{
$arr = array();
if (is_object($xmlobj)) {
$xml = get_object_vars($xmlobj);
} else {
$xml = $xmlobj;
}
foreach ($xml as $key => $val) {
if (is_object($val)) {
$arr[$key] = self::exec($xmlobj->{$key});
} else {
$arr[$key] = $val;
$attrs = get_object_vars($xmlobj->{$key});
if (isset($attrs['@attributes'])) {
$attrs = $attrs['@attributes'];
$arr[$key . '_attrs'] = $attrs;
}
}
}
return $arr;
}
}
application/views/templates/index/index.phtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Language" content="ja" /> <meta http-equiv="Content-Script-Type" content="text/javascript" /> <meta http-equiv="Content-Style-Type" content="text/css" /> <meta http-equiv="imagetoolbar" content="no" /> <title>tweet</title> <link rel="stylesheet" href="lib/global.css" type="text/css" /> <!-- JS --> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script> <!-- http://cssglobe.com/post/7161/jquery-plugin-simplest-twitterlike-dynamic-character-count-for-textareas --> <script type="text/javascript" src="/js/jquery/charCount.js"></script> <script type="text/javascript"> $(function() { //default usage $("#tweet").charCount(); }); </script> <!-- CSS --> <style type="text/css"> /* Character Count styles */ .tweet form { width:500px; } .tweet label{ display:block; font-size:12px; } .tweet textarea{ margin:5px 0; width:490px; height:60px; border:2px solid #ccc; padding:3px; color:#555; } .tweet form div { position:relative; margin:1em 0; } .tweet form .counter{ position:absolute; right:0; top:0; font-size:16px; font-weight:bold; color:#ccc; } .tweet form .warning { color:#600; } .tweet form .exceeded { color:#e00; } </style> </head> <body> <div class="tweet"> <form id="form" method="post" action="/twitter/tweetexec"> <div> <label for="message">Type your message</label> <textarea id="tweet" name="tweet"></textarea> <input type="submit" id="submit" value="Tweet"/> </div> </form> </div> </body> </html>
application/controller/ErrorController.php
<?php
/**
* エラーコントローラー
*/
class ErrorController extends Zend_Controller_Action
{
public function errorAction()
{
$errors = $this->_getParam('error_handler');
switch ($errors->type) {
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
// 404 error -- controller or action not found
$this->getResponse()->setHttpResponseCode(404);
$this->view->message = 'Page not found';
break;
default:
// application error
$this->getResponse()->setHttpResponseCode(500);
$this->view->message = 'Application error';
break;
}
// アプリエラー
if (get_class($errors->exception) == 'AppException') {
$this->view->message = $errors->exception->getMessage();
}
// Log exception, if logger available
if ($log = $this->getLog()) {
$log->crit($this->view->message, $errors->exception);
}
// conditionally display exceptions
if ($this->getInvokeArg('displayExceptions') == true) {
$this->view->exception = $errors->exception;
}
$this->view->request = $errors->request;
}
public function getLog()
{
$bootstrap = $this->getInvokeArg('bootstrap');
if (!$bootstrap->hasPluginResource('Log')) {
return false;
}
$log = $bootstrap->getResource('Log');
return $log;
}
}
application/configs/twitter.ini
[production] siteUrl = http://twitter.com/oauth callbackUrl = http://twitter.localhost/oauth/callback consumerKey = xxxxxxxxxxxxxxxxxxxxxx consumerSecret = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ; ログイン認証(毎回許可を出さない ; authorizeUrl = http://twitter.com/oauth/authenticate ; ログイン認証(毎回許可画面を出す authorizeUrl = http://twitter.com/oauth/authorize [staging : production] [testing : production] [development : production]
application/configs/db.ini
[production] # db設定 db.adapter = Pdo_Mysql db.params.host = localhost db.params.dbname = twitter db.params.username = xxxx db.params.password = xxxxxx db.params.charset = UTF8 [staging : production] [testing : production] [development : production]
twitter_usersテーブルSQL
CREATE TABLE twitter DEFAULT CHARACTER SET utf8; use twitter; DROP TABLE IF EXISTS `twitter_users`; CREATE TABLE twitter_users( `id` int unsigned NOT NULL PRIMARY KEY auto_increment , `user_id` int unsigned NOT NULL UNIQUE , `updated` datetime NOT NULL default 0 , `updated_ip` varchar(15) , `updated_host` varchar(127) , `created` datetime NOT NULL default 0 , `created_ip` varchar(15) , `created_host` varchar(127) , `oauth_token` varchar(63) , `oauth_token_secret` varchar(63) , `screen_name` varchar(31) , `name` varchar(31) ) ENGINE=InnoDB;
参考URL
- Zend_Oauthを使ってOAuth認証するチュートリアル(後編)
http://h2plus.biz/blog/2009/12/15/456/
- TwitterクライアントのOAuth認証(Zend_Oauth) - シンプルなTwitterクライアントの作成 -
http://blog3.logosware.com/archives/914
- Zend_Service_Twitter でBASIC認証からOAuthに切り替える方法
http://blog.ishinao.net/2010/09/17/3258/
- XmlToArray
http://soft.fpso.jp/develop/php/entry_2764.html
おまけ
フォローリストに友達追加
<?php
require_once APPLICATION_PATH . '/models/AppException.php';
require_once dirname(__FILE__) . '/../OauthTwitterModel.php';
// require_once dirname(__FILE__) . '/../dao/TwitterUsersDao.php';
require_once dirname(__FILE__) . '/../LoginSessionModel.php';
/**
* twitterフォロー取得ロジック
*/
class TwitterFriendshipCreateLogic
{
/**
* 実行
*
* @param twitterId or screenName
*/
public function execLogic($id)
{
// twitter設定取得
$config = new Zend_Config_Ini(APPLICATION_PATH . '/configs/twitter.ini', APPLICATION_ENV, true);
// ログインセッション取得
$session = new LoginSessionModel();
$tokenSession = $session->getTwitterToken();
// ZendAccessTokenロード
$token = new Zend_Oauth_Token_Access();
$token->setToken($tokenSession['oauth_token']);
$token->setTokenSecret($tokenSession['oauth_token_secret']);
// AccessToken設定
$config->accessToken = $token;
// Oauth認証Twitterモデルロード
$twitter = new OauthTwitterModel($token, $config->toArray());
// twitter投稿
$response = $twitter->friendshipCreate($id);
if (isset($response->error)) {
// フロー追加に失敗しました。
// throw new AppException(AppException::ERR_TWITTER_FRIENDSHIP_CREATE, $response->error);
}
return true;
}
}
