├── .gitignore ├── composer.json ├── README.md ├── CHANGELOG.md ├── src ├── te_example.php └── TaPhpSdk.php └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | .idea 17 | 18 | .DS_Store 19 | *phpunit* 20 | vendor -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "thinkinggame/ta-php-sdk", 3 | "type": "library", 4 | "homepage": "http://www.thinkingdata.cn", 5 | "description": "PHP SDK for thinkingdata Analytics", 6 | "keywords": [ 7 | "sdk", 8 | "thinkingdata" 9 | ], 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "ThinkingData", 14 | "email": "sdk@thinkingdata.cn" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=5.5.0", 19 | "ext-curl": "*", 20 | "ext-zlib": "*", 21 | "ext-json": "*" 22 | }, 23 | "autoload": { 24 | "files": [ 25 | "src/TaPhpSdk.php" 26 | ] 27 | }, 28 | "minimum-stability": "stable" 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ThinkingData SDK for PHP 2 | ![output](https://user-images.githubusercontent.com/53337625/205621683-ed9b97ef-6a52-4903-a2c0-a955dddebb7d.png) 3 | 4 | This is the [ThinkingData](https://www.thinkingdata.cn)™ SDK for PHP. Documentation is available on our help center in the following languages: 5 | 6 | - [English](https://docs.thinkingdata.cn/ta-manual/latest/en/installation/installation_menu/server_sdk/php_sdk_installation/php_sdk_installation.html) 7 | - [中文](https://docs.thinkingdata.cn/ta-manual/latest/installation/installation_menu/server_sdk/php_sdk_installation/php_sdk_installation.html) 8 | - [日本語](https://docs.thinkingdata.cn/ta-manual/latest/ja/installation/installation_menu/server_sdk/php_sdk_installation/php_sdk_installation.html) 9 | 10 | --- -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### v3.1.1 2 | **Date:** 2024/07/24 3 | 4 | **Notes:** 5 | 6 | * More permissions are supported 7 | 8 | ### v3.1.0 9 | **Date:** 2024/07/19 10 | 11 | **Notes:** 12 | 13 | * Supports user-defined permissions 14 | 15 | ### v3.0.0 16 | **Date:** 2023/10/08 17 | 18 | **Notes:** 19 | 20 | * Enabling new apis 21 | 22 | ### v2.2.2 23 | **Date:** 2023/06/29 24 | 25 | **Notes:** 26 | 27 | * bugfix: close file handler when file rotate 28 | 29 | ### v2.2.1 30 | **Date:** 2023/04/11 31 | 32 | **Notes:** 33 | 34 | * Close file handler when SDK close 35 | 36 | ### v2.2.0 37 | **Date:** 2023/04/10 38 | 39 | **Notes:** 40 | 41 | * Add TE namespace 42 | * Add buffer 43 | 44 | ### v2.1.1 45 | **Date:** 2022/11/10 46 | 47 | **Notes:** 48 | 49 | * Support debug mode in TE 50 | 51 | ### v2.1.0 52 | **Date:** 2022/10/25 53 | 54 | **Notes:** 55 | 56 | * Support log system 57 | 58 | ### v2.0.0 59 | **Date:** 2022/05/09 60 | 61 | **Notes:** 62 | 63 | * Support dynamic common properties 64 | * Add track_first api 65 | * Add user_uniq_append api -------------------------------------------------------------------------------- /src/te_example.php: -------------------------------------------------------------------------------- 1 | track($distinct_id, $account_id, "viewPage", $properties); 75 | } catch (Exception $e) { 76 | echo $e; 77 | } 78 | 79 | function dynamicPublicProperties() { 80 | $properties = array(); 81 | $properties['utc_time'] = date('Y-m-d h:i:s', time()); 82 | return $properties; 83 | } 84 | $teSDK->register_dynamic_public_properties(__NAMESPACE__ . '\dynamicPublicProperties'); 85 | 86 | $teSDK->register_public_properties(array("name" => "super_property")); 87 | 88 | try { 89 | $teSDK->track($distinct_id, $account_id, "viewPage", $properties); 90 | } catch (Exception $e) { 91 | echo $e; 92 | } 93 | 94 | try { 95 | $teSDK->track_first($distinct_id, $account_id, "track_first", "test_first_check_id", $properties); 96 | } catch (Exception $e) { 97 | //handle except 98 | echo $e; 99 | } 100 | 101 | try { 102 | $teSDK->track_update($distinct_id, $account_id, "track_update", "test_event_id", $properties); 103 | } catch (Exception $e) { 104 | //handle except 105 | echo $e; 106 | } 107 | 108 | try { 109 | $teSDK->track_overwrite($distinct_id, $account_id, "track_overwrite", "test_event_id", $properties); 110 | } catch (Exception $e) { 111 | //handle except 112 | echo $e; 113 | } 114 | 115 | $properties = array(); 116 | $properties['once_key'] = "once"; 117 | try { 118 | $teSDK->user_setOnce($distinct_id, $account_id, $properties); 119 | } catch (Exception $e) { 120 | //handle except 121 | echo $e; 122 | } 123 | 124 | $properties = array(); 125 | $properties['once_key'] = 'twice'; 126 | $properties['age'] = 10; 127 | $properties['money'] = 300; 128 | $properties['array1'] = ['str1', 'str2']; 129 | try { 130 | $teSDK->user_set($distinct_id, $account_id, $properties); 131 | } catch (Exception $e) { 132 | //handle except 133 | echo $e; 134 | } 135 | 136 | $properties = array( 137 | "age" 138 | ); 139 | try { 140 | $teSDK->user_unset(null, $account_id, $properties); 141 | } catch (Exception $e) { 142 | //handle except 143 | echo $e; 144 | } 145 | 146 | $properties = array(); 147 | $properties['array1'] = ['str3', 'str4']; 148 | try { 149 | $teSDK->user_append($distinct_id, $account_id, $properties); 150 | } catch (Exception $e) { 151 | //handle except 152 | echo $e; 153 | } 154 | 155 | $properties = array(); 156 | $properties['array1'] = ['str4', 'str5']; 157 | try { 158 | $teSDK->user_uniq_append($distinct_id, $account_id, $properties); 159 | } catch (Exception $e) { 160 | //handle except 161 | echo $e; 162 | } 163 | 164 | $properties = array(); 165 | $properties['level'] = 12.21123; 166 | try { 167 | $teSDK->user_add(null, $account_id, $properties); 168 | } catch (Exception $e) { 169 | //handle except 170 | echo $e; 171 | } 172 | 173 | $properties = array(); 174 | $properties['#app_id'] = "123123123"; 175 | $properties['name'] = "aaa"; 176 | try { 177 | $teSDK->user_del(null, $account_id, $properties); 178 | } catch (Exception $e) { 179 | //handle except 180 | echo $e; 181 | } 182 | 183 | $public_properties = array(); 184 | $public_properties['#country'] = 'china'; 185 | $public_properties['#ip'] = '123.123.123.123'; 186 | $teSDK->register_public_properties($public_properties); 187 | 188 | $properties = array(); 189 | $properties['Product_Name'] = 'a'; 190 | $properties['OrderId'] = "order_id_a"; 191 | try { 192 | $teSDK->track(null, $account_id, "Product_Purchase", $properties); 193 | } catch (Exception $e) { 194 | //handle except 195 | echo $e; 196 | } 197 | 198 | $teSDK->clear_public_properties(); 199 | 200 | $properties = array(); 201 | $properties['Product_Name'] = 'e'; 202 | try { 203 | $teSDK->track(null, $account_id, "Browse_Product", $properties); 204 | } catch (Exception $e) { 205 | echo $e; 206 | } 207 | 208 | $teSDK->flush(); 209 | 210 | try { 211 | $teSDK->close(); 212 | } catch (Exception $e) { 213 | echo 'error' . PHP_EOL; 214 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2019 thinking-analytics / data-collector / client-sdk 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/TaPhpSdk.php: -------------------------------------------------------------------------------- 1 | consumer = $consumer; 57 | $this->enableUUID = $enableUUID; 58 | $this->clear_public_properties(); 59 | } 60 | 61 | /** 62 | * Set user properties. would overwrite existing names 63 | * @param string $distinct_id distinct ID 64 | * @param string $account_id account ID 65 | * @param array $properties properties 66 | * @return boolean 67 | * @throws Exception exception 68 | */ 69 | public function user_set($distinct_id, $account_id, $properties = array()) 70 | { 71 | return $this->add($distinct_id, $account_id, USER_TYPE_SET, null, null, $properties); 72 | } 73 | 74 | /** 75 | * Set user properties, If such property had been set before, this message would be neglected. 76 | * @param string $distinct_id distinct ID 77 | * @param string $account_id account ID 78 | * @param array $properties properties 79 | * @return boolean 80 | * @throws Exception exception 81 | */ 82 | public function user_setOnce($distinct_id, $account_id, $properties = array()) 83 | { 84 | return $this->add($distinct_id, $account_id, USER_TYPE_SET_ONCE, null, null, $properties); 85 | } 86 | 87 | /** 88 | * To accumulate operations against the property 89 | * @param string $distinct_id distinct ID 90 | * @param string $account_id account ID 91 | * @param array $properties properties 92 | * @return boolean 93 | * @throws Exception exception 94 | */ 95 | public function user_add($distinct_id, $account_id, $properties = array()) 96 | { 97 | return $this->add($distinct_id, $account_id, USER_TYPE_ADD, null, null, $properties); 98 | } 99 | 100 | /** 101 | * To add user properties of array type 102 | * @param string $distinct_id distinct ID 103 | * @param string $account_id account ID 104 | * @param array $properties properties 105 | * @return boolean 106 | * @throws Exception exception 107 | */ 108 | public function user_append($distinct_id, $account_id, $properties = array()) 109 | { 110 | return $this->add($distinct_id, $account_id, USER_TYPE_APPEND, null, null, $properties); 111 | } 112 | 113 | /** 114 | * Append user properties to array type by unique. 115 | * @param string $distinct_id distinct ID 116 | * @param string $account_id account ID 117 | * @param array $properties properties 118 | * @return boolean 119 | * @throws Exception exception 120 | */ 121 | public function user_uniq_append($distinct_id, $account_id, $properties = array()) 122 | { 123 | return $this->add($distinct_id, $account_id, USER_TYPE_UNIQUE_APPEND, null, null, $properties); 124 | } 125 | 126 | /** 127 | * Clear the user properties of users 128 | * @param string $distinct_id distinct ID 129 | * @param string $account_id account ID 130 | * @param array $properties properties 131 | * @return boolean 132 | * @throws Exception exception 133 | */ 134 | public function user_unset($distinct_id, $account_id, $properties = array()) 135 | { 136 | if ($this->isStrict() && is_null($properties)) { 137 | throw new ThinkingDataException("property cannot be empty ."); 138 | } 139 | $arr = array_fill_keys($properties, 0); 140 | return $this->add($distinct_id, $account_id, USER_TYPE_UNSET, null, null, $arr); 141 | } 142 | 143 | /** 144 | * Delete a user, This operation cannot be undone 145 | * @param $distinct_id 146 | * @param $account_id 147 | * @param $properties 148 | * @return mixed 149 | * @throws Exception exception 150 | */ 151 | public function user_del($distinct_id, $account_id, $properties = array()) 152 | { 153 | return $this->add($distinct_id, $account_id, USER_TYPE_DEL, null, null, $properties); 154 | } 155 | 156 | /** 157 | * Report ordinary event 158 | * @param string $distinct_id distinct ID 159 | * @param string $account_id account ID 160 | * @param string $event_name event name 161 | * @param array $properties properties 162 | * @return boolean 163 | * @throws Exception exception 164 | */ 165 | public function track($distinct_id, $account_id, $event_name, $properties = array()) 166 | { 167 | $this->checkEventName($event_name); 168 | return $this->add($distinct_id, $account_id, TRACK_TYPE_NORMAL, $event_name, null, $properties); 169 | } 170 | 171 | /** 172 | * Report first event. 173 | * @param string $distinct_id distinct ID 174 | * @param string $account_id account ID 175 | * @param string $event_name event name 176 | * @param string $first_check_id event id 177 | * @param array $properties properties 178 | * @return boolean 179 | * @throws Exception exception 180 | */ 181 | public function track_first($distinct_id, $account_id, $event_name, $first_check_id, $properties = array()) 182 | { 183 | $this->checkEventName($event_name); 184 | $this->checkEventId($first_check_id); 185 | return $this->add($distinct_id, $account_id, TRACK_TYPE_FIRST, $event_name, $first_check_id, $properties); 186 | } 187 | 188 | /** 189 | * Report updatable event 190 | * @param string $distinct_id distinct ID 191 | * @param string $account_id account ID 192 | * @param string $event_name event name 193 | * @param string $event_id event id 194 | * @param array $properties properties 195 | * @return boolean 196 | * @throws Exception exception 197 | */ 198 | public function track_update($distinct_id, $account_id, $event_name, $event_id, $properties = array()) 199 | { 200 | $this->checkEventName($event_name); 201 | $this->checkEventId($event_id); 202 | return $this->add($distinct_id, $account_id, TRACK_TYPE_UPDATE, $event_name, $event_id, $properties); 203 | } 204 | 205 | /** 206 | * Report overwrite event. 207 | * @param $distinct_id string 208 | * @param $account_id string 209 | * @param $event_name string 210 | * @param $event_id string 211 | * @param $properties array 212 | * @return mixed 213 | * @throws Exception exception 214 | */ 215 | public function track_overwrite($distinct_id, $account_id, $event_name, $event_id, $properties = array()) 216 | { 217 | $this->checkEventName($event_name); 218 | $this->checkEventId($event_id); 219 | return $this->add($distinct_id, $account_id, TRACK_TYPE_OVERWRITE, $event_name, $event_id, $properties); 220 | } 221 | 222 | /** 223 | * Check event name 224 | * @throws Exception exception 225 | */ 226 | private function checkEventName($eventName) { 227 | if ($this->isStrict() && (!is_string($eventName) || empty($eventName))) { 228 | throw new ThinkingDataException("event name is not be empty"); 229 | } 230 | } 231 | 232 | /** 233 | * Check event id 234 | * @throws Exception exception 235 | */ 236 | private function checkEventId($eventId) { 237 | if ($this->isStrict() && empty($eventId)) { 238 | throw new ThinkingDataException("event id is not be empty"); 239 | } 240 | } 241 | 242 | /** 243 | * @throws Exception exception 244 | */ 245 | private function checkAccountIdAndDistinctId($accountId, $distinctId) { 246 | if ($this->isStrict() && empty($accountId) && empty($distinctId)) { 247 | throw new ThinkingDataException("account id and distinct id can't be both empty"); 248 | } 249 | } 250 | 251 | /** 252 | * @param $distinct_id string 253 | * @param $account_id string 254 | * @param $type string 255 | * @param $event_name string 256 | * @param $event_id string 257 | * @param $properties array 258 | * @return mixed 259 | * @throws Exception exception 260 | */ 261 | private function add($distinct_id, $account_id, $type, $event_name, $event_id, $properties) 262 | { 263 | $this->checkAccountIdAndDistinctId($account_id, $distinct_id); 264 | 265 | $event = array(); 266 | if ($type == TRACK_TYPE_NORMAL || $type == TRACK_TYPE_FIRST || $type == TRACK_TYPE_UPDATE || $type == TRACK_TYPE_OVERWRITE) { 267 | $properties = $this->merge_dynamic_public_properties($properties); 268 | $properties = $this->merge_public_properties($properties); 269 | if (!empty($event_id)) { 270 | $key = $type == TRACK_TYPE_FIRST ? '#first_check_id' : '#event_id'; 271 | $event[$key] = $event_id; 272 | } 273 | $type = $type == TRACK_TYPE_FIRST ? TRACK_TYPE_NORMAL : $type; 274 | } 275 | $event['#type'] = $type; 276 | if ($distinct_id) { 277 | $event['#distinct_id'] = $distinct_id; 278 | } 279 | if ($account_id) { 280 | $event['#account_id'] = $account_id; 281 | } 282 | if ($event_name) { 283 | $event['#event_name'] = $event_name; 284 | } 285 | if (array_key_exists('#ip', $properties)) { 286 | $event['#ip'] = $this->extractStringProperty('#ip', $properties); 287 | } 288 | $event['#time'] = $this->extractUserTime($properties); 289 | if (array_key_exists('#app_id', $properties)) { 290 | $event['#app_id'] = $this->extractStringProperty('#app_id', $properties); 291 | } 292 | // #uuid is v4 type 293 | if (array_key_exists('#uuid', $properties)) { 294 | $event['#uuid'] = $properties['#uuid']; 295 | unset($properties['#uuid']); 296 | } elseif ($this->enableUUID) { 297 | $event['#uuid'] = $this->uuid(); 298 | } 299 | 300 | $properties = $this->assertProperties($type, $properties); 301 | if (count($properties) > 0) { 302 | $event['properties'] = $properties; 303 | } 304 | $jsonStr = json_encode($event); 305 | return $this->consumer->send($jsonStr); 306 | } 307 | 308 | /** 309 | * @throws Exception exception 310 | */ 311 | private function assertProperties($type, $properties) 312 | { 313 | // check properties 314 | if (is_array($properties) && !empty($properties)) { 315 | foreach ($properties as $key => &$value) { 316 | // format date to string 317 | if ($value instanceof DateTime) { 318 | $properties[$key] = $this->getFormatDate($value->getTimestamp()); 319 | } 320 | if (is_array($value)) { 321 | if (array_values($value) === $value) { 322 | for ($i = 0; $i < count($value); $i++) { 323 | if ($value[$i] instanceof DateTime) { 324 | $value[$i] = $this->getFormatDate($value[$i]->getTimestamp()); 325 | } 326 | } 327 | } else { 328 | foreach ($value as $k => $v) { 329 | if ($v instanceof DateTime) { 330 | $value[$k] = $this->getFormatDate($v->getTimestamp()); 331 | } 332 | } 333 | } 334 | } 335 | if (($this->isStrict())) { 336 | if (is_null($value)) { 337 | continue; 338 | } 339 | if (!is_string($key)) { 340 | throw new ThinkingDataException("property key must be a str. [key=$key]"); 341 | } 342 | if (strlen($key) > 50) { 343 | throw new ThinkingDataException("the max length of property key is 50. [key=$key]"); 344 | } 345 | if (!preg_match(NAME_PATTERN, $key)) { 346 | throw new ThinkingDataException("property key must be a valid variable name. [key='$key']]"); 347 | } 348 | if (!is_scalar($value) && !$value instanceof DateTime && !is_array($value)) { 349 | throw new ThinkingDataException("property value must be a str/int/float/datetime/array. [key='$key']"); 350 | } 351 | if ($type == 'user_add' && !is_numeric($value)) { 352 | throw new ThinkingDataException("Type user_add only support Number [key='$key']"); 353 | } 354 | } 355 | } 356 | } 357 | return $properties; 358 | } 359 | 360 | public function getDatetime() 361 | { 362 | return $this->getFormatDate(); 363 | } 364 | 365 | /** 366 | * @param $time 367 | * @param $format 368 | * @return false|string 369 | */ 370 | function getFormatDate($time = null, $format = 'Y-m-d H:i:s.u') 371 | { 372 | $nowTimestamp = microtime(true); 373 | $timestamp = floor($nowTimestamp); 374 | $milliseconds = round(($nowTimestamp - $timestamp) * 1000); 375 | if ($milliseconds == 1000) { 376 | $timestamp = strtotime("+1second", $timestamp); 377 | $milliseconds = 0; 378 | } 379 | $new_format = preg_replace('`(?getDatetime(); 394 | } 395 | 396 | private function extractStringProperty($key, &$properties = array()) 397 | { 398 | if (array_key_exists($key, $properties)) { 399 | $value = $properties[$key]; 400 | unset($properties[$key]); 401 | return $value; 402 | } 403 | return ''; 404 | } 405 | 406 | function uuid() 407 | { 408 | return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', 409 | // 32 bits for "time_low" 410 | mt_rand(0, 0xffff), mt_rand(0, 0xffff), 411 | // 16 bits for "time_mid" 412 | mt_rand(0, 0xffff), 413 | // 16 bits for "time_hi_and_version", 414 | // four most significant bits holds version number 4 415 | mt_rand(0, 0x0fff) | 0x4000, 416 | // 16 bits, 8 bits for "clk_seq_hi_res", 417 | // 8 bits for "clk_seq_low", 418 | // two most significant bits holds zero and one for variant DCE1.1 419 | mt_rand(0, 0x3fff) | 0x8000, 420 | // 48 bits for "node" 421 | mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff) 422 | ); 423 | } 424 | 425 | /** 426 | * clear common properties 427 | */ 428 | public function clear_public_properties() 429 | { 430 | $this->publicProperties = array( 431 | '#lib' => SDK_LIB_NAME, 432 | '#lib_version' => SDK_VERSION, 433 | ); 434 | } 435 | 436 | /** 437 | * set common properties 438 | * 439 | * @param array $super_properties properties 440 | */ 441 | public function register_public_properties($super_properties) 442 | { 443 | $this->publicProperties = array_merge($this->publicProperties, $super_properties); 444 | } 445 | 446 | public function merge_public_properties($properties) 447 | { 448 | foreach ($this->publicProperties as $key => $value) { 449 | if (!isset($properties[$key])) { 450 | $properties[$key] = $value; 451 | } 452 | } 453 | return $properties; 454 | } 455 | 456 | /** 457 | * set dynamic common properties 458 | */ 459 | public function register_dynamic_public_properties($callback) 460 | { 461 | if (!empty($callback) && function_exists($callback)) { 462 | $this->dynamicPublicPropertiesCallback = $callback; 463 | } else { 464 | TDLog::log("dynamic common properties function is error"); 465 | } 466 | } 467 | 468 | public function merge_dynamic_public_properties($properties) 469 | { 470 | if (!empty($this->dynamicPublicPropertiesCallback)) { 471 | if (function_exists($this->dynamicPublicPropertiesCallback)) { 472 | $dynamicPublicProperties = call_user_func($this->dynamicPublicPropertiesCallback); 473 | if ($dynamicPublicProperties) { 474 | foreach ($dynamicPublicProperties as $key => $value) { 475 | if (!isset($properties[$key])) { 476 | $properties[$key] = $value; 477 | } 478 | } 479 | } 480 | } 481 | } 482 | return $properties; 483 | } 484 | 485 | /** 486 | * report data immediately 487 | */ 488 | public function flush() 489 | { 490 | $this->consumer->flush(); 491 | } 492 | 493 | /** 494 | * close and exit sdk 495 | */ 496 | public function close() 497 | { 498 | $this->consumer->close(); 499 | TDLog::log("SDK close"); 500 | } 501 | 502 | /** 503 | * @return bool get strict status 504 | */ 505 | private function isStrict() { 506 | return $this->consumer->getStrictStatus(); 507 | } 508 | } 509 | 510 | /** 511 | * Abstract consumer 512 | */ 513 | abstract class TDAbstractConsumer 514 | { 515 | /** 516 | * @var bool $strict check properties or not 517 | * true: the properties which invalidate will be dropped. 518 | * false: upload data anyway 519 | */ 520 | protected $strict = false; 521 | 522 | /** 523 | * Get strict status 524 | * @return bool 525 | */ 526 | public function getStrictStatus() { 527 | return $this->strict; 528 | } 529 | 530 | /** 531 | * report data 532 | * @param string $message data 533 | * @return bool 534 | */ 535 | public abstract function send($message); 536 | 537 | /** 538 | * report data immediately 539 | * @return bool 540 | */ 541 | public function flush() 542 | { 543 | return true; 544 | } 545 | 546 | /** 547 | * close and release resource 548 | * @return bool 549 | */ 550 | public abstract function close(); 551 | } 552 | 553 | /** 554 | * [Deprecated class] 555 | * @deprecated please use TDFileConsumer 556 | */ 557 | class FileConsumer extends TDFileConsumer {} 558 | 559 | 560 | /** 561 | * Write data to file, it works with LogBus. not support multiple thread 562 | */ 563 | class TDFileConsumer extends TDAbstractConsumer 564 | { 565 | private $fileHandler; 566 | private $fileName; 567 | private $fileDirectory; 568 | private $filePrefix; 569 | private $fileSize; 570 | private $rotateHourly; 571 | private $buffers; 572 | private $bufferSize; 573 | private $permission; 574 | 575 | /** 576 | * init FileConsumer 577 | * 578 | * @param string $file_directory directory of log file 579 | * @param int $file_size max size of single log file (MByte) 580 | * @param bool $rotate_hourly rotate by hour or not 581 | * @param string $file_prefix prefix of file 582 | */ 583 | function __construct($file_directory = '.', $file_size = 0, $rotate_hourly = false, $file_prefix = '', $bufferSize = 100) 584 | { 585 | TDLog::log("File consumer init success. Log_directory:" . $file_directory); 586 | $this->fileDirectory = $file_directory; 587 | if (!is_dir($file_directory)) { 588 | mkdir($file_directory, 0777, true); 589 | } 590 | $this->fileSize = $file_size; 591 | $this->rotateHourly = $rotate_hourly; 592 | $this->filePrefix = $file_prefix; 593 | $this->fileName = $this->getFileName(); 594 | $this->strict = false; 595 | $this->buffers = array(); 596 | $this->bufferSize = $bufferSize; 597 | $this->permission = TD_LOG_FILE_DEFAULT_PERMISSION; 598 | TDLog::$enable = false; 599 | } 600 | 601 | public function setLogFilePermission($permission) 602 | { 603 | if ($permission < TD_LOG_FILE_DEFAULT_PERMISSION) { 604 | $permission = TD_LOG_FILE_DEFAULT_PERMISSION; 605 | } 606 | $this->permission = $permission; 607 | } 608 | 609 | /** 610 | * write data 611 | * @param $message 612 | * @return bool|int 613 | */ 614 | public function send($message) 615 | { 616 | $this->buffers[] = $message . "\n"; 617 | if (count($this->buffers) >= $this->bufferSize) { 618 | return $this->flush(); 619 | } else { 620 | TDLog::log("Enqueue data: $message"); 621 | return true; 622 | } 623 | } 624 | 625 | /** 626 | * @return bool|int 627 | */ 628 | public function flush() 629 | { 630 | $file_name = $this->getFileName(); 631 | if ($this->fileHandler != null && $this->fileName != $file_name) { 632 | fclose($this->fileHandler); 633 | $this->fileName = $file_name; 634 | $this->fileHandler = null; 635 | } 636 | if ($this->fileHandler === null) { 637 | if (file_exists($file_name)) { 638 | $this->fileHandler = fopen($file_name, 'a+'); 639 | } else { 640 | $this->fileHandler = fopen($file_name, 'a+'); 641 | chmod($file_name, $this->permission); 642 | } 643 | } 644 | if (flock($this->fileHandler, LOCK_EX)) { 645 | $flush_cont = count($this->buffers); 646 | TDLog::log("Flush data, count: $flush_cont"); 647 | $result = fwrite($this->fileHandler, join("", $this->buffers)); 648 | flock($this->fileHandler, LOCK_UN); 649 | $this->buffers = array(); 650 | return $result; 651 | } 652 | return true; 653 | } 654 | 655 | public function close() 656 | { 657 | $this->flush(); 658 | TDLog::log("File consumer close"); 659 | if ($this->fileHandler === null) { 660 | return false; 661 | } 662 | return fclose($this->fileHandler); 663 | } 664 | 665 | private function getFileName() 666 | { 667 | $date_format = $this->rotateHourly ? 'Y-m-d-H' : 'Y-m-d'; 668 | $file_prefix = $this->filePrefix == '' ? '' : $this->filePrefix . '.'; 669 | $file_base = $this->fileDirectory . '/' . $file_prefix . 'log.' . date($date_format, time()) . "_"; 670 | $count = 0; 671 | $file_complete = $file_base . $count; 672 | if ($this->fileSize > 0) { 673 | while (file_exists($file_complete) && $this->fileSizeOut($file_complete)) { 674 | $count += 1; 675 | $file_complete = $file_base . $count; 676 | } 677 | } 678 | return $file_complete; 679 | } 680 | 681 | public function fileSizeOut($fp) 682 | { 683 | clearstatcache(); 684 | $fpSize = filesize($fp) / (1024 * 1024); 685 | if ($fpSize >= $this->fileSize) { 686 | return true; 687 | } else { 688 | return false; 689 | } 690 | } 691 | } 692 | 693 | /** 694 | * [Deprecated class] 695 | * @deprecated please use TDBatchConsumer 696 | */ 697 | class BatchConsumer extends TDBatchConsumer {} 698 | 699 | /** 700 | * upload data to TE by http. not support multiple thread 701 | */ 702 | class TDBatchConsumer extends TDAbstractConsumer 703 | { 704 | private $url; 705 | private $appid; 706 | private $buffers; 707 | private $maxSize; 708 | private $requestTimeout; 709 | private $compress = true; 710 | private $retryTimes; 711 | private $isThrowException = false; 712 | private $cacheBuffers; 713 | private $cacheCapacity; 714 | 715 | /** 716 | * init BatchConsumer 717 | * @param string $server_url server url 718 | * @param string $appid APP ID 719 | * @param int $max_size flush event count each time 720 | * @param int $retryTimes : retry times, default 3 721 | * @param int $request_timeout : http timeout, default 5s 722 | * @param int $cache_capacity : Multiple of $max_size, It determines the cache size 723 | * @throws ThinkingDataException 724 | */ 725 | function __construct($server_url, $appid, $max_size = 20, $retryTimes = 3, $request_timeout = 5, $cache_capacity = 50) 726 | { 727 | TDLog::log("Batch consumer init success. AppId:" . $appid . ", receiverUrl:" . $server_url); 728 | 729 | $this->buffers = array(); 730 | $this->appid = $appid; 731 | $this->maxSize = $max_size; 732 | $this->retryTimes = $retryTimes; 733 | $this->requestTimeout = $request_timeout; 734 | $parsed_url = parse_url($server_url); 735 | $this->cacheBuffers = array(); 736 | $this->cacheCapacity = $cache_capacity; 737 | if ($parsed_url === false) { 738 | throw new ThinkingDataException("Invalid server url"); 739 | } 740 | $this->url = $parsed_url['scheme'] . "://" . $parsed_url['host'] 741 | . ((isset($parsed_url['port'])) ? ':' . $parsed_url['port'] : '') 742 | . '/sync_server'; 743 | $this->strict = false; 744 | } 745 | 746 | /** 747 | * @throws ThinkingDataNetWorkException 748 | * @throws ThinkingDataException 749 | */ 750 | public function __destruct() 751 | { 752 | $this->flush(); 753 | } 754 | 755 | /** 756 | * @param $message 757 | * @return bool|null 758 | * @throws ThinkingDataException 759 | * @throws ThinkingDataNetWorkException 760 | */ 761 | public function send($message) 762 | { 763 | $this->buffers[] = $message; 764 | if (count($this->buffers) >= $this->maxSize) { 765 | return $this->flush(); 766 | } else { 767 | TDLog::log("Enqueue data: $message"); 768 | return null; 769 | } 770 | } 771 | 772 | /** 773 | * Flush data 774 | * 775 | * @param $flag 776 | * @return bool 777 | * @throws ThinkingDataException 778 | * @throws ThinkingDataNetWorkException 779 | */ 780 | public function flush($flag = false) 781 | { 782 | TDLog::log("Flush data"); 783 | if (empty($this->buffers) && empty($this->cacheBuffers)) { 784 | return true; 785 | } 786 | if ($flag || count($this->buffers) >= $this->maxSize || count($this->cacheBuffers) == 0) { 787 | $sendBuffers = $this->buffers; 788 | $this->buffers = array(); 789 | $this->cacheBuffers[] = $sendBuffers; 790 | } 791 | while (count($this->cacheBuffers) > 0) { 792 | $sendBuffers = $this->cacheBuffers[0]; 793 | 794 | try { 795 | $this->doRequest($sendBuffers); 796 | array_shift($this->cacheBuffers); 797 | if ($flag) { 798 | continue; 799 | } 800 | break; 801 | } catch (ThinkingDataNetWorkException $netWorkException) { 802 | if (count($this->cacheBuffers) > $this->cacheCapacity) { 803 | array_shift($this->cacheBuffers); 804 | } 805 | 806 | if ($this->isThrowException) { 807 | throw $netWorkException; 808 | } 809 | return false; 810 | } catch (ThinkingDataException $dataException) { 811 | array_shift($this->cacheBuffers); 812 | 813 | if ($this->isThrowException) { 814 | throw $dataException; 815 | } 816 | return false; 817 | } 818 | } 819 | return true; 820 | } 821 | 822 | /** 823 | * Close consumer 824 | * 825 | * @throws ThinkingDataNetWorkException 826 | * @throws ThinkingDataException 827 | */ 828 | public function close() 829 | { 830 | $this->flush(true); 831 | TDLog::log("Batch consumer close"); 832 | } 833 | 834 | public function setCompress($compress = true) 835 | { 836 | $this->compress = $compress; 837 | } 838 | 839 | public function setFlushSize($max_size = 20) 840 | { 841 | $this->maxSize = $max_size; 842 | } 843 | 844 | public function openThrowException() 845 | { 846 | $this->isThrowException = true; 847 | } 848 | 849 | /** 850 | * @throws ThinkingDataNetWorkException 851 | * @throws ThinkingDataException 852 | */ 853 | private function doRequest($message_array) 854 | { 855 | $consoleMessages = implode(PHP_EOL, $message_array); 856 | TDLog::log("Send data, request: [\n$consoleMessages\n]"); 857 | 858 | $ch = curl_init($this->url); 859 | 860 | curl_setopt($ch, CURLOPT_POST, 1); 861 | curl_setopt($ch, CURLOPT_HEADER, 0); 862 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 6); 863 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 864 | curl_setopt($ch, CURLOPT_TIMEOUT, $this->requestTimeout); 865 | 866 | if ($this->compress) { 867 | $data = gzencode("[" . implode(", ", $message_array) . "]"); 868 | } else { 869 | $data = "[" . implode(", ", $message_array) . "]"; 870 | } 871 | $compressType = $this->compress ? "gzip" : "none"; 872 | curl_setopt($ch, CURLOPT_POSTFIELDS, $data); 873 | //headers 874 | curl_setopt($ch, CURLOPT_HTTPHEADER, array("TA-Integration-Type:PHP", "TA-Integration-Version:" . SDK_VERSION, 875 | "TA-Integration-Count:" . count($message_array), "appid:" . $this->appid, "compress:" . $compressType, 'Content-Type: text/plain')); 876 | 877 | //https 878 | $pos = strpos($this->url, "https"); 879 | if ($pos === 0) { 880 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 881 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 882 | } 883 | 884 | // send request 885 | $curreyRetryTimes = 0; 886 | while ($curreyRetryTimes++ < $this->retryTimes) { 887 | $result = curl_exec($ch); 888 | TDLog::log("Send data, response: $result"); 889 | 890 | if (!$result) { 891 | echo new ThinkingDataNetWorkException("Cannot post message to server , error --> " . curl_error(($ch))); 892 | continue; 893 | } 894 | // parse data 895 | $json = json_decode($result, true); 896 | 897 | $curl_info = curl_getinfo($ch); 898 | 899 | curl_close($ch); 900 | if ($curl_info['http_code'] == 200) { 901 | if ($json['code'] == 0) { 902 | return; 903 | } else if ($json['code'] == -1) { 904 | throw new ThinkingDataException("data formatter is invalidated, code = -1"); 905 | } else if ($json['code'] == -2) { 906 | throw new ThinkingDataException("app id is invalidated, code = -2"); 907 | } else if ($json['code'] == -3) { 908 | throw new ThinkingDataException("ip is invalidated, code = -3"); 909 | } else { 910 | throw new ThinkingDataException("failed, code = " . $json['code']); 911 | } 912 | } else { 913 | echo new ThinkingDataNetWorkException("failed, http_code: " . $curl_info['http_code']); 914 | } 915 | } 916 | throw new ThinkingDataNetWorkException("retry " . $this->retryTimes . " times, but failed!"); 917 | } 918 | } 919 | 920 | /** 921 | * [Deprecated class] 922 | * @deprecated please use TDDebugConsumer 923 | */ 924 | class DebugConsumer extends TDDebugConsumer {} 925 | 926 | 927 | /** 928 | * The data is reported one by one, and when an error occurs, the exception will be thrown 929 | */ 930 | class TDDebugConsumer extends TDAbstractConsumer 931 | { 932 | private $url; 933 | private $appid; 934 | private $requestTimeout; 935 | private $writerData = true; 936 | private $deviceId; 937 | 938 | /** 939 | * init DebugConsumer 940 | * @param string $server_url server url 941 | * @param string $appid APP ID 942 | * @param int $request_timeout http timeout, default 5s 943 | * @throws ThinkingDataException 944 | */ 945 | function __construct($server_url, $appid, $request_timeout = 5, $deviceId = null) 946 | { 947 | TDLog::log("Debug consumer init success. AppId:" . $appid . ", receiverUrl:" . $server_url); 948 | 949 | $parsed_url = parse_url($server_url); 950 | if ($parsed_url === false) { 951 | throw new ThinkingDataException("Invalid server url"); 952 | } 953 | 954 | $this->url = $parsed_url['scheme'] . "://" . $parsed_url['host'] 955 | . ((isset($parsed_url['port'])) ? ':' . $parsed_url['port'] : '') 956 | . '/data_debug'; 957 | 958 | $this->appid = $appid; 959 | $this->requestTimeout = $request_timeout; 960 | $this->strict = true; 961 | $this->deviceId = $deviceId; 962 | } 963 | 964 | /** 965 | * @throws ThinkingDataNetWorkException 966 | * @throws ThinkingDataException 967 | */ 968 | public function send($message) 969 | { 970 | return $this->doRequest($message); 971 | } 972 | 973 | public function setDebugOnly($writer_data = true) 974 | { 975 | $this->writerData = $writer_data; 976 | } 977 | 978 | public function close() 979 | { 980 | } 981 | 982 | /** 983 | * @throws ThinkingDataNetWorkException 984 | * @throws ThinkingDataException 985 | */ 986 | private function doRequest($message) 987 | { 988 | TDLog::log("Send data, request: $message"); 989 | 990 | $ch = curl_init($this->url); 991 | $dryRun = $this->writerData ? 0 : 1; 992 | $data = "source=server&appid=" . $this->appid . "&dryRun=" . $dryRun . "&data=" . urlencode($message); 993 | 994 | if (is_string($this->deviceId) && strlen($this->deviceId) > 0) { 995 | $data = $data . "&deviceId=" . $this->deviceId; 996 | } 997 | 998 | curl_setopt($ch, CURLOPT_POST, 1); 999 | curl_setopt($ch, CURLOPT_HEADER, 0); 1000 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 6); 1001 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 1002 | curl_setopt($ch, CURLOPT_TIMEOUT, $this->requestTimeout); 1003 | curl_setopt($ch, CURLOPT_POSTFIELDS, $data); 1004 | 1005 | //https 1006 | $pos = strpos($this->url, "https"); 1007 | if ($pos === 0) { 1008 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 1009 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 1010 | } 1011 | 1012 | $result = curl_exec($ch); 1013 | 1014 | TDLog::log("Send data, response: $result"); 1015 | 1016 | if (!$result) { 1017 | throw new ThinkingDataNetWorkException("Cannot post message to server , error -->" . curl_error(($ch))); 1018 | } 1019 | 1020 | // parse data 1021 | $json = json_decode($result, true); 1022 | 1023 | $curl_info = curl_getinfo($ch); 1024 | 1025 | curl_close($ch); 1026 | if ($curl_info['http_code'] == 200) { 1027 | if ($json['errorLevel'] == 0) { 1028 | return true; 1029 | } else { 1030 | TDLog::log("Unexpected Return Code:" . $json['errorLevel'] . " for: " . $message); 1031 | throw new ThinkingDataException(print_r($json, true)); 1032 | } 1033 | } else { 1034 | throw new ThinkingDataNetWorkException("failed. HTTP code: " . $curl_info['http_code'] . "\t return content :" . $result); 1035 | } 1036 | } 1037 | } 1038 | 1039 | /** 1040 | * [Deprecated class] 1041 | * @deprecated please use TDLog 1042 | */ 1043 | class TALogger extends TDLog {} 1044 | 1045 | /** 1046 | * Log module 1047 | */ 1048 | class TDLog { 1049 | static $enable = false; 1050 | static function log() { 1051 | if (self::$enable) { 1052 | $params = implode("", func_get_args()); 1053 | $time = date("Y-m-d H:i:s", time()); 1054 | echo "[ThinkingData][$time]: ", $params, PHP_EOL; 1055 | } 1056 | } 1057 | } --------------------------------------------------------------------------------