* Notice: 在推送当msg_type为1的时候, 尽量使msg以array方式进行传递, 便于进行参数检查. * 其中一个必要的检查内容是, msg体必须包含description, 在使用string进行传递时, 方法内部将无法进行这些检查,只能等待服务器检查后返回错误 * * * @method boolean pushMsgToSingleDevice(string $channel_id, string|array $msg, array $opts) * 单播, 推送一条消息到指定设备
* * $channel_id 目标客户端设备所属的唯一id;
* $msg 消息内容.可参见消息体格式说明 当消息体类型为一个array时,将自动调用json_encode方法来将array转换为一个string;
* $opts 可选参数列表, 包含 msg_type, msg_expires, deploy_status, topic_id几项可选内容;
* * @method boolean pushMsgToAll(string|array $msg, array $opts) * 广播, 推送一条消息到全部设备 * * $msg 当消息体类型为一个array时,将自动调用json_encode方法来将array转换为一个string;
* $opts 可选参数, 包含 msg_type, msg_expires, deploy_status几项可选内容;
* * @method boolean pushMsgToTag(string $tag, string|array $msg,array $opts) * 组播, 推送一条消息到一组设备. * * $tag 组名, * $msg 消息内容.可参见消息体格式说明 当消息体类型为一个array时,将自动调用json_encode方法来将array转换为一个string;
* $opts 可选参数, 包含 msg_type, msg_expires, deploy_status几项可选内容;
* * @method boolean pushToSmartTag(string $type, string|array $msg, array $opts) * 组播, 根据$type类型,向指定标签组推送消息 * * $type 标签组类型
* $msg 消息内容.可参见消息体格式说明 当消息体类型为一个array时,将自动调用json_encode方法来将array转换为一个string;
* $opts 可选参数, 包含 selector, msg_type, msg_expires, deploy_status, send_time几项可选内容;
* * @method boolean pushBatchUniMsg(string $channel_ids, string $msg); * 批量单播, 向指定的一组channel_id发送一条消息. *
* $channel_ids; 一个数组, 每项为一个channel_id
* $msg 消息内容.可参见消息体格式说明 当消息体类型为一个array时,将自动调用json_encode方法来将array转换为一个string;
* $opts 可选参数, 包含 * * @method boolean queryMsgStatus(string $msg_id); * 查询消息发送状态 *
* $msg_id 在发送时, 由服务端返回的 msg_id * * @method boolean queryTimerRecords(string $timer_id, array $opts); * 根扰timer_id查询一个定时或周期的消息发送记录 *
* $timer_id 在发送消息时,由服务端产生的timer_id * $opts 可选参数, 包含 start, limit, start_time, end_time等几项可选内容;
* * @method boolean queryTopicRecords(string $topic_id, array $opts); * 根扰topic查询一个topic下的消息发送记录 *
* $topic_id 发送消息时指定的topic * $opts 可选参数, 包含 * * @method boolean queryTimerList(array $opts); * 查询当前应用下的定时消息及周期消息. *
* $opts 可选参数, 包含timer_id, start, limit等几项可选内容, 当指定timer_id时, 仅返回目标timer相关的信息. * * @method boolean createTag(string $tag); * 创建一个tag *
* $tag tag名称 * * @method boolean deleteTag(string $tag); * 删除一个tag *
* $tag tag名称 * * @method boolean queryTags(array $opts); * 查询当前应用下的tag列表 *
* $opts 可选参数, 包含tag, start, limit, 当指定tag时, 仅返回目标tag相关信息 * * @method boolean addDevicesToTag(string $tag, string $channel_ids); * 向一个tag下添加设备 *
* $tag tag名称
* $channel_ids 需要添加的一组channel_id * * @method boolean delDevicesFromTag(string $tag, string $channel_ids); * 从一个tag下删除一批设备 *
* $tag;
* $channel_ids 需要删除的一组channel_id * * @method boolean queryDeviceNumInTag(string $tag); * 查询一个tag下的设备数量 *
* $tag tag名称 * */ class PushSDK extends BaseSDK { const VERSION = '3.0.0'; const TAG_TYPE_LBS_LABEL = 2; // LBS 推送 const TAG_TYPE_INTEREST_LABEL = 3; // 兴趣组 const TAG_TYPE_AGE_LABEL = 4; // 年龄组 const TAG_TYPE_SEX_LABEL = 5; // 性别组 const TAG_TYPE_OP_LABEL = 6; // 组合运算 // required必须存在, 如果不存在必要参数. 请留空数组, optional为可选.不需要可以不写. /** * 为兼容php 5.2及5.4,不支持将const写成array. * * 获得一个方法映射关系的array. * @return multitype:multitype:string multitype:string multitype:string multitype: multitype:string multitype:string multitype: multitype:string multitype:string */ private function getMapping(){ /** * restApi 方法映射. * * 每项描述一个method的 包含以下内容: * url: string 方法调用位置 * required: array 必须的参数列表,K有参数名, V为验证条件 * optional: array 可选的参数列表,K有参数名, V为验证条件 * before: string 指定一个预处理方法. 用于在发送http request之前对payload内容进行一次处理. * * @var array */ $apiMapping = array ( 'pushMsgToSingleDevice' => array ( 'url' => '/push/single_device', 'required' => array ( 'channel_id' => self::$fieldIdStr, 'msg' => self::$fieldMsg, ), 'optional' => array ( 'msg_type' => self::$fieldMsgType, 'msg_expires' => self::$fieldMsgExpires, 'deploy_status' => self::$fieldDeployStatus, //'topic_id' => self::$fieldMsgTopic, ), 'before' => 'verifyMessage', ), 'pushMsgToAll' => array ( 'url' => '/push/all', 'required' => array ( 'msg' => self::$fieldMsg, ), 'optional' => array ( 'msg_type' => self::$fieldMsgType, 'msg_expires' => self::$fieldMsgExpires, 'deploy_status' => self::$fieldDeployStatus, 'send_time' => self::$fieldSendTime, ), 'before' => 'verifyMessage', ), 'pushMsgToTag' => array ( 'url' => '/push/tags', 'required' => array ( 'tag' => self::$fieldTagName, 'msg' => self::$fieldMsg, ), 'optional' => array ( 'msg_type' => self::$fieldMsgType, 'msg_expires' => self::$fieldMsgExpires, 'deploy_status' => self::$fieldDeployStatus, 'send_time' => self::$fieldSendTime, ), 'before' => 'verifyGroup', ), 'pushToSmartTag' => array ( 'url' => '/push/tags', 'required' => array ( 'type' => self::$fieldGroupType, 'selector' => self::$fieldGroupSelector, 'msg' => self::$fieldMsg, ), 'optional' => array ( 'msg_type' => self::$fieldMsgType, 'msg_expires' => self::$fieldMsgExpires, 'deploy_status' => self::$fieldDeployStatus, 'send_time' => self::$fieldSendTime, ), 'before' => 'verifyGroup', ), 'pushBatchUniMsg' => array ( 'url' => '/push/batch_device', 'required' => array ( 'channel_ids' => self::$fieldChannelIdList, 'msg' => self::$fieldMsg, ), 'optional' => array ( 'msg_type' => self::$fieldMsgType, 'msg_expires' => self::$fieldMsgExpires, 'deploy_status' => self::$fieldDeployStatus, 'topic_id' => self::$fieldMsgTopic, ), 'before' => 'verifyChannelIds', ), 'queryMsgStatus' => array ( 'url' => '/report/query_msg_status', 'required' => array ( 'msg_id' => self::$fieldIdOrList, ), 'optional' => array( 'statistic_type' => array ( 'type' => 'string'), ), 'before' => 'verifyMsgIds', ), 'queryTimerRecords' => array ( 'url' => '/report/query_timer_records', 'required' => array ( 'timer_id' => self::$fieldTimerId, ), 'optional' => array ( 'start' => self::$fieldPageStart, 'limit' => self::$fieldPageLimit, 'range_start' => self::$fieldTimestamp, 'range_end' => self::$fieldTimestamp, ), ), 'queryTopicRecords' => array ( 'url' => '/report/query_topic_records', 'required' => array ( 'topic_id' => self::$fieldMsgTopic, ), 'optional' => array ( 'start' => self::$fieldPageStart, 'limit' => self::$fieldPageLimit, 'range_start' => self::$fieldTimestamp, 'range_end' => self::$fieldTimestamp, ), ), 'queryTimerList' => array ( 'url' => '/timer/query_list', 'required' => array (), 'optional' => array ( 'timer_id' => self::$fieldTimerId, 'start' => self::$fieldPageStart, 'limit' => self::$fieldPageLimit, ), ), 'createTag' => array ( 'url' => '/app/create_tag', 'required' => array ( 'tag' => self::$fieldTagName, ), ), 'deleteTag' => array ( 'url' => '/app/del_tag', 'required' => array ( 'tag' => self::$fieldTagName, ), ), 'queryTags' => array ( 'url' => '/app/query_tags', 'required' => array (), 'optional' => array ( 'tag' => self::$fieldTagName, 'start' => self::$fieldPageStart, 'limit' => self::$fieldPageLimit, ), ), 'addDevicesToTag' => array ( 'url' => '/tag/add_devices', 'required' => array ( 'tag' => self::$fieldTagName, 'channel_ids' => self::$fieldChannelIdList, ), 'before' => 'verifyChannelIds', ), 'delDevicesFromTag' => array ( 'url' => '/tag/del_devices', 'required' => array ( 'tag' => self::$fieldTagName, 'channel_ids' => self::$fieldChannelIdList, ), 'before' => 'verifyChannelIds', ), 'queryDeviceNumInTag' => array ( 'url' => '/tag/device_num', 'required' => array ( 'tag' => self::$fieldTagName, ), ), 'queryMessageHistory' => array ( 'url' => '/app/query_msg_history', 'required' => array (), 'optional' => array ( 'msg_id' => self::$fieldIdOrList, 'topic_id' => self::$fieldMsgTopic, 'timer_id' => self::$fieldTimerId, 'push_from' => self::$fieldNumber, 'query_str' => array ( 'type' => 'string'), 'send_time_type' => self::$fieldNumber, 'msg_type' => self::$fieldNumber, 'start' => self::$fieldPageStart, 'limit' => self::$fieldPageLimit ), 'before' => 'verifyMsgIds', ), 'queryStatisticTopic' => array ( 'url' => '/report/statistic_topic', 'required' => array ( 'topic_id' => self::$fieldMsgTopic, ), 'optional' => array ( ), ), 'queryStatisticMsg' => array ( 'url' => '/report/statistic_msg', 'required' => array (), 'optional' => array ( 'query_type' => array ( 'type' => 'array'), ), 'before' => 'verifyRangeTypes', ), 'queryStatisticDevice' => array ( 'url' => '/report/statistic_device', 'required' => array (), ), 'queryTopicList' => array ( 'url' => '/topic/query_list', 'required' => array (), ), ); return $apiMapping; } /** * Constructor * * @param string $apiKey * 开发者apikey, 由开发者中心(http://developer.baidu.com)获取 * @param string $secretKey * 开发者当前secretKey, 在应用重新生成secret key后, 旧的secret key将失效, 由开发者中心(http://developer.baidu.com)获取. * @param array $curlopts * 用于cUrl::curl_setopt方法的控制参数. * @link http://php.net/manual/zh/function.curl-setopt.php (curl_setopt相关内容) */ function __construct($apiKey = null, $secretKey = null, $curlopts = array()) { if($apiKey !== null && $secretKey !== null){ $this -> setApiKey($apiKey); $this -> setSecretKey($secretKey); }else if($apiKey !== null || $secretKey !== null){ $loss = $apiKey == null ? 'apikey' : 'secretKey'; throw new Exception("need $loss."); }else{ $this -> setApiKey(BAIDU_PUSH_CONFIG::default_apiKey); $this -> setSecretKey(BAIDU_PUSH_CONFIG::default_secretkey); } parent::__construct($curlopts); $this -> log -> log("SDK ready to work !!"); } /** * 自动调用已配置的restApi方法. * * @param string $name * @param array $args * @throws ErrorException * @return mixed */ function __call($name, $originArgs) { $className = __CLASS__; $apiMapping = $this -> getMapping(); if (! array_key_exists($name, $apiMapping)) { throw new ErrorException("Call to undefined method $className::$name()"); } $methodDef = $apiMapping [$name]; $requiredDef = $methodDef ['required']; $optionalDef = array_key_exists('optional', $methodDef) ? $methodDef ['optional'] : null; // 取得参数列表. $keynames = array_keys($requiredDef); $realArgsLen = count($originArgs); // 实际参数个数 $requireArgsLen = count($keynames); // 必要参数个数 $opts = null; // 可选参数; $args = $originArgs; // 必要参数; // 验证一次参数长度 switch ($realArgsLen - count($keynames)) { case 0 : // this is good; break; case 1 : // have the $opts, take it out! if ($optionalDef != null) { $opts = array_pop($args); break; } default : // other case throw error; $this -> onError(null, 3, "SDK params invalid, check length of arguments, Need: $requireArgsLen Found: $realArgsLen"); return false; } $arguments = array(); // 合并, 便于验证时快速取值. if($requireArgsLen > 0 ){ $arguments = array_combine($keynames, $args); } $payload = $this -> newApiQueryTpl(); // 实际的http参数; // 必要参数验证; foreach ( $requiredDef as $key => $condition ) { $value = $arguments [$key]; if ($this -> assert -> makesure($value, $condition)) { $payload [$key] = $value; } else { $this -> onError(null, 3, "SDK params invalid, check the param [$key] is " . print_r($value,true)); return false; } } // 验证可选参数 if (is_array($opts)) { foreach ( $optionalDef as $key => $condition ) { // 可选参数,仅在设置时才进行验证, 否则直接忽略. 同时这里将忽略多余和不可识别内容. if (array_key_exists($key, $opts)) { $value = $opts [$key]; if ($this -> assert -> makesure($value, $condition)) { $payload [$key] = $value; } else { $assertErrorMsg = $this -> assert ->lastFailed; $this -> onError(null, 3, "SDK params invalid, $assertErrorMsg, check the param [$key]:" . print_r($value,true)); return false; } } } } // $before = array_key_exists('before', $methodDef) ? $methodDef ['before'] : 'none'; if (method_exists($this, $before)) { if (call_user_func_array(array ( $this, $before, ), array ( &$payload, ))) { return $this -> sendRequest($methodDef ['url'], $payload); } } else { $this -> onError(null, 2, "SDK running error, Missing the prepositive method [$before];"); } return false; } /** * do nothing; * * @return boolean */ private function none() { return true; } /** * 验证一个http请求参数中的msg内容是否正确 * @param array &$payload * @return boolean */ protected function verifyMessage(&$payload) { if (is_array($payload ['msg'])) { $payload ['msg'] = json_encode($payload ['msg']); } if (array_key_exists('send_time', $payload)) { if ($payload ['send_time'] < time() + 60) { $this -> onError(null, 3, "SDK params invalid, [send_time] must be greater than the current time + 60 seconds"); return false; } } return true; } /** * 验证一个组播API的http请求参数中的是否正确 * @param array $payload * @return boolean */ protected function verifyGroup(&$payload){ // 消息格式如果错误不继续. if(!$this->verifyMessage($payload)){ // 错误信息应有verifyMessage中生成. return false; } // 没有type,认为是push/tags的简单调用方式, 需要将type置为1 $payload['type'] = array_key_exists('type', $payload) ? $payload['type'] : 1; if($payload['type'] === 1){ // 必须要求有tag. if(!array_key_exists('tag', $payload)){ $this->onError(null,3,'SDK params invalid, [tag] is required, when the [type] not 2 ~ 6'); return false; } } // type不等于1, 必须有selector else if(array_key_exists('selector', $payload)){ if(is_array($payload['selector'])){ $payload['selector'] = json_encode($payload['selector']); } } // 其它情况,直接认为错误. else{ $this->onError(null,3,'SDK params invalid, [selector] is required, when the [type] between 2 and 6'); return false; } return true; } /** * 验证一个API请求中的channleIds是否正确 * @param array $payload * @return boolean */ protected function verifyChannelIds(&$payload){ if(array_key_exists('msg', $payload)){ // 消息格式如果错误不继续. if(!$this->verifyMessage($payload)){ // 错误信息应有verifyMessage中生成. return false; } } if(is_array($payload['channel_ids'])){ $payload['channel_ids'] = json_encode($payload['channel_ids']); } return true; } /** * 验证一个API请求中的msgIds是否正确 * @param array $payload * @return boolean */ protected function verifyMsgIds(&$payload){ if(isset($payload['msg_id']) && is_array($payload['msg_id'])){ $payload['msg_id'] = json_encode($payload['msg_id']); } return true; } /** * 验证一个API请求中的range_types是否正确 * @param array $payload * @return boolean */ protected function verifyRangeTypes(&$payload){ if(isset($payload['query_type']) && is_array($payload['query_type'])){ $payload['query_type'] = json_encode($payload['query_type']); } return true; } }