先日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のライブラリの設定の仕方がおかしかったので修正しました。