├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE ├── PULL_REQUEST_TEMPLATE └── SUPPORT.md ├── .gitignore ├── .travis.yml ├── LICENSE ├── NOTICE ├── README.md ├── composer.json ├── composer.lock ├── lib ├── API.php ├── Error.php └── data │ └── ca-certificates.pem ├── test ├── APITest.php ├── bootstrap.php ├── test_img.png └── test_txt.txt └── travisci-phpunit.xml /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Auto tag Sendwithus employees on new Issues/Pull Requests 2 | * @sendwithus/review-team -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE: -------------------------------------------------------------------------------- 1 | ### Client version 2 | 3 | ### Expected behaviour 4 | 5 | ### Actual behaviour 6 | 7 | ### Steps to reproduce 8 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | 6 | ## Motivation and Context 7 | 8 | 9 | 10 | ## How Has This Been Tested? 11 | 12 | 13 | 14 | 15 | ## Types of changes 16 | 17 | - [ ] Bug fix (non-breaking change which fixes an issue) 18 | - [ ] New feature (non-breaking change which adds functionality) 19 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 20 | 21 | ## Checklist: 22 | 23 | 24 | - [ ] My code follows the code style of this project. 25 | - [ ] My change requires a change to the documentation. 26 | - [ ] I have updated the documentation accordingly. 27 | - [ ] I have added tests to cover my changes. 28 | - [ ] All new and existing tests passed. 29 | -------------------------------------------------------------------------------- /.github/SUPPORT.md: -------------------------------------------------------------------------------- 1 | Please only file issues that you believe represent actual bugs or feature requests for this API client. 2 | 3 | If you are having issues with your Sendwithus integration, have questions about email, or have found a bug with Sendwithus’ API please reach out to support@sendwithus.com and our support team will be happy to help. 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tmp/* 2 | [Cc]onfig/core.php 3 | [Cc]onfig/database.php 4 | app/tmp/* 5 | app/[Cc]onfig/core.php 6 | app/[Cc]onfig/database.php 7 | !empty 8 | .DS_Store 9 | lib/.DS_Store 10 | test/.DS_Store 11 | *.swo 12 | *.swp 13 | vendor 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | matrix: 3 | include: 4 | - php: 7.0 5 | - php: 5.6 6 | - php: 5.5 7 | - php: 5.4 8 | - php: 5.3 9 | dist: precise 10 | script: phpunit --configuration travisci-phpunit.xml 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2013 Techdrop Labs Inc. 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Apache sendwithus 2 | Copyright 2013 The Apache Software Foundation 3 | 4 | This product includes software developed at Techdrop Labs Inc. 5 | The Apache Software Foundation (http://www.apache.org/). -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | sendwithus_php 2 | ============== 3 | 4 | Sendwithus PHP Client 5 | 6 | ## Status 7 | [](https://travis-ci.org/sendwithus/sendwithus_php) 8 | 9 | ## Requirements 10 | curl library must be installed and enabled in php.ini 11 | 12 | Install it via Composer 13 | ----------------------- 14 | 15 | Add it to your composer.json 16 | ```json 17 | { 18 | "require": { 19 | "sendwithus/api": "^6.4" 20 | } 21 | } 22 | ``` 23 | Then install it with 24 | 25 | composer install 26 | 27 | 28 | ## Getting started 29 | 30 | ```php 31 | // Yii Users 32 | Yii::$classMap = array( 33 | 'sendwithus\\API' => dirname($_SERVER['DOCUMENT_ROOT']) . '/path/to/sendwithus/lib/API.php' 34 | ); 35 | 36 | // composer users 37 | use sendwithus\API; 38 | 39 | require_once 'vendor/autoload.php'; 40 | 41 | 42 | $API_KEY = 'THIS_IS_A_TEST_API_KEY'; 43 | $options = array( 44 | 'DEBUG' => true, 45 | 'API_DEBUG_HANDLER' => function ($message, $priority_level) { 46 | // possible priority levels - http://php.net/manual/en/function.syslog.php 47 | error_log("[SendWithUs][$priority_level] " . $message); 48 | } 49 | ); 50 | 51 | $api = new API($API_KEY, $options); 52 | ``` 53 | 54 | # Emails 55 | 56 | ## Get emails 57 | 58 | ```php 59 | $response = $api->emails(); 60 | ``` 61 | 62 | ## Get specific template 63 | ```php 64 | $response = $api->get_template($template_id, //string id of template 65 | $version_id //optional string version id of template 66 | ); 67 | ``` 68 | 69 | 70 | ## Create emails 71 | ### Create new email 72 | _We validate all HTML content_ 73 | ```php 74 | $response = $api->create_email('Email Name', // string email name 75 | 'Email Subject', // string subject line of email 76 | '
Valid HTML', // string of HTML code for email 77 | 'Optional text content') // optional string of text for email 78 | ``` 79 | 80 | ### Create new email template version 81 | _We validate all HTML content_ 82 | ```php 83 | $response = $api->create_new_template_version( 84 | 'Email Name', // string email version name 85 | 'Email Subject', // string subject of email 86 | 'tem_JAksjdjwJXUVwnemljflksEJks', // string id of email used 87 | 'Valid HTML', // string block of HTML code used for email 88 | 'Optional text content') // optional string of text used for email 89 | ``` 90 | 91 | ### Update email version 92 | _We validate all HTML content_ 93 | ```php 94 | $response = $api->update_template_version( 95 | 'Email Name', // string email version name 96 | 'Email Subject', // string subject of email 97 | 'tem_JAkCjdjwJXUVwnemljflksEJks', // string id of email being updated 98 | 'ver_iuweJskj4Jwkj2ndclk4jJDken', // string version of email being updated 99 | 'Valid HTML', // string block of HTML code used for email 100 | 'Optional text content') // optional string of text used for email 101 | ``` 102 | 103 | 104 | ## Send emails 105 | 106 | *NOTE* - If a customer does not exist by the specified email (recipient address), the send call will create a customer. 107 | 108 | ```php 109 | // Send function header 110 | send( 111 | $email_id, // string, id of email to send (template id) 112 | $recipient, // associative array, ("address" => "ckent@dailyplanet.com", "name" => "Clark") to send to 113 | $args // (optional) array, (array) additional parameters - (see below) 114 | ) 115 | 116 | // Send function options 117 | 'template_data' // array of variables to merge into the template. 118 | 'sender' // array ("address", "name", "reply_to") of sender. 119 | 'cc' // array of ("address", "name") for carbon copy. 120 | 'bcc' // array of ("address", "name") for blind carbon copy. 121 | 'inline' // string, path to file to include inline 122 | // or an associative array with "id" containing filename 123 | // and "data" containing base64 encoded file content 124 | 'files' // array, each element represents either a string path to file to attach 125 | // or an associative array with "id" containing filename 126 | // and "data" containing base64 encoded file content 127 | 'tags' // array of strings to tag email send with. 128 | 'esp_account' // string of ESP ID to manually select ESP 129 | 'headers' // associative array of header name and value 130 | ``` 131 | 132 | ## Send Examples 133 | 134 | ### Send request with REQUIRED parameters only 135 | 136 | ```php 137 | $response = $api->send('email_id', 138 | array('address' => 'us@sendwithus.com') 139 | ); 140 | ``` 141 | 142 | ### Send request with REQUIRED and OPTIONAL parameters 143 | 144 | ```php 145 | $response = $api->send('email_id', 146 | array( 147 | 'name' => 'Matt', 148 | 'address' => 'us@sendwithus.com'), 149 | array( 150 | 'template_data' => array('name' => 'Jimmy the snake'), 151 | 'sender' => array( 152 | 'name' => 'Company', 153 | 'address' => 'company@company.com', 154 | 'reply_to' => 'info@company.com' 155 | ), 156 | 'esp_account' => 'esp_EMpi5eo59cG4cCWd7AdW7J' 157 | ) 158 | ); 159 | ``` 160 | 161 | ### Send an email with multiple CC/BCC recipients 162 | 163 | ```php 164 | $response = $api->send('email_id', 165 | array( 166 | 'name' => 'Matt', 167 | 'address' => 'us@sendwithus.com' 168 | ), 169 | array( 170 | 'template_data' => array('name' => 'Jimmy the snake'), 171 | 'sender' => array( 172 | 'name' => 'Company', 173 | 'address' => 'company@company.com', 174 | 'reply_to' => 'info@company.com' 175 | ), 176 | 'cc' => array( 177 | array( 178 | 'name' => 'CC Name', 179 | 'address' => 'CC@company.com' 180 | ), 181 | array( 182 | 'name' => 'CC 2 Name', 183 | 'address' => 'CC2@company.com' 184 | ) 185 | ), 186 | 'bcc' => array( 187 | array( 188 | 'name' => 'BCC Name', 189 | 'address' => 'BCC@company.com' 190 | ) 191 | ) 192 | ) 193 | ); 194 | ``` 195 | 196 | ### Send an email with a dynamic tag 197 | 198 | ```php 199 | $response = $api->send('email_id', 200 | array( 201 | 'name' => 'Matt', 202 | 'address' => 'us@sendwithus.com'), 203 | array( 204 | 'tags' => array('Production', 'Client1') 205 | ) 206 | ); 207 | ``` 208 | 209 | ### Send specific version of an email 210 | 211 | ```php 212 | $response = $api->send('email_id', 213 | array( 214 | 'name' => 'Matt', 215 | 'address' => 'us@sendwithus.com'), 216 | array( 217 | 'version_name' => 'My Version' 218 | ) 219 | ); 220 | ``` 221 | 222 | ### Send email with an inline image attachment 223 | 224 | ```php 225 | $response = $api->send('email_id', 226 | array( 227 | 'name' => 'Matt', 228 | 'address' => 'us@sendwithus.com'), 229 | array( 230 | 'inline' => 'filename.jpg' 231 | ) 232 | ); 233 | ``` 234 | 235 | ### Send email with an inline encoded image attachment 236 | 237 | ```php 238 | $response = $api->send('email_id', 239 | array( 240 | 'name' => 'Matt', 241 | 'address' => 'us@sendwithus.com'), 242 | array( 243 | 'inline' => array( 244 | 'id' => 'photo.jpg', 245 | 'data' => base64_encode(file_get_contents('filename.jpg')) 246 | ) 247 | ) 248 | ); 249 | ``` 250 | 251 | ### Send email with attachments 252 | 253 | ```php 254 | $response = $api->send('email_id', 255 | array( 256 | 'name' => 'Matt', 257 | 'address' => 'us@sendwithus.com'), 258 | array( 259 | 'files' => array( 260 | 'filename.txt', 261 | 'filename.pdf', 262 | array( 263 | 'id' => 'photo.jpg', 264 | 'data' => base64_encode(file_get_contents('filename.jpg')) 265 | ) 266 | ) 267 | ) 268 | ); 269 | ``` 270 | 271 | 272 | ## Render templates 273 | 274 | ```php 275 | // Render function header 276 | render( 277 | $email_id, // string, id of email to send (template id) 278 | $args // (optional) array, (array) additional parameters - (see below) 279 | ) 280 | 281 | // Send function options 282 | 'template_data' // Array of variables to merge into the template. 283 | 'version_id' // Version ID obtained from /templates/(:template_id)/versions 284 | 'version_name' // Version name that you want rendered (provide either a version_name or a version_id, not both) 285 | 'locale' // Template locale to render 286 | 'strict' // Render in strict mode (fails on missing template data) 287 | ``` 288 | 289 | ### Example: 290 | 291 | ```php 292 | $response = $api->render('email_id', 293 | array('address' => 'us@sendwithus.com'), 294 | array( 295 | 'template_data' => array( 296 | 'name' => 'Bobby Boucher' 297 | ) 298 | ) 299 | ); 300 | ``` 301 | 302 | ## Get a Specific Email's Log 303 | ```php 304 | get_log( 305 | $log_id // id of log to retrieve 306 | ) 307 | ``` 308 | 309 | Example 310 | 311 | ```php 312 | $response = api->get_log('log_d4R7hV4d0r') 313 | ``` 314 | 315 | Response 316 | 317 | ```php 318 | ( 319 | [email_id] => tem_1jeid84bg 320 | [recipient_name] => 321 | [message] => Mandrill: Message has been successfully delivered to the receiving server. 322 | [id] => log_d4R7hV4d0r 323 | [object] => log 324 | [created] => 1409287597 325 | [email_name] => test 326 | [recipient_address] => person@example.com 327 | [status] => sent 328 | [email_version] => Original Version 329 | ) 330 | ``` 331 | 332 | ## Resend a Specific Email from Log 333 | ```php 334 | resend( 335 | $log_id // id of log to resend 336 | ) 337 | ``` 338 | 339 | Example 340 | 341 | ```php 342 | $response = api->resend('log_d4R7hV4d0r') 343 | ``` 344 | 345 | Response 346 | 347 | ```php 348 | ( 349 | [status] => OK 350 | [receipt_id] => 130be975-dc07-4071-9333-58530e5df052-i03a5q 351 | [email] => stdClass Object 352 | ( 353 | [locale] => en-US 354 | [version_name] => Test Template 355 | [name] => test 356 | ) 357 | 358 | [success] => 1 359 | ) 360 | ``` 361 | 362 | ## Drip Unsubscribe 363 | ```php 364 | // Unsubscribe email address from active drips 365 | drip_unsubscribe( 366 | $email_address, // the email to unsubscribe from active drips 367 | ) 368 | ``` 369 | 370 | ## Drip Unsubscribe Example 371 | 372 | ```php 373 | $response = $api->drip_unsubscribe('us@sendwithus.com'); 374 | ``` 375 | 376 | ## Drips 2.0 377 | 378 | ### List Drip Campaigns 379 | List all drip campaigns for the current profile 380 | 381 | Example 382 | 383 | ```php 384 | $response = $api->list_drip_campaigns(); 385 | ``` 386 | 387 | Response 388 | 389 | ```php 390 | Array 391 | ( 392 | [0] => stdClass Object 393 | ( 394 | [drip_steps] => Array 395 | ( 396 | [0] => stdClass Object 397 | ( 398 | [id] => dcs_1234abcd1234 399 | [object] => drip_step 400 | [delay_seconds] => 0 401 | [email_id] => tem_1234abcd1234 402 | ) 403 | 404 | ) 405 | 406 | [name] => Drip Campaign 407 | [enabled] => 1 408 | [id] => dc_1234abcd1234 409 | [trigger_email_id] => tem_1234abcd1234 410 | [object] => drip_campaign 411 | ) 412 | ) 413 | ``` 414 | 415 | ### Start on Drip Campaign 416 | Starts a customer on the first step of a specified drip campaign 417 | 418 | ```php 419 | start_on_drip_campaign( 420 | $recipient_address, // string, email address being added to drip campaign 421 | $drip_campaign_id, // string, drip campaign being added to 422 | $data // array, (optional) email data being added to drip campaign 423 | $args // array, (optional) additional options being sent with email (tags, cc's, etc) 424 | ); 425 | 426 | // Args options 427 | 'sender' // array ("address", "name", "reply_to") of sender. 428 | 'cc' // array of ("address", "name") for carbon copy. 429 | 'bcc' // array of ("address", "name") for blind carbon copy. 430 | 'tags' // array of strings to tag email send with. 431 | 'esp_account' // string of ESP ID to manually select ESP 432 | ``` 433 | 434 | Example 435 | 436 | ```php 437 | $template_data = array( 438 | 'name' => 'Jean-Luc', 439 | 'rank' => 'Captain' 440 | ); 441 | 442 | $args = array( 443 | 'tags' => array('all', 'the', 'tags'), 444 | 'cc' => array('address' => 'them@sendwithus.com') 445 | ); 446 | $response = $api->start_on_drip_campaign('us@sendwithus.com', 'dc_1234abcd1234', $template_data, $args); 447 | ``` 448 | 449 | Response 450 | 451 | ```php 452 | stdClass Object 453 | ( 454 | [success] => 1 455 | [drip_campaign] => stdClass Object 456 | ( 457 | [id] => dc_1234abcd1234 458 | [name] => Drip Campaign 459 | ) 460 | 461 | [message] => Recipient successfully added to drip campaign. 462 | [status] => OK 463 | [recipient_address] => us@sendwithus.com 464 | ) 465 | ``` 466 | 467 | ### Remove from Drip Campaign 468 | Deactivates all pending emails for a customer on a specified drip campaign 469 | ```php 470 | $response = $api->remove_from_drip_campaign( 471 | $recipient_address, // string, email address being removed from drip campaign 472 | $drip_campaign_id // string, drip campaign being removed from 473 | ); 474 | ``` 475 | 476 | Example 477 | 478 | ```php 479 | $response = $api->remove_from_drip_campaign('us@sendwithus.com', 'dc_1234abcd1234'); 480 | ``` 481 | 482 | Response 483 | 484 | ```php 485 | stdClass Object 486 | ( 487 | [success] => 1 488 | [drip_campaign] => stdClass Object 489 | ( 490 | [id] => dc_1234abcd1234 491 | [name] => Drip Campaign 492 | ) 493 | 494 | [message] => Recipient successfully removed from drip campaign. 495 | [status] => OK 496 | [recipient_address] => us@sendwithus.com 497 | ) 498 | ``` 499 | 500 | ### List Drip Campaign Details 501 | Show all the steps and other information in a specified campaign 502 | ```php 503 | $response = $api->drip_campaign_details( 504 | $drip_campaign_id // string, drip campaign to list details from 505 | ); 506 | ``` 507 | 508 | Example 509 | 510 | ```php 511 | $response = $api->drip_campaign_details('dc_1234abcd1234'); 512 | ``` 513 | 514 | Response 515 | 516 | ```php 517 | stdClass Object 518 | ( 519 | [drip_steps] => Array 520 | ( 521 | [0] => stdClass Object 522 | ( 523 | [id] => dcs_1234abcd1234 524 | [object] => drip_step 525 | [delay_seconds] => 0 526 | [email_id] => tem_1234abcd1234 527 | ) 528 | 529 | ) 530 | 531 | [name] => Drip Campaign 532 | [enabled] => 1 533 | [id] => dc_1234abcd1234 534 | [trigger_email_id] => tem_1234abcd1234 535 | [object] => drip_campaign 536 | ) 537 | 538 | ``` 539 | 540 | ## Customers API 541 | 542 | ### Create Customer 543 | ```php 544 | create_customer( 545 | $email, // string, email of customer 546 | $data, // array, optional, data for customer 547 | $args // array, optional, optional parameters: 548 | 549 | // The additional optional parameters are as follows: 550 | // 'locale' - Default is null. String to specify a locale for this customer. 551 | ) 552 | ``` 553 | 554 | Example 555 | 556 | ```php 557 | $response = $api->create_customer('us@sendwithus.com', 558 | array('name' => 'Sendwithus') 559 | ); 560 | ``` 561 | 562 | ### Update Customer 563 | ```php 564 | update_customer( 565 | $email, // string, email of customer 566 | $data, // array, optional, data for customer 567 | ) 568 | ``` 569 | 570 | Example 571 | 572 | ```php 573 | $response = $api->update_customer('us@sendwithus.com', 574 | array('name' => 'Sendwithus.com') 575 | ); 576 | ``` 577 | 578 | ### Delete Customer 579 | ```php 580 | delete_customer( 581 | $email, // string, email of customer 582 | ) 583 | ``` 584 | 585 | Example 586 | 587 | ```php 588 | $response = $api->delete_customer('us@sendwithus.com'); 589 | ``` 590 | 591 | ### List Customer Logs 592 | List all customer logs 593 | 594 | Example 595 | 596 | ```php 597 | $response = api->get_customer_logs("email@email.com"); 598 | 599 | print_r($response); 600 | 601 | /* 602 | ( 603 | [success] => 1 604 | [logs] => Array 605 | ( 606 | [email_name] => Name of email 607 | [message] => Message body 608 | [recipient_name] => Recipient name 609 | [email_version] => Name of email version 610 | [object] => log 611 | [email_id] => ID of email 612 | [created] => Time stamp 613 | [recipient_address] => Email address of recipient 614 | [status] => Status of email 615 | [id] => ID of log 616 | ) 617 | [status] => OK 618 | ) 619 | */ 620 | 621 | ``` 622 | 623 | ## Batch API 624 | Batch requests together to be run all at once. 625 | 626 | ### Usage 627 | Create a batch_api object by calling `start_batch()`. 628 | 629 | Do any request you would do normally with the API but on the batch_api object. 630 | 631 | Execute all commands at once by calling `execute()` on the object. 632 | 633 | ### Example 634 | ```php 635 | $batch_api = api->start_batch(); 636 | for($i = 0; $i < 10; $i++) { 637 | $result = $batch_api->create_customer('us@sendwithus.com', 638 | array('name' => 'Sendwithus')); 639 | // $result->success == true && $result->status == 'Batched' 640 | } 641 | $result = $batch_api->execute(); 642 | 643 | // $result will be an array of responses for each command executed. 644 | 645 | ``` 646 | 647 | ### Canceling Batch Request 648 | Sometimes it is necessary to cancel all the api requests that have been batched, but not yet sent. 649 | To do that, use `cancel()`: 650 | 651 | ### Example 652 | ```php 653 | $batch_api = api->start_batch(); 654 | for($i = 0; $i < 10; $i++) { 655 | $batch_api->create_customer('us@sendwithus.com', 656 | array('name' => 'Sendwithus')); 657 | } 658 | $result = $batch_api->cancel(); 659 | // $result->success == true && $result->status == 'Canceled' 660 | ``` 661 | 662 | Once you have canceled a batch, you can continue to use the batch to make more requests. 663 | 664 | ## Tests 665 | 666 | ### Running Unit Tests 667 | 668 | Make sure to have phpunit installed (http://phpunit.de/) and run the following from the root directory 669 | 670 | ```php 671 | phpunit test 672 | ``` 673 | 674 | # Troubleshooting 675 | 676 | ## General Troubleshooting 677 | 678 | - Enable debug mode 679 | - Make sure you're using the latest PHP client 680 | - Make sure `data/ca-certificate.pem` is included. This file is *required* 681 | - Capture the response data and check your logs — often this will have the exact error 682 | 683 | ## Enable Debug Mode 684 | 685 | Debug mode prints out the underlying cURL information as well as the data payload that gets sent to Sendwithus. You will most likely find this information in your logs. To enable it, simply put `"DEBUG" => true` in the optional parameters when instantiating the API object. Use the debug mode to compare the data payload getting sent to [sendwithus' API docs](https://www.sendwithus.com/docs/api "Official Sendwithus API Docs"). 686 | 687 | ```php 688 | $API_KEY = 'THIS_IS_AN_EXAMPLE_API_KEY'; 689 | $options = array( 690 | 'DEBUG' => true 691 | ); 692 | 693 | $api = new API($API_KEY, $options); 694 | ``` 695 | 696 | ## Response Ranges 697 | 698 | Sendwithus' API typically sends responses back in these ranges: 699 | 700 | - 2xx – Successful Request 701 | - 4xx – Failed Request (Client error) 702 | - 5xx – Failed Request (Server error) 703 | 704 | If you're receiving an error in the 400 response range follow these steps: 705 | 706 | - Double check the data and ID's getting passed to Sendwithus 707 | - Ensure your API key is correct 708 | - Make sure there's no extraneous spaces in the id's getting passed 709 | 710 | *Note*: Enable Debug mode to check the response code. 711 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sendwithus/api", 3 | "description": "sendwithus.com PHP Client", 4 | "autoload": { 5 | "classmap": ["lib"] 6 | }, 7 | "require-dev": { 8 | "phpunit/phpunit": "^9.5" 9 | }, 10 | "license": "Apache-2.0" 11 | } 12 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "52c16e8846646e8d366d24147b40d956", 8 | "packages": [], 9 | "packages-dev": [ 10 | { 11 | "name": "doctrine/instantiator", 12 | "version": "2.0.0", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/doctrine/instantiator.git", 16 | "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", 21 | "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": "^8.1" 26 | }, 27 | "require-dev": { 28 | "doctrine/coding-standard": "^11", 29 | "ext-pdo": "*", 30 | "ext-phar": "*", 31 | "phpbench/phpbench": "^1.2", 32 | "phpstan/phpstan": "^1.9.4", 33 | "phpstan/phpstan-phpunit": "^1.3", 34 | "phpunit/phpunit": "^9.5.27", 35 | "vimeo/psalm": "^5.4" 36 | }, 37 | "type": "library", 38 | "autoload": { 39 | "psr-4": { 40 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 41 | } 42 | }, 43 | "notification-url": "https://packagist.org/downloads/", 44 | "license": [ 45 | "MIT" 46 | ], 47 | "authors": [ 48 | { 49 | "name": "Marco Pivetta", 50 | "email": "ocramius@gmail.com", 51 | "homepage": "https://ocramius.github.io/" 52 | } 53 | ], 54 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 55 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html", 56 | "keywords": [ 57 | "constructor", 58 | "instantiate" 59 | ], 60 | "support": { 61 | "issues": "https://github.com/doctrine/instantiator/issues", 62 | "source": "https://github.com/doctrine/instantiator/tree/2.0.0" 63 | }, 64 | "funding": [ 65 | { 66 | "url": "https://www.doctrine-project.org/sponsorship.html", 67 | "type": "custom" 68 | }, 69 | { 70 | "url": "https://www.patreon.com/phpdoctrine", 71 | "type": "patreon" 72 | }, 73 | { 74 | "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", 75 | "type": "tidelift" 76 | } 77 | ], 78 | "time": "2022-12-30T00:23:10+00:00" 79 | }, 80 | { 81 | "name": "myclabs/deep-copy", 82 | "version": "1.13.1", 83 | "source": { 84 | "type": "git", 85 | "url": "https://github.com/myclabs/DeepCopy.git", 86 | "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" 87 | }, 88 | "dist": { 89 | "type": "zip", 90 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", 91 | "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", 92 | "shasum": "" 93 | }, 94 | "require": { 95 | "php": "^7.1 || ^8.0" 96 | }, 97 | "conflict": { 98 | "doctrine/collections": "<1.6.8", 99 | "doctrine/common": "<2.13.3 || >=3 <3.2.2" 100 | }, 101 | "require-dev": { 102 | "doctrine/collections": "^1.6.8", 103 | "doctrine/common": "^2.13.3 || ^3.2.2", 104 | "phpspec/prophecy": "^1.10", 105 | "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" 106 | }, 107 | "type": "library", 108 | "autoload": { 109 | "files": [ 110 | "src/DeepCopy/deep_copy.php" 111 | ], 112 | "psr-4": { 113 | "DeepCopy\\": "src/DeepCopy/" 114 | } 115 | }, 116 | "notification-url": "https://packagist.org/downloads/", 117 | "license": [ 118 | "MIT" 119 | ], 120 | "description": "Create deep copies (clones) of your objects", 121 | "keywords": [ 122 | "clone", 123 | "copy", 124 | "duplicate", 125 | "object", 126 | "object graph" 127 | ], 128 | "support": { 129 | "issues": "https://github.com/myclabs/DeepCopy/issues", 130 | "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" 131 | }, 132 | "funding": [ 133 | { 134 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", 135 | "type": "tidelift" 136 | } 137 | ], 138 | "time": "2025-04-29T12:36:36+00:00" 139 | }, 140 | { 141 | "name": "nikic/php-parser", 142 | "version": "v5.5.0", 143 | "source": { 144 | "type": "git", 145 | "url": "https://github.com/nikic/PHP-Parser.git", 146 | "reference": "ae59794362fe85e051a58ad36b289443f57be7a9" 147 | }, 148 | "dist": { 149 | "type": "zip", 150 | "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9", 151 | "reference": "ae59794362fe85e051a58ad36b289443f57be7a9", 152 | "shasum": "" 153 | }, 154 | "require": { 155 | "ext-ctype": "*", 156 | "ext-json": "*", 157 | "ext-tokenizer": "*", 158 | "php": ">=7.4" 159 | }, 160 | "require-dev": { 161 | "ircmaxell/php-yacc": "^0.0.7", 162 | "phpunit/phpunit": "^9.0" 163 | }, 164 | "bin": [ 165 | "bin/php-parse" 166 | ], 167 | "type": "library", 168 | "extra": { 169 | "branch-alias": { 170 | "dev-master": "5.0-dev" 171 | } 172 | }, 173 | "autoload": { 174 | "psr-4": { 175 | "PhpParser\\": "lib/PhpParser" 176 | } 177 | }, 178 | "notification-url": "https://packagist.org/downloads/", 179 | "license": [ 180 | "BSD-3-Clause" 181 | ], 182 | "authors": [ 183 | { 184 | "name": "Nikita Popov" 185 | } 186 | ], 187 | "description": "A PHP parser written in PHP", 188 | "keywords": [ 189 | "parser", 190 | "php" 191 | ], 192 | "support": { 193 | "issues": "https://github.com/nikic/PHP-Parser/issues", 194 | "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0" 195 | }, 196 | "time": "2025-05-31T08:24:38+00:00" 197 | }, 198 | { 199 | "name": "phar-io/manifest", 200 | "version": "2.0.4", 201 | "source": { 202 | "type": "git", 203 | "url": "https://github.com/phar-io/manifest.git", 204 | "reference": "54750ef60c58e43759730615a392c31c80e23176" 205 | }, 206 | "dist": { 207 | "type": "zip", 208 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", 209 | "reference": "54750ef60c58e43759730615a392c31c80e23176", 210 | "shasum": "" 211 | }, 212 | "require": { 213 | "ext-dom": "*", 214 | "ext-libxml": "*", 215 | "ext-phar": "*", 216 | "ext-xmlwriter": "*", 217 | "phar-io/version": "^3.0.1", 218 | "php": "^7.2 || ^8.0" 219 | }, 220 | "type": "library", 221 | "extra": { 222 | "branch-alias": { 223 | "dev-master": "2.0.x-dev" 224 | } 225 | }, 226 | "autoload": { 227 | "classmap": [ 228 | "src/" 229 | ] 230 | }, 231 | "notification-url": "https://packagist.org/downloads/", 232 | "license": [ 233 | "BSD-3-Clause" 234 | ], 235 | "authors": [ 236 | { 237 | "name": "Arne Blankerts", 238 | "email": "arne@blankerts.de", 239 | "role": "Developer" 240 | }, 241 | { 242 | "name": "Sebastian Heuer", 243 | "email": "sebastian@phpeople.de", 244 | "role": "Developer" 245 | }, 246 | { 247 | "name": "Sebastian Bergmann", 248 | "email": "sebastian@phpunit.de", 249 | "role": "Developer" 250 | } 251 | ], 252 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 253 | "support": { 254 | "issues": "https://github.com/phar-io/manifest/issues", 255 | "source": "https://github.com/phar-io/manifest/tree/2.0.4" 256 | }, 257 | "funding": [ 258 | { 259 | "url": "https://github.com/theseer", 260 | "type": "github" 261 | } 262 | ], 263 | "time": "2024-03-03T12:33:53+00:00" 264 | }, 265 | { 266 | "name": "phar-io/version", 267 | "version": "3.2.1", 268 | "source": { 269 | "type": "git", 270 | "url": "https://github.com/phar-io/version.git", 271 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" 272 | }, 273 | "dist": { 274 | "type": "zip", 275 | "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 276 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 277 | "shasum": "" 278 | }, 279 | "require": { 280 | "php": "^7.2 || ^8.0" 281 | }, 282 | "type": "library", 283 | "autoload": { 284 | "classmap": [ 285 | "src/" 286 | ] 287 | }, 288 | "notification-url": "https://packagist.org/downloads/", 289 | "license": [ 290 | "BSD-3-Clause" 291 | ], 292 | "authors": [ 293 | { 294 | "name": "Arne Blankerts", 295 | "email": "arne@blankerts.de", 296 | "role": "Developer" 297 | }, 298 | { 299 | "name": "Sebastian Heuer", 300 | "email": "sebastian@phpeople.de", 301 | "role": "Developer" 302 | }, 303 | { 304 | "name": "Sebastian Bergmann", 305 | "email": "sebastian@phpunit.de", 306 | "role": "Developer" 307 | } 308 | ], 309 | "description": "Library for handling version information and constraints", 310 | "support": { 311 | "issues": "https://github.com/phar-io/version/issues", 312 | "source": "https://github.com/phar-io/version/tree/3.2.1" 313 | }, 314 | "time": "2022-02-21T01:04:05+00:00" 315 | }, 316 | { 317 | "name": "phpunit/php-code-coverage", 318 | "version": "9.2.32", 319 | "source": { 320 | "type": "git", 321 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 322 | "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" 323 | }, 324 | "dist": { 325 | "type": "zip", 326 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", 327 | "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", 328 | "shasum": "" 329 | }, 330 | "require": { 331 | "ext-dom": "*", 332 | "ext-libxml": "*", 333 | "ext-xmlwriter": "*", 334 | "nikic/php-parser": "^4.19.1 || ^5.1.0", 335 | "php": ">=7.3", 336 | "phpunit/php-file-iterator": "^3.0.6", 337 | "phpunit/php-text-template": "^2.0.4", 338 | "sebastian/code-unit-reverse-lookup": "^2.0.3", 339 | "sebastian/complexity": "^2.0.3", 340 | "sebastian/environment": "^5.1.5", 341 | "sebastian/lines-of-code": "^1.0.4", 342 | "sebastian/version": "^3.0.2", 343 | "theseer/tokenizer": "^1.2.3" 344 | }, 345 | "require-dev": { 346 | "phpunit/phpunit": "^9.6" 347 | }, 348 | "suggest": { 349 | "ext-pcov": "PHP extension that provides line coverage", 350 | "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" 351 | }, 352 | "type": "library", 353 | "extra": { 354 | "branch-alias": { 355 | "dev-main": "9.2.x-dev" 356 | } 357 | }, 358 | "autoload": { 359 | "classmap": [ 360 | "src/" 361 | ] 362 | }, 363 | "notification-url": "https://packagist.org/downloads/", 364 | "license": [ 365 | "BSD-3-Clause" 366 | ], 367 | "authors": [ 368 | { 369 | "name": "Sebastian Bergmann", 370 | "email": "sebastian@phpunit.de", 371 | "role": "lead" 372 | } 373 | ], 374 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 375 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 376 | "keywords": [ 377 | "coverage", 378 | "testing", 379 | "xunit" 380 | ], 381 | "support": { 382 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", 383 | "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", 384 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" 385 | }, 386 | "funding": [ 387 | { 388 | "url": "https://github.com/sebastianbergmann", 389 | "type": "github" 390 | } 391 | ], 392 | "time": "2024-08-22T04:23:01+00:00" 393 | }, 394 | { 395 | "name": "phpunit/php-file-iterator", 396 | "version": "3.0.6", 397 | "source": { 398 | "type": "git", 399 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 400 | "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" 401 | }, 402 | "dist": { 403 | "type": "zip", 404 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", 405 | "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", 406 | "shasum": "" 407 | }, 408 | "require": { 409 | "php": ">=7.3" 410 | }, 411 | "require-dev": { 412 | "phpunit/phpunit": "^9.3" 413 | }, 414 | "type": "library", 415 | "extra": { 416 | "branch-alias": { 417 | "dev-master": "3.0-dev" 418 | } 419 | }, 420 | "autoload": { 421 | "classmap": [ 422 | "src/" 423 | ] 424 | }, 425 | "notification-url": "https://packagist.org/downloads/", 426 | "license": [ 427 | "BSD-3-Clause" 428 | ], 429 | "authors": [ 430 | { 431 | "name": "Sebastian Bergmann", 432 | "email": "sebastian@phpunit.de", 433 | "role": "lead" 434 | } 435 | ], 436 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 437 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 438 | "keywords": [ 439 | "filesystem", 440 | "iterator" 441 | ], 442 | "support": { 443 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", 444 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" 445 | }, 446 | "funding": [ 447 | { 448 | "url": "https://github.com/sebastianbergmann", 449 | "type": "github" 450 | } 451 | ], 452 | "time": "2021-12-02T12:48:52+00:00" 453 | }, 454 | { 455 | "name": "phpunit/php-invoker", 456 | "version": "3.1.1", 457 | "source": { 458 | "type": "git", 459 | "url": "https://github.com/sebastianbergmann/php-invoker.git", 460 | "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" 461 | }, 462 | "dist": { 463 | "type": "zip", 464 | "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", 465 | "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", 466 | "shasum": "" 467 | }, 468 | "require": { 469 | "php": ">=7.3" 470 | }, 471 | "require-dev": { 472 | "ext-pcntl": "*", 473 | "phpunit/phpunit": "^9.3" 474 | }, 475 | "suggest": { 476 | "ext-pcntl": "*" 477 | }, 478 | "type": "library", 479 | "extra": { 480 | "branch-alias": { 481 | "dev-master": "3.1-dev" 482 | } 483 | }, 484 | "autoload": { 485 | "classmap": [ 486 | "src/" 487 | ] 488 | }, 489 | "notification-url": "https://packagist.org/downloads/", 490 | "license": [ 491 | "BSD-3-Clause" 492 | ], 493 | "authors": [ 494 | { 495 | "name": "Sebastian Bergmann", 496 | "email": "sebastian@phpunit.de", 497 | "role": "lead" 498 | } 499 | ], 500 | "description": "Invoke callables with a timeout", 501 | "homepage": "https://github.com/sebastianbergmann/php-invoker/", 502 | "keywords": [ 503 | "process" 504 | ], 505 | "support": { 506 | "issues": "https://github.com/sebastianbergmann/php-invoker/issues", 507 | "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" 508 | }, 509 | "funding": [ 510 | { 511 | "url": "https://github.com/sebastianbergmann", 512 | "type": "github" 513 | } 514 | ], 515 | "time": "2020-09-28T05:58:55+00:00" 516 | }, 517 | { 518 | "name": "phpunit/php-text-template", 519 | "version": "2.0.4", 520 | "source": { 521 | "type": "git", 522 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 523 | "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" 524 | }, 525 | "dist": { 526 | "type": "zip", 527 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", 528 | "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", 529 | "shasum": "" 530 | }, 531 | "require": { 532 | "php": ">=7.3" 533 | }, 534 | "require-dev": { 535 | "phpunit/phpunit": "^9.3" 536 | }, 537 | "type": "library", 538 | "extra": { 539 | "branch-alias": { 540 | "dev-master": "2.0-dev" 541 | } 542 | }, 543 | "autoload": { 544 | "classmap": [ 545 | "src/" 546 | ] 547 | }, 548 | "notification-url": "https://packagist.org/downloads/", 549 | "license": [ 550 | "BSD-3-Clause" 551 | ], 552 | "authors": [ 553 | { 554 | "name": "Sebastian Bergmann", 555 | "email": "sebastian@phpunit.de", 556 | "role": "lead" 557 | } 558 | ], 559 | "description": "Simple template engine.", 560 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 561 | "keywords": [ 562 | "template" 563 | ], 564 | "support": { 565 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues", 566 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" 567 | }, 568 | "funding": [ 569 | { 570 | "url": "https://github.com/sebastianbergmann", 571 | "type": "github" 572 | } 573 | ], 574 | "time": "2020-10-26T05:33:50+00:00" 575 | }, 576 | { 577 | "name": "phpunit/php-timer", 578 | "version": "5.0.3", 579 | "source": { 580 | "type": "git", 581 | "url": "https://github.com/sebastianbergmann/php-timer.git", 582 | "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" 583 | }, 584 | "dist": { 585 | "type": "zip", 586 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", 587 | "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", 588 | "shasum": "" 589 | }, 590 | "require": { 591 | "php": ">=7.3" 592 | }, 593 | "require-dev": { 594 | "phpunit/phpunit": "^9.3" 595 | }, 596 | "type": "library", 597 | "extra": { 598 | "branch-alias": { 599 | "dev-master": "5.0-dev" 600 | } 601 | }, 602 | "autoload": { 603 | "classmap": [ 604 | "src/" 605 | ] 606 | }, 607 | "notification-url": "https://packagist.org/downloads/", 608 | "license": [ 609 | "BSD-3-Clause" 610 | ], 611 | "authors": [ 612 | { 613 | "name": "Sebastian Bergmann", 614 | "email": "sebastian@phpunit.de", 615 | "role": "lead" 616 | } 617 | ], 618 | "description": "Utility class for timing", 619 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 620 | "keywords": [ 621 | "timer" 622 | ], 623 | "support": { 624 | "issues": "https://github.com/sebastianbergmann/php-timer/issues", 625 | "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" 626 | }, 627 | "funding": [ 628 | { 629 | "url": "https://github.com/sebastianbergmann", 630 | "type": "github" 631 | } 632 | ], 633 | "time": "2020-10-26T13:16:10+00:00" 634 | }, 635 | { 636 | "name": "phpunit/phpunit", 637 | "version": "9.6.23", 638 | "source": { 639 | "type": "git", 640 | "url": "https://github.com/sebastianbergmann/phpunit.git", 641 | "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95" 642 | }, 643 | "dist": { 644 | "type": "zip", 645 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", 646 | "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", 647 | "shasum": "" 648 | }, 649 | "require": { 650 | "doctrine/instantiator": "^1.5.0 || ^2", 651 | "ext-dom": "*", 652 | "ext-json": "*", 653 | "ext-libxml": "*", 654 | "ext-mbstring": "*", 655 | "ext-xml": "*", 656 | "ext-xmlwriter": "*", 657 | "myclabs/deep-copy": "^1.13.1", 658 | "phar-io/manifest": "^2.0.4", 659 | "phar-io/version": "^3.2.1", 660 | "php": ">=7.3", 661 | "phpunit/php-code-coverage": "^9.2.32", 662 | "phpunit/php-file-iterator": "^3.0.6", 663 | "phpunit/php-invoker": "^3.1.1", 664 | "phpunit/php-text-template": "^2.0.4", 665 | "phpunit/php-timer": "^5.0.3", 666 | "sebastian/cli-parser": "^1.0.2", 667 | "sebastian/code-unit": "^1.0.8", 668 | "sebastian/comparator": "^4.0.8", 669 | "sebastian/diff": "^4.0.6", 670 | "sebastian/environment": "^5.1.5", 671 | "sebastian/exporter": "^4.0.6", 672 | "sebastian/global-state": "^5.0.7", 673 | "sebastian/object-enumerator": "^4.0.4", 674 | "sebastian/resource-operations": "^3.0.4", 675 | "sebastian/type": "^3.2.1", 676 | "sebastian/version": "^3.0.2" 677 | }, 678 | "suggest": { 679 | "ext-soap": "To be able to generate mocks based on WSDL files", 680 | "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" 681 | }, 682 | "bin": [ 683 | "phpunit" 684 | ], 685 | "type": "library", 686 | "extra": { 687 | "branch-alias": { 688 | "dev-master": "9.6-dev" 689 | } 690 | }, 691 | "autoload": { 692 | "files": [ 693 | "src/Framework/Assert/Functions.php" 694 | ], 695 | "classmap": [ 696 | "src/" 697 | ] 698 | }, 699 | "notification-url": "https://packagist.org/downloads/", 700 | "license": [ 701 | "BSD-3-Clause" 702 | ], 703 | "authors": [ 704 | { 705 | "name": "Sebastian Bergmann", 706 | "email": "sebastian@phpunit.de", 707 | "role": "lead" 708 | } 709 | ], 710 | "description": "The PHP Unit Testing framework.", 711 | "homepage": "https://phpunit.de/", 712 | "keywords": [ 713 | "phpunit", 714 | "testing", 715 | "xunit" 716 | ], 717 | "support": { 718 | "issues": "https://github.com/sebastianbergmann/phpunit/issues", 719 | "security": "https://github.com/sebastianbergmann/phpunit/security/policy", 720 | "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.23" 721 | }, 722 | "funding": [ 723 | { 724 | "url": "https://phpunit.de/sponsors.html", 725 | "type": "custom" 726 | }, 727 | { 728 | "url": "https://github.com/sebastianbergmann", 729 | "type": "github" 730 | }, 731 | { 732 | "url": "https://liberapay.com/sebastianbergmann", 733 | "type": "liberapay" 734 | }, 735 | { 736 | "url": "https://thanks.dev/u/gh/sebastianbergmann", 737 | "type": "thanks_dev" 738 | }, 739 | { 740 | "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", 741 | "type": "tidelift" 742 | } 743 | ], 744 | "time": "2025-05-02T06:40:34+00:00" 745 | }, 746 | { 747 | "name": "sebastian/cli-parser", 748 | "version": "1.0.2", 749 | "source": { 750 | "type": "git", 751 | "url": "https://github.com/sebastianbergmann/cli-parser.git", 752 | "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" 753 | }, 754 | "dist": { 755 | "type": "zip", 756 | "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", 757 | "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", 758 | "shasum": "" 759 | }, 760 | "require": { 761 | "php": ">=7.3" 762 | }, 763 | "require-dev": { 764 | "phpunit/phpunit": "^9.3" 765 | }, 766 | "type": "library", 767 | "extra": { 768 | "branch-alias": { 769 | "dev-master": "1.0-dev" 770 | } 771 | }, 772 | "autoload": { 773 | "classmap": [ 774 | "src/" 775 | ] 776 | }, 777 | "notification-url": "https://packagist.org/downloads/", 778 | "license": [ 779 | "BSD-3-Clause" 780 | ], 781 | "authors": [ 782 | { 783 | "name": "Sebastian Bergmann", 784 | "email": "sebastian@phpunit.de", 785 | "role": "lead" 786 | } 787 | ], 788 | "description": "Library for parsing CLI options", 789 | "homepage": "https://github.com/sebastianbergmann/cli-parser", 790 | "support": { 791 | "issues": "https://github.com/sebastianbergmann/cli-parser/issues", 792 | "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" 793 | }, 794 | "funding": [ 795 | { 796 | "url": "https://github.com/sebastianbergmann", 797 | "type": "github" 798 | } 799 | ], 800 | "time": "2024-03-02T06:27:43+00:00" 801 | }, 802 | { 803 | "name": "sebastian/code-unit", 804 | "version": "1.0.8", 805 | "source": { 806 | "type": "git", 807 | "url": "https://github.com/sebastianbergmann/code-unit.git", 808 | "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" 809 | }, 810 | "dist": { 811 | "type": "zip", 812 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", 813 | "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", 814 | "shasum": "" 815 | }, 816 | "require": { 817 | "php": ">=7.3" 818 | }, 819 | "require-dev": { 820 | "phpunit/phpunit": "^9.3" 821 | }, 822 | "type": "library", 823 | "extra": { 824 | "branch-alias": { 825 | "dev-master": "1.0-dev" 826 | } 827 | }, 828 | "autoload": { 829 | "classmap": [ 830 | "src/" 831 | ] 832 | }, 833 | "notification-url": "https://packagist.org/downloads/", 834 | "license": [ 835 | "BSD-3-Clause" 836 | ], 837 | "authors": [ 838 | { 839 | "name": "Sebastian Bergmann", 840 | "email": "sebastian@phpunit.de", 841 | "role": "lead" 842 | } 843 | ], 844 | "description": "Collection of value objects that represent the PHP code units", 845 | "homepage": "https://github.com/sebastianbergmann/code-unit", 846 | "support": { 847 | "issues": "https://github.com/sebastianbergmann/code-unit/issues", 848 | "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" 849 | }, 850 | "funding": [ 851 | { 852 | "url": "https://github.com/sebastianbergmann", 853 | "type": "github" 854 | } 855 | ], 856 | "time": "2020-10-26T13:08:54+00:00" 857 | }, 858 | { 859 | "name": "sebastian/code-unit-reverse-lookup", 860 | "version": "2.0.3", 861 | "source": { 862 | "type": "git", 863 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 864 | "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" 865 | }, 866 | "dist": { 867 | "type": "zip", 868 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", 869 | "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", 870 | "shasum": "" 871 | }, 872 | "require": { 873 | "php": ">=7.3" 874 | }, 875 | "require-dev": { 876 | "phpunit/phpunit": "^9.3" 877 | }, 878 | "type": "library", 879 | "extra": { 880 | "branch-alias": { 881 | "dev-master": "2.0-dev" 882 | } 883 | }, 884 | "autoload": { 885 | "classmap": [ 886 | "src/" 887 | ] 888 | }, 889 | "notification-url": "https://packagist.org/downloads/", 890 | "license": [ 891 | "BSD-3-Clause" 892 | ], 893 | "authors": [ 894 | { 895 | "name": "Sebastian Bergmann", 896 | "email": "sebastian@phpunit.de" 897 | } 898 | ], 899 | "description": "Looks up which function or method a line of code belongs to", 900 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 901 | "support": { 902 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", 903 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" 904 | }, 905 | "funding": [ 906 | { 907 | "url": "https://github.com/sebastianbergmann", 908 | "type": "github" 909 | } 910 | ], 911 | "time": "2020-09-28T05:30:19+00:00" 912 | }, 913 | { 914 | "name": "sebastian/comparator", 915 | "version": "4.0.8", 916 | "source": { 917 | "type": "git", 918 | "url": "https://github.com/sebastianbergmann/comparator.git", 919 | "reference": "fa0f136dd2334583309d32b62544682ee972b51a" 920 | }, 921 | "dist": { 922 | "type": "zip", 923 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", 924 | "reference": "fa0f136dd2334583309d32b62544682ee972b51a", 925 | "shasum": "" 926 | }, 927 | "require": { 928 | "php": ">=7.3", 929 | "sebastian/diff": "^4.0", 930 | "sebastian/exporter": "^4.0" 931 | }, 932 | "require-dev": { 933 | "phpunit/phpunit": "^9.3" 934 | }, 935 | "type": "library", 936 | "extra": { 937 | "branch-alias": { 938 | "dev-master": "4.0-dev" 939 | } 940 | }, 941 | "autoload": { 942 | "classmap": [ 943 | "src/" 944 | ] 945 | }, 946 | "notification-url": "https://packagist.org/downloads/", 947 | "license": [ 948 | "BSD-3-Clause" 949 | ], 950 | "authors": [ 951 | { 952 | "name": "Sebastian Bergmann", 953 | "email": "sebastian@phpunit.de" 954 | }, 955 | { 956 | "name": "Jeff Welch", 957 | "email": "whatthejeff@gmail.com" 958 | }, 959 | { 960 | "name": "Volker Dusch", 961 | "email": "github@wallbash.com" 962 | }, 963 | { 964 | "name": "Bernhard Schussek", 965 | "email": "bschussek@2bepublished.at" 966 | } 967 | ], 968 | "description": "Provides the functionality to compare PHP values for equality", 969 | "homepage": "https://github.com/sebastianbergmann/comparator", 970 | "keywords": [ 971 | "comparator", 972 | "compare", 973 | "equality" 974 | ], 975 | "support": { 976 | "issues": "https://github.com/sebastianbergmann/comparator/issues", 977 | "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" 978 | }, 979 | "funding": [ 980 | { 981 | "url": "https://github.com/sebastianbergmann", 982 | "type": "github" 983 | } 984 | ], 985 | "time": "2022-09-14T12:41:17+00:00" 986 | }, 987 | { 988 | "name": "sebastian/complexity", 989 | "version": "2.0.3", 990 | "source": { 991 | "type": "git", 992 | "url": "https://github.com/sebastianbergmann/complexity.git", 993 | "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" 994 | }, 995 | "dist": { 996 | "type": "zip", 997 | "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", 998 | "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", 999 | "shasum": "" 1000 | }, 1001 | "require": { 1002 | "nikic/php-parser": "^4.18 || ^5.0", 1003 | "php": ">=7.3" 1004 | }, 1005 | "require-dev": { 1006 | "phpunit/phpunit": "^9.3" 1007 | }, 1008 | "type": "library", 1009 | "extra": { 1010 | "branch-alias": { 1011 | "dev-master": "2.0-dev" 1012 | } 1013 | }, 1014 | "autoload": { 1015 | "classmap": [ 1016 | "src/" 1017 | ] 1018 | }, 1019 | "notification-url": "https://packagist.org/downloads/", 1020 | "license": [ 1021 | "BSD-3-Clause" 1022 | ], 1023 | "authors": [ 1024 | { 1025 | "name": "Sebastian Bergmann", 1026 | "email": "sebastian@phpunit.de", 1027 | "role": "lead" 1028 | } 1029 | ], 1030 | "description": "Library for calculating the complexity of PHP code units", 1031 | "homepage": "https://github.com/sebastianbergmann/complexity", 1032 | "support": { 1033 | "issues": "https://github.com/sebastianbergmann/complexity/issues", 1034 | "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" 1035 | }, 1036 | "funding": [ 1037 | { 1038 | "url": "https://github.com/sebastianbergmann", 1039 | "type": "github" 1040 | } 1041 | ], 1042 | "time": "2023-12-22T06:19:30+00:00" 1043 | }, 1044 | { 1045 | "name": "sebastian/diff", 1046 | "version": "4.0.6", 1047 | "source": { 1048 | "type": "git", 1049 | "url": "https://github.com/sebastianbergmann/diff.git", 1050 | "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" 1051 | }, 1052 | "dist": { 1053 | "type": "zip", 1054 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", 1055 | "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", 1056 | "shasum": "" 1057 | }, 1058 | "require": { 1059 | "php": ">=7.3" 1060 | }, 1061 | "require-dev": { 1062 | "phpunit/phpunit": "^9.3", 1063 | "symfony/process": "^4.2 || ^5" 1064 | }, 1065 | "type": "library", 1066 | "extra": { 1067 | "branch-alias": { 1068 | "dev-master": "4.0-dev" 1069 | } 1070 | }, 1071 | "autoload": { 1072 | "classmap": [ 1073 | "src/" 1074 | ] 1075 | }, 1076 | "notification-url": "https://packagist.org/downloads/", 1077 | "license": [ 1078 | "BSD-3-Clause" 1079 | ], 1080 | "authors": [ 1081 | { 1082 | "name": "Sebastian Bergmann", 1083 | "email": "sebastian@phpunit.de" 1084 | }, 1085 | { 1086 | "name": "Kore Nordmann", 1087 | "email": "mail@kore-nordmann.de" 1088 | } 1089 | ], 1090 | "description": "Diff implementation", 1091 | "homepage": "https://github.com/sebastianbergmann/diff", 1092 | "keywords": [ 1093 | "diff", 1094 | "udiff", 1095 | "unidiff", 1096 | "unified diff" 1097 | ], 1098 | "support": { 1099 | "issues": "https://github.com/sebastianbergmann/diff/issues", 1100 | "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" 1101 | }, 1102 | "funding": [ 1103 | { 1104 | "url": "https://github.com/sebastianbergmann", 1105 | "type": "github" 1106 | } 1107 | ], 1108 | "time": "2024-03-02T06:30:58+00:00" 1109 | }, 1110 | { 1111 | "name": "sebastian/environment", 1112 | "version": "5.1.5", 1113 | "source": { 1114 | "type": "git", 1115 | "url": "https://github.com/sebastianbergmann/environment.git", 1116 | "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" 1117 | }, 1118 | "dist": { 1119 | "type": "zip", 1120 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", 1121 | "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", 1122 | "shasum": "" 1123 | }, 1124 | "require": { 1125 | "php": ">=7.3" 1126 | }, 1127 | "require-dev": { 1128 | "phpunit/phpunit": "^9.3" 1129 | }, 1130 | "suggest": { 1131 | "ext-posix": "*" 1132 | }, 1133 | "type": "library", 1134 | "extra": { 1135 | "branch-alias": { 1136 | "dev-master": "5.1-dev" 1137 | } 1138 | }, 1139 | "autoload": { 1140 | "classmap": [ 1141 | "src/" 1142 | ] 1143 | }, 1144 | "notification-url": "https://packagist.org/downloads/", 1145 | "license": [ 1146 | "BSD-3-Clause" 1147 | ], 1148 | "authors": [ 1149 | { 1150 | "name": "Sebastian Bergmann", 1151 | "email": "sebastian@phpunit.de" 1152 | } 1153 | ], 1154 | "description": "Provides functionality to handle HHVM/PHP environments", 1155 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1156 | "keywords": [ 1157 | "Xdebug", 1158 | "environment", 1159 | "hhvm" 1160 | ], 1161 | "support": { 1162 | "issues": "https://github.com/sebastianbergmann/environment/issues", 1163 | "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" 1164 | }, 1165 | "funding": [ 1166 | { 1167 | "url": "https://github.com/sebastianbergmann", 1168 | "type": "github" 1169 | } 1170 | ], 1171 | "time": "2023-02-03T06:03:51+00:00" 1172 | }, 1173 | { 1174 | "name": "sebastian/exporter", 1175 | "version": "4.0.6", 1176 | "source": { 1177 | "type": "git", 1178 | "url": "https://github.com/sebastianbergmann/exporter.git", 1179 | "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" 1180 | }, 1181 | "dist": { 1182 | "type": "zip", 1183 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", 1184 | "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", 1185 | "shasum": "" 1186 | }, 1187 | "require": { 1188 | "php": ">=7.3", 1189 | "sebastian/recursion-context": "^4.0" 1190 | }, 1191 | "require-dev": { 1192 | "ext-mbstring": "*", 1193 | "phpunit/phpunit": "^9.3" 1194 | }, 1195 | "type": "library", 1196 | "extra": { 1197 | "branch-alias": { 1198 | "dev-master": "4.0-dev" 1199 | } 1200 | }, 1201 | "autoload": { 1202 | "classmap": [ 1203 | "src/" 1204 | ] 1205 | }, 1206 | "notification-url": "https://packagist.org/downloads/", 1207 | "license": [ 1208 | "BSD-3-Clause" 1209 | ], 1210 | "authors": [ 1211 | { 1212 | "name": "Sebastian Bergmann", 1213 | "email": "sebastian@phpunit.de" 1214 | }, 1215 | { 1216 | "name": "Jeff Welch", 1217 | "email": "whatthejeff@gmail.com" 1218 | }, 1219 | { 1220 | "name": "Volker Dusch", 1221 | "email": "github@wallbash.com" 1222 | }, 1223 | { 1224 | "name": "Adam Harvey", 1225 | "email": "aharvey@php.net" 1226 | }, 1227 | { 1228 | "name": "Bernhard Schussek", 1229 | "email": "bschussek@gmail.com" 1230 | } 1231 | ], 1232 | "description": "Provides the functionality to export PHP variables for visualization", 1233 | "homepage": "https://www.github.com/sebastianbergmann/exporter", 1234 | "keywords": [ 1235 | "export", 1236 | "exporter" 1237 | ], 1238 | "support": { 1239 | "issues": "https://github.com/sebastianbergmann/exporter/issues", 1240 | "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" 1241 | }, 1242 | "funding": [ 1243 | { 1244 | "url": "https://github.com/sebastianbergmann", 1245 | "type": "github" 1246 | } 1247 | ], 1248 | "time": "2024-03-02T06:33:00+00:00" 1249 | }, 1250 | { 1251 | "name": "sebastian/global-state", 1252 | "version": "5.0.7", 1253 | "source": { 1254 | "type": "git", 1255 | "url": "https://github.com/sebastianbergmann/global-state.git", 1256 | "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" 1257 | }, 1258 | "dist": { 1259 | "type": "zip", 1260 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", 1261 | "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", 1262 | "shasum": "" 1263 | }, 1264 | "require": { 1265 | "php": ">=7.3", 1266 | "sebastian/object-reflector": "^2.0", 1267 | "sebastian/recursion-context": "^4.0" 1268 | }, 1269 | "require-dev": { 1270 | "ext-dom": "*", 1271 | "phpunit/phpunit": "^9.3" 1272 | }, 1273 | "suggest": { 1274 | "ext-uopz": "*" 1275 | }, 1276 | "type": "library", 1277 | "extra": { 1278 | "branch-alias": { 1279 | "dev-master": "5.0-dev" 1280 | } 1281 | }, 1282 | "autoload": { 1283 | "classmap": [ 1284 | "src/" 1285 | ] 1286 | }, 1287 | "notification-url": "https://packagist.org/downloads/", 1288 | "license": [ 1289 | "BSD-3-Clause" 1290 | ], 1291 | "authors": [ 1292 | { 1293 | "name": "Sebastian Bergmann", 1294 | "email": "sebastian@phpunit.de" 1295 | } 1296 | ], 1297 | "description": "Snapshotting of global state", 1298 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1299 | "keywords": [ 1300 | "global state" 1301 | ], 1302 | "support": { 1303 | "issues": "https://github.com/sebastianbergmann/global-state/issues", 1304 | "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" 1305 | }, 1306 | "funding": [ 1307 | { 1308 | "url": "https://github.com/sebastianbergmann", 1309 | "type": "github" 1310 | } 1311 | ], 1312 | "time": "2024-03-02T06:35:11+00:00" 1313 | }, 1314 | { 1315 | "name": "sebastian/lines-of-code", 1316 | "version": "1.0.4", 1317 | "source": { 1318 | "type": "git", 1319 | "url": "https://github.com/sebastianbergmann/lines-of-code.git", 1320 | "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" 1321 | }, 1322 | "dist": { 1323 | "type": "zip", 1324 | "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", 1325 | "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", 1326 | "shasum": "" 1327 | }, 1328 | "require": { 1329 | "nikic/php-parser": "^4.18 || ^5.0", 1330 | "php": ">=7.3" 1331 | }, 1332 | "require-dev": { 1333 | "phpunit/phpunit": "^9.3" 1334 | }, 1335 | "type": "library", 1336 | "extra": { 1337 | "branch-alias": { 1338 | "dev-master": "1.0-dev" 1339 | } 1340 | }, 1341 | "autoload": { 1342 | "classmap": [ 1343 | "src/" 1344 | ] 1345 | }, 1346 | "notification-url": "https://packagist.org/downloads/", 1347 | "license": [ 1348 | "BSD-3-Clause" 1349 | ], 1350 | "authors": [ 1351 | { 1352 | "name": "Sebastian Bergmann", 1353 | "email": "sebastian@phpunit.de", 1354 | "role": "lead" 1355 | } 1356 | ], 1357 | "description": "Library for counting the lines of code in PHP source code", 1358 | "homepage": "https://github.com/sebastianbergmann/lines-of-code", 1359 | "support": { 1360 | "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", 1361 | "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" 1362 | }, 1363 | "funding": [ 1364 | { 1365 | "url": "https://github.com/sebastianbergmann", 1366 | "type": "github" 1367 | } 1368 | ], 1369 | "time": "2023-12-22T06:20:34+00:00" 1370 | }, 1371 | { 1372 | "name": "sebastian/object-enumerator", 1373 | "version": "4.0.4", 1374 | "source": { 1375 | "type": "git", 1376 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1377 | "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" 1378 | }, 1379 | "dist": { 1380 | "type": "zip", 1381 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", 1382 | "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", 1383 | "shasum": "" 1384 | }, 1385 | "require": { 1386 | "php": ">=7.3", 1387 | "sebastian/object-reflector": "^2.0", 1388 | "sebastian/recursion-context": "^4.0" 1389 | }, 1390 | "require-dev": { 1391 | "phpunit/phpunit": "^9.3" 1392 | }, 1393 | "type": "library", 1394 | "extra": { 1395 | "branch-alias": { 1396 | "dev-master": "4.0-dev" 1397 | } 1398 | }, 1399 | "autoload": { 1400 | "classmap": [ 1401 | "src/" 1402 | ] 1403 | }, 1404 | "notification-url": "https://packagist.org/downloads/", 1405 | "license": [ 1406 | "BSD-3-Clause" 1407 | ], 1408 | "authors": [ 1409 | { 1410 | "name": "Sebastian Bergmann", 1411 | "email": "sebastian@phpunit.de" 1412 | } 1413 | ], 1414 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1415 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1416 | "support": { 1417 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", 1418 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" 1419 | }, 1420 | "funding": [ 1421 | { 1422 | "url": "https://github.com/sebastianbergmann", 1423 | "type": "github" 1424 | } 1425 | ], 1426 | "time": "2020-10-26T13:12:34+00:00" 1427 | }, 1428 | { 1429 | "name": "sebastian/object-reflector", 1430 | "version": "2.0.4", 1431 | "source": { 1432 | "type": "git", 1433 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 1434 | "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" 1435 | }, 1436 | "dist": { 1437 | "type": "zip", 1438 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", 1439 | "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", 1440 | "shasum": "" 1441 | }, 1442 | "require": { 1443 | "php": ">=7.3" 1444 | }, 1445 | "require-dev": { 1446 | "phpunit/phpunit": "^9.3" 1447 | }, 1448 | "type": "library", 1449 | "extra": { 1450 | "branch-alias": { 1451 | "dev-master": "2.0-dev" 1452 | } 1453 | }, 1454 | "autoload": { 1455 | "classmap": [ 1456 | "src/" 1457 | ] 1458 | }, 1459 | "notification-url": "https://packagist.org/downloads/", 1460 | "license": [ 1461 | "BSD-3-Clause" 1462 | ], 1463 | "authors": [ 1464 | { 1465 | "name": "Sebastian Bergmann", 1466 | "email": "sebastian@phpunit.de" 1467 | } 1468 | ], 1469 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 1470 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 1471 | "support": { 1472 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues", 1473 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" 1474 | }, 1475 | "funding": [ 1476 | { 1477 | "url": "https://github.com/sebastianbergmann", 1478 | "type": "github" 1479 | } 1480 | ], 1481 | "time": "2020-10-26T13:14:26+00:00" 1482 | }, 1483 | { 1484 | "name": "sebastian/recursion-context", 1485 | "version": "4.0.5", 1486 | "source": { 1487 | "type": "git", 1488 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1489 | "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" 1490 | }, 1491 | "dist": { 1492 | "type": "zip", 1493 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", 1494 | "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", 1495 | "shasum": "" 1496 | }, 1497 | "require": { 1498 | "php": ">=7.3" 1499 | }, 1500 | "require-dev": { 1501 | "phpunit/phpunit": "^9.3" 1502 | }, 1503 | "type": "library", 1504 | "extra": { 1505 | "branch-alias": { 1506 | "dev-master": "4.0-dev" 1507 | } 1508 | }, 1509 | "autoload": { 1510 | "classmap": [ 1511 | "src/" 1512 | ] 1513 | }, 1514 | "notification-url": "https://packagist.org/downloads/", 1515 | "license": [ 1516 | "BSD-3-Clause" 1517 | ], 1518 | "authors": [ 1519 | { 1520 | "name": "Sebastian Bergmann", 1521 | "email": "sebastian@phpunit.de" 1522 | }, 1523 | { 1524 | "name": "Jeff Welch", 1525 | "email": "whatthejeff@gmail.com" 1526 | }, 1527 | { 1528 | "name": "Adam Harvey", 1529 | "email": "aharvey@php.net" 1530 | } 1531 | ], 1532 | "description": "Provides functionality to recursively process PHP variables", 1533 | "homepage": "https://github.com/sebastianbergmann/recursion-context", 1534 | "support": { 1535 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues", 1536 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" 1537 | }, 1538 | "funding": [ 1539 | { 1540 | "url": "https://github.com/sebastianbergmann", 1541 | "type": "github" 1542 | } 1543 | ], 1544 | "time": "2023-02-03T06:07:39+00:00" 1545 | }, 1546 | { 1547 | "name": "sebastian/resource-operations", 1548 | "version": "3.0.4", 1549 | "source": { 1550 | "type": "git", 1551 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1552 | "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" 1553 | }, 1554 | "dist": { 1555 | "type": "zip", 1556 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", 1557 | "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", 1558 | "shasum": "" 1559 | }, 1560 | "require": { 1561 | "php": ">=7.3" 1562 | }, 1563 | "require-dev": { 1564 | "phpunit/phpunit": "^9.0" 1565 | }, 1566 | "type": "library", 1567 | "extra": { 1568 | "branch-alias": { 1569 | "dev-main": "3.0-dev" 1570 | } 1571 | }, 1572 | "autoload": { 1573 | "classmap": [ 1574 | "src/" 1575 | ] 1576 | }, 1577 | "notification-url": "https://packagist.org/downloads/", 1578 | "license": [ 1579 | "BSD-3-Clause" 1580 | ], 1581 | "authors": [ 1582 | { 1583 | "name": "Sebastian Bergmann", 1584 | "email": "sebastian@phpunit.de" 1585 | } 1586 | ], 1587 | "description": "Provides a list of PHP built-in functions that operate on resources", 1588 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1589 | "support": { 1590 | "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" 1591 | }, 1592 | "funding": [ 1593 | { 1594 | "url": "https://github.com/sebastianbergmann", 1595 | "type": "github" 1596 | } 1597 | ], 1598 | "time": "2024-03-14T16:00:52+00:00" 1599 | }, 1600 | { 1601 | "name": "sebastian/type", 1602 | "version": "3.2.1", 1603 | "source": { 1604 | "type": "git", 1605 | "url": "https://github.com/sebastianbergmann/type.git", 1606 | "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" 1607 | }, 1608 | "dist": { 1609 | "type": "zip", 1610 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", 1611 | "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", 1612 | "shasum": "" 1613 | }, 1614 | "require": { 1615 | "php": ">=7.3" 1616 | }, 1617 | "require-dev": { 1618 | "phpunit/phpunit": "^9.5" 1619 | }, 1620 | "type": "library", 1621 | "extra": { 1622 | "branch-alias": { 1623 | "dev-master": "3.2-dev" 1624 | } 1625 | }, 1626 | "autoload": { 1627 | "classmap": [ 1628 | "src/" 1629 | ] 1630 | }, 1631 | "notification-url": "https://packagist.org/downloads/", 1632 | "license": [ 1633 | "BSD-3-Clause" 1634 | ], 1635 | "authors": [ 1636 | { 1637 | "name": "Sebastian Bergmann", 1638 | "email": "sebastian@phpunit.de", 1639 | "role": "lead" 1640 | } 1641 | ], 1642 | "description": "Collection of value objects that represent the types of the PHP type system", 1643 | "homepage": "https://github.com/sebastianbergmann/type", 1644 | "support": { 1645 | "issues": "https://github.com/sebastianbergmann/type/issues", 1646 | "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" 1647 | }, 1648 | "funding": [ 1649 | { 1650 | "url": "https://github.com/sebastianbergmann", 1651 | "type": "github" 1652 | } 1653 | ], 1654 | "time": "2023-02-03T06:13:03+00:00" 1655 | }, 1656 | { 1657 | "name": "sebastian/version", 1658 | "version": "3.0.2", 1659 | "source": { 1660 | "type": "git", 1661 | "url": "https://github.com/sebastianbergmann/version.git", 1662 | "reference": "c6c1022351a901512170118436c764e473f6de8c" 1663 | }, 1664 | "dist": { 1665 | "type": "zip", 1666 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", 1667 | "reference": "c6c1022351a901512170118436c764e473f6de8c", 1668 | "shasum": "" 1669 | }, 1670 | "require": { 1671 | "php": ">=7.3" 1672 | }, 1673 | "type": "library", 1674 | "extra": { 1675 | "branch-alias": { 1676 | "dev-master": "3.0-dev" 1677 | } 1678 | }, 1679 | "autoload": { 1680 | "classmap": [ 1681 | "src/" 1682 | ] 1683 | }, 1684 | "notification-url": "https://packagist.org/downloads/", 1685 | "license": [ 1686 | "BSD-3-Clause" 1687 | ], 1688 | "authors": [ 1689 | { 1690 | "name": "Sebastian Bergmann", 1691 | "email": "sebastian@phpunit.de", 1692 | "role": "lead" 1693 | } 1694 | ], 1695 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1696 | "homepage": "https://github.com/sebastianbergmann/version", 1697 | "support": { 1698 | "issues": "https://github.com/sebastianbergmann/version/issues", 1699 | "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" 1700 | }, 1701 | "funding": [ 1702 | { 1703 | "url": "https://github.com/sebastianbergmann", 1704 | "type": "github" 1705 | } 1706 | ], 1707 | "time": "2020-09-28T06:39:44+00:00" 1708 | }, 1709 | { 1710 | "name": "theseer/tokenizer", 1711 | "version": "1.2.3", 1712 | "source": { 1713 | "type": "git", 1714 | "url": "https://github.com/theseer/tokenizer.git", 1715 | "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" 1716 | }, 1717 | "dist": { 1718 | "type": "zip", 1719 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", 1720 | "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", 1721 | "shasum": "" 1722 | }, 1723 | "require": { 1724 | "ext-dom": "*", 1725 | "ext-tokenizer": "*", 1726 | "ext-xmlwriter": "*", 1727 | "php": "^7.2 || ^8.0" 1728 | }, 1729 | "type": "library", 1730 | "autoload": { 1731 | "classmap": [ 1732 | "src/" 1733 | ] 1734 | }, 1735 | "notification-url": "https://packagist.org/downloads/", 1736 | "license": [ 1737 | "BSD-3-Clause" 1738 | ], 1739 | "authors": [ 1740 | { 1741 | "name": "Arne Blankerts", 1742 | "email": "arne@blankerts.de", 1743 | "role": "Developer" 1744 | } 1745 | ], 1746 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 1747 | "support": { 1748 | "issues": "https://github.com/theseer/tokenizer/issues", 1749 | "source": "https://github.com/theseer/tokenizer/tree/1.2.3" 1750 | }, 1751 | "funding": [ 1752 | { 1753 | "url": "https://github.com/theseer", 1754 | "type": "github" 1755 | } 1756 | ], 1757 | "time": "2024-03-03T12:36:25+00:00" 1758 | } 1759 | ], 1760 | "aliases": [], 1761 | "minimum-stability": "stable", 1762 | "stability-flags": [], 1763 | "prefer-stable": false, 1764 | "prefer-lowest": false, 1765 | "platform": [], 1766 | "platform-dev": [], 1767 | "plugin-api-version": "2.2.0" 1768 | } 1769 | -------------------------------------------------------------------------------- /lib/API.php: -------------------------------------------------------------------------------- 1 | API_KEY = $api_key; 36 | $this->API_CLIENT_STUB = sprintf($this->API_CLIENT_STUB, 37 | $this->API_CLIENT_VERSION); 38 | 39 | foreach ($options as $key => $value) { 40 | $this->$key = $value; 41 | } 42 | } 43 | 44 | /** 45 | * Send an email 46 | * 47 | * The additional optional parameters are as follows: 48 | * 'email_data' - Default is null. Array of variables to merge into the template. 49 | * 'sender' - Default is null. Array ("address", "name", "reply_to") of sender. 50 | * 'cc' - Default is null. Array of ("address", "name") for carbon copy. 51 | * 'bcc' - Default is null. Array of ("address", "name") for blind carbon copy. 52 | * 'inline' - Default is null. String, path to file to include inline. 53 | * 'tags' - Default is null. Array of strings to tag email send with. 54 | * 'version_name' - Default is blank. String, name of version to send 55 | * 56 | * @param string $email_id ID of email to send 57 | * @param array $recipient array of ("address", "name") to send to 58 | * @param array $args (optional) additional optional parameters 59 | * @return array API response object 60 | */ 61 | public function send($email_id, $recipient, $args = null) { 62 | $endpoint = "send"; 63 | 64 | $payload = array( 65 | "email_id" => $email_id, 66 | "recipient" => $recipient 67 | ); 68 | 69 | if (is_array($args)) { 70 | $payload = array_merge($args, $payload); 71 | } 72 | 73 | // Optional inline attachment 74 | if (isset($payload['inline'])) { 75 | 76 | if (is_string($payload['inline'])) { 77 | 78 | $inline_attachment_path = $payload['inline']; 79 | 80 | $payload["inline"] = array( 81 | "id" => basename($inline_attachment_path), 82 | "data" => $this->encode_attachment($inline_attachment_path) 83 | ); 84 | } 85 | } 86 | 87 | // Optional file attachment 88 | if (isset($payload['files'])) { 89 | foreach ($payload['files'] as &$file) { 90 | if (is_array($file) && isset($file['id']) && isset($file['data'])) { 91 | continue; 92 | } 93 | $file = array( 94 | "id" => basename($file), 95 | "data" => $this->encode_attachment($file) 96 | ); 97 | } 98 | } 99 | 100 | if ($this->DEBUG) { 101 | $message = sprintf( 102 | "Sending email `%s` to \n `%s`", 103 | $email_id, 104 | print_r($recipient, true) 105 | ); 106 | if (isset($payload['sender'])) { 107 | $message .= sprintf( 108 | "\nfrom\n `%s`", 109 | print_r($payload['sender'], true) 110 | ); 111 | } 112 | $message .= sprintf("\nwith\n%s", print_r($payload, true)); 113 | $this->log_message($message); 114 | } 115 | 116 | return $this->api_request($endpoint, self::HTTP_POST, $payload); 117 | } 118 | 119 | /** 120 | * Resend a specific email by id 121 | * 122 | * @param string $log_id log id 123 | * @return array API response object 124 | */ 125 | public function resend($log_id){ 126 | $endpoint = "resend"; 127 | 128 | $payload = array( 129 | "log_id" => $log_id 130 | ); 131 | 132 | return $this->api_request($endpoint, self::HTTP_POST, $payload); 133 | } 134 | 135 | /** 136 | * Get Emails 137 | * 138 | * @return array API response object. 139 | */ 140 | public function emails() { 141 | $endpoint = "templates"; 142 | return $this->api_request($endpoint, self::HTTP_GET); 143 | } 144 | 145 | /** 146 | * Get a specific template 147 | * 148 | * @param string $template_id template id 149 | * @param string $version_id optional version id to get template version 150 | * 151 | * @return array API response object 152 | */ 153 | public function get_template($template_id, $version_id = null){ 154 | $endpoint = "templates/" . $template_id; 155 | 156 | if($version_id){ 157 | $endpoint .= "/versions/" . $version_id; 158 | } 159 | 160 | return $this->api_request($endpoint, self::HTTP_GET); 161 | } 162 | 163 | /** 164 | * Get Customer 165 | * 166 | * @param string $email customer email 167 | * 168 | * @return array API response object. 169 | */ 170 | public function get_customer($email) { 171 | $endpoint = "customers/" . $email; 172 | 173 | return $this->api_request($endpoint, self::HTTP_GET); 174 | } 175 | 176 | /** 177 | * Get Customer Logs 178 | * 179 | * @param string $email customer email 180 | * 181 | * @return array API response object. 182 | */ 183 | public function get_customer_logs( $email ) { 184 | $endpoint = "customers/" . $email . "/logs"; 185 | 186 | return $this->api_request( $endpoint, self::HTTP_GET ); 187 | } 188 | 189 | /** 190 | * Create Customer 191 | * 192 | * @param string $email customer email 193 | * @param array $data (optional) customer data to 194 | * @param array $args (optional) optional arguments 195 | * 196 | * The additional optional parameters are as follows: 197 | * 'locale' - Default is null. String to specify a locale for this customer. 198 | * 199 | * @return array API response object. 200 | */ 201 | public function create_customer($email, $data=null, $args=null) { 202 | $endpoint = "customers"; 203 | $payload = array("email" => $email); 204 | 205 | if (is_array($data)) { 206 | $payload['data'] = $data; 207 | } 208 | 209 | if (is_array($args)) { 210 | $payload = array_merge($args, $payload); 211 | } 212 | 213 | return $this->api_request($endpoint, self::HTTP_POST, $payload); 214 | } 215 | 216 | /** 217 | * Update Customer 218 | * 219 | * @param string $email customer email 220 | * @param array $data customer data to 221 | * 222 | * @return array API response object. 223 | */ 224 | public function update_customer($email, $data=null) { 225 | return $this->create_customer($email, $data); 226 | } 227 | 228 | /** 229 | * Delete Customer 230 | * 231 | * @param string $email customer email 232 | * 233 | * @return array API response object. 234 | */ 235 | public function delete_customer($email) { 236 | $endpoint = "customers/" . $email; 237 | return $this->api_request($endpoint, self::HTTP_DELETE); 238 | } 239 | 240 | /** 241 | * Create an Email 242 | * 243 | * @param string $name name of the email template 244 | * @param string $subject subject line for the email template 245 | * @param string $html HTML code for the email template 246 | * @param string $text Optional text version of the email template 247 | * @param string $preheader Optional preheader for the email template 248 | * @param string $amp_html Optional AMP version of the email template 249 | * @param array $template_data Optional Array of variables to merge into the template. 250 | * @return array API response object 251 | */ 252 | public function create_email($name, $subject, $html, $text=null, $preheader=null, $amp_html=null, $template_data=null) { 253 | $endpoint = "templates"; 254 | 255 | $payload = array( 256 | "name" => $name, 257 | "subject" => $subject, 258 | "html" => $html 259 | ); 260 | 261 | // set optionals 262 | if ($text) { 263 | $payload["text"] = $text; 264 | } 265 | if (!is_null($preheader)) { 266 | $payload["preheader"] = $preheader; 267 | } 268 | if (!is_null($amp_html)) { 269 | $payload["amp_html"] = $amp_html; 270 | } 271 | if (!is_null($template_data)) { 272 | $payload["template_data"] = $template_data; 273 | } 274 | 275 | if ($this->DEBUG) { 276 | $this->log_message(sprintf("creating email with name %s and subject %s\n", $name, $subject)); 277 | } 278 | 279 | return $this->api_request($endpoint, self::HTTP_POST, $payload); 280 | } 281 | 282 | /** 283 | * Create new template version 284 | * @param string $name name of the email template 285 | * @param string $subject subject line for the email template 286 | * @param string $template_id template id 287 | * @param string $html HTML code for the email template 288 | * @param string $text Optional text version of the email template 289 | * @param string $preheader Optional preheader for the email template 290 | * @param string $amp_html Optional AMP version of the email template 291 | * @param array $template_data Optional Array of variables to merge into the template. 292 | * @return array API response object 293 | */ 294 | public function create_new_template_version($name, $subject, $template_id, $html, $text=null, $preheader=null, $amp_html=null, $template_data=null) { 295 | $endpoint = "templates/" . $template_id . "/versions"; 296 | 297 | $payload = array( 298 | "name" => $name, 299 | "subject" => $subject, 300 | "html" => $html 301 | ); 302 | 303 | // set optionals 304 | if ($text) { 305 | $payload["text"] = $text; 306 | } 307 | if (!is_null($preheader)) { 308 | $payload["preheader"] = $preheader; 309 | } 310 | if (!is_null($amp_html)) { 311 | $payload["amp_html"] = $amp_html; 312 | } 313 | if (!is_null($template_data)) { 314 | $payload["template_data"] = $template_data; 315 | } 316 | 317 | if ($this->DEBUG) { 318 | $this->log_message( 319 | sprintf("creating a new template version with name %s and subject %s\n", $name, $subject) 320 | ); 321 | } 322 | 323 | return $this->api_request($endpoint, self::HTTP_POST, $payload); 324 | } 325 | 326 | /** 327 | * Update template version 328 | * @param string $name name of the email template 329 | * @param string $subject subject line for the email template 330 | * @param string $template_id template id 331 | * @param string $version_id template version id 332 | * @param string $html HTML code for the email template 333 | * @param string $text Optional text version of the email template 334 | * @param string $preheader Optional preheader for the email template 335 | * @param string $amp_html Optional AMP version of the email template 336 | * @param array $template_data Optional Array of variables to merge into the template. 337 | * @return array API response object 338 | */ 339 | public function update_template_version($name, $subject, $template_id, $version_id, $html, $text=null, $preheader=null, $amp_html=null, $template_data=null) { 340 | $endpoint = "templates/" . $template_id . "/versions/" . $version_id; 341 | 342 | $payload = array( 343 | "name" => $name, 344 | "subject" => $subject, 345 | "html" => $html 346 | ); 347 | 348 | // set optionals 349 | if ($text) { 350 | $payload["text"] = $text; 351 | } 352 | if (!is_null($preheader)) { 353 | $payload["preheader"] = $preheader; 354 | } 355 | if (!is_null($amp_html)) { 356 | $payload["amp_html"] = $amp_html; 357 | } 358 | if (!is_null($template_data)) { 359 | $payload["template_data"] = $template_data; 360 | } 361 | 362 | if ($this->DEBUG) { 363 | $this->log_message( 364 | sprintf( 365 | "updating template\n ID:%s\nVERSION:%s\n with name %s and subject %s\n", 366 | $template_id, 367 | $version_id, 368 | $name, 369 | $subject 370 | ) 371 | ); 372 | } 373 | 374 | return $this->api_request($endpoint, self::HTTP_PUT, $payload); 375 | } 376 | 377 | /** 378 | * Get Specific Email Log 379 | * 380 | * @param string $log_id the log getting retrieved 381 | * @return array API response object 382 | */ 383 | public function get_log($log_id) { 384 | $endpoint = "logs/" . $log_id; 385 | 386 | return $this->api_request($endpoint, self::HTTP_GET); 387 | } 388 | 389 | /** 390 | * Get Specific Email's Events 391 | * 392 | * @param string $log_id the log getting retrieved 393 | * @return array API response object 394 | */ 395 | public function get_events($log_id) { 396 | $endpoint = "logs/" . $log_id . "/events"; 397 | 398 | return $this->api_request($endpoint, self::HTTP_GET); 399 | } 400 | 401 | /** 402 | * Unsubscribe email address from active drips 403 | * 404 | * @param string $email_address the email to unsubscribe from active drips 405 | * @return array API response object 406 | */ 407 | public function drip_unsubscribe($email_address) { 408 | $endpoint = "drips/unsubscribe"; 409 | 410 | $payload = array( 411 | "email_address" => $email_address 412 | ); 413 | 414 | return $this->api_request($endpoint, self::HTTP_POST, $payload); 415 | } 416 | 417 | /** 418 | * List drip campaigns 419 | * 420 | * @return array API response object 421 | */ 422 | public function list_drip_campaigns(){ 423 | $endpoint = "drip_campaigns"; 424 | return $this->api_request($endpoint, self::HTTP_GET); 425 | } 426 | 427 | /** 428 | * List drip campaign details 429 | * 430 | * @param string $drip_campaign_id id of drip campaign 431 | * @return array API response object 432 | */ 433 | public function drip_campaign_details($drip_campaign_id){ 434 | $endpoint = "drip_campaigns/" . $drip_campaign_id; 435 | 436 | return $this->api_request($endpoint, self::HTTP_GET); 437 | } 438 | 439 | /** 440 | * Start on drip campaign 441 | * 442 | * The additional optional parameters for $args are as follows: 443 | * 'sender' - Default is null. Array ("address", "name", "reply_to") of sender. 444 | * 'cc' - Default is null. Array of ("address", "name") for carbon copy. 445 | * 'bcc' - Default is null. Array of ("address", "name") for blind carbon copy. 446 | * 'tags' - Default is null. Array of strings to tag email send with. 447 | * 'esp' - Default is null. Value of ("esp_account": "esp_id") 448 | * 449 | * @param array $recipient_address array of ("address", "name") to send to 450 | * @param string $drip_campaign_id drip campaign being added to 451 | * @param array (optional) $data email data being sent with drip 452 | * @param array (optional) $args additional options being sent with email (tags, cc's, etc) 453 | * @return array API response object 454 | */ 455 | public function start_on_drip_campaign($recipient_address, $drip_campaign_id, $data=null, $args=null){ 456 | $endpoint = "drip_campaigns/" . $drip_campaign_id . "/activate"; 457 | 458 | $payload = array(); 459 | if (is_array($recipient_address)) { 460 | $payload["recipient"] = $recipient_address; 461 | } else if (is_string($recipient_address)){ 462 | $payload = array( 463 | "recipient_address" => $recipient_address 464 | ); 465 | } 466 | 467 | if (is_array($data)) { 468 | $payload['email_data'] = $data; 469 | } 470 | 471 | if (is_array($args)) { 472 | $payload = array_merge($args, $payload); 473 | } 474 | 475 | return $this->api_request($endpoint, self::HTTP_POST, $payload); 476 | } 477 | 478 | /** 479 | * Remove customer from drip campaign 480 | * 481 | * @param string $recipient_address email address being removed drip campaign 482 | * @param string $drip_campaign_id drip campaign being removed to 483 | * @return array API response object 484 | */ 485 | public function remove_from_drip_campaign($recipient_address, $drip_campaign_id){ 486 | $endpoint = "drip_campaigns/" . $drip_campaign_id . "/deactivate"; 487 | 488 | $payload = array( 489 | "recipient_address" => $recipient_address 490 | ); 491 | 492 | return $this->api_request($endpoint, self::HTTP_POST, $payload); 493 | } 494 | 495 | /** 496 | * Remove customer from all drip campaigns 497 | * 498 | * @param string $recipient_address email address being removed from all drip campaigns 499 | * @return array API response object 500 | */ 501 | public function remove_from_all_drip_campaigns($recipient_address){ 502 | $endpoint = "drip_campaigns/deactivate"; 503 | 504 | $payload = array( 505 | "recipient_address" => $recipient_address 506 | ); 507 | 508 | return $this->api_request($endpoint, self::HTTP_POST, $payload); 509 | } 510 | 511 | /** 512 | * Start Batch API transaction 513 | * 514 | * @return BatchAPI object 515 | */ 516 | public function start_batch() { 517 | return new BatchAPI( 518 | $this->API_KEY, 519 | array( 520 | 'API_HOST' => $this->API_HOST, 521 | 'API_PROTO' => $this->API_PROTO, 522 | 'API_PORT' => $this->API_PORT, 523 | 'API_VERSION' => $this->API_VERSION, 524 | 'DEBUG' => $this->DEBUG, 525 | ) 526 | ); 527 | } 528 | 529 | /** 530 | * Render an email template with the provided data 531 | * 532 | * The additional optional parameters are as follows: 533 | * 'template_data' - Default is null. Array of variables to merge into the template. 534 | * 535 | * @param string $email_id ID of email to send 536 | * @param array $args (optional) additional optional parameters 537 | * @return array API response object 538 | */ 539 | public function render($email_id, $args = null) { 540 | $endpoint = "render"; 541 | 542 | $payload = array( 543 | "template_id" => $email_id 544 | ); 545 | 546 | if (is_array($args)) { 547 | $payload = array_merge($args, $payload); 548 | } 549 | 550 | if ($this->DEBUG) { 551 | $this->log_message(sprintf( 552 | "rendering template `%s` with \n%s", 553 | $email_id, 554 | print_r($payload, true) 555 | )); 556 | } 557 | 558 | return $this->api_request($endpoint, self::HTTP_POST, $payload); 559 | } 560 | 561 | /** 562 | * Helper function to Base64 encode files and return the encoded data 563 | * 564 | * @param string $path Local path of the file to encode 565 | * @return string/false the encoded file data or false on failure 566 | */ 567 | protected function encode_attachment($path) { 568 | if (!is_string($path)) { 569 | $e = sprintf("inline parameter must be path to file as string, received: %s", gettype($path)); 570 | throw new API_Error($e); 571 | } 572 | 573 | $file_data = file_get_contents($path); 574 | 575 | return base64_encode($file_data); 576 | } 577 | 578 | protected function build_path($endpoint, $absolute = True) { 579 | $path = sprintf("/api/v%s/%s", $this->API_VERSION, $endpoint); 580 | if ($absolute) { 581 | $path = sprintf("%s://%s:%s%s", 582 | $this->API_PROTO, 583 | $this->API_HOST, 584 | $this->API_PORT, 585 | $path); 586 | } 587 | return $path; 588 | } 589 | 590 | protected function api_request($endpoint, $request = "POST", $payload = null, $params = null) { 591 | $path = $this->build_path($endpoint); 592 | $response = array(); 593 | 594 | if ($params) 595 | { 596 | $path = $path . '?' . http_build_query($params); 597 | } 598 | 599 | $ch = curl_init($path); 600 | 601 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $request); 602 | 603 | // set payload 604 | $payload_string = null; 605 | if ($payload) { 606 | $payload_string = json_encode($payload); 607 | curl_setopt($ch, CURLOPT_POSTFIELDS, $payload_string); 608 | } 609 | 610 | // set headers 611 | if ($payload && ($request == "POST" || $request == "PUT")) { 612 | $httpheaders = array( 613 | 'Content-Type: application/json', 614 | 'Content-Length: ' . strlen($payload_string), 615 | $this->API_HEADER_KEY . ": " . $this->API_KEY, 616 | $this->API_HEADER_CLIENT . ": " . $this->API_CLIENT_STUB 617 | ); 618 | } 619 | else { 620 | $httpheaders = array( 621 | $this->API_HEADER_KEY . ": " . $this->API_KEY, 622 | $this->API_HEADER_CLIENT . ": " . $this->API_CLIENT_STUB 623 | ); 624 | } 625 | 626 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); 627 | curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/data/ca-certificates.pem'); 628 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 629 | curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheaders); 630 | 631 | if ($this->DEBUG) { 632 | // enable curl verbose output to STDERR 633 | curl_setopt($ch, CURLOPT_VERBOSE, true); 634 | 635 | $this->log_message(sprintf("payload: %s\r\npath: %s\r\n", $payload_string, $path)); 636 | } 637 | 638 | $code = null; 639 | try { 640 | $result = curl_exec($ch); 641 | $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 642 | $response = json_decode( $result ); 643 | 644 | if ($code != 200) { 645 | throw new API_Error("Request was not successful", $code, $result, $response); 646 | } 647 | } catch (API_Error $e) { 648 | if ($this->DEBUG) { 649 | $this->log_message( 650 | sprintf("Caught exception: %s\r\n%s", $e->getMessage(), print_r($e, true)), 651 | self::LOG_ERR 652 | ); 653 | } 654 | 655 | $response = (object) array( 656 | 'code' => $code, 657 | 'status' => "error", 658 | 'success' => false, 659 | 'exception' => $e 660 | ); 661 | } 662 | 663 | curl_close($ch); 664 | 665 | return $response; 666 | } 667 | 668 | /** 669 | * Log debug messages using the custom handler passed as an option in the constructor. 670 | * If not handler is defined it falls back to using 'error_log'. 671 | * 672 | * @param $message string the logged message 673 | * @param string $priority_level based on syslog priority levels http://php.net/manual/en/function.syslog.php 674 | * @return bool true on success or false on failure 675 | */ 676 | protected function log_message($message, $priority_level = self::LOG_DEBUG) 677 | { 678 | if ( 679 | $this->DEBUG && 680 | $this->API_DEBUG_HANDLER && 681 | is_callable($this->API_DEBUG_HANDLER) 682 | ) { 683 | $response = call_user_func($this->API_DEBUG_HANDLER, $message, $priority_level); 684 | } else { 685 | $response = error_log($message); 686 | } 687 | 688 | return $response; 689 | } 690 | } 691 | 692 | 693 | class BatchAPI extends API { 694 | private $commands; 695 | 696 | public function __construct($api_key, $options = array()) { 697 | parent::__construct($api_key, $options); 698 | $this->commands = array(); 699 | } 700 | 701 | protected function api_request($endpoint, $request = "POST", $payload = null, $params = null) { 702 | $path = $this->build_path($endpoint, $absolute = false); 703 | 704 | if ($params) { 705 | $path = $path . '?' . http_build_query($params); 706 | } 707 | 708 | $command = array( 709 | 'path' => $path, 710 | 'method' => $request 711 | ); 712 | 713 | // set payload 714 | if ($payload) { 715 | $command['body'] = $payload; 716 | } 717 | 718 | $this->commands[] = $command; 719 | 720 | return (object) array( 721 | 'status' => 'Batched', 722 | 'success' => true, 723 | ); 724 | } 725 | 726 | /** 727 | * Execute all currently queued commands 728 | * 729 | * @return array BatchAPI response object. 730 | */ 731 | public function execute() { 732 | $endpoint = "batch"; 733 | $path = $this->build_path($endpoint); 734 | 735 | $ch = curl_init($path); 736 | 737 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, self::HTTP_POST); 738 | 739 | // set payload 740 | $payload_string = json_encode($this->commands); 741 | curl_setopt($ch, CURLOPT_POSTFIELDS, $payload_string); 742 | 743 | // set headers 744 | $httpheaders = array( 745 | 'Content-Type: application/json', 746 | 'Content-Length: ' . strlen($payload_string), 747 | $this->API_HEADER_KEY . ": " . $this->API_KEY, 748 | $this->API_HEADER_CLIENT . ": " . $this->API_CLIENT_STUB 749 | ); 750 | 751 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); 752 | curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/data/ca-certificates.pem'); 753 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 754 | curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheaders); 755 | 756 | if ($this->DEBUG) { 757 | // enable curl verbose output to STDERR 758 | curl_setopt($ch, CURLOPT_VERBOSE, true); 759 | 760 | $this->log_message(sprintf("payload: %s\r\npath: %s\r\n", $payload_string, $path)); 761 | } 762 | 763 | $code = null; 764 | 765 | try { 766 | $result = curl_exec($ch); 767 | $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 768 | $response = json_decode( $result ); 769 | 770 | if ($code != 200) { 771 | throw new API_Error("Request was not successful", $code, $result, $response); 772 | } 773 | } catch (API_Error $e) { 774 | if ($this->DEBUG) { 775 | $this->log_message( 776 | sprintf("Caught exception: %s\r\n%s", $e->getMessage(), print_r($e, true)), 777 | self::LOG_ERR 778 | ); 779 | } 780 | 781 | $response = (object) array( 782 | 'code' => $code, 783 | 'status' => "error", 784 | 'success' => false, 785 | 'exception' => $e 786 | ); 787 | } 788 | 789 | curl_close($ch); 790 | $this->commands = array(); 791 | 792 | return $response; 793 | } 794 | 795 | /** 796 | * Cancel any pending batched commands to be sent. 797 | * 798 | * @return object 799 | */ 800 | public function cancel() { 801 | $this->commands = array(); 802 | return (object) array( 803 | 'code' => 0, // Use 0 because we didn't even talk to the server. 804 | 'status' => 'Canceled', 805 | 'success' => true, 806 | 'exception' => null 807 | ); 808 | } 809 | 810 | public function command_length() { 811 | return count($this->commands); 812 | } 813 | } 814 | 815 | ?> 816 | -------------------------------------------------------------------------------- /lib/Error.php: -------------------------------------------------------------------------------- 1 | status = $status; 16 | $this->body = $body; 17 | $this->json = $json; 18 | } 19 | 20 | public function getStatus() { 21 | return $this->status; 22 | } 23 | 24 | public function getBody() { 25 | return $this->body; 26 | } 27 | 28 | public function getJson() { 29 | return $this->json; 30 | } 31 | } 32 | 33 | ?> -------------------------------------------------------------------------------- /test/APITest.php: -------------------------------------------------------------------------------- 1 | api_key = getenv('SWU_API_KEY') ?: 'PHP_API_CLIENT_TEST_KEY'; 45 | $this->email_id = getenv('TEMPLATE_ID') ?: 'test_fixture_1'; 46 | 47 | $this->options = array( 48 | 'DEBUG' => false 49 | ); 50 | 51 | $this->api = new \sendwithus\API($this->api_key, $this->options); 52 | 53 | $this->good_html = ''; 54 | 55 | $this->bad_html = '