• 微信公众号


    <?php
    /**
    * 微信公共平台整合库
    * @author Ligboy (ligboy@gmail.com)
    * @license 本库的很多思路来自于网上的其他热心人士的贡献,大家任意使用,我本人放弃所有权利,如果您心情好,给我留个署名也行。
    *
    */
    class Wechat {
    /* 配置参数 */
    /**
    *
    * @var array
    * @example array('token'=>'微信接口密钥','account'=>'微信公共平台账号','password'=>'微信公共平台密码','webtoken'=>"微信公共平台网页url的token");
    */
    private $wechatOptions=array('token'=>'rqerwer','account'=>'ligboy@gmail.com','password'=>'wwwwww'); //
    private $cookiefilepath = ""; //以文件形式保存cookie的保存目录,肯定是可写的
    public $webtoken = '742432903';
    private $webtokenStoragefile = ""; //微信公共平台的token存储文件,就是公共平后台网页的token
    public $debug = true; //调试开关
    public $protocol = "https"; //使用协议类型 http or https

    /* 静态常量 */
    const MSGTYPE_TEXT = 'text';
    const MSGTYPE_IMAGE = 'image';
    const MSGTYPE_LOCATION = 'location';
    const MSGTYPE_LINK = 'link';
    const MSGTYPE_EVENT = 'event';
    const MSGTYPE_MUSIC = 'music';
    const MSGTYPE_NEWS = 'news';
    const MSGTYPE_VOICE = 'voice';

    /* 私有参数 */
    private $_msg;
    private $_funcflag = false;
    private $_receive;
    private $_logcallback;
    private $_token;
    private $_cookies;
    private $_tmp1;
    private $_tmp2;



    /**
    * 初始化工作
    * @param array $option array('token'=>'微信接口密钥','account'=>'微信公共平台账号','password'=>'微信公共平台密码');
    */
    function __construct($option=array())
    {
    if (!empty($option))
    {
    $this->wechatOptions = array_merge($this->wechatOptions, $option);
    }
    $this->setCookiefilepath("./app/Runtime", "wechatcookies".md5($this->wechatOptions['account']).".txt");
    if ($this->webtokenStoragefile) {
    $this->webtoken = (string)file_get_contents($this->webtokenStoragefile);
    }


    }

    /**
    * 验证请求签名操作
    * @return boolean
    */
    private function checkSignature()
    {
    $signature = $_GET["signature"];
    $timestamp = $_GET["timestamp"];
    $nonce = $_GET["nonce"];

    $token = $this->wechatOptions['token'];
    $tmpArr = array($token, $timestamp, $nonce);
    sort($tmpArr);
    $tmpStr = implode( $tmpArr );
    $tmpStr = sha1( $tmpStr );

    if( $tmpStr == $signature )
    {
    return true;
    }
    else
    {
    return false;
    }
    }

    /**
    * 验证当前请求是否有效
    * @param bool $return 是否返回
    */
    public function valid($return=false)
    {
    $echoStr = isset($_GET["echostr"]) ? $_GET["echostr"]: '';
    if ($return)
    {
    if ($echoStr)
    {
    if ($this->checkSignature())
    {
    return $echoStr;
    }
    else
    {
    return false;
    }
    } else
    return $this->checkSignature();
    }
    else
    {
    if ($echoStr)
    {
    if ($this->checkSignature())
    {
    die($echoStr);
    }
    else
    {
    die('no access');
    }
    }
    else
    {
    if ($this->checkSignature())
    {
    return true;
    }
    else
    {
    die('no access');
    }
    }
    }
    return false;
    }


    /**
    * 设置发送消息
    * @param array $msg 消息数组
    * @param bool $append 是否在原消息数组追加
    */
    public function Message($msg = '',$append = false){
    if (is_null($msg)) {
    $this->_msg =array();
    }elseif (is_array($msg)) {
    if ($append)
    $this->_msg = array_merge($this->_msg,$msg);
    else
    $this->_msg = $msg;
    return $this->_msg;
    } else {
    return $this->_msg;
    }
    }

    public function setFuncFlag($flag) {
    $this->_funcflag = $flag;
    return $this;
    }

    private function log($log){
    if ($this->debug && function_exists($this->_logcallback)) {
    if (is_array($log)) $log = print_r($log,true);
    return call_user_func($this->_logcallback,$log);
    }
    }

    /**
    * 获取微信服务器发来的信息
    * @return mixed
    */
    public function getRev()
    {
    $postStr = file_get_contents("php://input");
    $this->log($postStr);
    if (!empty($postStr)) {
    $this->_receive = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);

    //debug 调试记录回复信息
    if ($this->debug){file_put_contents($this->debugpath."./rev.txt"," ---".date('Y-m-d H:i:s')." ".print_r($this->_receive, true),FILE_APPEND);}

    }
    return $this;
    }

    /**
    * 获取消息发送者
    * @return string or boolean
    */
    public function getRevFrom() {
    if ($this->_receive)
    return $this->_receive['FromUserName'];
    else
    return false;
    }

    /**
    * 获取消息接受者
    * @return string or boolean
    */
    public function getRevTo() {
    if ($this->_receive)
    return $this->_receive['ToUserName'];
    else
    return false;
    }

    /**
    * 获取接收消息的类型
    */
    public function getRevType() {
    if (isset($this->_receive['MsgType']))
    return $this->_receive['MsgType'];
    else
    return false;
    }

    /**
    * 获取消息ID
    */
    public function getRevID() {
    if (isset($this->_receive['MsgId']))
    return $this->_receive['MsgId'];
    else
    return false;
    }

    /**
    * 获取消息发送时间
    */
    public function getRevCtime() {
    if (isset($this->_receive['CreateTime']))
    return $this->_receive['CreateTime'];
    else
    return false;
    }

    /**
    * 获取接收消息内容正文
    */
    public function getRevContent(){
    if (isset($this->_receive['Content']))
    return $this->_receive['Content'];
    else
    return false;
    }

    /**
    * 获取接收消息图片
    */
    public function getRevPic(){
    if (isset($this->_receive['PicUrl']))
    return $this->_receive['PicUrl'];
    else
    return false;
    }

    /**
    * 获取接收消息链接
    */
    public function getRevLink(){
    if (isset($this->_receive['Url'])){
    return array(
    'url'=>$this->_receive['Url'],
    'title'=>$this->_receive['Title'],
    'description'=>$this->_receive['Description']
    );
    } else
    return false;
    }

    /**
    * 获取接收地理位置
    * @return array('x'=>'','y'=>'','scale'=>'','label'=>'')
    */
    public function getRevGeo(){
    if (isset($this->_receive['Location_X'])){
    return array(
    'x'=>$this->_receive['Location_X'],
    'y'=>$this->_receive['Location_Y'],
    'scale'=>$this->_receive['Scale'],
    'label'=>$this->_receive['Label']
    );
    } else
    return false;
    }

    /**
    * 获取接收事件推送
    * @return array 成功返回事件数组,失败返回false
    */
    public function getRevEvent(){
    if (isset($this->_receive['Event'])){
    return array(
    'event'=>$this->_receive['Event'],
    'key'=>$this->_receive['EventKey'],
    );
    } else
    return false;
    }

    /**
    * 获取接收语言推送
    * @return
    */
    public function getRevVoice(){
    if (isset($this->_receive['MediaId'])){
    return array(
    'mediaid'=>$this->_receive['MediaId'],
    'format'=>$this->_receive['Format'],
    );
    } else
    return false;
    }

    public static function xmlSafeStr($str)
    {
    return '<![CDATA['.preg_replace("/[\x00-\x08\x0b-\x0c\x0e-\x1f]/",'',$str).']]>';
    }

    /**
    * 数据XML编码
    * @param mixed $data 数据
    * @return string
    */
    public static function data_to_xml($data) {
    $xml = '';
    foreach ($data as $key => $val) {
    is_numeric($key) && $key = "item id="$key"";
    $xml .= "<$key>";
    $xml .= ( is_array($val) || is_object($val)) ? self::data_to_xml($val) : self::xmlSafeStr($val);
    list($key, ) = explode(' ', $key);
    $xml .= "</$key>";
    }
    return $xml;
    }

    /**
    * XML编码
    * @param mixed $data 数据
    * @param string $root 根节点名
    * @param string $item 数字索引的子节点名
    * @param string $attr 根节点属性
    * @param string $id 数字索引子节点key转换的属性名
    * @param string $encoding 数据编码
    * @return string
    */
    public function xml_encode($data, $root='xml', $item='item', $attr='', $id='id', $encoding='utf-8') {
    if(is_array($attr)){
    $_attr = array();
    foreach ($attr as $key => $value) {
    $_attr[] = "{$key}="{$value}"";
    }
    $attr = implode(' ', $_attr);
    }
    $attr = trim($attr);
    $attr = empty($attr) ? '' : " {$attr}";
    $xml .= "<{$root}{$attr}>";
    $xml .= self::data_to_xml($data, $item, $id);
    $xml .= "</{$root}>";
    return $xml;
    }

    /**
    * 设置回复消息
    * Examle: $obj->text('hello')->reply();
    * @param string $text
    */
    public function text($text='')
    {
    $FuncFlag = $this->_funcflag ? 1 : 0;
    $msg = array(
    'ToUserName' => $this->getRevFrom(),
    'FromUserName'=>$this->getRevTo(),
    'MsgType'=>self::MSGTYPE_TEXT,
    'Content'=>$text,
    'CreateTime'=>time(),
    'FuncFlag'=>$FuncFlag
    );
    $this->Message($msg);
    return $this;
    }

    /**
    * 设置回复音乐
    * @param string $title
    * @param string $desc
    * @param string $musicurl
    * @param string $hgmusicurl
    */
    public function music($title,$desc,$musicurl,$hgmusicurl='') {
    $FuncFlag = $this->_funcflag ? 1 : 0;
    $msg = array(
    'ToUserName' => $this->getRevFrom(),
    'FromUserName'=>$this->getRevTo(),
    'CreateTime'=>time(),
    'MsgType'=>self::MSGTYPE_MUSIC,
    'Music'=>array(
    'Title'=>$title,
    'Description'=>$desc,
    'MusicUrl'=>$musicurl,
    'HQMusicUrl'=>$hgmusicurl
    ),
    'FuncFlag'=>$FuncFlag
    );
    $this->Message($msg);
    return $this;
    }

    /**
    * 设置回复图文
    * @param array $newsData
    * @example 数组结构:
    * array(
    * [0]=>array(
    * 'Title'=>'msg title',
    * 'Description'=>'summary text',
    * 'PicUrl'=>'http://www.domain.com/1.jpg',
    * 'Url'=>'http://www.domain.com/1.html'
    * ),
    * [1]=>....
    * )
    */
    public function news($newsData=array())
    {
    $FuncFlag = $this->_funcflag ? 1 : 0;
    $count = count($newsData);

    $msg = array(
    'ToUserName' => $this->getRevFrom(),
    'FromUserName'=>$this->getRevTo(),
    'MsgType'=>self::MSGTYPE_NEWS,
    'CreateTime'=>time(),
    'ArticleCount'=>$count,
    'Articles'=>$newsData,
    'FuncFlag'=>$FuncFlag
    );
    $this->Message($msg);
    return $this;
    }

    /**
    *
    * 向微信服务器回复消息
    * Example: $this->text('msg tips')->reply();
    * @param string $msg 要发送的信息, 默认取$this->_msg
    * @param bool $return 是否返回信息而输出 默认:false
    */
    public function reply($msg=array(),$return = false)
    {
    if (empty($msg))
    {
    $msg = $this->_msg;
    }
    $xmldata= $this->xml_encode($msg);
    $this->log($xmldata);
    if ($return)
    {
    return $xmldata;
    }
    else
    {
    echo $xmldata;
    }
    //debug 调试记录回复信息
    if ($this->debug){file_put_contents($this->debugpath."./reply.txt"," ---".date('Y-m-d H:i:s')." ".$xmldata,FILE_APPEND);}
    }


    /**
    * 登录微信公共平台,获取并保存cookie、webtoken到指定文件
    * @return mixed 成功则返回true,失败则返回失败
    */
    public function login(){
    $url = $this->protocol."://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN";
    $postfields["username"] = $this->wechatOptions['account'];
    $postfields["pwd"] = md5($this->wechatOptions['password']);
    $postfields["f"] = "json";
    $postfieldss = "username=".urlencode($this->wechatOptions['account'])."&pwd=".urlencode(md5($this->wechatOptions['password']))."&f=json";
    $response = $this->post($url, $postfields, $this->protocol."://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN");
    $result = json_decode($response, true);
    if ($result['ErrCode']=="65201"||$result['ErrCode']=="65202"||$result['ErrCode']=="0")
    {
    preg_match('/&token=([d]+)/i', $result['ErrMsg'],$match);
    file_put_contents($this->webtokenStoragefile, $match[1]);
    $this->webtoken = $match[1];
    return true;
    }
    else
    {
    unlink($this->cookiefilepath);
    return false;
    // return $result['ErrCode'];
    }
    }

    /**
    * 读取缓存的cookies文件
    * @param string $filename 文件名
    * @param string $content 文件内容
    * @return [type] [description]
    */
    public function readFileCookies(){

    if(file_exists($this->cookiefilepath)){
    $url = $this->protocol."://mp.weixin.qq.com/cgi-bin/getregions?id=1054&t=ajax-getregions&lang=zh_CN&token=".$this->webtoken;
    $response = $this->get($url, $this->protocol."://mp.weixin.qq.com/");
    $result = json_decode($response,true);
    if($result['num'])
    {
    return true;
    }
    else
    {
    return true===$this->login();
    }
    }
    else
    {
    return true===$this->login();
    }
    }

    /**
    * 验证cookie的有效性
    * @return boolean
    */
    public function checkValid()
    {
    $postfields = array();
    $url = $this->protocol.":https://mp.weixin.qq.com/cgi-bin/getregions?id=1054&t=ajax-getregions&lang=zh_CN&token=".$this->webtoken;
    //判断cookie是否为空,为空的话自动执行登录
    if (file_exists($this->cookiefilepath))
    {
    $response = $this->get($url, $this->protocol."://mp.weixin.qq.com/cgi-bin/userinfopage?t=wxm-setting&token=383506232&lang=zh_CN");
    $result = json_decode($response,1);
    if(isset($result['num'])){
    return true;
    }else{
    return false;
    }
    }
    else
    {
    return false;
    }
    }

    /**
    * 主动单条发消息
    * @param string $id 用户的fakeid
    * @param string $content 发送的内容
    * @return boolean 返回发送结果:成功返回:1,登录问题返回:-1,其他原因返回:0
    */
    public function send($fakeid,$content)
    {
    //判断cookie是否为空,为空的话自动执行登录
    if (file_exists($this->cookiefilepath)||true===$this->login())
    {
    $postfields = array();
    $postfields['tofakeid'] = $fakeid;
    $postfields['type'] = 1;
    $postfields['error']= "false";
    $postfields['token']= $this->webtoken;
    $postfields['content'] = $content;
    $postfields['ajax'] = 1;
    $url = $this->protocol."://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response";
    $response = $this->post($url, $postfields, $this->protocol."://mp.weixin.qq.com/");
    $tmp = json_decode($response,true);
    //判断发送结果的逻辑部分
    if ('ok'==$tmp["msg"]) {
    return 1;
    }
    elseif ($tmp['ret']=="-2000")
    {
    return -1;
    }
    else
    {
    return false;
    }
    }
    else //登录失败返回false
    {
    return false;
    }
    }

    /**
    * 主动批量发送,目前暂支持文本方式
    * @param array $fakeidGroup 接受微信fakeid集合数组
    * @param [type] $content [description]
    * @return mixed 如果所有都发送失败,返回false,否则,返回一个数组分别记录成功的列表
    * 这里需要注意请求耗时问题,目前采用curl并发性请求,并发请求数10个
    */
    public function batSend($fakeidGroup,$content)
    {
    $result = array();
    $successCount = 0;
    $requestArray = array();
    foreach ($fakeidGroup as $key =>$value)
    {
    $postfields = array();
    $postfields['tofakeid'] = $value;
    $postfields['type'] = 1;
    $postfields['error']= "false";
    $postfields['token']= $this->webtoken;
    $postfields['content'] = $content;
    $postfields['ajax'] = 1;
    $url = $this->protocol."://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response";
    $requestArray[] = array('url'=>$url,'method'=>'post','postfields'=>$postfields,'referer'=>$this->protocol."://mp.weixin.qq.com/",'cookiefilepath'=>$this->cookiefilepath);
    }
    //DEBUG:
    // dump($requestArray);
    $callback = function ($result, $key){
    $tmp = json_decode($result,true);
    //判断发送结果的逻辑部分
    if ('ok'==$tmp["msg"]) {
    return 1;
    }
    elseif ($tmp['ret']=="-2000")
    {
    return -1;
    }
    else
    {
    return 0;
    }
    };
    $rollingCurlObj = new Rollingcurl();
    $response = $rollingCurlObj->setCallback($callback)->request($requestArray);
    return $response;
    }

    /**
    * 获取用户的信息
    * @param string $fakeid 用户的fakeid
    * @return mixed 如果成功获取返回数据数组,登录问题返回false,其他未知问题返回true,
    */
    public function getContactInfo($fakeid)
    {
    if (file_exists($this->cookiefilepath)||true===$this->login())
    {
    $url = $this->protocol."://mp.weixin.qq.com/cgi-bin/getcontactinfo?t=ajax-getcontactinfo&lang=zh_CN&fakeid=".$fakeid;
    $response = $this->get($url, $this->protocol."://mp.weixin.qq.com/");
    $result = json_decode($response,1);
    if($result['FakeId']){
    return $result;
    }
    elseif ($result['ret'])
    {
    return false;
    }
    else
    {
    return true;
    }
    }
    else
    {
    return false;
    }
    }


    public function getfriendlist($groupid=0)
    {
    $url = $this->protocol."://mp.weixin.qq.com/cgi-bin/contactmanagepage?token=$this->webtoken&t=wxm-friend&pagesize=100&groupid=$groupid";
    $referer = $this->protocol."://mp.weixin.qq.com/";
    $response = $this->get($url, $referer);
    $tmp = "";
    if (preg_match('%<script id="json-friendList" type="json/text">([sS]*?)</script>%', $response, $match)) {
    $tmp = json_decode($match[1], true);
    }
    return empty($tmp)?false:$tmp;

    }


    public function getfakeid($callback)
    {
    $subscribeusersModel = D("Subscribeusers");
    $data = $subscribeusersModel->where(' `fakeid` IS NULL and `unsubscribed`=0')->select();
    if (!is_array($data))
    {
    die("none data");
    }
    $unfriendList = $this->getfriendlist(0);
    if (!$unfriendList){
    die("none friendlist");
    }
    $requestArray = array();
    foreach ($unfriendList as $key => $value)
    {
    // $requestArray[$key]['postfields']['createtime'] = time()-60000;
    $requestArray[$key]['postfields']['fromfakeid'] = $value['fakeId'];
    $requestArray[$key]['postfields']['opcode'] = 1;
    // $requestArray[$key]['postfields']['lastmsgid'] = 0;
    // $requestArray[$key]['lastmsgfromfakeid']['2399396672'] = 2399396672;
    $requestArray[$key]['postfields']['token'] = $this->webtoken;
    $requestArray[$key]['postfields']['ajax'] = 1;
    $requestArray[$key]['referer'] = $this->protocol."://mp.weixin.qq.com/";
    $requestArray[$key]['cookiefilepath'] = $this->cookiefilepath;
    $requestArray[$key]['method'] = "post";
    $requestArray[$key]['url'] = $this->protocol."://mp.weixin.qq.com/cgi-bin/singlemsgpage?t=ajax-single-getnewmsg";
    }
    // $callback = '';
    $rollingCurlObj = new Rollingcurl();
    $rollingCurlObj->setOtherCallbackArg(array('data'=>$data, 'wechatObj'=>$this));
    $response = $rollingCurlObj->setCallback($callback)->request($requestArray);
    dump($response);
    }

    /**
    * 将用户放入制定的分组
    * @param array $fakeidsList
    * @param string $groupid
    * @return boolean 放入是否成功
    */
    public function putIntoGroup($fakeidsList, $groupid)
    {
    $fakeidsListString = "";
    if(is_array($fakeidsList))
    {
    foreach ($fakeidsList as $value)
    {
    $fakeidsListString .= $value."|";
    }
    }
    else
    {
    $fakeidsListString = $fakeidsList;
    }
    $postfields['contacttype'] = $groupid;
    $postfields['tofakeidlist'] = $fakeidsListString;
    $postfields['token'] = $this->webtoken;
    $postfields['ajax'] = 1;
    $referer = $this->protocol."://mp.weixin.qq.com/";
    $url = $this->protocol."://mp.weixin.qq.com/cgi-bin/modifycontacts?action=modifycontacts&t=ajax-putinto-group";
    $response = $this->post($url, $postfields, $referer);
    $tmp = json_decode($response, true);
    $result = $tmp['ret']=="0"&&!empty($tmp)?true:false;
    return $result;
    }


    /**
    * @return the $wechatOptions
    */
    public function getWechatOptions() {
    return $this->wechatOptions;
    }

    /**
    * @return the $cookiestoragemode
    */
    public final function getCookiestoragemode() {
    return $this->cookiestoragemode;
    }

    /**
    * @return the $cookiefilepath
    */
    public final function getCookiefilepath() {
    return $this->cookiefilepath;
    }

    /**
    * @return the $debug
    */
    public function getDebug() {
    return $this->debug;
    }

    /**
    * 设置webtoken的保存文件路径和文件名
    * @return the $webtokenStoragefile
    */
    public function getWebtokenStoragefile() {
    return $this->webtokenStoragefile;
    }

    /**
    * 设置webtoken的保存文件路径和文件名
    * @param string $webtokenStoragefile
    */
    public function setWebtokenStoragefile($webtokenStoragefile) {
    $this->webtokenStoragefile = $webtokenStoragefile;
    $this->webtoken = (string)file_get_contents($this->webtokenStoragefile);
    }

    /**
    * 设置微信配置信息
    * @param multitype:string $wechatOptions
    */
    public function setWechatOptions($wechatOptions) {
    $this->wechatOptions = array_merge($this->wechatOptions, $wechatOptions);
    }

    /**
    * 设置cookie文件保存位置
    * @param string $cookiefilepath cookie保存路径
    * @param string $cookiefilename 默认:wechatcookies".md5($this->wechatOptions['account']).".txt"
    */
    public function setCookiefilepath($cookiefilepath, $cookiefilename = "") {
    $this->cookiefilepath = $cookiefilepath.(substr($cookiefilepath, -1, 1)=="/"?"":"/").(is_null($cookiefilename))?"wechatcookies".md5($this->wechatOptions['account']).".txt":$cookiefilename;
    }

    /**
    * @param boolean $debug
    */
    public function setDebug($debug) {
    $this->debug = $debug;
    }

    private function post($url, $postfields, $referer)
    {
    $ch = curl_init($url);
    $options = array(
    CURLOPT_RETURNTRANSFER => true, // return web page
    CURLOPT_HEADER => false, // don't return headers
    CURLOPT_FOLLOWLOCATION => true, // follow redirects
    CURLOPT_ENCODING => "", // handle all encodings
    CURLOPT_USERAGENT => "", // who am i
    CURLOPT_AUTOREFERER => true, // set referer on redirect
    CURLOPT_CONNECTTIMEOUT => 120, // timeout on connect
    CURLOPT_TIMEOUT => 120, // timeout on response
    CURLOPT_MAXREDIRS => 10, // stop after 10 redirects
    CURLOPT_POST => true, // i am sending post data
    CURLOPT_POSTFIELDS => $postfields, // this are my post vars
    CURLOPT_SSL_VERIFYHOST => 0, // don't verify ssl
    CURLOPT_SSL_VERIFYPEER => false, //
    CURLOPT_COOKIEFILE =>$this->cookiefilepath,
    CURLOPT_COOKIEJAR =>$this->cookiefilepath,
    CURLOPT_REFERER =>$referer,
    );
    curl_setopt_array($ch, $options);
    $result = curl_exec($ch);
    curl_close($ch);
    return $result;
    }

    private function get($url, $referer)
    {
    $info = null;
    $ch = curl_init($url);
    $options = array(
    CURLOPT_RETURNTRANSFER => true, // return web page
    CURLOPT_HEADER => false, // don't return headers
    CURLOPT_FOLLOWLOCATION => true, // follow redirects
    CURLOPT_ENCODING => "", // handle all encodings
    CURLOPT_USERAGENT => "", // who am i
    CURLOPT_AUTOREFERER => true, // set referer on redirect
    CURLOPT_CONNECTTIMEOUT => 120, // timeout on connect
    CURLOPT_TIMEOUT => 120, // timeout on response
    CURLOPT_MAXREDIRS => 10, // stop after 10 redirects
    CURLOPT_SSL_VERIFYHOST => 0, // don't verify ssl
    CURLOPT_SSL_VERIFYPEER => false, //
    CURLOPT_COOKIEFILE =>$this->cookiefilepath,
    CURLOPT_COOKIEJAR =>$this->cookiefilepath,
    CURLOPT_REFERER =>$referer,
    );
    curl_setopt_array($ch, $options);
    $result = curl_exec($ch);
    curl_close($ch);
    return $result;
    }
    }


    /**
    * Rolling Curl Request Class
    * @author Ligboy (ligboy@gamil.com)
    * @copyright
    * @example
    *
    *
    */
    class Rollingcurl {
    private $limitCount = 10; //并发请求数量
    public $returninfoswitch = false; //是否返回请求信息,开启后单项请求返回结果为:array('info'=>请求信息, 'result'=>返回内容, 'error'=>错误信息)


    //私有属性
    private $queue = null;
    private $_requstItems = null;
    private $_callback = null;
    private $_otherCallbackArg = null;
    private $_options = array(
    CURLOPT_RETURNTRANSFER => true, // return web page
    CURLOPT_HEADER => false, // don't return headers
    CURLOPT_FOLLOWLOCATION => true, // follow redirects
    CURLOPT_NOSIGNAL =>true,
    CURLOPT_TIMEOUT =>true,
    CURLOPT_ENCODING => "", // handle all encodings
    CURLOPT_USERAGENT => "", // who am i
    CURLOPT_AUTOREFERER => true, // set referer on redirect
    CURLOPT_CONNECTTIMEOUT => 120, // timeout on connect
    CURLOPT_TIMEOUT => 120, // timeout on response
    CURLOPT_MAXREDIRS => 10, // stop after 10 redirects
    CURLOPT_SSL_VERIFYHOST => 0, // don't verify ssl
    CURLOPT_SSL_VERIFYPEER => false, //
    );


    /**
    *
    * @param array $options [可选的]公共的Curl请求参数
    */
    function __construct($options = array()) {
    $this->queue = curl_multi_init();
    if ($options) {
    array_merge($this->_options, $options);
    }
    }


    /**
    * 并发行的curl方法
    * @param unknown $requestArray
    * @param string $callback
    * @return multitype:multitype:
    */
    function request($requestArray, $callback="")
    {
    $this->_requstItems = $requestArray;
    $requestArrayKeys = array_keys($requestArray);
    /* $requestArray = array(
    array(
    'url' => "",
    'method' => "post",
    'postfields' => array(),
    'cookiefilepath' => "",
    'cookiefilepath' => "",
    'referer' => "",
    ),
    array(
    'url' => "",
    'postfields' => array(),
    'cookiefilepath' => "",
    'cookiefilepath' => "",
    'referer' => "",
    ),
    ); */
    $this->queue = curl_multi_init();
    $map = array();
    for ($i=0;$i<$this->limitCount && !empty($requestArrayKeys);$i++)
    {
    $keyvalue = array_shift($requestArrayKeys);
    $this->addToQueue ( $requestArray, $keyvalue, $map );

    }

    $responses = array();
    do {
    while (($code = curl_multi_exec($this->queue, $active)) == CURLM_CALL_MULTI_PERFORM) ;

    if ($code != CURLM_OK) { break; }

    // 找到刚刚完成的任务句柄
    while ($done = curl_multi_info_read($this->queue)) {
    // 处理当前句柄的信息、错误、和返回内容
    $info = curl_getinfo($done['handle']);
    $error = curl_error($done['handle']);
    if (is_callable($this->_callback))
    {
    $result = $this->callback ( $requestArray, $map, $done, $this->_otherCallbackArg );

    }
    else
    {
    //如果callback为空,直接返回内容
    $result = curl_multi_getcontent($done['handle']);
    }
    if ($this->returninfoswitch) {
    $responses[$map[(string) $done['handle']]] = compact('info', 'error', 'result');
    }
    else
    {
    $responses[$map[(string) $done['handle']]] = $result;
    }

    // 从队列里移除上面完成处理的句柄
    curl_multi_remove_handle($this->queue, $done['handle']);
    curl_close($done['handle']);
    if (!empty($requestArrayKeys))
    {
    $addkey = array_shift($requestArrayKeys);
    $this->addToQueue ( $requestArray, $addkey, $map );
    }
    }

    // Block for data in / output; error handling is done by curl_multi_exec
    if ($active > 0) {
    curl_multi_select($this->queue, 0.5);
    }

    } while ($active);

    curl_multi_close($this->queue);
    return $responses;
    }
    /**
    * @param requestArray
    * @param map
    * @param done
    */private function callback($requestArray, $map, $done, $otherCallbackArg) {
    //调用callback函数处理当前句柄的返回内容,callback函数参数有:(返回内容, 队列id)
    $result = call_user_func($this->_callback, curl_multi_getcontent($done['handle']), $map[(string) $done['handle']], $requestArray[$map[(string) $done['handle']]], $otherCallbackArg );
    return $result;
    }

    /**
    * @param requestArray
    * @param map
    * @param keyvalue
    */private function addToQueue($requestArray, $keyvalue, &$map) {
    $ch = curl_init();
    curl_setopt_array($ch, $this->_options);
    //检查提交方式,并设置对应的设置,为空的话默认采用get方式
    if ("post" === $requestArray[$keyvalue]['method'])
    {
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $requestArray[$keyvalue]['postfields']);
    }
    else
    {
    curl_setopt($ch, CURLOPT_HTTPGET, true);
    }

    //设置cookie保存文件路径
    if (!empty($requestArray[$keyvalue]['cookiefilepath']))
    {
    //如果这个文件存在,则采用采用此文件配置cookie
    if (file_exists($requestArray[$keyvalue]['cookiefilepath']))
    {
    curl_setopt($ch, CURLOPT_COOKIEFILE, $requestArray[$keyvalue]['cookiefilepath']);
    }
    curl_setopt($ch, CURLOPT_COOKIEJAR, $requestArray[$keyvalue]['cookiefilepath']);
    }
    ////直接设定cookie。多个cookie用分号分隔,分号后带一个空格(例如, "username=ligboy; password=123456; ")。
    if (!empty($requestArray[$keyvalue]['cookie']))
    {
    curl_setopt($ch, CURLOPT_COOKIE, $requestArray[$keyvalue]['cookie']);
    }
    curl_setopt($ch, CURLOPT_URL, $requestArray[$keyvalue]['url']);
    curl_setopt($ch, CURLOPT_REFERER, $requestArray[$keyvalue]['referer']);
    curl_multi_add_handle($this->queue, $ch);
    $map[(string) $ch] = $keyvalue;
    }

    /**
    * @return the $limitCount
    */
    public function getLimitCount() {
    return $this->limitCount;
    }

    /**
    * 设置并发性请求数量
    * @param number $limitCount
    */
    public function setLimitCount($limitCount) {
    $this->limitCount = $limitCount;
    return $this;
    }

    /**
    * 设置回调函数
    * @param field_type $_callback
    */
    public function setCallback($_callback) {
    $this->_callback = $_callback;
    return $this;
    }
    /**
    * @return the $_otherCallbackArg
    */
    public function getOtherCallbackArg() {
    return $this->_otherCallbackArg;
    }

    /**
    * @param field_type $_otherCallbackArg
    */
    public function setOtherCallbackArg($_otherCallbackArg) {
    $this->_otherCallbackArg = $_otherCallbackArg;
    }


  • 相关阅读:
    ThinkPHP6.0.5 验证码 遇到的坑 (验证失败)
    css已知宽高和未知宽高的居中定位
    有关CLR的初学小整理(可能理解不深刻,望大牛指出)
    关于CRC循环冗余校验的总结(C#)
    lesson 4 再谈继承多态,抽象类和接口
    Lesson 3
    Lesson 2
    lesson 1
    08_Spring实现action调用service,service调用dao的过程
    07_配置文件中决定接口的实现类
  • 原文地址:https://www.cnblogs.com/luyu521/p/6655797.html
Copyright © 2020-2023  润新知