|
PHP复制代码
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* QQ PHP SDK 2.0.0 For CodeIgniter 2.1.3
*
* 源自官方QQ PHP SDK 2.0.0,适用于CodeIgniter 2.1.3
*
* @version 0.1.0
* @author 颜风
*/
// --------------------------------------------------------------------
/**
* Qq_error_case类
*
* 封闭异常
*
* @author 颜风
* @since 0.1.0
*/
class Qq_error_case {
private $error_msg;
public function __construct (){
$this->error_msg = array(
"30001" => "<h2>The state does not match. You may be a victim of CSRF.</h2>",
"50001" => "<h2>可能是服务器无法请求https协议</h2>可能未开启curl支持,请尝试开启curl支持,重启web服务器,如果问题仍未解决,请联系我们"
);
}
// --------------------------------------------------------------------
/**
* 显示错误信息
*
* @access public
* @param int $code 错误代码
* @param string $description 描述信息(可选)
* @return void
*/
public function show_error ($code, $description = '$')
{
$recorder = new Qq_recorder ();
if(! $recorder->read_inc("error_report"))
{
die();//die quietly
}
echo "<meta charset=\"UTF-8\">";
if($description == "$"){
die($this->error_msg[$code]);
}else{
echo "<h3>error:</h3>$code";
echo "<h3>msg :</h3>$description";
exit();
}
}
// --------------------------------------------------------------------
}
// Qq_error_case类结束
// --------------------------------------------------------------------
class Qq_recorder {
private static $data;
private $inc;
private $error;
public $ci;
public function __construct (){
$this->ci = & get_instance ();
$this->error = new Qq_error_case ();
$this->ci->load->library(array('session'));
//加载配置文件
$this->ci->config->load('qc');
$this->inc = $this->ci->config->item('qc');
if( ! $this->ci->session->userdata('qc_user_data'))
{
self::$data = array();
}
else
{
self::$data = $this->ci->session->userdata('qc_user_data');
}
}
public function write ($name,$value)
{
self::$data[$name] = $value;
$this->ci->session->set_userdata('qc_user_data',self::$data);
}
public function read ($name)
{
if(empty(self::$data[$name]))
{
return null;
}
else
{
return self::$data[$name];
}
}
public function read_inc ($name)
{
if(empty($this->inc[$name]))
{
return null;
}
else
{
return $this->inc[$name];
}
}
public function delete ($name){
unset(self::$data[$name]);
$this->ci->session->set_userdata('qc_user_data',self::$data);
}
}
// Qq_recorder类结束
// --------------------------------------------------------------------
/**
* Qq_url类
*
* url封装类,将常用的url请求操作封装在一起
*
* @author 颜风
* @since 0.1.0
*/
class Qq_url {
private $error;
public function __construct ()
{
$this->error = new Qq_error_case ();
}
// --------------------------------------------------------------------
/**
* 拼接url
*
* @access public
* @param string $base_url 基于的url
* @param array $keys_arr 参数列表数组
* @return string 返回拼接的url
*/
public function combine_url ($base_url,$keys_arr){
$combined = $base_url."?";
$value_arr = array();
foreach($keys_arr as $key => $val){
$value_arr[] = "{$key}={$val}";
}
$key_str = implode("&",$value_arr);
$combined .= ($key_str);
return $combined;
}
// --------------------------------------------------------------------
/**
* 服务器通过get请求获得内容
*
* @access public
* @param string $url 请求的url,拼接后的
* @@return string 请求返回的内容
*/
public function get_contents ($url)
{
if (ini_get("allow_url_fopen") == "1")
{
$response = file_get_contents($url);
}
else
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER , FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER , TRUE);
curl_setopt($ch, CURLOPT_URL , $url);
$response = curl_exec($ch);
curl_close($ch);
}
//-------请求为空
if(empty($response)){
$this->error->show_error("50001");
}
return $response;
}
// --------------------------------------------------------------------
/**
* get方式请求资源
*
* @access public
* @param string $url 基于的base_url
* @param array $keys_arr 参数列表数组
* @return string 返回的资源内容
*/
public function get ($url, $keys_arr)
{
$combined = $this->combine_url($url, $keys_arr);
return $this->get_contents($combined);
}
// --------------------------------------------------------------------
/**
* post方式请求资源
*
* @access public
* @param string $url 基于的base_url
* @param array $keys_arr 请求的参数列表
* @param int $flag 标志位
* @return string 返回的资源内容
*/
public function post ($url, $keys_arr, $flag = 0){
$ch = curl_init();
if(! $flag) curl_setopt($ch, CURLOPT_SSL_VERIFYPEER , FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER , TRUE);
curl_setopt($ch, CURLOPT_POST , TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS , $keys_arr);
curl_setopt($ch, CURLOPT_URL , $url);
$ret = curl_exec($ch);
curl_close($ch);
return $ret;
}
}
// Qq_url类结束
// --------------------------------------------------------------------
class Qq_oauth {
const VERSION = "2.0";
const GET_AUTH_CODE_URL = "https://graph.qq.com/oauth2.0/authorize";
const GET_ACCESS_TOKEN_URL = "https://graph.qq.com/oauth2.0/token";
const GET_OPENID_URL = "https://graph.qq.com/oauth2.0/me";
protected $recorder;
public $url_utils;
protected $error;
function __construct ()
{
$this->recorder = new Qq_recorder ();
$this->url_utils = new Qq_url ();
$this->error = new Qq_error_case ();
}
// --------------------------------------------------------------------
public function qq_login (){
$appid = $this->recorder->read_inc("appid");
$callback = $this->recorder->read_inc("callback");
$scope = $this->recorder->read_inc("scope");
//-------生成唯一随机串防CSRF攻击
$state = md5(uniqid(rand(), TRUE));
$this->recorder->write('state',$state);
//-------构造请求参数列表
$keys_arr = array(
"response_type" => "code",
"client_id" => $appid,
"redirect_uri" => $callback,
"state" => $state,
"scope" => $scope
);
$login_url = $this->url_utils->combine_url(self::GET_AUTH_CODE_URL, $keys_arr);
header("Location:$login_url");
}
// --------------------------------------------------------------------
public function qq_callback ()
{
$state = $this->recorder->read("state");
//验证state防止CSRF攻击
if($_GET['state'] != $state)
{
$this->error->show_error("30001");
}
//请求参数列表
$keys_arr = array(
"grant_type" => "authorization_code",
"client_id" => $this->recorder->read_inc("appid"),
"redirect_uri" => urlencode($this->recorder->read_inc("callback")),
"client_secret" => $this->recorder->read_inc("appkey"),
"code" => $_GET['code']
);
//造请求access_token的url
$token_url = $this->url_utils->combine_url(self::GET_ACCESS_TOKEN_URL, $keys_arr);
$response = $this->url_utils->get_contents($token_url);
if(strpos($response, "callback") !== false){
$lpos = strpos($response, "(");
$rpos = strrpos($response, ")");
$response = substr($response, $lpos + 1, $rpos - $lpos -1);
$msg = json_decode($response);
if(isset($msg->error))
{
$this->error->show_error($msg->error, $msg->error_description);
}
}
$params = array();
parse_str($response, $params);
$this->recorder->write("access_token", $params["access_token"]);
return $params["access_token"];
}
// --------------------------------------------------------------------
public function get_openid ()
{
//请求参数列表
$keys_arr = array(
"access_token" => $this->recorder->read("access_token")
);
$graph_url = $this->url_utils->combine_url(self::GET_OPENID_URL, $keys_arr);
$response = $this->url_utils->get_contents($graph_url);
//检测错误是否发生
if(strpos($response, "callback") !== false){
$lpos = strpos($response, "(");
$rpos = strrpos($response, ")");
$response = substr($response, $lpos + 1, $rpos - $lpos -1);
}
$user = json_decode($response);
if(isset($user->error)){
$this->error->show_error($user->error, $user->error_description);
}
//记录openid
$this->recorder->write("openid", $user->openid);
return $user->openid;
}
}
// Qq_oauth类结束
// --------------------------------------------------------------------
/**
* Qc类
*
* api外部对象,调用接口全部依赖于此对象
*
* @author 颜风
* @since 0.1.0
*/
class Qc extends Qq_oauth {
private $kes_arr, $api_map;
/**
* 构造方法
*
* @access public
* @param string $access_token access_token value
* @param string $openid openid value
* @return Object Qc
*/
public function __construct ($access_token = "", $openid = "")
{
parent ::__construct ();
//如果access_token和openid为空,则从session里去取,适用于demo展示情形
if($access_token === "" || $openid === "")
{
$this->keys_arr = array(
"oauth_consumer_key" => (int )$this->recorder->read_inc("appid"),
"access_token" => $this->recorder->read("access_token"),
"openid" => $this->recorder->read("openid")
);
}
else
{
$this->keys_arr = array(
"oauth_consumer_key" => (int )$this->recorder->read_inc("appid"),
"access_token" => $access_token,
"openid" => $openid
);
}
//初始化api_map
/*
* 加#表示非必须,无则不传入url(url中不会出现该参数), "key" => "val" 表示key如果没有定义则使用默认值val
* 规则 array( baseUrl, argListArr, method)
*/
$this->api_map = array(
//qzone
"add_blog" => array(
"https://graph.qq.com/blog/add_one_blog",
array("title", "format" => "json", "content" => null),
"POST"
),
"add_topic" => array(
"https://graph.qq.com/shuoshuo/add_topic",
array("richtype","richval","con","#lbs_nm","#lbs_x","#lbs_y","format" => "json", "#third_source"),
"POST"
),
"get_user_info" => array(
"https://graph.qq.com/user/get_user_info",
array("format" => "json"),
"GET"
),
"add_one_blog" => array(
"https://graph.qq.com/blog/add_one_blog",
array("title", "content", "format" => "json"),
"GET"
),
"add_album" => array(
"https://graph.qq.com/photo/add_album",
array("albumname", "#albumdesc", "#priv", "format" => "json"),
"POST"
),
"upload_pic" => array(
"https://graph.qq.com/photo/upload_pic",
array("picture", "#photodesc", "#title", "#albumid", "#mobile", "#x", "#y", "#needfeed", "#successnum", "#picnum", "format" => "json"),
"POST"
),
"list_album" => array(
"https://graph.qq.com/photo/list_album",
array("format" => "json")
),
"add_share" => array(
"https://graph.qq.com/share/add_share",
array("title", "url", "#comment","#summary","#images","format" => "json","#type","#playurl","#nswb","site","fromurl"),
"POST"
),
"check_page_fans" => array(
"https://graph.qq.com/user/check_page_fans",
array("page_id" => "314416946","format" => "json")
),
//wblog
"add_t" => array(
"https://graph.qq.com/t/add_t",
array("format" => "json", "content","#clientip","#longitude","#compatibleflag"),
"POST"
),
"add_pic_t" => array(
"https://graph.qq.com/t/add_pic_t",
array("content", "pic", "format" => "json", "#clientip", "#longitude", "#latitude", "#syncflag", "#compatiblefalg"),
"POST"
),
"del_t" => array(
"https://graph.qq.com/t/del_t",
array("id", "format" => "json"),
"POST"
),
"get_repost_list" => array(
"https://graph.qq.com/t/get_repost_list",
array("flag", "rootid", "pageflag", "pagetime", "reqnum", "twitterid", "format" => "json")
),
"get_info" => array(
"https://graph.qq.com/user/get_info",
array("format" => "json")
),
"get_other_info" => array(
"https://graph.qq.com/user/get_other_info",
array("format" => "json", "#name", "fopenid")
),
"get_fanslist" => array(
"https://graph.qq.com/relation/get_fanslist",
array("format" => "json", "reqnum", "startindex", "#mode", "#install", "#sex")
),
"get_idollist" => array(
"https://graph.qq.com/relation/get_idollist",
array("format" => "json", "reqnum", "startindex", "#mode", "#install")
),
"add_idol" => array(
"https://graph.qq.com/relation/add_idol",
array("format" => "json", "#name-1", "#fopenids-1"),
"POST"
),
"del_idol" => array(
"https://graph.qq.com/relation/del_idol",
array("format" => "json", "#name-1", "#fopenid-1"),
"POST"
),
//pay
"get_tenpay_addr" => array(
"https://graph.qq.com/cft_info/get_tenpay_addr",
array("ver" => 1,"limit" => 5,"offset" => 0,"format" => "json")
)
);
}
// --------------------------------------------------------------------
/**
* 调用相应api
*
* @access private
*/
private function _apply_api ($arr, $args_list, $base_url, $method){
$pre = "#";
$keys_arr = $this->keys_arr;
$option_arg_list = array();//一些多项选填参数必选一的情形
foreach($args_list as $key => $val)
{
$tmp_key = $key;
$tmp_val = $val;
if(!is_string($key))
{
$tmp_key = $val;
if(strpos($val,$pre) === 0)
{
$tmp_val = $pre;
$tmp_key = substr($tmp_key,1);
if(preg_match("/-(\d$)/", $tmp_key, $res))
{
$tmp_key = str_replace($res[0], "", $tmp_key);
$option_arg_list[$res[1]][] = $tmp_key;
}
}else
{
$tmp_val = null;
}
}
//-----如果没有设置相应的参数
if(!isset($arr[$tmp_key]) || $arr[$tmp_key] === "")
{
if($tmp_val == $pre)
{//则使用默认的值
continue;
}
else if($tmp_val)
{
$arr[$tmp_key] = $tmp_val;
}
else
{
if($v = $_FILES[$tmp_key])
{
$filename = dirname($v['tmp_name'])."/".$v['name'];
move_uploaded_file($v['tmp_name'], $filename);
$arr[$tmp_key] = "@$filename";
}
else
{
$this->error->show_error("api调用参数错误","未传入参数$tmp_key");
}
}
}
$keys_arr[$tmp_key] = $arr[$tmp_key];
}
//检查选填参数必填一的情形
foreach($option_arg_list as $val)
{
$n = 0;
foreach($val as $v)
{
if(in_array($v, array_keys($keys_arr)))
{
$n ++;
}
}
if( ! $n)
{
$str = implode(",",$val);
$this->error->showError("api调用参数错误",$str."必填一个");
}
}
if($method == "POST")
{
if($base_url == "https://graph.qq.com/blog/add_one_blog") $response = $this->url_utils->post($base_url, $keys_arr, 1);
else $response = $this->url_utils->post($base_url, $keys_arr, 0);
}else if($method == "GET"){
$response = $this->url_utils->get($base_url, $keys_arr);
}
return $response;
}
// --------------------------------------------------------------------
/**
* 魔术方法,做api调用转发
*
* @access public
* @param string $name 调用的方法名称
* @param array $arg 参数列表数组
* @return array 返加调用结果数组
*/
public function __call ($name,$arg)
{
//如果api_map不存在相应的api
if(empty($this->api_map[$name])){
$this->error->show_error("api调用名称错误","不存在的API: <span style='color:red;'>{$name}</span>");
}
//从api_map获取api相应参数
$base_url = $this->api_map[$name][0];
$args_list = $this->api_map[$name][1];
$method = isset($this->api_map[$name][2]) ? $this->api_map[$name][2] : "GET";
if(empty($arg)){
$arg[0] = null;
}
//对于get_tenpay_addr,特殊处理,php json_decode对\xA312此类字符支持不好
if($name != "get_tenpay_addr"){
$response = json_decode($this->_apply_api ($arg[0], $args_list, $base_url, $method));
$response_arr = $this->obj_to_arr($response);
}else{
$response_arr = $this->simple_json_parser($this->_apply_api ($arg[0], $args_list, $base_url, $method));
}
//检查返回ret判断api是否成功调用
if($response_arr['ret'] == 0){
return $response_arr;
}else{
$this->error->show_error($response->ret, $response->msg);
}
}
// --------------------------------------------------------------------
/**
*
* php 对象到数组转换
*
* @access private
*/
private function obj_to_arr ($obj)
{
if(!is_object($obj) && !is_array($obj)) {
return $obj;
}
$arr = array();
foreach($obj as $k => $v){
$arr[$k] = $this->obj_to_arr($v);
}
return $arr;
}
// --------------------------------------------------------------------
/**
*
* 获得access_token
*
* @access public
* @param void
* @return string 返加access_token
*/
public function get_access_token ()
{
return $this->recorder->read("access_token");
}
// --------------------------------------------------------------------
/**
*
* 简单实现json到php数组转换功能
*
* @access private
*/
private function simple_json_parser ($json)
{
$json = str_replace("{","",str_replace("}","", $json));
$json_value = explode(",", $json);
$arr = array();
foreach($json_value as $v){
$j_value = explode(":", $v);
$arr[str_replace('"',"", $j_value[0])] = (str_replace('"', "", $j_value[1]));
}
return $arr;
}
}
// Qc类结束
// --------------------------------------------------------------------
/* Qc.php文件结束 */
/* 位置: ./application/libraries/Qc.php */
复制代码PHP复制代码
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/*
* QQ API配置
*/
$config['qc']['appid'] = '';
$config['qc']['appkey'] = '';
$config['qc']['callback'] = 'www.example.com/oauth/callback/';
$config['qc']['scope'] = 'get_user_info,add_share,list_album,add_album,upload_pic,add_topic,add_one_blog,add_weibo,check_page_fans,add_t,add_pic_t,del_t,get_repost_list,get_info,get_other_info,get_fanslist,get_idolist,add_idol,del_idol,get_tenpay_addr';
$config['qc']['error_report'] = TRUE;
$config['qc']['storage_type'] = 'file';
$config['qc']['host'] = 'localhost';
$config['qc']['user'] = 'root';
$config['qc']['password'] = 'root';
$config['qc']['database'] = 'test';
/* qc.php文件结束 */
/* 位置: ./application/config/qc.php */
复制代码
主要改进:
1.不再依赖短标记。
2.静态配置。
3.用CI的session类替代php的session。
使用方法:
将第一段代码保存为Qc.php文件,放于./application/libraries文件夹中;将第二段代码保存为qc.php文件,放于./application/config/文件夹中。然后使用其他类库一样使用即可。未改变任何api,可以参考【QQ登录】API文档。
新手上路,多多指教。本代码会随qq官方php sdk持续更新,欢迎关注。
【注:以上代码的正确性和可用性已经楼主严格测试,如有错误,请不吝赐教。】 |
评分
-
查看全部评分
|