月別アーカイブ: 2011年8月

CodeIgniterでTwitterに画像を投稿するフォームを作ってみた

先日Twitterに画像を投稿する機能が追加され、それを利用するためのAPIも公開された。

で、その機能を使ってCodeIgniterで画像をアップするためのフォームを試しに作ってみました。

作ったと言っても、上記APIページからリンクされているPHPのライブラリであるtmhOAuthというのを拝借して、OAuthとupdateを組み込んだだけ。

また、画像のアップロードに関しては、「CodeIgniter ユーザガイド 日本語版」のファイルアップロードクラスのサンプルを流用しました。

まず、tmhOAuthのライブラリをダウンロードしてきて、CodeIgniter(CI)のsystemディレクトリ以下にtmhOAuthというディレクトリを作ってその中に入れます。

/system/tmhOAuth/tmhOAuth.php
/system/tmhOAuth/tmhUtilities.php

という感じ。

つぎにtmhOAuthについているexamplesのなかから、photo_tweet.phpとかoauth_flow.phpとかを参考にして、自前のtwitterライブラリを作りました。tmhOAuthのラッパーという感じです。

/application/libraries/twitter.php

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class CI_twitter {

  public function __construct($config = array()){

    if (count($config) > 0){
      $this->initialize($config);
    }

    log_message('debug', "Twitter Class Initialized");

  }

  public function initialize($config = array()){

    $defaults = array(
       'tmhOAuth_dir'    => BASEPATH.'tmhOAuth',
       'consumer_key'    => '',
       'consumer_secret' => ''
    );

    foreach ($defaults as $key => $val){
      if (isset($config[$key])){
        $this->$key = $config[$key];
      }else{
        $this->$key = $val;
      }
    }

    require $this->tmhOAuth_dir.'/tmhOAuth.php'
    $this->to= new tmhOAuth(
      array(
        'consumer_key'    => $this->consumer_key,
        'consumer_secret' => $this->consumer_secret
      )
    );

  }

  function request_token($callback=''){

    $code = $this->to->request(
      'POST',
      $this->to->url('oauth/request_token', ''),
      array(
        'oauth_callback' => $callback
      )
    );

    return $code;

  }

  function authorize(){

    $CI =& get_instance();
    $oauth=$this->to->extract_params($this->to->response['response']);
    $CI->session->set_userdata('oauth',$oauth);
    $authurl=$this->to->url("oauth/authorize", '')."?oauth_token={$oauth['oauth_token']}";
    header("Location: {$authurl}");

  }

  function access_token($user_token,$user_secret){

    $this->to->config['user_token']  = $user_token;
    $this->to->config['user_secret'] = $user_secret;

    $code = $this->to->request(
      'POST',
      $this->to->url('oauth/access_token', ''),
      array(
        'oauth_verifier' => $_REQUEST['oauth_verifier']
      )
    );

    if($code==200){
      $access_token=$this->to->extract_params($this->to->response['response']);
    }else{
      $access_token=NULL;
    }

    return $access_token;

  }

  function update_with_media($user_token,$user_secret,$image,$status){

    $this->to->config['user_token']  = $user_token;
    $this->to->config['user_secret'] = $user_secret;

    $code = $this->to->request(
      'POST',
      'https://upload.twitter.com/1/statuses/update_with_media.json',
      array(
        'media[]'  => "@{$image}",
        'status'   => $status
      ),
      true, // use auth
      true  // multipart
    );

    return $code;

  }

  function update($user_token,$user_secret,$status){

    $this->to->config['user_token']  = $user_token;
    $this->to->config['user_secret'] = $user_secret;

    $code = $this->to->request(
      'POST',
       $this->to->url('1/statuses/update'),
      array(
        'status'   => $status
      )
    );

    return $code;

  }

  function response(){
    return $this->to->response['response'];
  }

}

今回使おうとしてるOAuth関連と画像つきupdate関連しか実装してませんが、元のtmhOAuthにはいろいろ他にも機能があるので、必要なものを足していけばいいのかなー、と思います。

で、そのつぎに、コントローラをファイルアップロードクラスのサンプルのものをごにょごにょいじって作ります。

/application/controller/welcome.php

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 
class Welcome extends CI_Controller {
  function __construct(){
    parent::__construct();
    $this->load->library(array('session','form_validation'));

    $this->load->library(
      'twitter',
      array(
        'consumer_key'    => $this->config->item('consumer_key'),
        'consumer_secret' => $this->config->item('consumer_secret')
      )
    );

    $this->load->helper(array('form', 'url'));

//    $this->output->enable_profiler(TRUE);

  }

  function index(){

    $data['status']='';
    $data['error'] ='';

    $this->load->view('form', $data);

  }

