用户
 找回密码
 入住 CI 中国社区
搜索
查看: 20122|回复: 13
收起左侧

[库 Library] 【0.1.0】QQ PHP SDK 2.0.0 For CodeIgniter 2.1.3

[复制链接]
发表于 2013-4-10 18:43:08 | 显示全部楼层 |阅读模式
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持续更新,欢迎关注。
【注:以上代码的正确性和可用性已经楼主严格测试,如有错误,请不吝赐教。】

评分

参与人数 1威望 +5 收起 理由
Hex + 5 赞一个!

查看全部评分

发表于 2013-4-29 20:23:51 | 显示全部楼层
收藏了。楼主好人
 楼主| 发表于 2013-4-30 16:27:09 | 显示全部楼层
zybo 发表于 2013-4-29 20:23
收藏了。楼主好人

与君共勉
发表于 2014-4-29 12:21:32 | 显示全部楼层
好东西啊!必须收藏
 楼主| 发表于 2013-4-10 18:49:54 | 显示全部楼层
源代码贴到网页后被错误转义了,我还是把源代码打包上传吧。
发表于 2013-4-10 19:08:02 | 显示全部楼层
太强大了!我帮你改改转义成表情的问题~
 楼主| 发表于 2013-4-10 22:55:41 | 显示全部楼层
Hex 发表于 2013-4-10 19:08
太强大了!我帮你改改转义成表情的问题~

谢谢,其实并不复杂
发表于 2013-4-11 09:44:27 | 显示全部楼层
{:soso_e179:}
 楼主| 发表于 2013-4-11 10:01:57 | 显示全部楼层
shibazizhan 发表于 2013-4-11 09:44

I see YOU
发表于 2013-4-12 22:35:27 | 显示全部楼层
Mark一下
发表于 2013-4-27 12:35:22 | 显示全部楼层
这个要赞!

本版积分规则