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