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.
 
 
 
 
 

656 lines
17 KiB

  1. <?php
  2. /***************************************************************************
  3. *
  4. * Copyright (c) 2014 Baidu.com, Inc. All Rights Reserved
  5. *
  6. **************************************************************************/
  7. /**
  8. * 仅提供最基本的错误控制和初始化参数功能
  9. *
  10. * @file BaseSDK.php
  11. * @encoding UTF-8
  12. *
  13. * @date 2014年12月29日
  14. *
  15. */
  16. require_once(PUSH_SDK_HOME.'/configure.php');
  17. require_once(PUSH_SDK_HOME.'/lib/HttpRequest.php');
  18. require_once(PUSH_SDK_HOME.'/lib/PushException.php');
  19. require_once(PUSH_SDK_HOME.'/lib/PushSimpleLog.php');
  20. require_once PUSH_SDK_HOME . '/lib/AssertHelper.php';
  21. /**
  22. * 仅提供最基本的错误控制和初始化参数功能
  23. * 包装最基本的http访问动作, 参数类型定义等
  24. *
  25. *
  26. */
  27. class BaseSDK{
  28. /**
  29. * 版本标识;
  30. *
  31. * @var string
  32. */
  33. const CURRENT_VERSION = '3.0.0';
  34. /*
  35. * 定义异常类型
  36. */
  37. const ERR_UNKNOWN = 'Exception';
  38. const ERR_CLIENT = 'ClientException';
  39. const ERR_NET = 'HttpException';
  40. const ERR_SERVER = 'ServerException';
  41. static protected $fieldNumber = array (
  42. 'type' => 'number',
  43. );
  44. static protected $fieldIdStr = array (
  45. 'type' => 'string',
  46. 'match' => '/\d{10,25}/',
  47. );
  48. static protected $fieldIdOrList = array (
  49. 'type' => array (
  50. 'array',
  51. 'string'
  52. )
  53. );
  54. static protected $fieldTimestamp = array (
  55. 'type' => 'number',
  56. );
  57. /**
  58. * channel_id 验证规则
  59. *
  60. * @var array
  61. */
  62. static protected $fieldChannelId = array (
  63. 'type' => 'string',
  64. 'match' => '/\d{10,25}/',
  65. );
  66. /**
  67. * 一组channel_id
  68. *
  69. * @var array
  70. */
  71. static protected $fieldChannelIdList = array (
  72. 'type' => 'array',
  73. );
  74. /**
  75. * 一条消息的id
  76. *
  77. * @var array
  78. */
  79. static protected $fieldMsgId = array (
  80. 'type' => 'string',
  81. 'match' => '/\d{10,25}/',
  82. );
  83. /**
  84. * 一个定时器的id
  85. *
  86. * @var array
  87. */
  88. static protected $fieldTimerId = array (
  89. 'type' => 'string',
  90. 'match' => '/\d{10,25}/',
  91. );
  92. /**
  93. * record limit
  94. *
  95. * @var array
  96. */
  97. static protected $fieldPageLimit = array (
  98. 'between' => array (
  99. 1,
  100. 100,
  101. ),
  102. );
  103. /**
  104. * record start
  105. *
  106. * @var array
  107. */
  108. static protected $fieldPageStart = array (
  109. 'type' => 'number',
  110. );
  111. /**
  112. * msg
  113. *
  114. * @var array
  115. */
  116. static protected $fieldMsg = array (
  117. 'type' => array (
  118. 'string',
  119. 'array',
  120. ),
  121. );
  122. /**
  123. * MSG_TYPE 验证规则
  124. *
  125. * @var array
  126. */
  127. static protected $fieldMsgType = array (
  128. 'maybe' => array (
  129. 0,
  130. 1,
  131. ),
  132. );
  133. /**
  134. * MSG_EXPIRES 验证规则
  135. *
  136. * @var array
  137. */
  138. static protected $fieldMsgExpires = array (
  139. 'between' => array (
  140. 1,
  141. 604800, // 1 ~ 86400 * 7
  142. ),
  143. );
  144. /**
  145. * DEPLOY_STATUS 验证规则
  146. *
  147. * @var array
  148. */
  149. static protected $fieldDeployStatus = array (
  150. 'maybe' => array (
  151. 1,
  152. 2,
  153. ),
  154. );
  155. /**
  156. * MSG_TOPIC 验证规则
  157. *
  158. * @var array
  159. */
  160. static protected $fieldMsgTopic = array (
  161. 'type' => 'string',
  162. 'match' => '/^.{1,128}$/i',
  163. );
  164. /**
  165. * SEND_TIME 验证规则
  166. *
  167. * @var array
  168. */
  169. static protected $fieldSendTime = array (
  170. 'type' => 'number',
  171. );
  172. /**
  173. * TAG_NAME 验证规则
  174. *
  175. * @var array
  176. */
  177. static protected $fieldTagName = array (
  178. 'type' => 'string',
  179. 'match' => '/^.{1,128}$/',
  180. );
  181. /**
  182. * GROUP_TYPE 验证规则
  183. *
  184. * @var array
  185. */
  186. static protected $fieldGroupType = array (
  187. 'between' => array (
  188. 2,
  189. 6,
  190. ),
  191. );
  192. /**
  193. * GROUP_SELECTOR 验证规则
  194. *
  195. * @var array
  196. */
  197. static protected $fieldGroupSelector = array (
  198. 'type' => array (
  199. 'string',
  200. 'array',
  201. ),
  202. );
  203. // const FIELD_ = array (
  204. // 'type' => 'number'
  205. // );
  206. /**
  207. * 设备类型常量 Android
  208. *
  209. * @var int 3
  210. */
  211. const DEVICE_ANDROID = 3;
  212. /**
  213. * 设备类型常量 IOS
  214. *
  215. * @var int 4
  216. */
  217. const DEVICE_IOS = 4;
  218. /*
  219. * 错误信息
  220. */
  221. protected $errorCode = null;
  222. protected $errorMsg = '';
  223. /*
  224. * requireId
  225. */
  226. protected $requestId = null;
  227. /**
  228. * http请求包装
  229. *
  230. * @var HttpRequest
  231. */
  232. public $http = null;
  233. /**
  234. * 目志输出对象
  235. *
  236. * @var PushSimpleLog
  237. */
  238. public $log = null;
  239. /**
  240. * 断言对象
  241. *
  242. * @var AssertHelper
  243. */
  244. public $assert = null;
  245. // 是否抛出异常
  246. private static $suppressException = false;
  247. // ------ business require ------- //
  248. /**
  249. * api key of app,
  250. * you can get it from http://developer.baidu.com
  251. *
  252. * @var string
  253. */
  254. protected $apikey = '';
  255. /**
  256. * secret key of app,
  257. * you can get it from http://developer.baidu.com.
  258. *
  259. * @var string
  260. */
  261. protected $secretKey = '';
  262. /**
  263. * 指定一个默认的deviceType, 默认为null即不指定,
  264. * 兼容老版本app时,必须指定一个固定值.
  265. * 3为android, 4为ios;
  266. *
  267. * @deprecated ;
  268. * @var int
  269. */
  270. protected $deviceType = null;
  271. /**
  272. * 决定是否携带expires参数, 当值大于0时, 将请求时的expires参数置为 : time() + $signExpires, 否则不携带expires
  273. *
  274. * @var int
  275. */
  276. protected $signExpires = 0;
  277. // ======== to prevent modify the value from external ======== //
  278. /**
  279. * 取得最后一次错误码
  280. *
  281. * @return string
  282. */
  283. public function getLastErrorCode() {
  284. return $this -> errorCode;
  285. }
  286. /**
  287. * 取得最后一次错误描述
  288. *
  289. * @return string
  290. */
  291. public function getLastErrorMsg() {
  292. return $this -> errorMsg;
  293. }
  294. /**
  295. * 取得最后一次与服务端交互产生的requestId, 无论实际API调用是否成功, requestId都应当存在
  296. *
  297. * @return string
  298. */
  299. public function getRequestId() {
  300. return $this -> requestId;
  301. }
  302. /**
  303. * 设置当前使用的apikey
  304. *
  305. * @param string $apiKey
  306. */
  307. public function setApiKey($apiKey) {
  308. $this -> apikey = $apiKey;
  309. }
  310. /**
  311. * 设置当前使用的secretKey
  312. *
  313. * @param string $secretKey
  314. */
  315. public function setSecretKey($secretKey) {
  316. $this -> secretKey = $secretKey;
  317. }
  318. /**
  319. * 设置一个默认的deviceType.
  320. *
  321. * @deprecated
  322. *
  323. * @param string $deviceType
  324. */
  325. public function setDeviceType($deviceType = null) {
  326. switch ($deviceType) {
  327. case self::DEVICE_ANDROID :
  328. $this -> deviceType = self::DEVICE_ANDROID;
  329. break;
  330. case self::DEVICE_IOS :
  331. $this -> deviceType = self::DEVICE_IOS;
  332. break;
  333. default :
  334. $this -> deviceType = null;
  335. }
  336. }
  337. /**
  338. * 记录当前发生的错误内容,供getErrorxxxxx相关接口使用, 同时根据是否抛出异常的配置决定是否生成并抛出一个错误.
  339. *
  340. * @param int $code 错误码, 非0表示有错误发生
  341. *
  342. * -1 未知错误
  343. * 0 表示没有错误
  344. * 1 ~ 99 clientSDK错误
  345. * 400 ~ 600 网络错误, http status code, 200-300为成功消息, 不可占用.
  346. * 10000 ~ 20000 服务端错误
  347. *
  348. * @param string $requestId 请求ID, 由服务端生成并返回
  349. * @param int $code 错误码
  350. * @param string $msg 错误消息
  351. * @return Exception
  352. */
  353. protected function onError($requestId = null, $code = 0, $msg = ''){
  354. $this -> errorMsg = $msg;
  355. $this -> errorCode = $code;
  356. $this -> requestId = $requestId;
  357. if ($code === 0) {
  358. // 无错误;
  359. return null;
  360. }
  361. if ($code > 0 && $code < 100) {
  362. // Http状态应小于100,所以认为小于100的均为SDK内部错误
  363. $type = self::ERR_CLIENT;
  364. } else if ($code >= 400 && $code < 600) {
  365. // 只将Http的错误状态认为是网络错误. 200及300对于当前restApi来说将不能被理解.
  366. $type = self::ERR_NET;
  367. } else if ($code >= 10000) {
  368. // 大于10000应为服务端返回的错误
  369. $type = self::ERR_SERVER;
  370. } else {
  371. $code = - 1;
  372. $type = self::ERR_UNKNOWN;
  373. }
  374. // 生产错误
  375. $err = new $type("[$code] $msg", $code);
  376. if (self::$suppressException) {
  377. $this -> log -> err($err -> __toString());
  378. return $err; // 拒绝抛出
  379. } else {
  380. throw $err;
  381. }
  382. }
  383. // ------- common method ------ //
  384. /**
  385. * Constructor
  386. *
  387. * @param array $curlOpts 用于内部http包装对象使用的全局curl参数.
  388. */
  389. function __construct($curlOpts = array()) {
  390. date_default_timezone_set('Asia/Shanghai');
  391. self::$suppressException = BAIDU_PUSH_CONFIG::SUPPRESS_EXCPTION;
  392. $this -> log = new PushSimpleLog(BAIDU_PUSH_CONFIG::LOG_OUTPUT, BAIDU_PUSH_CONFIG::LOG_LEVEL);
  393. try {
  394. $this -> http = new HttpRequest(BAIDU_PUSH_CONFIG::HOST, $curlOpts, $this -> log);
  395. $this -> log -> log("HttpRequest: ready to work...");
  396. } catch ( Exception $e ) {
  397. $this -> log -> fault($e -> __toString());
  398. die();
  399. }
  400. $this -> assert = new AssertHelper();
  401. $this -> signExpires = intval(BAIDU_PUSH_CONFIG::SIGN_EXPIRES);
  402. $this -> log -> log("SDK: initializing...");
  403. }
  404. /**
  405. * 判断一个字符串是否为以http://开头
  406. *
  407. * @param string $url
  408. * @return boolean
  409. */
  410. public function isUrlFormat($url) {
  411. return $this -> http -> isUrlFormat($url);
  412. }
  413. /**
  414. * 判断一个字符串,是否符合给写的格式
  415. *
  416. * @param string $match
  417. * 正则表达式格式字符串 PREG库风格
  418. * @param string $str
  419. * 将验证的字符串
  420. * @return boolean
  421. */
  422. function isMatchReg($match, $str) {
  423. return $this -> http -> isMatchReg($match, $str);
  424. }
  425. /**
  426. * 计算请求迁名
  427. *
  428. * @param string $method
  429. * GET|POST
  430. * @param string $url
  431. * 请求的完整url, 不含query部份
  432. * @param array $arrContent
  433. * 提交的数据项,包含get及post,但不包含签名项
  434. * @return string
  435. */
  436. function genSign($method, $url, $arrContent) {
  437. if ($this -> isMatchReg('/(^get$)|(^post$)/i', $method) === 0) {
  438. $this -> onError(null, 3, 'SDK: params invalid, $method must be "GET" or "POST"');
  439. return null;
  440. }
  441. if (! $this -> isUrlFormat($url)) {
  442. $this -> onError(null, 3, 'SDK: params invalid, $url is invalid!');
  443. return null;
  444. }
  445. $secret_key = $this -> secretKey;
  446. $baseStr = strtoupper($method) . $url;
  447. ksort($arrContent);
  448. foreach ( $arrContent as $key => $value ) {
  449. $baseStr .= $key . '=' . $value;
  450. }
  451. $sign = md5(urlencode($baseStr . $secret_key));
  452. return $sign;
  453. }
  454. /**
  455. * 生成携带默认请求的数组对象.
  456. *
  457. * @return multitype:number string
  458. */
  459. public function newApiQueryTpl() {
  460. $rs = array (
  461. 'apikey' => $this -> apikey,
  462. 'timestamp' => time(),
  463. );
  464. if ($this -> deviceType === null && BAIDU_PUSH_CONFIG::default_devicetype !== null) {
  465. $this->setDeviceType(BAIDU_PUSH_CONFIG::default_devicetype);
  466. }
  467. if($this -> deviceType !== null ){
  468. $rs ['device_type'] = $this -> deviceType;
  469. }
  470. if ($this -> signExpires > 0) {
  471. $rs ['expires'] = $rs ['timestamp'] + $this -> signExpires;
  472. }
  473. return $rs;
  474. }
  475. /**
  476. * 解析response并判断响应内容是否正确.
  477. * 如果结果符合restAPI结果格式,则返回结果中包含的response_params内容, 否则依据配置中是不是压制错误产生错误或返回null
  478. *
  479. * @param array $response
  480. * 由httpRequest对象get,post,request等方法返回的结果数组
  481. * @return mixed|NULL
  482. */
  483. public function parse($response) {
  484. extract($response);
  485. $this -> log -> log("Parse Response: $status, $statusMsg, $content");
  486. $result = json_decode($content, true);
  487. if ($result !== null && array_key_exists('request_id', $result)) {
  488. if ($status == 200) {
  489. if (array_key_exists('response_params', $result)) {
  490. // 状态200,并且成功解析出json结果. 表示请求成功
  491. $this -> onError($result ['request_id']);
  492. return $result ['response_params'];
  493. } else {
  494. // json可以解析, 但是格式不可理解.
  495. $this -> onError($result ['request_id'], 4, "http status is ok, but REST returned invalid json struct.");
  496. }
  497. } else {
  498. if (array_key_exists('error_code', $result) && array_key_exists('error_msg', $result)) {
  499. // 状态不等于200, 但能解析出结果. 表示请求成功, 但是服务端返回错误
  500. $this -> onError($result ['request_id'], $result ['error_code'], $result ['error_msg']);
  501. } else {
  502. // json可以解析, 但是格式不可理解.
  503. $this -> onError($result ['request_id'], 4, "http status is ok, but REST returned invalid json struct.");
  504. }
  505. }
  506. } else {
  507. if ($status == 200) {
  508. // 无法解析出json, 响应200,
  509. $this -> onError(null, 4, "http status is ok, but REST returned invalid json string.");
  510. } else if ($status >= 400) {
  511. // 无法解析出json, 但是响应一个可理解的http错误
  512. $this -> onError(null, $status, "http response status : $statusMsg");
  513. } else {
  514. // 响应200-399 , 即除200外任意http成功状态, 均不能被理解, 响应 (-1) 错误.
  515. $limitContent = strlen($content);
  516. if ($limitContent > 0) {
  517. $limitContent = substr($content, 200) . "..." . strlen($content);
  518. } else {
  519. $limitContent = 'Empty';
  520. }
  521. $this -> onError(null, - 1, "Can not process the http response. with status ($status, $statusMsg), content ($limitContent);");
  522. }
  523. }
  524. return false;
  525. }
  526. /**
  527. * 发送一个rest Api 请求
  528. * @param string $api 请求地址
  529. * @param array<K,V> $query 请求参数
  530. * @param string $method 请求方法 目前只支持 GET, POST
  531. * @param string $header http header
  532. * @param string $curlOpts curl选项
  533. * @return mixed 解析结果,如果失败将返回false, 否则应返回解析后的服务端返回的json数据
  534. */
  535. protected function sendRequest($api, $query, $method = "POST", $header=null, $curlOpts = null){
  536. $url = $this->http->getResourceAddress($api);
  537. $query['sign'] = $this -> genSign($method, $url, $query);
  538. if($header == null){
  539. $header = array('User-Agent: '. $this->makeUA());
  540. }
  541. if($method == 'GET'){
  542. $response = $this -> http -> get($api, $query, $header, $curlOpts);
  543. }else{
  544. $response = $this -> http -> post($api, $query, $header, $curlOpts);
  545. }
  546. return $this->parse($response);
  547. }
  548. protected function makeUA(){
  549. $sdkVersion = self::CURRENT_VERSION;
  550. $sysName = php_uname('s');
  551. $sysVersion = php_uname('v');
  552. $machineName = php_uname('m');
  553. $systemInfo = "$sysName; $sysVersion; $machineName";
  554. $langName = 'PHP';
  555. $langVersion = phpversion();
  556. $serverName = php_sapi_name();
  557. $serverVersion = "Unknown";
  558. $sendInfo = 'ZEND/' . zend_version();
  559. $serverInfo = "$serverName/$serverVersion";
  560. if(isset($_SERVER['SERVER_SOFTWARE'])){
  561. $serverInfo .= '(' . $_SERVER['SERVER_SOFTWARE'] . ')';
  562. }
  563. $tpl = "BCCS_SDK/3.0 ($systemInfo) $langName/$langVersion (Baidu Push SDK for PHP v$sdkVersion) $serverInfo $sendInfo";
  564. return $tpl;
  565. }
  566. }