  function do_upload(){

    $config['upload_path']  ='<ファイルアップロード用のパス>/uploads/';
    $config['allowed_types']='gif|jpg|png';
    $config['max_size']     ='1000';
    $config['max_width']    ='1024';
    $config['max_height']   ='768';
    $config['encrypt_name'] =TRUE;

    $this->load->library('upload', $config);

    $this->_set_validation();
    $info=$this->_bind_data();

    $data=array();
    $data['info']=$info;

    if(!$this->upload->do_upload() || !$this->form_validation->run()){

      $data['error']=$this->upload->display_errors();

      $this->load->view('form', $data);

    }else{

      if($this->session->userdata('access_token')){

        $access_token=$this->session->userdata('access_token');
        $upload_data=$this->upload->data();

        $this->_update_status(
          $access_token,
          $upload_data,
          $data
        );

      }else{

        $this->session->set_userdata('info',$info);
        $this->session->set_userdata('upload_data',$this->upload->data());

        $code=$this->twitter->request_token(// callback url
          $this->config->item('base_url').'welcome/access_token/'
        );

        if ($code == 200) {
          $this->twitter->authorize();
        } else {
          echo 'There was an error: ' . $this->twitter->response() . PHP_EOL;
        }

      }
    }

  }

  function access_token(){
    $oauth=$this->session->userdata('oauth');

    $access_token=$this->twitter->access_token($oauth['oauth_token'],$oauth['oauth_token_secret']);

    if ($access_token) {
      $this->session->set_userdata('access_token',$access_token);
      $this->session->unset_userdata('oauth');

      $data['info']=$this->session->userdata('info');
      $upload_data=$this->session->userdata('upload_data');

      $this->_update_status(
        $access_token,
        $upload_data,
        $data
      );

    } else {
      echo 'There was an error: ' . $this->twitter->response() . PHP_EOL;
    }
  }

  function sess_clear(){

    $this->session->sess_destroy();
    redirect('');

  }

  function _update_status($access_token,$upload_data,$data){

    $data['image_dir']='<ドキュメントルートからのパス>/uploads/';

    $code = $this->twitter->update_with_media(
      $access_token['oauth_token'],
      $access_token['oauth_token_secret'],
      $upload_data['full_path'],
      $data['info']['status']
    );

    if ($code == 200) {
      $data['upload_data']=$upload_data;
    }else{
      $data['upload_data']=array();
      $data['info']['status']=$this->twitter->response();
    }

    unlink($upload_data['full_path']);

    $this->load->view('success', $data);

  }

  function _set_validation(){

    $validate_rules=array(
      array(
        'field' => 'status',
        'label' => 'status',
        'rules' => 'required'
      )
    );

    $this->form_validation->set_rules($validate_rules);

  }

  function _bind_data(){

    $params=array(
      'status'
    );
    foreach($params as $val){
      $data[$val]=$this->input->post($val);
    }

    return $data;
  }

}

コンストラクタ内でさっきのtwitter.phpライブラリを呼んでいます。引数はconsumer_keyとconsumer_secretです。これらの値はtwitterのアプリを作成したときのものをconfig.phpのなかに追加してあります。
おもなメソッドはindexとdo_uploadとaccess_tokenです。do_uploadが呼ばれて、OAuthのaccess_tokenがない場合、OAuthの手続きに入ります。

その際に$this->twitter->request_tokenの引数にコールバックするURLを入れるようにしています。この場合はaccess_tokenメソッドを呼ぶようにしています。access_tokenを呼ぶときにさらに引数を足してaccess_token内で処理を分けるようにすることもできると思います。

その他の_set_validationとか_bind_dataとかはフォームからサブミットされた値を取るときの僕のいつもの流儀なので、このへんの処理は好きにやって下さい。

あとはビューを2つ。フォームと送信後の完了画面です。

/application/views/form.php

<html>
<head>
<title>アップロードフォーム</title>
</head>
<body>

<?php echo $error;?>
<?php echo validation_errors();?>

<form action="/photoup/welcome/do_upload" method="POST" enctype="multipart/form-data">

<input type="file" name="userfile" size="20" /><br>

status <input type="text" name="status" size="100">

<br /><br />

<input type="submit" value="upload" />

</form>
<p><a href="/photoup/welcome/sess_clear">clear session</a></p>
</body>
</html>

/application/views/success.php

<html>
<head>
<title>アップロードフォーム</title>
</head>
<body>

<h3>成功!</h3>

<ul>
<?php foreach ($upload_data as $item => $value):?>
<li><?php echo $item;?>: <?php echo $value;?></li>
<?php endforeach; ?>
</ul>
<p><strong><?=form_prep($info['status'])?></strong></p>

<p><?php echo anchor('', '次の画像をアップする!'); ?></p>

</body>
</html>

こんな感じです。ユーザガイドのサンプルのほぼパクリです。

CodeIgniterのwikiにOAuthのライブラリとかも紹介されているんですが、画像つきのアップロードとかやるときにちょっと不便かなー、と思ってtwitterのAPIのページからリンクされてたライブラリを直に使ってしまいました。あんまりCI的には美しい実装とは言えないかもしれません…。

(追記:2011/8/19)
twitter.phpのライブラリの設定の仕方がおかしかったので修正しました。