facebook twitter hatena line email

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