You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

607 lines
23 KiB

  1. <?php
  2. /**
  3. * *************************************************************************
  4. *
  5. * Copyright (c) 2012 Baidu.com, Inc. All Rights Reserved
  6. *
  7. * ************************************************************************
  8. */
  9. /**
  10. * Baidu push service sdk for php
  11. * @file sdk.php
  12. *
  13. *
  14. *
  15. */
  16. if(!defined('PUSH_SDK_HOME')){
  17. define('PUSH_SDK_HOME', dirname(__FILE__));
  18. }
  19. //echo PUSH_SDK_HOME;
  20. require_once PUSH_SDK_HOME . '/configure.php';
  21. require_once PUSH_SDK_HOME . '/lib/BaseSDK.php';
  22. require_once PUSH_SDK_HOME . '/lib/AssertHelper.php';
  23. /**
  24. *
  25. * <i>
  26. * Notice: 在推送当msg_type为1的时候, 尽量使msg以array方式进行传递, 便于进行参数检查.
  27. * 其中一个必要的检查内容是, msg体必须包含description, 在使用string进行传递时, 方法内部将无法进行这些检查,只能等待服务器检查后返回错误
  28. * </i>
  29. *
  30. * @method boolean pushMsgToSingleDevice(string $channel_id, string|array $msg, array $opts)
  31. * 单播, 推送一条消息到指定设备<br />
  32. * <b>
  33. * $channel_id 目标客户端设备所属的唯一id;<br />
  34. * $msg 消息内容.可参见<u>消息体格式说明</u> 当消息体类型为一个array时,将自动调用json_encode方法来将array转换为一个string;<br />
  35. * $opts 可选参数列表, 包含 msg_type, msg_expires, deploy_status, topic_id几项可选内容; <br />
  36. *
  37. * @method boolean pushMsgToAll(string|array $msg, array $opts)
  38. * 广播, 推送一条消息到全部设备
  39. * <b>
  40. * $msg 当消息体类型为一个array时,将自动调用json_encode方法来将array转换为一个string;<br />
  41. * $opts 可选参数, 包含 msg_type, msg_expires, deploy_status几项可选内容; <br />
  42. *
  43. * @method boolean pushMsgToTag(string $tag, string|array $msg,array $opts)
  44. * 组播, 推送一条消息到一组设备.
  45. * <b>
  46. * $tag 组名,
  47. * $msg 消息内容.可参见<u>消息体格式说明</u> 当消息体类型为一个array时,将自动调用json_encode方法来将array转换为一个string;<br />
  48. * $opts 可选参数, 包含 msg_type, msg_expires, deploy_status几项可选内容; <br />
  49. *
  50. * @method boolean pushToSmartTag(string $type, string|array $msg, array $opts)
  51. * 组播, 根据$type类型,向指定标签组推送消息
  52. * <b>
  53. * $type 标签组类型<br />
  54. * $msg 消息内容.可参见<u>消息体格式说明</u> 当消息体类型为一个array时,将自动调用json_encode方法来将array转换为一个string;<br />
  55. * $opts 可选参数, 包含 selector, msg_type, msg_expires, deploy_status, send_time几项可选内容; <br />
  56. *
  57. * @method boolean pushBatchUniMsg(string $channel_ids, string $msg);
  58. * 批量单播, 向指定的一组channel_id发送一条消息.
  59. * <br/>
  60. * $channel_ids; 一个数组, 每项为一个channel_id <br />
  61. * $msg 消息内容.可参见<u>消息体格式说明</u> 当消息体类型为一个array时,将自动调用json_encode方法来将array转换为一个string;<br />
  62. * $opts 可选参数, 包含
  63. *
  64. * @method boolean queryMsgStatus(string $msg_id);
  65. * 查询消息发送状态
  66. * <br/>
  67. * $msg_id 在发送时, 由服务端返回的 msg_id
  68. *
  69. * @method boolean queryTimerRecords(string $timer_id, array $opts);
  70. * 根扰timer_id查询一个定时或周期的消息发送记录
  71. * <br/>
  72. * $timer_id 在发送消息时,由服务端产生的timer_id
  73. * $opts 可选参数, 包含 start, limit, start_time, end_time等几项可选内容;<br />
  74. *
  75. * @method boolean queryTopicRecords(string $topic_id, array $opts);
  76. * 根扰topic查询一个topic下的消息发送记录
  77. * <br/>
  78. * $topic_id 发送消息时指定的topic
  79. * $opts 可选参数, 包含
  80. *
  81. * @method boolean queryTimerList(array $opts);
  82. * 查询当前应用下的定时消息及周期消息.
  83. * <br/>
  84. * $opts 可选参数, 包含timer_id, start, limit等几项可选内容, 当指定timer_id时, 仅返回目标timer相关的信息.
  85. *
  86. * @method boolean createTag(string $tag);
  87. * 创建一个tag
  88. * <br/>
  89. * $tag tag名称
  90. *
  91. * @method boolean deleteTag(string $tag);
  92. * 删除一个tag
  93. * <br/>
  94. * $tag tag名称
  95. *
  96. * @method boolean queryTags(array $opts);
  97. * 查询当前应用下的tag列表
  98. * <br>
  99. * $opts 可选参数, 包含tag, start, limit, 当指定tag时, 仅返回目标tag相关信息
  100. *
  101. * @method boolean addDevicesToTag(string $tag, string $channel_ids);
  102. * 向一个tag下添加设备
  103. * <br/>
  104. * $tag tag名称 <br />
  105. * $channel_ids 需要添加的一组channel_id
  106. *
  107. * @method boolean delDevicesFromTag(string $tag, string $channel_ids);
  108. * 从一个tag下删除一批设备
  109. * <br/>
  110. * $tag; <br />
  111. * $channel_ids 需要删除的一组channel_id
  112. *
  113. * @method boolean queryDeviceNumInTag(string $tag);
  114. * 查询一个tag下的设备数量
  115. * <br/>
  116. * $tag tag名称
  117. *
  118. */
  119. class PushSDK extends BaseSDK {
  120. const VERSION = '3.0.0';
  121. const TAG_TYPE_LBS_LABEL = 2; // LBS 推送
  122. const TAG_TYPE_INTEREST_LABEL = 3; // 兴趣组
  123. const TAG_TYPE_AGE_LABEL = 4; // 年龄组
  124. const TAG_TYPE_SEX_LABEL = 5; // 性别组
  125. const TAG_TYPE_OP_LABEL = 6; // 组合运算
  126. // required必须存在, 如果不存在必要参数. 请留空数组, optional为可选.不需要可以不写.
  127. /**
  128. * 为兼容php 5.2及5.4,不支持将const写成array.
  129. *
  130. * 获得一个方法映射关系的array.
  131. * @return multitype:multitype:string multitype:string multitype:string multitype: multitype:string multitype:string multitype: multitype:string multitype:string
  132. */
  133. private function getMapping(){
  134. /**
  135. * restApi 方法映射.
  136. *
  137. * 每项描述一个method的 包含以下内容:
  138. * url: string 方法调用位置
  139. * required: array<K,V> 必须的参数列表,K有参数名, V为验证条件
  140. * optional: array<K,V> 可选的参数列表,K有参数名, V为验证条件
  141. * before: string 指定一个预处理方法. 用于在发送http request之前对payload内容进行一次处理.
  142. *
  143. * @var array
  144. */
  145. $apiMapping = array (
  146. 'pushMsgToSingleDevice' => array (
  147. 'url' => '/push/single_device',
  148. 'required' => array (
  149. 'channel_id' => self::$fieldIdStr,
  150. 'msg' => self::$fieldMsg,
  151. ),
  152. 'optional' => array (
  153. 'msg_type' => self::$fieldMsgType,
  154. 'msg_expires' => self::$fieldMsgExpires,
  155. 'deploy_status' => self::$fieldDeployStatus,
  156. //'topic_id' => self::$fieldMsgTopic,
  157. ),
  158. 'before' => 'verifyMessage',
  159. ),
  160. 'pushMsgToAll' => array (
  161. 'url' => '/push/all',
  162. 'required' => array (
  163. 'msg' => self::$fieldMsg,
  164. ),
  165. 'optional' => array (
  166. 'msg_type' => self::$fieldMsgType,
  167. 'msg_expires' => self::$fieldMsgExpires,
  168. 'deploy_status' => self::$fieldDeployStatus,
  169. 'send_time' => self::$fieldSendTime,
  170. ),
  171. 'before' => 'verifyMessage',
  172. ),
  173. 'pushMsgToTag' => array (
  174. 'url' => '/push/tags',
  175. 'required' => array (
  176. 'tag' => self::$fieldTagName,
  177. 'msg' => self::$fieldMsg,
  178. ),
  179. 'optional' => array (
  180. 'msg_type' => self::$fieldMsgType,
  181. 'msg_expires' => self::$fieldMsgExpires,
  182. 'deploy_status' => self::$fieldDeployStatus,
  183. 'send_time' => self::$fieldSendTime,
  184. ),
  185. 'before' => 'verifyGroup',
  186. ),
  187. 'pushToSmartTag' => array (
  188. 'url' => '/push/tags',
  189. 'required' => array (
  190. 'type' => self::$fieldGroupType,
  191. 'selector' => self::$fieldGroupSelector,
  192. 'msg' => self::$fieldMsg,
  193. ),
  194. 'optional' => array (
  195. 'msg_type' => self::$fieldMsgType,
  196. 'msg_expires' => self::$fieldMsgExpires,
  197. 'deploy_status' => self::$fieldDeployStatus,
  198. 'send_time' => self::$fieldSendTime,
  199. ),
  200. 'before' => 'verifyGroup',
  201. ),
  202. 'pushBatchUniMsg' => array (
  203. 'url' => '/push/batch_device',
  204. 'required' => array (
  205. 'channel_ids' => self::$fieldChannelIdList,
  206. 'msg' => self::$fieldMsg,
  207. ),
  208. 'optional' => array (
  209. 'msg_type' => self::$fieldMsgType,
  210. 'msg_expires' => self::$fieldMsgExpires,
  211. 'deploy_status' => self::$fieldDeployStatus,
  212. 'topic_id' => self::$fieldMsgTopic,
  213. ),
  214. 'before' => 'verifyChannelIds',
  215. ),
  216. 'queryMsgStatus' => array (
  217. 'url' => '/report/query_msg_status',
  218. 'required' => array (
  219. 'msg_id' => self::$fieldIdOrList,
  220. ),
  221. 'optional' => array(
  222. 'statistic_type' => array ( 'type' => 'string'),
  223. ),
  224. 'before' => 'verifyMsgIds',
  225. ),
  226. 'queryTimerRecords' => array (
  227. 'url' => '/report/query_timer_records',
  228. 'required' => array (
  229. 'timer_id' => self::$fieldTimerId,
  230. ),
  231. 'optional' => array (
  232. 'start' => self::$fieldPageStart,
  233. 'limit' => self::$fieldPageLimit,
  234. 'range_start' => self::$fieldTimestamp,
  235. 'range_end' => self::$fieldTimestamp,
  236. ),
  237. ),
  238. 'queryTopicRecords' => array (
  239. 'url' => '/report/query_topic_records',
  240. 'required' => array (
  241. 'topic_id' => self::$fieldMsgTopic,
  242. ),
  243. 'optional' => array (
  244. 'start' => self::$fieldPageStart,
  245. 'limit' => self::$fieldPageLimit,
  246. 'range_start' => self::$fieldTimestamp,
  247. 'range_end' => self::$fieldTimestamp,
  248. ),
  249. ),
  250. 'queryTimerList' => array (
  251. 'url' => '/timer/query_list',
  252. 'required' => array (),
  253. 'optional' => array (
  254. 'timer_id' => self::$fieldTimerId,
  255. 'start' => self::$fieldPageStart,
  256. 'limit' => self::$fieldPageLimit,
  257. ),
  258. ),
  259. 'createTag' => array (
  260. 'url' => '/app/create_tag',
  261. 'required' => array (
  262. 'tag' => self::$fieldTagName,
  263. ),
  264. ),
  265. 'deleteTag' => array (
  266. 'url' => '/app/del_tag',
  267. 'required' => array (
  268. 'tag' => self::$fieldTagName,
  269. ),
  270. ),
  271. 'queryTags' => array (
  272. 'url' => '/app/query_tags',
  273. 'required' => array (),
  274. 'optional' => array (
  275. 'tag' => self::$fieldTagName,
  276. 'start' => self::$fieldPageStart,
  277. 'limit' => self::$fieldPageLimit,
  278. ),
  279. ),
  280. 'addDevicesToTag' => array (
  281. 'url' => '/tag/add_devices',
  282. 'required' => array (
  283. 'tag' => self::$fieldTagName,
  284. 'channel_ids' => self::$fieldChannelIdList,
  285. ),
  286. 'before' => 'verifyChannelIds',
  287. ),
  288. 'delDevicesFromTag' => array (
  289. 'url' => '/tag/del_devices',
  290. 'required' => array (
  291. 'tag' => self::$fieldTagName,
  292. 'channel_ids' => self::$fieldChannelIdList,
  293. ),
  294. 'before' => 'verifyChannelIds',
  295. ),
  296. 'queryDeviceNumInTag' => array (
  297. 'url' => '/tag/device_num',
  298. 'required' => array (
  299. 'tag' => self::$fieldTagName,
  300. ),
  301. ),
  302. 'queryMessageHistory' => array (
  303. 'url' => '/app/query_msg_history',
  304. 'required' => array (),
  305. 'optional' => array (
  306. 'msg_id' => self::$fieldIdOrList,
  307. 'topic_id' => self::$fieldMsgTopic,
  308. 'timer_id' => self::$fieldTimerId,
  309. 'push_from' => self::$fieldNumber,
  310. 'query_str' => array ( 'type' => 'string'),
  311. 'send_time_type' => self::$fieldNumber,
  312. 'msg_type' => self::$fieldNumber,
  313. 'start' => self::$fieldPageStart,
  314. 'limit' => self::$fieldPageLimit
  315. ),
  316. 'before' => 'verifyMsgIds',
  317. ),
  318. 'queryStatisticTopic' => array (
  319. 'url' => '/report/statistic_topic',
  320. 'required' => array (
  321. 'topic_id' => self::$fieldMsgTopic,
  322. ),
  323. 'optional' => array (
  324. ),
  325. ),
  326. 'queryStatisticMsg' => array (
  327. 'url' => '/report/statistic_msg',
  328. 'required' => array (),
  329. 'optional' => array (
  330. 'query_type' => array ( 'type' => 'array'),
  331. ),
  332. 'before' => 'verifyRangeTypes',
  333. ),
  334. 'queryStatisticDevice' => array (
  335. 'url' => '/report/statistic_device',
  336. 'required' => array (),
  337. ),
  338. 'queryTopicList' => array (
  339. 'url' => '/topic/query_list',
  340. 'required' => array (),
  341. ),
  342. );
  343. return $apiMapping;
  344. }
  345. /**
  346. * Constructor
  347. *
  348. * @param string $apiKey
  349. * 开发者apikey, 由开发者中心(http://developer.baidu.com)获取
  350. * @param string $secretKey
  351. * 开发者当前secretKey, 在应用重新生成secret key后, 旧的secret key将失效, 由开发者中心(http://developer.baidu.com)获取.
  352. * @param array $curlopts
  353. * 用于cUrl::curl_setopt方法的控制参数.
  354. * @link http://php.net/manual/zh/function.curl-setopt.php (curl_setopt相关内容)
  355. */
  356. function __construct($apiKey = null, $secretKey = null, $curlopts = array()) {
  357. if($apiKey !== null && $secretKey !== null){
  358. $this -> setApiKey($apiKey);
  359. $this -> setSecretKey($secretKey);
  360. }else if($apiKey !== null || $secretKey !== null){
  361. $loss = $apiKey == null ? 'apikey' : 'secretKey';
  362. throw new Exception("need $loss.");
  363. }else{
  364. $this -> setApiKey(BAIDU_PUSH_CONFIG::default_apiKey);
  365. $this -> setSecretKey(BAIDU_PUSH_CONFIG::default_secretkey);
  366. }
  367. parent::__construct($curlopts);
  368. $this -> log -> log("SDK ready to work !!");
  369. }
  370. /**
  371. * 自动调用已配置的restApi方法.
  372. *
  373. * @param string $name
  374. * @param array $args
  375. * @throws ErrorException
  376. * @return mixed
  377. */
  378. function __call($name, $originArgs) {
  379. $className = __CLASS__;
  380. $apiMapping = $this -> getMapping();
  381. if (! array_key_exists($name, $apiMapping)) {
  382. throw new ErrorException("Call to undefined method $className::$name()");
  383. }
  384. $methodDef = $apiMapping [$name];
  385. $requiredDef = $methodDef ['required'];
  386. $optionalDef = array_key_exists('optional', $methodDef) ? $methodDef ['optional'] : null;
  387. // 取得参数列表.
  388. $keynames = array_keys($requiredDef);
  389. $realArgsLen = count($originArgs); // 实际参数个数
  390. $requireArgsLen = count($keynames); // 必要参数个数
  391. $opts = null; // 可选参数;
  392. $args = $originArgs; // 必要参数;
  393. // 验证一次参数长度
  394. switch ($realArgsLen - count($keynames)) {
  395. case 0 :
  396. // this is good;
  397. break;
  398. case 1 :
  399. // have the $opts, take it out!
  400. if ($optionalDef != null) {
  401. $opts = array_pop($args);
  402. break;
  403. }
  404. default :
  405. // other case throw error;
  406. $this -> onError(null, 3, "SDK params invalid, check length of arguments, Need: $requireArgsLen Found: $realArgsLen");
  407. return false;
  408. }
  409. $arguments = array();
  410. // 合并, 便于验证时快速取值.
  411. if($requireArgsLen > 0 ){
  412. $arguments = array_combine($keynames, $args);
  413. }
  414. $payload = $this -> newApiQueryTpl(); // 实际的http参数;
  415. // 必要参数验证;
  416. foreach ( $requiredDef as $key => $condition ) {
  417. $value = $arguments [$key];
  418. if ($this -> assert -> makesure($value, $condition)) {
  419. $payload [$key] = $value;
  420. } else {
  421. $this -> onError(null, 3, "SDK params invalid, check the param [$key] is " . print_r($value,true));
  422. return false;
  423. }
  424. }
  425. // 验证可选参数
  426. if (is_array($opts)) {
  427. foreach ( $optionalDef as $key => $condition ) {
  428. // 可选参数,仅在设置时才进行验证, 否则直接忽略. 同时这里将忽略多余和不可识别内容.
  429. if (array_key_exists($key, $opts)) {
  430. $value = $opts [$key];
  431. if ($this -> assert -> makesure($value, $condition)) {
  432. $payload [$key] = $value;
  433. } else {
  434. $assertErrorMsg = $this -> assert ->lastFailed;
  435. $this -> onError(null, 3, "SDK params invalid, $assertErrorMsg, check the param [$key]:" . print_r($value,true));
  436. return false;
  437. }
  438. }
  439. }
  440. }
  441. //
  442. $before = array_key_exists('before', $methodDef) ? $methodDef ['before'] : 'none';
  443. if (method_exists($this, $before)) {
  444. if (call_user_func_array(array (
  445. $this,
  446. $before,
  447. ), array (
  448. &$payload,
  449. ))) {
  450. return $this -> sendRequest($methodDef ['url'], $payload);
  451. }
  452. } else {
  453. $this -> onError(null, 2, "SDK running error, Missing the prepositive method [$before];");
  454. }
  455. return false;
  456. }
  457. /**
  458. * do nothing;
  459. *
  460. * @return boolean
  461. */
  462. private function none() {
  463. return true;
  464. }
  465. /**
  466. * 验证一个http请求参数中的msg内容是否正确
  467. * @param array &$payload
  468. * @return boolean
  469. */
  470. protected function verifyMessage(&$payload) {
  471. if (is_array($payload ['msg'])) {
  472. $payload ['msg'] = json_encode($payload ['msg']);
  473. }
  474. if (array_key_exists('send_time', $payload)) {
  475. if ($payload ['send_time'] < time() + 60) {
  476. $this -> onError(null, 3, "SDK params invalid, [send_time] must be greater than the current time + 60 seconds");
  477. return false;
  478. }
  479. }
  480. return true;
  481. }
  482. /**
  483. * 验证一个组播API的http请求参数中的是否正确
  484. * @param array $payload
  485. * @return boolean
  486. */
  487. protected function verifyGroup(&$payload){
  488. // 消息格式如果错误不继续.
  489. if(!$this->verifyMessage($payload)){
  490. // 错误信息应有verifyMessage中生成.
  491. return false;
  492. }
  493. // 没有type,认为是push/tags的简单调用方式, 需要将type置为1
  494. $payload['type'] = array_key_exists('type', $payload) ? $payload['type'] : 1;
  495. if($payload['type'] === 1){
  496. // 必须要求有tag.
  497. if(!array_key_exists('tag', $payload)){
  498. $this->onError(null,3,'SDK params invalid, [tag] is required, when the [type] not 2 ~ 6');
  499. return false;
  500. }
  501. }
  502. // type不等于1, 必须有selector
  503. else if(array_key_exists('selector', $payload)){
  504. if(is_array($payload['selector'])){
  505. $payload['selector'] = json_encode($payload['selector']);
  506. }
  507. }
  508. // 其它情况,直接认为错误.
  509. else{
  510. $this->onError(null,3,'SDK params invalid, [selector] is required, when the [type] between 2 and 6');
  511. return false;
  512. }
  513. return true;
  514. }
  515. /**
  516. * 验证一个API请求中的channleIds是否正确
  517. * @param array $payload
  518. * @return boolean
  519. */
  520. protected function verifyChannelIds(&$payload){
  521. if(array_key_exists('msg', $payload)){
  522. // 消息格式如果错误不继续.
  523. if(!$this->verifyMessage($payload)){
  524. // 错误信息应有verifyMessage中生成.
  525. return false;
  526. }
  527. }
  528. if(is_array($payload['channel_ids'])){
  529. $payload['channel_ids'] = json_encode($payload['channel_ids']);
  530. }
  531. return true;
  532. }
  533. /**
  534. * 验证一个API请求中的msgIds是否正确
  535. * @param array $payload
  536. * @return boolean
  537. */
  538. protected function verifyMsgIds(&$payload){
  539. if(isset($payload['msg_id']) && is_array($payload['msg_id'])){
  540. $payload['msg_id'] = json_encode($payload['msg_id']);
  541. }
  542. return true;
  543. }
  544. /**
  545. * 验证一个API请求中的range_types是否正确
  546. * @param array $payload
  547. * @return boolean
  548. */
  549. protected function verifyRangeTypes(&$payload){
  550. if(isset($payload['query_type']) && is_array($payload['query_type'])){
  551. $payload['query_type'] = json_encode($payload['query_type']);
  552. }
  553. return true;
  554. }
  555. }