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; } }