├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── INSTALLATION.md ├── LICENSE ├── README.md ├── bin └── build.php ├── composer.json ├── phpcs.xml ├── phpunit.xml ├── src └── Interfax │ ├── Client.php │ ├── Document.php │ ├── Documents.php │ ├── Exception │ └── RequestException.php │ ├── File.php │ ├── GenericFactory.php │ ├── Image.php │ ├── Inbound.php │ ├── Inbound │ └── Fax.php │ ├── Outbound.php │ ├── Outbound │ ├── Delivery.php │ └── Fax.php │ └── Resource.php └── tests └── Interfax ├── BaseTest.php ├── ClientTest.php ├── DocumentTest.php ├── DocumentsTest.php ├── FileTest.php ├── ImageTest.php ├── Inbound └── FaxTest.php ├── InboundTest.php ├── Outbound ├── DeliveryTest.php └── FaxTest.php ├── OutboundTest.php └── test.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /build/ 3 | /vendor/ 4 | .idea 5 | composer.lock 6 | 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 7.3 4 | - 7.4 5 | - 8.0 6 | - 8.1 7 | install: 8 | - composer install --no-interaction 9 | - php -v 10 | - phpunit --version 11 | - echo "$TRAVIS_TAG" 12 | - echo "$TRAVIS_COMMIT" 13 | - echo "$TRAVIS_BUILD_NUMBER" 14 | before_deploy: 15 | - php ./bin/build.php 16 | - export RELEASE_PKG_FILE=$(ls build/*zip) 17 | 18 | deploy: 19 | provider: releases 20 | api_key: 21 | secure: QM1l+bhiQOLnZaRWVG+9KJ4sx8k2qKD4zveFyh9N1hHI3SV1zv3UKrHR3O2a6RL433smzBhwuCVmz5TT0m/V2yixl7ti7Bij7ENNqrvTWJTvAUbF5gCmXZbVmjBxggAVSsWo3szZgMSe1dDrdBkEQwOTADp9vUv49GhPLRCjfSr0+AR2woPhL6dphwpmha6UIl1eSDF6StaIVf+KIj1v794oAyWqe2sWjsPP9CwYZeNTJXMZwvSClKD7MTRgtbYJJYwkCmVWUJwqGLEbHCUMRrSUsvLkQkmlEymYS2T9buCnUHSUSrHe23UKyCYhUTFxDqSyuYsGbtpa7mdLe6ShqHl6PrkIsyugkXfAw6Td+M8VEyaAD68+7xhw6NEJHo7mpQzHLrd+t2jIfQgD6d3NsiioTdv5srgyMos1X+yjjM4f9dqzmp8ltaQXlcBgo+WKPW9V98rJCLR+UaCoyScbi/jKH/CdQUhdkIP1QMfyfRNGBSeqNtvhm554A0kfh3Euf2oicjeGOI3oRVXPvETSxOuBhiIQ+pJSn9qDnkntLlS3swX29OPqzTzZQ/Qpm8rGlCmcJIUeFfplrMmAZs+dgFTo/hcxvWzVHC3b+6XvQkGy9NR9Tc0hnm7yTFdExnP8rHeVDNHtU4E+yvjKYA/uvacyDEJ/tXeSwHCgUjIljiY= 22 | file: ${RELEASE_PKG_FILE} 23 | skip_cleanup: true 24 | on: 25 | tags: true 26 | condition: $TRAVIS_PHP_VERSION = 7.4 27 | repo: interfax/interfax-php 28 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). 4 | 5 | ## 2.0.0 6 | 7 | ### Changed 8 | + Conform to PSR12 coding standard 9 | + Composer dependency updates to match PHP version constraints for split branch versions of the library 10 | + README updates to reflect branch differences for library 11 | 12 | ### Fixed 13 | + Revert phpunit suggestion to dev dependency with correct version constraints 14 | + Restore missing CHANGELOG contents 15 | 16 | ## 1.1.5 17 | 18 | ### Fixed 19 | + Guzzle 7 support 20 | 21 | 1.1.4 22 | ===== 23 | + accept any 2xx status responses from interfax api 24 | + minor improvement to response exception generation 25 | 26 | 1.1.3 27 | ===== 28 | + Enhancement: Make fax id available when first created 29 | 30 | 1.1.2 31 | ===== 32 | + [BUG] Fix bug in marking inbound faxes as read/unread 33 | 34 | 35 | 1.1.1 36 | ===== 37 | + Bugfix: urlencode filename attribute for multipart fax document upload. 38 | 39 | 1.1.0 40 | ===== 41 | + Fluent method signatures where appropriate on Fax and Document objects. 42 | + Distinct Test namespace to ensure individual test file running is supported. 43 | 44 | 1.0.3 45 | ===== 46 | + Documentation correction for the outbound fax status retrieval. 47 | 48 | 1.0.2 49 | ===== 50 | + Update to project structure to support automatic package generation. 51 | 52 | 1.0.1 53 | ===== 54 | + Added INSTALLATION.md file to document how to use the library outside of a composer managed framework. 55 | + Added support for using stream resources to send faxes. 56 | 57 | 1.0.0 58 | ===== 59 | + Initial release 60 | -------------------------------------------------------------------------------- /INSTALLATION.md: -------------------------------------------------------------------------------- 1 | # InterFAX PHP Library 2 | 3 | This file provides more detail as to how to install the Interfax PHP library in a project. 4 | 5 | ## Standard installation 6 | 7 | The standard approach is to use the composer dependency manager to add the library to your project requirements: 8 | 9 | ```bash 10 | composer require interfax/interfax 11 | ``` 12 | 13 | ## Standalone installation 14 | 15 | Many projects may not use composer to manage their dependencies. In these circumstances, it's possible to include the library 16 | as a self contained dependency. The following steps will allow you to do this. 17 | 18 | 1. Clone the Interfax PHP repo into your project. 19 | 1. Inside the Interfax PHP directory, download composer. 20 | 1. Run composer to download the dependencies for the Interfax library. 21 | 1. Back in your main project file(s), include the autoload file generated by composer to include the library and its dependencies. 22 | 1. Follow the documentation to use the library as normal. 23 | 24 | ### Example project 25 | 26 | Assuming a simple project for Acme Corp that consists of a single ```index.php``` file. 27 | 28 | #### Download the library 29 | 30 | ```sh 31 | cd acme-corp 32 | git clone https://github.com/interfax/interfax-php.git 33 | cd interfax-php 34 | ``` 35 | 36 | #### Setup Composer 37 | 38 | Assuming composer is not already installed globally, install it into a bin directory. 39 | 40 | ```sh 41 | mkdir bin 42 | ``` 43 | _Note the [composer site](http://getcomposer.org) has more detailed instructions for installation, the following is a small simplification_ 44 | ```sh 45 | php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" 46 | php composer-setup.php --install-dir=bin 47 | php -r "unlink('composer-setup.php');" 48 | ``` 49 | 50 | ### Download dependencies 51 | 52 | Simply run the install command to download the dependencies 53 | 54 | ```sh 55 | php ./bin/composer.phar install --no-dev 56 | ``` 57 | 58 | _The 'no-dev' option prevents the testing dependencies for the library from being installed to save space/bandwidth_ 59 | 60 | ### Use the library 61 | 62 | inside acme-corp/index.php 63 | 64 | ```php 65 | require_once(__DIR__ . '/interfax-php/vendor/autoload.php'); 66 | 67 | use Interfax\Client; 68 | 69 | $interfax = new Client(['username' => 'username', 'password' => 'password']); 70 | // etc 71 | ``` 72 | 73 | ### Deployment 74 | 75 | When it comes to deploying the project, the following paths will be required: 76 | 77 | ``` 78 | acme-corp/interfax-php/src/ 79 | acme-corp/interfax-php/vendor/ 80 | ``` 81 | 82 | The rest of the files can safely be ignored. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 InterFAX 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # InterFAX PHP Library 2 | 3 | [![PHP version](https://badge.fury.io/ph/interfax%2Finterfax.svg)](https://badge.fury.io/ph/interfax%2Finterfax)[![Build status](https://travis-ci.com/interfax/interfax-php.svg?branch=master)](https://travis-ci.com/interfax/interfax-php) 4 | 5 | [Installation](#installation) | [Getting Started](#getting-started) | [Contributing](#contributing) | [Usage](#usage) | [License](#license) 6 | 7 | Send and receive faxes in PHP with the [InterFAX](https://www.interfax.net/en/dev) REST API. 8 | 9 | ## Installation 10 | 11 | Two versions of this library exist, 1.x and 2.x. The 1.x version provides support for older versions of PHP, whilst the 2.x branch is designed to support the more recent releases of the language: 12 | 13 | | Package Version | Minimum PHP Version | Maximum PHP Version | 14 | |-----------------|----------------------|---------------------| 15 | | 1.x | 5.6 | 7.2 | 16 | | 2.x | 7.3 | 8.1 | 17 | 18 | ### Composer 19 | 20 | The preferred method of installation is via [Packagist](http://www.packagist.org) and [Composer](http://www.composer.org). Run the following command to install the package and add it as a requirement to your project's `composer.json`: 21 | 22 | ```bash 23 | composer require interfax/interfax 24 | ``` 25 | 26 | with 1.x version constraint 27 | ```bash 28 | composer require interfax/interfax:"^1" 29 | ``` 30 | 31 | or with 2.x version constraint 32 | 33 | ```bash 34 | composer require interfax/interfax:"^2" 35 | ``` 36 | 37 | ### Download the release 38 | 39 | You can download the package in its entirety (from 1.0.2 onward). The [Releases](https://github.com/interfax/interfax-php/releases) page lists all stable versions. Download any file 40 | with the name `interFAX-PHP-[RELEASE_NAME].zip` for a package including this library and its dependencies. 41 | 42 | Uncompress the zip file you download, and include the autoloader in your project: 43 | 44 | ```php 45 | require_once '/path/to/interFAX-PHP-[RELEASE_NAME]/vendor/autoload.php'; 46 | ``` 47 | 48 | You may wish to rename the release folder to not include the RELEASE_NAME, so that you can drop in future versions without changing the include. 49 | 50 | ### Build it yourself 51 | 52 | The [installation](INSTALLATION.md) docs explain how to create a standalone installation. 53 | 54 | ## Getting started 55 | 56 | To send a fax for a pdf file: 57 | 58 | ```php 59 | use Interfax\Client; 60 | 61 | $interfax = new Client(['username' => 'username', 'password' => 'password']); 62 | $fax = $interfax->deliver(['faxNumber' => '+11111111112', 'file' => 'folder/file.pdf']); 63 | 64 | // get the latest status: 65 | $fax->refresh()->status; // Pending if < 0. Error if > 0 66 | 67 | // Simple polling 68 | while ($fax->refresh()->status < 0) { 69 | sleep(5); 70 | } 71 | ``` 72 | 73 | # Usage 74 | 75 | [Client](#client) | [Account](#account) | [Outbound](#outbound) | [Inbound](#inbound) | [Documents](#documents) | [Helper Classes](#helper-classes) | [Query Parameters](#query-parameters) | [Exceptions](#exceptions) 76 | 77 | ## Client 78 | 79 | The client follows the [12-factor](http://12factor.net/config) apps principle and can be either set directly or via environment variables. 80 | 81 | ```php 82 | $client = new Interfax\Client(['username' => '...', 'password' => '...']); 83 | 84 | // Alternative: will utilise environment variables: 85 | // * INTERFAX_USERNAME 86 | // * INTERFAX_PASSWORD 87 | 88 | $client = new Interfax\Client(); 89 | ``` 90 | ### Send a Fax 91 | 92 | To send a fax, call the deliver method on the client with the appropriate array of parameters. 93 | 94 | ```php 95 | $client = new Interfax\Client(['username' => '...', 'password' => '...']); 96 | $fax = $client->deliver([ 97 | 'faxNumber' => '+442086090368', 98 | 'file' => __DIR__ . '/../tests/Interfax/test.pdf' 99 | ]); 100 | ``` 101 | 102 | The ```deliver``` method will take either a ```file``` or ```files``` argument. The ```files``` is an array of file values. 103 | 104 | Each ```file``` entry can be: 105 | 106 | * local path - if the file is larger than the allowed limit, it will be automatically uploaded as an ```Interfax\Document``` 107 | * uri (from an ```Interfax\Document```) 108 | * an array defining a streamed resource (see below) 109 | * ```Interfax\File``` 110 | * ```Interfax\Document``` 111 | 112 | #### Sending a stream 113 | 114 | Because of the high degree of flexibility that PHP stream resources offer, it's not practical to retrieve information automatically from a stream to send as a fax. As such, there are certain required parameters that must be provided: 115 | 116 | ```php 117 | $stream = fopen('/tmp/fax.pdf', 'rb'); 118 | $fax = $client->deliver([ 119 | 'faxNumber' => '+442086090368', 120 | 'file' => [$stream, ['name' => 'fax.pdf', 'mime_type' => 'application/pdf']] 121 | ``` 122 | 123 | Note that it is assumed that the stream will not exceed the file size limitation for an inline file to be sent. However, if a size parameter is provided for the stream, and this exceeds the limit, it will be automatically uploaded as a ```Interfax\Document``` 124 | 125 | [Documentation](https://www.interfax.net/en/dev/rest/reference/2918) 126 | 127 | ## Account 128 | 129 | ### Balance 130 | 131 | Determine the remaining faxing credits in your account. 132 | 133 | ```php 134 | echo $client->getBalance(); 135 | // (string) 9.86 136 | ``` 137 | 138 | **More:** [documentation](https://www.interfax.net/en/dev/rest/reference/3001) 139 | 140 | ## Outbound 141 | 142 | [Get list](#get-outbound-fax-list) | [Get completed list](#get-completed-fax-list) | [Get record](#get-outbound-fax-record) | [Get image](#get-outbound-fax-image) | [Cancel fax](#cancel-an-outbound-fax) | [Search](#search-fax-list) | [Resend fax](#resend-a-fax) 143 | 144 | ```Interfax\Client``` has an outbound property that can be accessed: 145 | 146 | ```php 147 | $outbound = $client->outbound; 148 | ``` 149 | 150 | ### Get outbound fax list 151 | 152 | ```php 153 | $faxes = $client->outbound->recent(); 154 | // Interfax\Outbound\Fax[] 155 | ``` 156 | 157 | **Options:** [`limit`, `lastId`, `sortOrder`, `userId`](https://www.interfax.net/en/dev/rest/reference/2920) 158 | 159 | ---- 160 | 161 | ### Get completed fax list 162 | 163 | ```php 164 | $fax_ids = [ ... ]; // array of fax ids 165 | $client->outbound->completed($fax_ids); 166 | // Interfax\Outbound\Fax[] 167 | ``` 168 | 169 | ### Get outbound fax record 170 | 171 | `$client->outbound->find(fax_id)` 172 | 173 | Retrieves information regarding a previously-submitted fax, including its current status. 174 | 175 | ```php 176 | $fax = $client->outbound->find(123456); 177 | //Interfax\Outbound\Fax || null 178 | ``` 179 | 180 | [Documentation](https://www.interfax.net/en/dev/rest/reference/2921) 181 | 182 | ### Get outbound fax image 183 | 184 | `$client->outbound->find(fax_id)->image()` 185 | 186 | The image is retrieved via a method on the outbound fax image object. 187 | 188 | ```php 189 | $fax = $client->outbound->find(123456); 190 | if ($fax) { 191 | $image = $fax->image(); 192 | $image->save('path/to/save/file/to.tif'); 193 | } 194 | ``` 195 | 196 | ### Cancel an outbound fax 197 | 198 | `$client->outbound->find(fax_id)->cancel();` 199 | 200 | A fax is cancelled via a method on the `Interfax\Outbound\Fax` model. 201 | 202 | ```php 203 | $fax = $client->outbound->find(123456); 204 | if ($fax) { 205 | $fax->cancel(); 206 | } 207 | ``` 208 | 209 | ### Search fax list 210 | 211 | `$client->outbound->search($options)` 212 | 213 | Search for outbound faxes with a hash array of options keyed by the accepted search parameters for the outbound search API endpoint. 214 | 215 | ```php 216 | $faxes = $client->outbound->search(['faxNumber' => '+1230002305555']); 217 | // Interfax\Outbound\Fax[] 218 | ``` 219 | 220 | **Options:** [`ids`, `reference`, `dateFrom`, `dateTo`, `status`, `userId`, `faxNumber`, `limit`, `offset`](https://www.interfax.net/en/dev/rest/reference/2959) 221 | 222 | ### Resend a Fax 223 | 224 | `$client->outbound->resend($id[,$newNumber])` 225 | 226 | Resend the fax identified by the given id (optionally to a new fax number). 227 | 228 | ```php 229 | $fax = $client->outbound->resend($id); 230 | // Interfax\Outbound\Fax sent to the original number 231 | $fax = $client->outbound->resend($id, $new_number); 232 | // Interfax\Outbound\Fax sent to the $new_number 233 | ``` 234 | 235 | Returns a new `Interfax\Outbound\Fax` representing the newly created outbound fax. 236 | 237 | ## Inbound 238 | 239 | [Get list](#get-inbound-fax-list) | [Get record](#get-inbound-fax-record) | [Get image](#get-inbound-fax-image) | [Get emails](#get-forwarding-emails) | [Mark as read](#mark-as-readunread) | [Resend to email](#resend-inbound-fax) 240 | 241 | ```Interfax\Client``` has an ```inbound``` property that supports the API endpoints for receiving faxes, as described in the [Documentation](https://www.interfax.net/en/dev/rest/reference/2913) 242 | 243 | ```php 244 | $inbound = $client->inbound; 245 | //Interfax\Inbound 246 | ``` 247 | 248 | ### Get inbound fax list 249 | 250 | `$inbound->incoming($options = []);` 251 | 252 | Retrieves a user's list of inbound faxes. 253 | 254 | ```php 255 | $faxes = $inbound->incoming(); 256 | //\Interfax\Inbound\Fax[] 257 | $faxes = $inbound->incoming(['unreadOnly' => true ...]); // see docs for list of supported query params 258 | //\Interfax\Inbound\Fax[] 259 | ``` 260 | 261 | [Documentation](https://www.interfax.net/en/dev/rest/reference/2935) 262 | 263 | --- 264 | 265 | ### Get inbound fax record 266 | 267 | `$inbound->find($id);` 268 | 269 | Retrieves a single fax's metadata (receive time, sender number, etc.). 270 | 271 | ```php 272 | $fax = $inbound->find(123456); 273 | //\Interfax\Inbound\Fax || null 274 | ``` 275 | 276 | null is returned if the resource is not found. Note that this could be because the user is not permissioned for the specific fax. 277 | 278 | [Documentation](https://www.interfax.net/en/dev/rest/reference/2938) 279 | 280 | --- 281 | 282 | ### Get inbound fax image 283 | 284 | `$inbound->find($id)->image()` 285 | 286 | The image is retrieved via a method on the inbound fax object. 287 | 288 | ```php 289 | $fax = $client->inbound->find(123456); 290 | if ($fax) { 291 | $image = $fax->image(); 292 | $image->save('path/to/save/file/to.pdf'); 293 | } 294 | ``` 295 | 296 | [Documentation](https://www.interfax.net/en/dev/rest/reference/2937) 297 | 298 | --- 299 | 300 | ### Get forwarding emails 301 | 302 | `$inbound->find($id)->emails()` 303 | 304 | The forwarding email details are retrieved via a method on the inbound fax object. 305 | 306 | ```php 307 | $fax = $client->inbound->find(123456); 308 | if ($fax) { 309 | $emails = $fax->emails(); // array structure of forwarding emails. 310 | } 311 | ``` 312 | 313 | The returned array is a reflection of the values returned from the emails endpoint of the REST API: 314 | 315 | ```php 316 | [ 317 | [ 318 | 'emailAddress' => 'username@interfax.net', 319 | 'messageStatus' => 0, 320 | 'completionTime' => '2012-0623T17 => 24 => 11' 321 | ], 322 | //... 323 | ]; 324 | ``` 325 | 326 | [Documentation](https://www.interfax.net/en/dev/rest/reference/2930) 327 | 328 | --- 329 | 330 | ### Mark as read/unread 331 | 332 | `$inbound->find($id)->markRead();` 333 | `$inbound->find($id)->markUnread();` 334 | 335 | The inbound fax object provides methods to change the read status. 336 | 337 | ```php 338 | $fax = $client->inbound->find(123456); 339 | if ($fax) { 340 | $fax->markUnread(); // returns true 341 | $fax->markRead(); //returns true 342 | } 343 | ``` 344 | 345 | [Documentation](https://www.interfax.net/en/dev/rest/reference/2936) 346 | 347 | --- 348 | 349 | ### Resend inbound fax 350 | 351 | `$inbound->find($id)->resend();` 352 | 353 | The resend method is on the inbound fax object. 354 | 355 | ```php 356 | $fax = $client->inbound->find(123456); 357 | if ($fax) { 358 | $fax->resend(); 359 | } 360 | ``` 361 | 362 | --- 363 | 364 | ## Documents 365 | 366 | [Create](#create-document) | [Upload chunk](#upload-chunk) | [Properties](#document-properties) | [Cancel](#cancel-document) 367 | 368 | The ```Interfax\Document``` class allows for the uploading of larger files for faxing. The following is an example of how one should be created: 369 | 370 | ```php 371 | $document = $client->documents->create('test.pdf', filesize('test.pdf')); 372 | $stream = fopen('test.pdf', 'rb'); 373 | $current = 0; 374 | while (!feof($stream)) { 375 | $chunk = fread($stream, 500); 376 | $end = $current + strlen($chunk); 377 | $doc->upload($current, $end-1, $chunk); 378 | $current = $end; 379 | } 380 | fclose($stream); 381 | ``` 382 | 383 | ### Create document 384 | 385 | ```php 386 | $params = [...]; // see documentation for possible params 387 | $document = $client->documents->create($filename, filesize($filename), $params); 388 | // Interfax\Document 389 | ``` 390 | 391 | [Documentation](https://www.interfax.net/en/dev/rest/reference/2967) 392 | 393 | ### Upload chunk 394 | 395 | ```php 396 | $document->upload($start, $end, $data); // returns the document object. 397 | ``` 398 | 399 | Note no verification of data takes place - an exception wil be raised if values do not match appropriately. 400 | 401 | ### Document properties 402 | 403 | As per the [documentation](https://www.interfax.net/en/dev/rest/reference/2965) a Document has a number of properties which are accessible: 404 | 405 | ```php 406 | $document->status; 407 | $document->fileName; 408 | $document->refresh()->attributes(); 409 | ``` 410 | 411 | ```php 412 | $document->location; 413 | // or as returned by the API: 414 | $document->uri; 415 | ``` 416 | 417 | ### Cancel document 418 | 419 | `$document->cancel(); //returns the $document instance` 420 | 421 | Can be done prior to completion or afterward 422 | 423 | ## Helper Classes 424 | 425 | ### Outbound Fax 426 | 427 | The `Interfax\Outbound\Fax` class wraps the details of any fax sent, and is returned by most of the ```Outbound``` methods. 428 | 429 | It offers several methods to manage or retrieve information about the fax. 430 | 431 | ```php 432 | // fluent methods that return the $fax instance 433 | $fax->refresh(); // refreshes the data on the fax object 434 | $fax->cancel(); // cancel the fax, returns true on success 435 | $fax->hide(); // hides the faxes from the fax lists 436 | 437 | $image = $fax->image(); // returns Interfax\Image 438 | $new_fax = $fax->resend('+1111111'); // returns a new Interfax\Outbound\Fax 439 | $fax->attributes(); // hash array of fax data properties - see details below 440 | ``` 441 | 442 | ### Outbound fax properties 443 | 444 | Properties on the Fax vary depending on which method call has been used to create the instance. Requesting a property that has not been received will raise a SPL ```\OutOfBoundsException``` 445 | 446 | [Documentation](https://www.interfax.net/en/dev/rest/reference/2921) 447 | 448 | These are all accessible on a fax instance: 449 | 450 | ```php 451 | echo $fax->completionTime 452 | echo $fax->duration 453 | ... 454 | ``` 455 | 456 | Note values will all be returned as strings. 457 | 458 | For convenience, a hash array of the properties can be retrieved 459 | 460 | ```php 461 | $fax->attributes(); 462 | ``` 463 | 464 | Status should always be available. The values of the status codes are [Documented here](https://www.interfax.net/en/help/error_codes) 465 | 466 | ### Inbound Fax 467 | 468 | The incoming equivalent of the outbound fax class, the ```Interfax\Inbound\Fax``` class wraps the details of any incoming fax, and is returned by the ```Interfax\Inbound``` methods where appropriate. 469 | 470 | ```php 471 | // fluent methods that return the $fax instance for method chaining 472 | $fax->refresh(); // reload properties of the inbound fax 473 | $fax->markRead(); // mark the fax read - returns true or throws exception 474 | $fax->markUnread(); // mark the fax unread - returns true or throws exception 475 | $fax->resend(); 476 | 477 | $image = $fax->image(); // Returns a Interfax\Image for this fax 478 | $email_array = $fax->emails(); // see below for details on the structure of this array 479 | $fax->attributes(); // hash array of properties 480 | ``` 481 | 482 | ## Query parameters 483 | 484 | Where methods support a hash array structure of query parameters, these will be passed through to the API endpoint as provided. This ensures that any future parameters that might be added will be supported by the API as is. 485 | 486 | The only values that are manipulated are booleans, which will be translated to the text 'TRUE' and 'FALSE' as appropriate. 487 | 488 | [Documentation](https://www.interfax.net/en/dev/rest/reference/2927) 489 | 490 | ## Exceptions 491 | 492 | Any method call that involves a call to the Interfax RESTful API may throw an instance of ```Interfax\Exception\RequestException```. 493 | 494 | An exception is thrown for any requests that do not return a successful HTTP Status code. The goal of this Exception is to provide a convenience wrapper around information that may have been returned. 495 | 496 | Certain responses from the API will provide more detail, and where this occurs, it will be appended to the message of the Exception. 497 | 498 | ``` 499 | try { 500 | $interfax->deliver(...); 501 | } catch (Interfax\Exception\RequestException $e) { 502 | echo $e->getMessage(); 503 | // contains text detail that is available 504 | echo $e->getStatusCode(); 505 | // the http status code that was received 506 | throw $e->getWrappedException(); 507 | // The underlying Guzzle exception that was caught by the Interfax Client. 508 | } 509 | ``` 510 | 511 | ## Contributing 512 | 513 | 1. **Fork** the repo on GitHub 514 | 2. **Clone** the project to your own machine 515 | 3. **Commit** changes to your own branch 516 | 4. **Push** your work back up to your fork 517 | 5. Submit a **Pull request** so that we can review your changes 518 | 519 | ### Which version of the library? 520 | 521 | Functionality for the API in both versions of the library should be the same, and its therefore intended that the core code continues to be the same in both versions. The differences primarily exist to enable continuity of test coverage as PHP evolves (and phpunit evolves with it) 522 | 523 | As such, the core functionality should be developed in a way that will work with PHP 5.6 and up, and be consistent in both versions. Usage of newer language features in PHP will not be accepted in the core code. 524 | 525 | ### Running tests 526 | 527 | Ensure that composer is installed, then run the following commands. 528 | 529 | ```sh 530 | composer install 531 | ./vendor/bin/phpunit 532 | ``` 533 | 534 | ## License 535 | 536 | This library is released under the [MIT License](LICENSE). 537 | -------------------------------------------------------------------------------- /bin/build.php: -------------------------------------------------------------------------------- 1 | isDir() ? rmdir($file) : unlink($file); 31 | } 32 | return true; 33 | } 34 | 35 | function copyDirectory($from, $to) 36 | { 37 | $from_real = realpath($from); 38 | 39 | if (!mkdir($to)) return false; 40 | $to_real = realpath($to); 41 | 42 | $from_len = strlen($from_real)+1; 43 | $di = new RecursiveDirectoryIterator($from_real, FilesystemIterator::SKIP_DOTS); 44 | $ri = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::SELF_FIRST); 45 | foreach ( $ri as $file ) { 46 | $file->isDir() 47 | ? mkdir($to_real . '/' . substr($file->getRealPath(), $from_len)) 48 | : copy($file->getRealPath(), $to_real . '/' . substr($file->getRealPath(), $from_len)); 49 | } 50 | 51 | return true; 52 | } 53 | 54 | function copyRequiredFiles($base_path, $build_path) 55 | { 56 | if (!copyDirectory($base_path . '/src', $build_path . '/src')) return false; 57 | if (!copy($base_path . '/composer.json', $build_path. '/composer.json')) return false; 58 | if (!copy($base_path . '/composer.lock', $build_path. '/composer.lock')) return false; 59 | return true; 60 | } 61 | 62 | function runComposerInstall($build_path) 63 | { 64 | return `composer install --no-dev --no-interaction --optimize-autoloader --working-dir={$build_path}` == 0; 65 | } 66 | 67 | function createCompressedFiles($build_path) 68 | { 69 | $real_bp = realpath($build_path); 70 | $bp_len = strlen($real_bp) + 1; 71 | $archive = new ZipArchive(); 72 | $archive->open($real_bp . '/' . getReleaseName() . '.zip', ZIPARCHIVE::CREATE); 73 | foreach (['src', 'vendor'] as $dir) { 74 | $build_dir = $real_bp . '/' . $dir; 75 | 76 | $di = new RecursiveDirectoryIterator($build_dir, FilesystemIterator::SKIP_DOTS); 77 | $ri = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::SELF_FIRST); 78 | foreach ($ri as $file) { 79 | $file->isDir() 80 | ? $archive->addEmptyDir(substr($file->getRealPath(), $bp_len)) 81 | : $archive->addFile($file, substr($file->getRealPath(), $bp_len)); 82 | } 83 | } 84 | $archive->close(); 85 | return true; 86 | } 87 | 88 | function getReleaseName() 89 | { 90 | return 'interFAX-PHP-' . Interfax\Client::VERSION; 91 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "interfax/interfax", 3 | "description": "Send and receive faxes with InterFAX", 4 | "type": "library", 5 | "require": { 6 | "php": "^7.3|^7.4|^8", 7 | "guzzlehttp/guzzle": "^7.0" 8 | }, 9 | "require-dev": { 10 | "mikey179/vfsstream": "^1.6.7", 11 | "squizlabs/php_codesniffer": "^3", 12 | "phpunit/phpunit": "^9" 13 | }, 14 | "license": "MIT", 15 | "authors": [ 16 | { 17 | "name": "InterFAX", 18 | "email": "dev@interfax.net" 19 | }, 20 | { 21 | "name": "Mike Smith", 22 | "email": "mike.smith@camc-ltd.co.uk" 23 | } 24 | ], 25 | "autoload": { 26 | "psr-4": { 27 | "Interfax\\": "src/Interfax", 28 | "Test\\": "tests" 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PHPCS configuration file. 6 | 9 | src 10 | tests 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | */*Test.php 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | ./tests/ 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Interfax/Client.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright Copyright (c) 2016, InterFAX 11 | * @license MIT 12 | */ 13 | 14 | namespace Interfax; 15 | 16 | use GuzzleHttp\Client as GuzzleClient; 17 | use Interfax\Exception\RequestException; 18 | use Interfax\Outbound\Fax; 19 | use Psr\Http\Message\ResponseInterface; 20 | 21 | class Client 22 | { 23 | const VERSION = '2.0.0'; 24 | 25 | /** 26 | * @var GenericFactory 27 | */ 28 | private $factory; 29 | 30 | /** 31 | * @var Outbound 32 | */ 33 | protected $outbound; 34 | /** 35 | * @var Inbound 36 | */ 37 | protected $inbound; 38 | /** 39 | * @var Documents 40 | */ 41 | protected $documents; 42 | 43 | protected static $ENV_USERNAME = 'INTERFAX_USERNAME'; 44 | protected static $ENV_PASSWORD = 'INTERFAX_PASSWORD'; 45 | protected static $DEFAULT_BASE_URI = 'https://rest.interfax.net'; 46 | 47 | public $username; 48 | public $password; 49 | 50 | /** 51 | * @var GuzzleClient 52 | */ 53 | protected $http; 54 | 55 | /** 56 | * @var bool 57 | */ 58 | private $debug = false; 59 | private $base_uri; 60 | 61 | /** 62 | * Client constructor. 63 | * @param array $params 64 | * 'username' - string 65 | * 'password' - string 66 | * 'base_uri' - string override the API endpoint base (useful for testing) 67 | * 'debug' - bool - put the Guzzle client into debug mode 68 | * @param GenericFactory|null $factory - allows for testing injection with abstract class instantiation 69 | * @throws \InvalidArgumentException 70 | */ 71 | public function __construct($params = [], GenericFactory $factory = null) 72 | { 73 | if ($params === null || !is_array($params)) { 74 | throw new \InvalidArgumentException('array of parameters expected to instantiate ' . __CLASS__); 75 | } 76 | 77 | $username = array_key_exists('username', $params) ? $params['username'] : getenv(static::$ENV_USERNAME); 78 | $password = array_key_exists('password', $params) ? $params['password'] : getenv(static::$ENV_PASSWORD); 79 | 80 | if (array_key_exists('debug', $params)) { 81 | $this->debug = $params['debug']; 82 | } 83 | if (array_key_exists('base_uri', $params)) { 84 | $this->base_uri = rtrim($params['base_uri'], '/'); 85 | } 86 | 87 | $this->username = $username; 88 | $this->password = $password; 89 | if ($this->username === '' || $this->password === '') { 90 | throw new \InvalidArgumentException( 91 | 'Username and Password must be provided or defined as environment variables ' 92 | . static::$ENV_USERNAME . ' & ' . static::$ENV_PASSWORD 93 | ); 94 | } 95 | 96 | // if its not injected, we instantiate directly 97 | if ($factory === null) { 98 | $factory = new GenericFactory(); 99 | } 100 | 101 | $this->factory = $factory; 102 | } 103 | 104 | private static $cached_accessible = [ 105 | 'outbound' => 'Interfax\Outbound', 106 | 'inbound' => 'Interfax\Inbound', 107 | 'documents' => 'Interfax\Documents' 108 | ]; 109 | 110 | /** 111 | * Simplifies the route to accessing specific 'route' classes for the client 112 | * 113 | * @param $name 114 | * @return mixed 115 | */ 116 | public function __get($name) 117 | { 118 | if (in_array($name, array_keys(static::$cached_accessible))) { 119 | if (!$this->$name) { 120 | $this->$name = $this->factory->instantiateClass(static::$cached_accessible[$name], [$this]); 121 | } 122 | return $this->$name; 123 | } 124 | } 125 | 126 | /** 127 | * @return GuzzleClient 128 | */ 129 | protected function getHttpClient() 130 | { 131 | if (!$this->http) { 132 | $config = ['base_uri' => $this->base_uri ?: static::$DEFAULT_BASE_URI]; 133 | if ($this->debug) { 134 | $config['debug'] = true; 135 | } 136 | $this->http = $this->factory->instantiateClass( 137 | 'GuzzleHttp\Client', 138 | [$config] 139 | ); 140 | } 141 | 142 | return $this->http; 143 | } 144 | 145 | /** 146 | * @return string 147 | */ 148 | public function getUserAgent() 149 | { 150 | return 'InterFAX PHP ' . static::VERSION; 151 | } 152 | 153 | /** 154 | * @return array 155 | */ 156 | protected function getBaseRequestParams() 157 | { 158 | return [ 159 | 'auth' => [$this->username, $this->password], 160 | 'headers' => [ 161 | 'User-Agent' => $this->getUserAgent() 162 | ] 163 | ]; 164 | } 165 | 166 | /** 167 | * @param array $params 168 | * @return array 169 | */ 170 | protected function getCompleteRequestParams($params = []) 171 | { 172 | $complete = array_merge_recursive($this->getBaseRequestParams(), $params); 173 | 174 | if (array_key_exists('query', $complete)) { 175 | foreach ($complete['query'] as $k => $v) { 176 | if (is_bool($v)) { 177 | $complete['query'][$k] = $v ? 'TRUE' : 'FALSE'; 178 | } 179 | } 180 | } 181 | 182 | return $complete; 183 | } 184 | 185 | /** 186 | * POST request. 187 | * 188 | * @param $uri 189 | * @param array $params 190 | * @param $multipart 191 | * @return string|array 192 | * @throws RequestException 193 | */ 194 | public function post($uri, $params = [], $multipart = []) 195 | { 196 | if ($multipart && count($multipart)) { 197 | $request_params = $this->getCompleteRequestParams(array_merge($params, ['multipart' => $multipart])); 198 | } else { 199 | $request_params = $this->getCompleteRequestParams($params); 200 | } 201 | 202 | try { 203 | return $this->parseResponse( 204 | $this->getHttpClient()->request('POST', $uri, $request_params) 205 | ); 206 | } catch (\GuzzleHttp\Exception\RequestException $e) { 207 | throw RequestException::create('Problem with POST request', $e); 208 | } 209 | } 210 | 211 | /** 212 | * GET request. 213 | * 214 | * @param $uri 215 | * @param array $params 216 | * @return string|array 217 | * @throws RequestException 218 | */ 219 | public function get($uri, $params = []) 220 | { 221 | $request_params = $this->getCompleteRequestParams($params); 222 | 223 | try { 224 | $response = $this->getHttpClient()->request('GET', $uri, $request_params); 225 | return $this->parseResponse($response); 226 | } catch (\GuzzleHttp\Exception\RequestException $e) { 227 | throw RequestException::create('Problem with GET request', $e); 228 | } 229 | } 230 | 231 | /** 232 | * @param $uri 233 | * @return int - status code of the API request 234 | * 235 | */ 236 | public function delete($uri) 237 | { 238 | $params = $this->getCompleteRequestParams(); 239 | try { 240 | $response = $this->getHttpClient()->request('DELETE', $uri, $params); 241 | return $response->getStatusCode(); 242 | } catch (\GuzzleHttp\Exception\RequestException $e) { 243 | throw RequestException::create('Problem with DELETE request', $e); 244 | } 245 | } 246 | 247 | /** 248 | * Parses the responses in a consistent manner for handling by various classes. 249 | * 250 | * @param ResponseInterface $response 251 | * @return mixed|string|array 252 | * @throws RequestException 253 | */ 254 | protected function parseResponse(ResponseInterface $response) 255 | { 256 | if ($response->getStatusCode() >= 200 && $response->getStatusCode() < 300) { 257 | if ($location = $response->getHeaderLine('Location')) { 258 | return $location; 259 | } elseif ($response->getHeaderLine('Content-Type') === 'text/json') { 260 | return json_decode((string) $response->getBody(), true); 261 | } else { 262 | return $response->getBody(); 263 | } 264 | } else { 265 | throw new RequestException( 266 | 'Unexpected response code', 267 | RequestException::$UNEXPECTED_RESPONSE_CODE, 268 | $response->getStatusCode() 269 | ); 270 | } 271 | } 272 | 273 | /** 274 | * @param $params 275 | * @return Fax 276 | * @throws \InvalidArgumentException 277 | * 278 | */ 279 | public function deliver($params) 280 | { 281 | $delivery = $this->factory->instantiateClass('Interfax\Outbound\Delivery', [$this, $params]); 282 | return $delivery->send(); 283 | } 284 | 285 | /** 286 | * @return string 287 | * @throws RequestException 288 | */ 289 | public function getBalance() 290 | { 291 | return $this->get('/accounts/self/ppcards/balance'); 292 | } 293 | 294 | public function getBaseUri() 295 | { 296 | return $this->getHttpClient()->getConfig('base_uri'); 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /src/Interfax/Document.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Interfax; 16 | 17 | class Document extends Resource 18 | { 19 | protected static $resource_uri_stem = '/outbound/documents/'; 20 | 21 | /** 22 | * @param $start 23 | * @param $end 24 | * @param $data 25 | * @return self 26 | * @throws \Interfax\Exception\RequestException 27 | */ 28 | public function upload($start, $end, $data) 29 | { 30 | $params = [ 31 | 'headers' => [ 32 | 'Range' => 'bytes=' . $start . '-' . $end, 33 | 'Content-Length' => $end - $start + 1, 34 | ], 35 | 'body' => $data 36 | ]; 37 | 38 | $this->client->post($this->resource_uri, $params); 39 | 40 | return $this; 41 | } 42 | 43 | /** 44 | * @return self 45 | */ 46 | public function cancel() 47 | { 48 | $this->client->delete($this->resource_uri); 49 | $this->record = []; 50 | return $this; 51 | } 52 | 53 | public function getHeaderLocation() 54 | { 55 | return $this->client->getBaseUri() . $this->resource_uri; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Interfax/Documents.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Interfax; 16 | 17 | class Documents 18 | { 19 | /** 20 | * @var GenericFactory 21 | */ 22 | private $factory; 23 | 24 | /** 25 | * @var Client 26 | */ 27 | protected $client; 28 | 29 | public function __construct(Client $client, GenericFactory $factory = null) 30 | { 31 | $this->client = $client; 32 | if ($factory === null) { 33 | $factory = new GenericFactory(); 34 | } 35 | $this->factory = $factory; 36 | } 37 | 38 | /** 39 | * @param $document_name - name used when defining document on the API 40 | * @param $size - in bytes 41 | * @param array $params - additional parameters to be used when creating the document on the API 42 | * @return Document 43 | * @throws \Interfax\Exception\RequestException 44 | */ 45 | public function create($document_name, $size, $params = []) 46 | { 47 | $all_params = array_merge($params, [ 48 | 'name' => $document_name, 49 | 'size' => $size 50 | ]); 51 | 52 | $location = $this->client->post('/outbound/documents', ['query' => $all_params]); 53 | 54 | $path = parse_url($location, PHP_URL_PATH); 55 | $bits = explode('/', $path); 56 | $id = array_pop($bits); 57 | $all_params['id'] = $id; 58 | // spoof the attributes for the Document object from the given parameters 59 | return $this->factory->instantiateClass('Interfax\Document', [$this->client, $id, $all_params]); 60 | } 61 | 62 | /** 63 | * Get list of the available documents previously uploaded. 64 | * 65 | * @return array 66 | */ 67 | public function available($query_params = []) 68 | { 69 | $params = []; 70 | if (count($query_params)) { 71 | $params = ['query' => $query_params]; 72 | } 73 | 74 | $response = $this->client->get('/outbound/documents', $params); 75 | 76 | if (is_array($response)) { 77 | $result = []; 78 | foreach ($response as $definition) { 79 | $result[] = $this->factory->instantiateClass('Interfax\Document', [$this->client, $definition]); 80 | } 81 | return $result; 82 | } 83 | 84 | throw new \RuntimeException('A reasonable but unhandled response was received'); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Interfax/Exception/RequestException.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Interfax\Exception; 16 | 17 | use GuzzleHttp\Exception\RequestException as GuzzleException; 18 | 19 | /** 20 | * Class RequestException 21 | * @package Interfax\Exception 22 | */ 23 | class RequestException extends \RuntimeException 24 | { 25 | protected $http_status; 26 | protected $request_exception; 27 | public static $UNEXPECTED_RESPONSE_CODE = -9999; 28 | 29 | /** 30 | * RequestException constructor. 31 | * @param string $message 32 | * @param int $code 33 | * @param int $http_status 34 | * @param GuzzleException $request_exception 35 | * @param \Exception|null $previous 36 | */ 37 | public function __construct( 38 | $message, 39 | $code, 40 | $http_status, 41 | GuzzleException $request_exception = null, 42 | \Exception $previous = null 43 | ) { 44 | $this->http_status = $http_status; 45 | $this->request_exception = $request_exception; 46 | 47 | parent::__construct($message, $code, $previous); 48 | } 49 | 50 | /** 51 | * @param $message 52 | * @param \GuzzleHttp\Exception\RequestException $exception 53 | * @return RequestException 54 | */ 55 | public static function create($message, GuzzleException $exception) 56 | { 57 | $code = static::$UNEXPECTED_RESPONSE_CODE; 58 | 59 | $http_status = null; 60 | if ($exception->hasResponse()) { 61 | $response = $exception->getResponse(); 62 | $http_status = $response->getStatusCode(); 63 | if ($response->hasHeader('Content-Type') && $response->getHeaderLine('Content-Type') === 'text/json') { 64 | $json = json_decode((string)$response->getBody(), true); 65 | if ($json !== null) { 66 | if (array_key_exists('code', $json)) { 67 | $code = $json['code']; 68 | } 69 | if (array_key_exists('message', $json)) { 70 | $message .= ': ' . $json['message']; 71 | } 72 | } 73 | } else { 74 | $message .= ': ' . ($response->getBody() ?: 'no further information provided.'); 75 | } 76 | } 77 | 78 | return new self($message, $code, $http_status, $exception); 79 | } 80 | 81 | /** 82 | * @return int 83 | */ 84 | public function getStatusCode() 85 | { 86 | return $this->http_status; 87 | } 88 | 89 | /** 90 | * @return \GuzzleHttp\Exception\RequestException 91 | */ 92 | public function getWrappedException() 93 | { 94 | return $this->request_exception; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Interfax/File.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright Copyright (c) 2016, InterFAX 11 | * @license MIT 12 | */ 13 | 14 | namespace Interfax; 15 | 16 | use Doctrine\Instantiator\Exception\InvalidArgumentException; 17 | 18 | class File 19 | { 20 | /** 21 | * @var GenericFactory 22 | */ 23 | private $factory; 24 | /** 25 | * @var Client 26 | */ 27 | protected $client; 28 | 29 | protected $headers = []; 30 | protected $mime_type; 31 | protected $name; 32 | protected $body; 33 | protected $size; 34 | private $chunk_size; 35 | protected static $DEFAULT_CHUNK_SIZE = 1048576; // 1024*1024 36 | 37 | /** 38 | * File constructor. 39 | * @param $location 40 | * @param array $params 41 | * @throws \InvalidArgumentException 42 | */ 43 | public function __construct(Client $client, $location, $params = [], GenericFactory $factory = null) 44 | { 45 | $this->client = $client; 46 | if ($factory === null) { 47 | $factory = new GenericFactory(); 48 | } 49 | 50 | $this->factory = $factory; 51 | 52 | if (is_resource($location)) { 53 | $this->initialiseParams($params); 54 | $this->initialiseFromResource($location); 55 | } elseif (preg_match('/^https?:\/\//', $location)) { 56 | $this->initialiseFromUri($location); 57 | } else { 58 | $this->initialiseParams($params); 59 | $this->initialiseFromPath($location); 60 | } 61 | } 62 | 63 | /** 64 | * @param array $params 65 | */ 66 | protected function initialiseParams($params = []) 67 | { 68 | if (array_key_exists('mime_type', $params)) { 69 | $this->setMimeType($params['mime_type']); 70 | } 71 | 72 | if (array_key_exists('name', $params)) { 73 | $this->name = $params['name']; 74 | } 75 | 76 | if (array_key_exists('chunk_size', $params)) { 77 | $this->chunk_size = $params['chunk_size']; 78 | } else { 79 | $this->chunk_size = static::$DEFAULT_CHUNK_SIZE; 80 | } 81 | 82 | if (array_key_exists('size', $params)) { 83 | $this->size = $params['size']; 84 | } 85 | } 86 | 87 | /** 88 | * @param $mime_type 89 | */ 90 | public function setMimeType($mime_type) 91 | { 92 | $this->headers = [ 93 | 'Content-Type' => $mime_type 94 | ]; 95 | $this->mime_type = $mime_type; 96 | } 97 | 98 | protected function initialiseFromResource($resource) 99 | { 100 | $meta = stream_get_meta_data($resource); 101 | if (strpos($meta['mode'], 'r') === false) { 102 | throw new \InvalidArgumentException( 103 | 'Resource not opened with valid mode (r) for ' . __CLASS__ 104 | ); 105 | } 106 | $missing = []; 107 | foreach (['name', 'mime_type'] as $required) { 108 | if (!$this->$required) { 109 | $missing[] = $required; 110 | } 111 | } 112 | if (count($missing)) { 113 | throw new \InvalidArgumentException( 114 | 'Required parameters "' . implode(', ', $missing) . '" not set for resource initialisation of ' . __CLASS__ 115 | ); 116 | } 117 | 118 | if ($this->size && $this->size > $this->chunk_size) { 119 | $this->initialiseFromLargeResource($resource); 120 | } else { 121 | $this->body = $resource; 122 | } 123 | } 124 | 125 | /** 126 | * Simple abstraction to initialise a document for the given resource 127 | * 128 | * @param $resource 129 | * @return Document 130 | */ 131 | private function createDocumentFromResource($resource) 132 | { 133 | $document = $this->client->documents->create($this->name, $this->size); 134 | $current = 0; 135 | while (!feof($resource)) { 136 | $chunk = fread($resource, $this->chunk_size); 137 | $end = $current + strlen($chunk); 138 | $document->upload($current, $end - 1, $chunk); 139 | $current = $end; 140 | } 141 | return $document; 142 | } 143 | 144 | /** 145 | * Use the given resource to create a Document on the Interfax server for this file. 146 | * 147 | * @param $resource 148 | */ 149 | protected function initialiseFromLargeResource($resource) 150 | { 151 | $document = $this->createDocumentFromResource($resource); 152 | $this->initialiseFromUri($document->getHeaderLocation()); 153 | } 154 | 155 | /** 156 | * @param $location 157 | * @throws \InvalidArgumentException 158 | */ 159 | protected function initialiseFromPath($location) 160 | { 161 | if (!file_exists($location)) { 162 | throw new \InvalidArgumentException( 163 | $location . ' not found. File must exists on filesystem to construct ' . __CLASS__ 164 | ); 165 | } 166 | 167 | if (!$this->name) { 168 | $this->name = basename($location); 169 | } 170 | if (!$this->size) { 171 | $this->size = filesize($location); 172 | } 173 | 174 | if ($this->size > $this->chunk_size) { 175 | $this->initialiseFromLargeFile($location); 176 | } else { 177 | if (!$this->mime_type) { 178 | $finfo = finfo_open(FILEINFO_MIME_TYPE); 179 | $this->setMimeType(finfo_file($finfo, $location)); 180 | } 181 | 182 | $this->body = fopen($location, 'r'); 183 | } 184 | } 185 | 186 | /** 187 | * @param $location 188 | */ 189 | protected function initialiseFromLargeFile($location) 190 | { 191 | $stream = fopen($location, 'rb'); 192 | $document = $this->createDocumentFromResource($stream); 193 | fclose($stream); 194 | 195 | $this->initialiseFromUri($document->getHeaderLocation()); 196 | } 197 | 198 | /** 199 | * @param $location 200 | */ 201 | protected function initialiseFromUri($location) 202 | { 203 | $this->headers = [ 204 | 'Content-Location' => $location 205 | ]; 206 | $this->body = null; 207 | } 208 | 209 | /** 210 | * @return string 211 | */ 212 | public function getHeader() 213 | { 214 | return $this->headers; 215 | } 216 | 217 | /** 218 | * @return string 219 | */ 220 | public function getName() 221 | { 222 | return $this->name; 223 | } 224 | 225 | /** 226 | * @return mixed 227 | */ 228 | public function getBody() 229 | { 230 | return $this->body; 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /src/Interfax/GenericFactory.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Interfax; 16 | 17 | class GenericFactory 18 | { 19 | public function instantiateClass($classname, $args = []) 20 | { 21 | if (count($args)) { 22 | $reflect = new \ReflectionClass($classname); 23 | 24 | return $reflect->newInstanceArgs($args); 25 | } else { 26 | return new $classname(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Interfax/Image.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Interfax; 16 | 17 | use GuzzleHttp\Psr7\Stream; 18 | use RuntimeException; 19 | 20 | /** 21 | * Class Image 22 | * 23 | * Simple class to accept a response stream and allow content to be saved to a file 24 | * 25 | * @package Interfax 26 | */ 27 | class Image 28 | { 29 | /** 30 | * @var Stream 31 | */ 32 | private $stream; 33 | 34 | public function __construct(Stream $stream) 35 | { 36 | $this->stream = $stream; 37 | } 38 | 39 | /** 40 | * Note a return of false does not indicate that a file has not been created or written to, 41 | * just that it failed at some point. 42 | * 43 | * @param $path 44 | * @return bool 45 | * @throws \RuntimeException 46 | */ 47 | public function save($path) 48 | { 49 | $handle = fopen($path, 'w'); 50 | 51 | if (!$handle) { 52 | throw new \RuntimeException("Could not open {$path} for saving"); 53 | } 54 | 55 | try { 56 | while (!$this->stream->eof()) { 57 | // TODO consider chunking size configuration 58 | fwrite($handle, $this->stream->read(1024 * 1024)); 59 | } 60 | } catch (\RuntimeException $e) { 61 | // try to at least tidy up the resource 62 | fclose($handle); 63 | throw $e; 64 | } 65 | 66 | return fclose($handle); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Interfax/Inbound.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Interfax; 16 | 17 | use Interfax\Exception\RequestException; 18 | 19 | class Inbound 20 | { 21 | /** 22 | * @var Client 23 | */ 24 | protected $client; 25 | 26 | /** 27 | * @var GenericFactory 28 | */ 29 | protected $factory; 30 | 31 | /** 32 | * Inbound constructor. 33 | * 34 | * @param Client $client 35 | * @param GenericFactory|null $factory 36 | */ 37 | public function __construct(Client $client, GenericFactory $factory = null) 38 | { 39 | $this->client = $client; 40 | if ($factory === null) { 41 | $factory = new GenericFactory(); 42 | } 43 | $this->factory = $factory; 44 | } 45 | 46 | /** 47 | * Retrieve a list of incoming faxes for the Client account. 48 | * 49 | * @param array $query_params 50 | * @return Inbound\Fax[] 51 | * @throws \RuntimeException 52 | */ 53 | public function incoming($query_params = []) 54 | { 55 | $params = [ 56 | 'query' => $query_params 57 | ]; 58 | 59 | $json = $this->client->get('/inbound/faxes', $params); 60 | 61 | if (is_array($json)) { 62 | $result = []; 63 | foreach ($json as $incoming) { 64 | $id = $incoming['messageId']; 65 | $result[] = $this->factory->instantiateClass('Interfax\Inbound\Fax', [$this->client, $id, $incoming]); 66 | } 67 | return $result; 68 | } 69 | 70 | throw new \RuntimeException('A reasonable but unhandled response was received'); 71 | } 72 | 73 | /** 74 | * Search for an incoming fax with the given id. 75 | * 76 | * @param $id 77 | * @return \Interfax\Inbound\Fax|null 78 | * @throws RequestException 79 | * @throws \RuntimeException 80 | */ 81 | public function find($id) 82 | { 83 | try { 84 | $json = $this->client->get('/inbound/faxes/' . $id); 85 | 86 | if (is_array($json)) { 87 | return $this->factory->instantiateClass('Interfax\Inbound\Fax', [$this->client, $id, $json]); 88 | } 89 | } catch (RequestException $e) { 90 | //TODO: test me 91 | if ((int) $e->getStatusCode() === 404) { 92 | return null; 93 | } 94 | throw $e; 95 | } 96 | 97 | throw new \RuntimeException('A reasonable but unhandled response was received'); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/Interfax/Inbound/Fax.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Interfax\Inbound; 16 | 17 | use Interfax\Client; 18 | use Interfax\Exception\RequestException; 19 | use Interfax\GenericFactory; 20 | use Interfax\Resource; 21 | 22 | class Fax extends Resource 23 | { 24 | protected static $resource_uri_stem = '/inbound/faxes/'; 25 | 26 | /** 27 | * @param bool $unread 28 | * @return self 29 | * @throws RequestException 30 | */ 31 | protected function mark($unread = true) 32 | { 33 | $this->client->post($this->resource_uri . '/mark', ['query' => ['unread' => $unread]]); 34 | 35 | return $this; 36 | } 37 | 38 | /** 39 | * @return self 40 | * @throws RequestException 41 | */ 42 | public function markRead() 43 | { 44 | return $this->mark(false); 45 | } 46 | 47 | /** 48 | * @return self 49 | * @throws RequestException 50 | */ 51 | public function markUnread() 52 | { 53 | return $this->mark(true); 54 | } 55 | 56 | /** 57 | * @param string $email 58 | * @return self 59 | * @throws RequestException 60 | */ 61 | public function resend($email = null) 62 | { 63 | $params = []; 64 | if ($email !== null) { 65 | $params['query'] = ['email' => $email]; 66 | } 67 | $this->client->post($this->resource_uri . '/resend', $params); 68 | 69 | return $this; 70 | } 71 | 72 | /** 73 | * @return \Interfax\Image 74 | * @throws RequestException 75 | */ 76 | public function image() 77 | { 78 | $response = $this->client->get($this->resource_uri . '/image'); 79 | 80 | return $this->factory->instantiateClass('Interfax\Image', [$response]); 81 | } 82 | 83 | /** 84 | * Returns an array of hasharrays with the structure returned from the emails endpoint. 85 | * 86 | * @return array 87 | * @throws RequestException 88 | */ 89 | public function emails() 90 | { 91 | return $this->client->get($this->resource_uri . '/emails'); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Interfax/Outbound.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Interfax; 16 | 17 | use Interfax\Exception\RequestException; 18 | use Interfax\Outbound\Fax; 19 | 20 | class Outbound 21 | { 22 | /** 23 | * @var GenericFactory 24 | */ 25 | private $factory; 26 | 27 | protected $client; 28 | 29 | public function __construct(Client $client, GenericFactory $factory = null) 30 | { 31 | $this->client = $client; 32 | if ($factory === null) { 33 | $factory = new GenericFactory(); 34 | } 35 | $this->factory = $factory; 36 | } 37 | 38 | 39 | 40 | /** 41 | * @param array $definitions 42 | * @return Fax[] 43 | * @throws \InvalidArgumentException 44 | */ 45 | protected function createFaxes($definitions) 46 | { 47 | $res = []; 48 | foreach ($definitions as $f_data) { 49 | if (array_key_exists('id', $f_data)) { 50 | $res[] = $this->factory->instantiateClass( 51 | 'Interfax\Outbound\Fax', 52 | [$this->client, $f_data['id'], $f_data] 53 | ); 54 | } else { 55 | throw new \InvalidArgumentException('No id attribute found in fax definition'); 56 | } 57 | } 58 | 59 | return $res; 60 | } 61 | 62 | /** 63 | * @param array $ids 64 | * @return Fax[] 65 | * @throws \InvalidArgumentException 66 | * @throws RequestException 67 | */ 68 | public function completed($ids = []) 69 | { 70 | $params = []; 71 | if (count($ids) === 0) { 72 | throw new \InvalidArgumentException('Must provide at least one id for completed request'); 73 | } 74 | 75 | $params['query'] = ['ids' => implode(',', $ids)]; 76 | 77 | $json = $this->client->get('/outbound/faxes/completed', $params); 78 | return $this->createFaxes($json); 79 | } 80 | 81 | /** 82 | * @param array $query_params 83 | * @return Outbound\Fax[] 84 | * @internal param $params 85 | */ 86 | public function recent($query_params = []) 87 | { 88 | $params = []; 89 | if (count($query_params)) { 90 | $params = ['query' => $query_params]; 91 | } 92 | return $this->createFaxes($this->client->get('/outbound/faxes', $params)); 93 | } 94 | 95 | 96 | /** 97 | * @param $id 98 | * @param null $fax_number 99 | * @return mixed 100 | * @throws RequestException 101 | */ 102 | public function resend($id, $fax_number = null) 103 | { 104 | $fax = $this->factory->instantiateClass('Interfax\Outbound\Fax', [$this->client, $id]); 105 | 106 | return $fax->resend($fax_number); 107 | } 108 | 109 | /** 110 | * Get an individual Fax resource for the given $id. 111 | * 112 | * @param $id 113 | * @return null|Interfax\Outbound\Fax 114 | */ 115 | public function find($id) 116 | { 117 | try { 118 | $response = $this->client->get('/outbound/faxes/' . $id); 119 | 120 | if (is_array($response) && array_key_exists('id', $response)) { 121 | return $this->factory->instantiateClass( 122 | 'Interfax\Outbound\Fax', 123 | [$this->client, $response['id'], $response] 124 | ); 125 | } 126 | } catch (\RuntimeException $e) { 127 | if ((int) $e->getStatusCode() === 404) { 128 | return null; 129 | } 130 | throw $e; 131 | } 132 | 133 | throw new \RuntimeException('A reasonable but unhandled response was received'); 134 | } 135 | 136 | /** 137 | * @param array $params 138 | * @return Outbound\Fax[] 139 | * @throws \InvalidArgumentException 140 | * @throws RequestException 141 | */ 142 | public function search($params = []) 143 | { 144 | return $this->createFaxes($this->client->get('/outbound/search', ['query' => $params])); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/Interfax/Outbound/Delivery.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright Copyright (c) 2016, InterFAX 11 | * @license MIT 12 | */ 13 | 14 | namespace Interfax\Outbound; 15 | 16 | use Interfax\Client; 17 | use Interfax\Exception\RequestException; 18 | use Interfax\File; 19 | use Interfax\GenericFactory; 20 | 21 | class Delivery 22 | { 23 | protected static $required_qparams = ['faxNumber']; 24 | protected $query_params = []; 25 | /** 26 | * @var File[] 27 | */ 28 | protected $files = []; 29 | /** 30 | * @var \Interfax\Client 31 | */ 32 | private $client; 33 | 34 | /** 35 | * Delivery constructor. 36 | * 37 | * @param \Interfax\Client $client 38 | * @param array $params 39 | * @param GenericFactory $factory 40 | * @throws \InvalidArgumentException 41 | */ 42 | public function __construct(Client $client, $params = [], GenericFactory $factory = null) 43 | { 44 | $this->client = $client; 45 | 46 | $missing_qparams = array_diff(static::$required_qparams, array_keys($params)); 47 | 48 | if (count($missing_qparams)) { 49 | throw new \InvalidArgumentException('missing required query parameters ' . implode(', ', $missing_qparams)); 50 | } 51 | 52 | if ($factory === null) { 53 | $factory = new GenericFactory(); 54 | } 55 | 56 | $this->factory = $factory; 57 | 58 | $this->resolveFiles($params); 59 | 60 | foreach ($params as $k => $v) { 61 | $this->query_params[$k] = $v; 62 | } 63 | } 64 | 65 | /** 66 | * Runs through the file/files parameter of params and instantiates File objects for delivery. 67 | * 68 | * @param $params 69 | * @throws \InvalidArgumentException 70 | */ 71 | protected function resolveFiles(&$params) 72 | { 73 | // normalise to a single array of files 74 | $files = []; 75 | if (isset($params['file'])) { 76 | if (isset($params['files'])) { 77 | throw new \InvalidArgumentException('Can only provide file or files for ' . __CLASS__); 78 | } 79 | $files[] = $params['file']; 80 | unset($params['file']); 81 | } elseif (isset($params['files'])) { 82 | $files = $params['files']; 83 | unset($params['files']); 84 | } else { 85 | throw new \InvalidArgumentException('Must provide a file or files for Delivery'); 86 | } 87 | 88 | // create file objects where necessary 89 | foreach ($files as $f) { 90 | if (is_object($f)) { 91 | $cls = get_class($f); 92 | if (is_a($f, 'Interfax\File')) { 93 | $this->files[] = $f; 94 | } elseif (is_a($f, 'Interfax\Document')) { 95 | $this->files[] = $this->factory->instantiateClass( 96 | 'Interfax\File', 97 | [$this->client, $f->getHeaderLocation()] 98 | ); 99 | } else { 100 | throw new \InvalidArgumentException( 101 | 'File objects must be Interfax\File or Interfax\Document objects for Delivery. not ' . $cls 102 | ); 103 | } 104 | } elseif (is_array($f)) { 105 | $args = array_merge([$this->client], $f); 106 | $this->files[] = $this->factory->instantiateClass('Interfax\File', $args); 107 | } else { 108 | // assumed to be a path 109 | $this->files[] = $this->factory->instantiateClass('Interfax\File', [$this->client, $f]); 110 | } 111 | } 112 | } 113 | 114 | /** 115 | * @return string 116 | */ 117 | public function getMultipart() 118 | { 119 | $multipart = []; 120 | foreach ($this->files as $i => $file) { 121 | $multipart[] = [ 122 | 'name' => 'file' . $i, 123 | 'filename' => urlencode($file->getName()), 124 | 'contents' => $file->getBody(), 125 | 'headers' => $file->getHeader(), 126 | ]; 127 | } 128 | return $multipart; 129 | } 130 | 131 | /** 132 | * @return Fax 133 | * @throws \InvalidArgumentException 134 | * @throws RequestException 135 | */ 136 | public function send() 137 | { 138 | $params = [ 139 | 'query' => $this->query_params, 140 | ]; 141 | 142 | if (count($this->files) > 1) { 143 | $location = $this->client->post('/outbound/faxes', $params, $this->getMultipart()); 144 | } else { 145 | $params['headers'] = $this->files[0]->getHeader(); 146 | $params['body'] = $this->files[0]->getBody(); 147 | $location = $this->client->post('/outbound/faxes', $params); 148 | } 149 | 150 | // retrieve ID (last element of location path) for outbound fax object 151 | $path = parse_url($location, PHP_URL_PATH); 152 | $bits = explode('/', $path); 153 | return $this->factory->instantiateClass('Interfax\Outbound\Fax', [$this->client, array_pop($bits)]); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/Interfax/Outbound/Fax.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright Copyright (c) 2016, InterFAX 11 | * @license MIT 12 | */ 13 | 14 | namespace Interfax\Outbound; 15 | 16 | use Interfax\Client; 17 | use Interfax\Exception\RequestException; 18 | use Interfax\Image; 19 | use Interfax\Resource; 20 | 21 | class Fax extends Resource 22 | { 23 | protected static $resource_uri_stem = '/outbound/faxes/'; 24 | 25 | /** 26 | * @return string 27 | */ 28 | public function getLocation() 29 | { 30 | return $this->resource_uri; 31 | } 32 | 33 | /** 34 | * Resend the fax, possibly to a new fax number 35 | * 36 | * @param string $fax_number 37 | * @return $this 38 | * @throws RequestException 39 | */ 40 | public function resend($fax_number = null) 41 | { 42 | $params = []; 43 | if ($fax_number !== null) { 44 | $params['query'] = ['faxNumber' => $fax_number]; 45 | } 46 | 47 | $location = $this->client->post($this->resource_uri . '/resend', $params); 48 | 49 | $path = parse_url($location, PHP_URL_PATH); 50 | $bits = explode('/', $path); 51 | return $this->factory->instantiateClass(__CLASS__, [$this->client, array_pop($bits)]); 52 | } 53 | 54 | /** 55 | * @return self 56 | * @throws RequestException 57 | */ 58 | public function cancel() 59 | { 60 | $this->client->post($this->resource_uri . '/cancel'); 61 | 62 | return $this; 63 | } 64 | 65 | /** 66 | * @return self 67 | * @throws RequestException 68 | */ 69 | public function hide() 70 | { 71 | $this->client->post($this->resource_uri . '/hide'); 72 | 73 | return $this; 74 | } 75 | 76 | /** 77 | * @return Image 78 | * @throws RequestException 79 | */ 80 | public function image() 81 | { 82 | $response = $this->client->get($this->resource_uri . '/image'); 83 | 84 | return $this->factory->instantiateClass('Interfax\Image', [$response]); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Interfax/Resource.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Interfax; 16 | 17 | use Interfax\Exception\RequestException; 18 | 19 | /** 20 | * Class Resource 21 | * 22 | * Base Resource Class to be used for resource classes that are represented by specific endpoints on the API. 23 | * 24 | * @package Interfax 25 | */ 26 | abstract class Resource 27 | { 28 | /** 29 | * @var GenericFactory 30 | */ 31 | protected $factory; 32 | 33 | /** 34 | * @var Client 35 | */ 36 | protected $client; 37 | /** 38 | * Base URI used for carrying out actions on the resource. 39 | * 40 | * @var string 41 | */ 42 | protected $resource_uri; 43 | /** 44 | * Stores the internal properties of the resource. 45 | * 46 | * @var array 47 | */ 48 | protected $record = []; 49 | 50 | /** 51 | * Should be overridden in inheriting class 52 | * 53 | * @var 54 | */ 55 | protected static $resource_uri_stem; 56 | 57 | /** 58 | * Resource constructor. 59 | * 60 | * @param Client $client 61 | * @param $id 62 | * @param array $definition 63 | * @param GenericFactory|null $factory 64 | */ 65 | public function __construct(Client $client, $id, $definition = [], GenericFactory $factory = null) 66 | { 67 | $this->client = $client; 68 | 69 | $this->resource_uri = static::$resource_uri_stem . $id; 70 | $this->record['id'] = $id; 71 | 72 | foreach ($definition as $k => $v) { 73 | $this->record[$k] = $v; 74 | } 75 | 76 | if ($factory === null) { 77 | $factory = new GenericFactory(); 78 | } 79 | $this->factory = $factory; 80 | } 81 | 82 | /** 83 | * @param $name 84 | * @return mixed|null 85 | */ 86 | public function __get($name) 87 | { 88 | if ($name === 'location') { 89 | return $this->resource_uri; 90 | } 91 | 92 | if (array_key_exists($name, $this->record)) { 93 | return $this->record[$name]; 94 | } 95 | 96 | return null; 97 | } 98 | 99 | /** 100 | * @return array 101 | */ 102 | public function attributes() 103 | { 104 | return $this->record; 105 | } 106 | 107 | /** 108 | * @return self 109 | * @throws RequestException 110 | */ 111 | public function refresh() 112 | { 113 | $response = $this->client->get($this->resource_uri); 114 | $this->record = $response; 115 | return $this; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /tests/Interfax/BaseTest.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Test\Interfax; 16 | 17 | use GuzzleHttp\Client as GuzzleClient; 18 | use GuzzleHttp\Handler\MockHandler; 19 | use GuzzleHttp\HandlerStack; 20 | use GuzzleHttp\Middleware; 21 | use Interfax\Client; 22 | use PHPUnit\Framework\TestCase; 23 | 24 | /** 25 | * Class BaseTest 26 | * 27 | * @package Interfax 28 | */ 29 | abstract class BaseTest extends TestCase 30 | { 31 | private function getExpectedClassForFactory($obj) 32 | { 33 | $cls = get_class($obj); 34 | if (strpos($cls, 'Mock') === 0) { 35 | $r = new \ReflectionClass($obj); 36 | $cls = $r->getParentClass()->name; 37 | } 38 | return $cls; 39 | } 40 | 41 | /** 42 | * Provides a mock generic factory for handling instantiation of objects within the code structure 43 | * 44 | * @param array $returns 45 | * @return \PHPUnit_Framework_MockObject_MockObject 46 | */ 47 | protected function getFactory($returns = []) 48 | { 49 | $factory = $this->getMockBuilder('Interfax\GenericFactory') 50 | ->onlyMethods(['instantiateClass']) 51 | ->getMock(); 52 | 53 | $consecutiveReturns = array_map( 54 | function ($args) { 55 | return is_array($args) 56 | ? $args[0] 57 | : $args; 58 | }, 59 | $returns 60 | ); 61 | 62 | $factory->method('instantiateClass') 63 | ->will($this->onConsecutiveCalls(...$consecutiveReturns)); 64 | 65 | return $factory; 66 | } 67 | 68 | // Basic wrapper to allow mocking out of different classes a Client might need to instantiate 69 | protected function getClientWithFactory($returns = []) 70 | { 71 | $factory = $this->getFactory($returns); 72 | 73 | return new Client(['username' => 'test_user', 'password' => 'test_password'], $factory); 74 | } 75 | 76 | protected function constructGuzzleWithResponses(&$container, $responses = []) 77 | { 78 | $mock = new MockHandler($responses); 79 | 80 | $stack = HandlerStack::create($mock); 81 | 82 | $history = Middleware::history($container); 83 | 84 | $stack->push($history); 85 | 86 | $guzzle = new GuzzleClient(['handler' => $stack]); 87 | 88 | return $guzzle; 89 | } 90 | 91 | // wrapper to allow "simple" inspection of the requests sent to the API endpoints 92 | protected function getClientWithResponses(&$container, $responses = []) 93 | { 94 | return $this->getClientWithFactory([$this->constructGuzzleWithResponses($container, $responses)]); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /tests/Interfax/ClientTest.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Test\Interfax; 16 | 17 | use GuzzleHttp\Client as GuzzleClient; 18 | use GuzzleHttp\HandlerStack; 19 | use GuzzleHttp\Middleware; 20 | use GuzzleHttp\Handler\MockHandler; 21 | use GuzzleHttp\Psr7\Response; 22 | use Interfax\Client; 23 | 24 | class ClientTest extends BaseTest 25 | { 26 | protected function getClient($properties = []) 27 | { 28 | $client = new Client(['username' => 'test_user', 'password' => 'test_password']); 29 | foreach ($properties as $k => $v) { 30 | $client->$k = $v; 31 | } 32 | 33 | return $client; 34 | } 35 | 36 | public function test_constructing_with_variables_overrides_env() 37 | { 38 | $current_username = getenv('INTERFAX_USERNAME'); 39 | $current_password = getenv('INTERFAX_PASSWORD'); 40 | putenv('INTERFAX_USERNAME=env_username'); 41 | putenv('INTERFAX_PASSWORD=env_password'); 42 | 43 | $client = new Client(['username' => 'override_username', 'password' => 'override_password']); 44 | $this->assertEquals('override_username', $client->username); 45 | $this->assertEquals('override_password', $client->password); 46 | 47 | putenv('INTERFAX_USERNAME=' . $current_username); 48 | putenv('INTERFAX_PASSWORD=' . $current_password); 49 | } 50 | 51 | public function test_constructing_without_variables_uses_env() 52 | { 53 | $current_username = getenv('INTERFAX_USERNAME'); 54 | $current_password = getenv('INTERFAX_PASSWORD'); 55 | putenv('INTERFAX_USERNAME=env_username'); 56 | putenv('INTERFAX_PASSWORD=env_password'); 57 | 58 | $client = new Client(); 59 | $this->assertEquals('env_username', $client->username); 60 | $this->assertEquals('env_password', $client->password); 61 | 62 | putenv('INTERFAX_USERNAME=' . $current_username); 63 | putenv('INTERFAX_PASSWORD=' . $current_password); 64 | } 65 | 66 | public function test_construction_should_fail_without_credentials() 67 | { 68 | $this->expectException('InvalidArgumentException'); 69 | $client = new Client(); 70 | } 71 | 72 | public function test_post_success() 73 | { 74 | $mock = new MockHandler([ 75 | new Response(201, ['Location' => 'http://myfax.resource.uri'], '') 76 | ]); 77 | $stack = HandlerStack::create($mock); 78 | 79 | $container = []; 80 | $history = Middleware::history($container); 81 | 82 | $stack->push($history); 83 | 84 | $guzzle = new GuzzleClient(['handler' => $stack]); 85 | 86 | $client = $this->getClientWithFactory([$guzzle]); 87 | 88 | $response = $client->post('test/uri', ['query' => ['foo' => 'bar']], [['name' => 'doc1', 'headers' => ['X-Bar' => 'FOO'], 'contents' => 'testString']]); 89 | 90 | $this->assertEquals('http://myfax.resource.uri', $response); 91 | $this->assertCount(1, $container); 92 | $transaction = $container[0]; 93 | $this->assertEquals('POST', $transaction['request']->getMethod()); 94 | $this->assertNotNull($transaction['options']['auth']); 95 | $this->assertEquals('foo=bar', $transaction['request']->getUri()->getQuery()); 96 | $this->assertEquals('test/uri', $transaction['request']->getUri()->getPath()); 97 | $this->assertEquals(1, preg_match('/testString/', $transaction['request']->getBody())); 98 | $this->assertEquals(1, preg_match('/InterFAX PHP/', $transaction['request']->getHeaderLine('User-Agent'))); 99 | } 100 | 101 | public function test_get_success() 102 | { 103 | $mock = new MockHandler([ 104 | new Response(200, ['Content-Type' => 'text/json'], '{"id":279415116,"uri":"https://rest.interfax.net/outbound/faxes/279415116","status":0}') 105 | ]); 106 | $stack = HandlerStack::create($mock); 107 | 108 | $container = []; 109 | $history = Middleware::history($container); 110 | 111 | $stack->push($history); 112 | 113 | $guzzle = new GuzzleClient(['handler' => $stack]); 114 | 115 | $client = $this->getClientWithFactory([$guzzle]); 116 | 117 | $response = $client->get('test/uri', ['query' => ['foo' => 'bar']]); 118 | $this->assertTrue(is_array($response)); 119 | 120 | $this->assertCount(1, $container); 121 | $transaction = $container[0]; 122 | $this->assertEquals('GET', $transaction['request']->getMethod()); 123 | $this->assertNotNull($transaction['options']['auth']); 124 | $this->assertEquals('foo=bar', $transaction['request']->getUri()->getQuery()); 125 | $this->assertEquals('test/uri', $transaction['request']->getUri()->getPath()); 126 | $this->assertEquals(1, preg_match('/InterFAX PHP/', $transaction['request']->getHeaderLine('User-Agent'))); 127 | } 128 | 129 | public function test_delete_success() 130 | { 131 | $container = []; 132 | $client = $this->getClientWithResponses( 133 | $container, 134 | [ 135 | new Response(200) 136 | ] 137 | ); 138 | 139 | $response = $client->delete('test/uri'); 140 | $this->assertEquals(200, $response); 141 | $transaction = $container[0]; 142 | $this->assertEquals('DELETE', $transaction['request']->getMethod()); 143 | $this->assertNotNull($transaction['options']['auth']); 144 | $this->assertEquals(1, preg_match('/InterFAX PHP/', $transaction['request']->getHeaderLine('User-Agent'))); 145 | } 146 | 147 | public function test_deliver_user_delivery_class_to_send_fax() 148 | { 149 | $delivery = $this->getMockBuilder('Interfax\Outbound\Delivery') 150 | ->disableOriginalConstructor() 151 | ->setMethods(array('send')) 152 | ->getMock(); 153 | 154 | $fake_return = 'test'; 155 | $delivery->expects($this->once()) 156 | ->method('send') 157 | ->will($this->returnValue($fake_return)); 158 | 159 | $client = $this->getClientWithFactory([$delivery]); 160 | 161 | $params = ['foo' => 'bar']; 162 | 163 | $this->assertEquals($fake_return, $client->deliver($params)); 164 | } 165 | 166 | public function test_get_balance() 167 | { 168 | $client = $this->getMockBuilder('Interfax\Client') 169 | ->disableOriginalConstructor() 170 | ->setMethods(['get']) 171 | ->getMock(); 172 | 173 | $client->expects($this->once()) 174 | ->method('get') 175 | ->with('/accounts/self/ppcards/balance') 176 | ->will($this->returnValue('4.35')); 177 | 178 | $this->assertEquals('4.35', $client->getBalance()); 179 | } 180 | 181 | public function test_outbound_property_returns_outbound_instance() 182 | { 183 | $outbound = $this->getMockBuilder('Interfax\Outbound') 184 | ->disableOriginalConstructor() 185 | ->getMock(); 186 | 187 | $client = $this->getClientWithFactory([$outbound]); 188 | 189 | $this->assertEquals($outbound, $client->outbound); 190 | } 191 | 192 | public function test_inbound_property() 193 | { 194 | $client = $this->getClient(); 195 | 196 | $this->assertInstanceOf('Interfax\Inbound', $client->inbound); 197 | } 198 | 199 | public function test_documents_property() 200 | { 201 | $client = $this->getClient(); 202 | 203 | $this->assertInstanceOf('Interfax\Documents', $client->documents); 204 | } 205 | 206 | public function test_boolean_parsing_for_query_string() 207 | { 208 | $mock = new MockHandler([ 209 | new Response(201, ['Location' => 'http://myfax.resource.uri'], '') 210 | ]); 211 | $stack = HandlerStack::create($mock); 212 | 213 | $container = []; 214 | $history = Middleware::history($container); 215 | 216 | $stack->push($history); 217 | 218 | $guzzle = new GuzzleClient(['handler' => $stack]); 219 | 220 | $client = $this->getClientWithFactory([$guzzle]); 221 | 222 | $response = $client->get('test/uri', ['query' => ['foo' => true, 'bar' => false]]); 223 | 224 | $this->assertEquals('http://myfax.resource.uri', $response); 225 | $this->assertEquals(1, count($container)); 226 | $transaction = $container[0]; 227 | $this->assertEquals('GET', $transaction['request']->getMethod()); 228 | $this->assertEquals('foo=TRUE&bar=FALSE', $transaction['request']->getUri()->getQuery()); 229 | $this->assertEquals('test/uri', $transaction['request']->getUri()->getPath()); 230 | } 231 | 232 | public function test_getBaseUri() 233 | { 234 | $guzzle = new GuzzleClient(['base_uri' => 'http://test.foo.bar.com']); 235 | $client = $this->getClientWithFactory([$guzzle]); 236 | $this->assertEquals('http://test.foo.bar.com', $client->getBaseUri()); 237 | } 238 | 239 | public function test_prevents_trailing_slash() 240 | { 241 | $client = new Client(['base_uri' => 'http://test.foo.com/', 'username' => 'foo', 'password' => 'bar']); 242 | 243 | $this->assertEquals('http://test.foo.com', $client->getBaseUri()); 244 | } 245 | 246 | /** 247 | * Accept any 2xx status codes, throw an exception otherwise 248 | */ 249 | public function test_response_status_parsing() 250 | { 251 | for ($i = 0; $i < 10; $i++) { 252 | $container = []; 253 | $client = $this->getClientWithResponses( 254 | $container, 255 | [ 256 | new Response(rand(200, 299), [], 'foo') 257 | ] 258 | ); 259 | 260 | $response = $client->get('test/uri', ['query' => ['foo' => true, 'bar' => false]]); 261 | 262 | $this->assertEquals('foo', $response); 263 | } 264 | 265 | for ($i = 0; $i < 10; $i++) { 266 | $status_code = rand(100, 550); 267 | if ($status_code >= 200 && $status_code <= 299) { 268 | $status_code += 100; 269 | } 270 | 271 | $container = []; 272 | $client = $this->getClientWithResponses( 273 | $container, 274 | [ 275 | new Response($status_code, ['Content-type' => 'text/json'], 'foo') 276 | ] 277 | ); 278 | 279 | $this->expectException('Interfax\Exception\RequestException'); 280 | 281 | $response = $client->get('test/uri', ['query' => ['foo' => true, 'bar' => false]]); 282 | } 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /tests/Interfax/DocumentTest.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Test\Interfax; 16 | 17 | use GuzzleHttp\Psr7\Response; 18 | use Interfax\Document; 19 | 20 | class DocumentTest extends BaseTest 21 | { 22 | public function test_upload() 23 | { 24 | $container = []; 25 | $client = $this->getClientWithResponses( 26 | $container, 27 | [ 28 | new Response(202, [], '') 29 | ] 30 | ); 31 | 32 | $document = new Document($client, 42, ['id' => 42]); 33 | 34 | $this->assertEquals($document, $document->upload(0, 300, 'the quick brown fox')); 35 | $transaction = $container[0]; 36 | $this->assertEquals('POST', $transaction['request']->getMethod()); 37 | $this->assertEquals('/outbound/documents/42', $transaction['request']->getUri()->getPath()); 38 | $this->assertEquals('', $transaction['request']->getUri()->getQuery()); 39 | $this->assertEquals('bytes=0-300', $transaction['request']->getHeaderLine('Range')); 40 | } 41 | 42 | public function test_refresh() 43 | { 44 | $response = [ 45 | 'userId' => 'nadya', 46 | 'fileName' => 'sampledoc.pdf', 47 | 'fileSize' => 82318, 48 | 'uploaded' => 0, 49 | 'uri' => 'https:/rest.interfax.net/outbound/documents/89a48657279d45429c646029bd9227e6', 50 | 'creationTime' => '2012-06-23T17:49:25', 51 | 'lastusageTime' => '2012-06-23T17:49:25', 52 | 'status' => 'Created', 53 | 'disposition' => 'SingleUse', 54 | 'sharing' => 'Private' 55 | ]; 56 | 57 | $container = []; 58 | $client = $this->getClientWithResponses( 59 | $container, 60 | [ 61 | new Response(200, ['Content-Type' => 'text/json'], json_encode($response)) 62 | ] 63 | ); 64 | 65 | $document = new Document($client, '89a48657279d45429c646029bd9227e6'); 66 | $this->assertNull($document->status); 67 | $this->assertCount(0, $container); 68 | $this->assertInstanceOf('Interfax\Document', $document->refresh()); 69 | $this->assertCount(1, $container); 70 | $transaction = $container[0]; 71 | $this->assertEquals('GET', $transaction['request']->getMethod()); 72 | $this->assertEquals('/outbound/documents/89a48657279d45429c646029bd9227e6', $transaction['request']->getUri()->getPath()); 73 | $this->assertEquals('', $transaction['request']->getUri()->getQuery()); 74 | $this->assertEquals($response, $document->attributes()); 75 | } 76 | 77 | public function test_cancel() 78 | { 79 | $struct = $response = [ 80 | 'userId' => 'nadya', 81 | 'fileName' => 'sampledoc.pdf', 82 | 'fileSize' => 82318, 83 | 'uploaded' => 0, 84 | 'uri' => 'https:/rest.interfax.net/outbound/documents/123124124', 85 | 'creationTime' => '2012-06-23T17:49:25', 86 | 'lastusageTime' => '2012-06-23T17:49:25', 87 | 'status' => 'Created', 88 | 'disposition' => 'SingleUse', 89 | 'sharing' => 'Private' 90 | ]; 91 | $container = []; 92 | $client = $this->getClientWithResponses( 93 | $container, 94 | [ 95 | new Response(200, [], '') 96 | ] 97 | ); 98 | 99 | $document = new Document($client, '123124124', $struct); 100 | $this->assertEquals($document, $document->cancel()); 101 | $this->assertCount(0, $document->attributes()); 102 | $transaction = $container[0]; 103 | $this->assertEquals('DELETE', $transaction['request']->getMethod()); 104 | $this->assertEquals('/outbound/documents/123124124', $transaction['request']->getUri()->getPath()); 105 | $this->assertEquals('', $transaction['request']->getUri()->getQuery()); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /tests/Interfax/DocumentsTest.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Test\Interfax; 16 | 17 | use GuzzleHttp\Psr7\Response; 18 | use Interfax\Documents; 19 | 20 | class DocumentsTest extends BaseTest 21 | { 22 | public function test_available() 23 | { 24 | // sample taken from docs 25 | $response = [ 26 | [ 27 | 'userId' => 'nadya', 28 | 'fileName' => 'sampledoc.pdf', 29 | 'fileSize' => 82318, 30 | 'uploaded' => 0, 31 | 'uri' => 'https://rest.interfax.net/outbound/documents/89a48657279d45429c646029bd9227e6', 32 | 'creationTime' => '2012-06-23T17:49:25', 33 | 'lastusageTime' => '2012-06-23T17:49:25', 34 | 'status' => 'Created' 35 | ], 36 | [ 37 | 'userId' => 'nadya', 38 | 'fileName' => 'sampledoc.pdf', 39 | 'fileSize' => 82318, 40 | 'uploaded' => 0, 41 | 'uri' => 'https://rest.interfax.net/outbound/documents/89a48657279d45429c646029bd9227e6', 42 | 'creationTime' => '2012-06-23T17:49:25', 43 | 'lastusageTime' => '2012-06-23T17:49:25', 44 | 'status' => 'Created' 45 | ] 46 | ]; 47 | $container = []; 48 | $client = $this->getClientWithResponses( 49 | $container, 50 | [ 51 | new Response(200, ['Content-Type' => 'text/json'], json_encode($response)) 52 | ] 53 | ); 54 | 55 | $factory_returns = [ 56 | $this->getMockBuilder('Interfax\Document')->disableOriginalConstructor()->getMock(), $this->getMockBuilder('Interfax\Document')->disableOriginalConstructor()->getMock()]; 57 | $factory = $this->getFactory($factory_returns); 58 | 59 | $documents = new Documents($client, $factory); 60 | 61 | $this->assertEquals($factory_returns, $documents->available()); 62 | $transaction = $container[0]; 63 | $this->assertEquals('GET', $transaction['request']->getMethod()); 64 | $this->assertEquals('/outbound/documents', $transaction['request']->getUri()->getPath()); 65 | $this->assertEquals('', $transaction['request']->getUri()->getQuery()); 66 | } 67 | 68 | public function test_available_with_params() 69 | { 70 | $container = []; 71 | $client = $this->getClientWithResponses( 72 | $container, 73 | [ 74 | new Response(200, ['Content-Type' => 'text/json'], '[]') 75 | ] 76 | ); 77 | 78 | $documents = new Documents($client); 79 | 80 | $this->assertEquals([], $documents->available(['limit' => 20, 'foo' => 'bar'])); 81 | $transaction = $container[0]; 82 | $this->assertEquals('GET', $transaction['request']->getMethod()); 83 | $this->assertEquals('/outbound/documents', $transaction['request']->getUri()->getPath()); 84 | $this->assertEquals('limit=20&foo=bar', $transaction['request']->getUri()->getQuery()); 85 | } 86 | 87 | public function test_create_no_params() 88 | { 89 | $container = []; 90 | $client = $this->getClientWithResponses( 91 | $container, 92 | [ 93 | new Response(201, ['Location' => 'http://mydoc.resource.uri/outbound/documents/21'], '') 94 | ] 95 | ); 96 | 97 | $document = $this->getMockBuilder('Interfax\Document') 98 | ->disableOriginalConstructor() 99 | ->getMock(); 100 | 101 | $factory = $this->getFactory([[$document, [$client, 21, ['id' => 21, 'size' => '200', 'name' => 'test.pdf']]]]); 102 | 103 | $documents = new Documents($client, $factory); 104 | 105 | $this->assertEquals($document, $documents->create('test.pdf', 200)); 106 | $transaction = $container[0]; 107 | $this->assertEquals('POST', $transaction['request']->getMethod()); 108 | $this->assertEquals('/outbound/documents', $transaction['request']->getUri()->getPath()); 109 | $this->assertEquals('name=test.pdf&size=200', $transaction['request']->getUri()->getQuery()); 110 | } 111 | 112 | public function test_create_with_params() 113 | { 114 | $container = []; 115 | $client = $this->getClientWithResponses( 116 | $container, 117 | [ 118 | new Response(201, ['Location' => 'http://mydoc.resource.uri/outbound/documents/21'], '') 119 | ] 120 | ); 121 | 122 | $document = $this->getMockBuilder('Interfax\Document') 123 | ->disableOriginalConstructor() 124 | ->getMock(); 125 | 126 | $factory = $this->getFactory([[$document, [$client, 21, ['id' => 21, 'size' => '200', 'name' => 'test.pdf', 'foo' => 'bar']]]]); 127 | 128 | $documents = new Documents($client, $factory); 129 | 130 | $this->assertEquals($document, $documents->create('test.pdf', 200, ['foo' => 'bar'])); 131 | $transaction = $container[0]; 132 | $this->assertEquals('POST', $transaction['request']->getMethod()); 133 | $this->assertEquals('/outbound/documents', $transaction['request']->getUri()->getPath()); 134 | $this->assertEquals('foo=bar&name=test.pdf&size=200', $transaction['request']->getUri()->getQuery()); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /tests/Interfax/FileTest.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright Copyright (c) 2016, InterFAX 11 | * @license MIT 12 | */ 13 | 14 | namespace Test\Interfax; 15 | 16 | use GuzzleHttp\Psr7\Response; 17 | use Interfax\Documents; 18 | use Interfax\File; 19 | 20 | class FileTest extends BaseTest 21 | { 22 | public static function tearDownAfterClass(): void 23 | { 24 | if (file_exists(__DIR__ . '/fail.txt')) { 25 | unlink(__DIR__ . '/fail.txt'); 26 | } 27 | 28 | parent::tearDownAfterClass(); 29 | } 30 | 31 | public function test_it_errors_for_invalid_path() 32 | { 33 | $i = 1; 34 | $missing_file_path = "/tmp/missing{$i}.txt"; 35 | while (file_exists($missing_file_path)) { 36 | $i++; 37 | $missing_file_path = "/tmp/missing{$i}.txt"; 38 | } 39 | 40 | $this->expectException('InvalidArgumentException'); 41 | new File($this->getClientWithFactory(), $missing_file_path); 42 | } 43 | 44 | public function test_it_sets_values_from_valid_file() 45 | { 46 | $file = new File($this->getClientWithFactory(), __DIR__ . '/test.pdf'); 47 | $header = $file->getHeader(); 48 | $this->assertArrayHasKey('Content-Type', $header); 49 | $this->assertEquals('application/pdf', $header['Content-Type']); 50 | $this->assertEquals('test.pdf', $file->getName()); 51 | } 52 | 53 | public function test_it_automatically_creates_document_for_large_files() 54 | { 55 | $container = []; 56 | 57 | $documents_client = $this->getClientWithResponses( 58 | $container, 59 | [ 60 | new Response(200, ['Location' => 'http://test.com/foo/3425'], ''), 61 | new Response(202), 62 | new Response(200) 63 | ] 64 | ); 65 | 66 | $file_client = $this->getClientWithFactory([ 67 | new Documents($documents_client) 68 | ]); 69 | 70 | $file = new File($file_client, __DIR__ . '/test.pdf', ['chunk_size' => 5000]); 71 | // no base uri on guzzle client 72 | $this->assertEquals(['Content-Location' => '/outbound/documents/3425'], $file->getHeader()); 73 | } 74 | 75 | public function test_attribute_overrides() 76 | { 77 | // this is not a real world use case, but the principle here is to allow both attributes to be set by 78 | // the method call to ensure erroneous details can be altered correctly 79 | $file = new File($this->getClientWithFactory(), __DIR__ . '/test.pdf', ['mime_type' => 'text/html', 'name' => 'foobar.html']); 80 | $header = $file->getHeader(); 81 | $this->assertArrayHasKey('Content-Type', $header); 82 | $this->assertEquals('text/html', $header['Content-Type']); 83 | $this->assertEquals('foobar.html', $file->getName()); 84 | } 85 | 86 | public function test_initialise_from_uri() 87 | { 88 | $file = new File($this->getClientWithFactory(), 'https://foo.com/bar.pdf'); 89 | $header = $file->getHeader(); 90 | $this->assertArrayHasKey('Content-Location', $header); 91 | $this->assertEquals('https://foo.com/bar.pdf', $header['Content-Location']); 92 | } 93 | 94 | public function test_initialise_with_invalid_stream() 95 | { 96 | $stream = fopen(__DIR__ . '/fail.txt', 'w'); 97 | $this->expectException('InvalidArgumentException'); 98 | new File($this->getClientWithFactory(), $stream); 99 | } 100 | 101 | public function test_initialise_with_readable_stream_and_missing_args() 102 | { 103 | $stream = fopen(__DIR__ . '/test.pdf', 'rb'); 104 | $this->expectException('InvalidArgumentException'); 105 | new File($this->getClientWithFactory(), $stream); 106 | } 107 | 108 | public function test_initialise_with_readable_stream_and_valid_args() 109 | { 110 | $stream = fopen(__DIR__ . '/test.pdf', 'rb'); 111 | 112 | $file = new File($this->getClientWithFactory(), $stream, ['name' => 'test.pdf', 'mime_type' => 'application/pdf']); 113 | $this->assertInstanceOf('Interfax\File', $file); 114 | $header = $file->getHeader(); 115 | $this->assertArrayHasKey('Content-Type', $header); 116 | $this->assertEquals('application/pdf', $header['Content-Type']); 117 | fclose($stream); 118 | } 119 | 120 | public function test_large_stream_converted_to_document() 121 | { 122 | $container = []; 123 | 124 | $documents_client = $this->getClientWithResponses( 125 | $container, 126 | [ 127 | new Response(200, ['Location' => 'http://test.com/foo/3425'], ''), 128 | new Response(202), 129 | new Response(200) 130 | ] 131 | ); 132 | 133 | $file_client = $this->getClientWithFactory([ 134 | new Documents($documents_client) 135 | ]); 136 | 137 | $stream = fopen(__DIR__ . '/test.pdf', 'rb'); 138 | 139 | $file = new File($file_client, $stream, ['size' => 9147, 'name' => 'test.pdf', 'mime_type' => 'application/pdf', 'chunk_size' => 5000]); 140 | // no base uri on guzzle client 141 | $this->assertEquals(['Content-Location' => '/outbound/documents/3425'], $file->getHeader()); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /tests/Interfax/ImageTest.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Test\Interfax; 16 | 17 | use Interfax\Image; 18 | use org\bovigo\vfs\vfsStream; 19 | 20 | class ImageTest extends BaseTest 21 | { 22 | public function test_save() 23 | { 24 | $stream = $this->getMockBuilder('GuzzleHttp\Psr7\Stream')->disableOriginalConstructor()->getMock(); 25 | 26 | $stream->method('eof') 27 | ->willReturnOnConsecutiveCalls(false, true); 28 | $stream->expects($this->once()) 29 | ->method('read') 30 | ->will($this->returnValue('abc')); 31 | 32 | $image = new Image($stream); 33 | 34 | $directory = vfsStream::setup('test_location'); 35 | $this->assertFalse($directory->hasChild('save_test.txt')); 36 | $this->assertTrue($image->save(vfsStream::url('test_location/save_test.txt'))); 37 | $this->assertTrue($directory->hasChild('save_test.txt')); 38 | $this->assertEquals('abc', $directory->getChild('save_test.txt')->getContent()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/Interfax/Inbound/FaxTest.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Test\Interfax\Inbound; 16 | 17 | use Interfax\Inbound\Fax; 18 | use Test\Interfax\BaseTest; 19 | use GuzzleHttp\Psr7\Response; 20 | 21 | class FaxTest extends BaseTest 22 | { 23 | public function test_successful_construction() 24 | { 25 | $client = $this->getMockBuilder('Interfax\Client') 26 | ->disableOriginalConstructor() 27 | ->getMock(); 28 | 29 | $fax = new Fax($client, 854759652); 30 | 31 | $this->assertInstanceOf('Interfax\Inbound\Fax', $fax); 32 | $this->assertNotNull($fax->id); 33 | $this->assertEquals(854759652, $fax->id); 34 | } 35 | 36 | public function test_markRead() 37 | { 38 | $client = $this->getMockBuilder('Interfax\Client') 39 | ->disableOriginalConstructor() 40 | ->setMethods(array('post')) 41 | ->getMock(); 42 | 43 | $client->expects($this->once()) 44 | ->method('post') 45 | ->with('/inbound/faxes/854759652/mark', ['query' => ['unread' => false]]) 46 | ->will($this->returnValue('')); 47 | 48 | $fax = new Fax($client, 854759652); 49 | 50 | $this->assertEquals($fax, $fax->markRead()); 51 | } 52 | 53 | public function test_markUnread() 54 | { 55 | $client = $this->getMockBuilder('Interfax\Client') 56 | ->disableOriginalConstructor() 57 | ->setMethods(array('post')) 58 | ->getMock(); 59 | 60 | $client->expects($this->once()) 61 | ->method('post') 62 | ->with('/inbound/faxes/854759652/mark', ['query' => ['unread' => true]]) 63 | ->will($this->returnValue('')); 64 | 65 | $fax = new Fax($client, 854759652); 66 | 67 | $this->assertEquals($fax, $fax->markUnread()); 68 | } 69 | 70 | public function test_resend() 71 | { 72 | $client = $this->getMockBuilder('Interfax\Client') 73 | ->disableOriginalConstructor() 74 | ->setMethods(array('post')) 75 | ->getMock(); 76 | 77 | $client->expects($this->once()) 78 | ->method('post') 79 | ->with('/inbound/faxes/854759652/resend') 80 | ->will($this->returnValue('')); 81 | 82 | $fax = new Fax($client, 854759652); 83 | 84 | $this->assertEquals($fax, $fax->resend()); 85 | } 86 | 87 | public function test_resend_with_email() 88 | { 89 | $client = $this->getMockBuilder('Interfax\Client') 90 | ->disableOriginalConstructor() 91 | ->setMethods(array('post')) 92 | ->getMock(); 93 | 94 | $client->expects($this->once()) 95 | ->method('post') 96 | ->with('/inbound/faxes/854759652/resend', ['query' => ['email' => 'foo@bar.com']]) 97 | ->will($this->returnValue('')); 98 | 99 | $fax = new Fax($client, 854759652); 100 | 101 | $this->assertEquals($fax, $fax->resend('foo@bar.com')); 102 | } 103 | 104 | public function test_image() 105 | { 106 | $container = []; 107 | $resp_resource = fopen(__DIR__ . '/../test.pdf', 'r'); 108 | $stream = \GuzzleHttp\Psr7\Utils::streamFor($resp_resource); 109 | $client = $this->getClientWithResponses( 110 | $container, 111 | [ 112 | new Response(200, [], $stream), 113 | ] 114 | ); 115 | 116 | $result_image = $this->getMockBuilder('Interfax\Image')->disableOriginalConstructor()->getMock(); 117 | $factory = $this->getFactory([ 118 | // $result_image 119 | [$result_image, [$stream]], 120 | ]); 121 | 122 | $fax = new Fax($client, 854759652, [], $factory); 123 | //$image = $fax->image(); 124 | $this->assertEquals($result_image, $fax->image()); 125 | $transaction = $container[0]; 126 | $this->assertEquals('GET', $transaction['request']->getMethod()); 127 | $this->assertEquals('/inbound/faxes/854759652/image', $transaction['request']->getUri()->getPath()); 128 | 129 | fclose($resp_resource); 130 | } 131 | 132 | public function test_emails() 133 | { 134 | $client = $this->getMockBuilder('Interfax\Client') 135 | ->disableOriginalConstructor() 136 | ->setMethods(array('get')) 137 | ->getMock(); 138 | 139 | $response = [ 140 | [ 141 | 'emailAddress' => 'username@interfax.net', 142 | 'messageStatus' => 0, 143 | 'completionTime' => '2012-0623T17:24:11', 144 | ], 145 | [ 146 | 'emailAddress' => 'username2@interfax.net', 147 | 'messageStatus' => 0, 148 | 'completionTime' => '2012-0623T17:25:11', 149 | ], 150 | ]; 151 | 152 | $client->expects($this->once()) 153 | ->method('get') 154 | ->with('/inbound/faxes/854759652/emails') 155 | ->will($this->returnValue($response)); 156 | 157 | $fax = new Fax($client, 854759652); 158 | $this->assertEquals($response, $fax->emails()); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /tests/Interfax/InboundTest.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Test\Interfax; 16 | 17 | use GuzzleHttp\Psr7\Response; 18 | use Interfax\Inbound; 19 | 20 | class InboundTest extends BaseTest 21 | { 22 | public function test_incoming() 23 | { 24 | $response = [['messageId' => 12, 'phoneNumber' => '111'],['messageId' => 14, 'phoneNumber' => '2222']]; 25 | $container = []; 26 | $client = $this->getClientWithResponses( 27 | $container, 28 | [ 29 | new Response('200', ['Content-type' => 'text/json'], json_encode($response)) 30 | ] 31 | ); 32 | 33 | // 2 inbound faxes will be crated for the 2 response structs 34 | $factory = $this->getFactory( 35 | [ 36 | [new Inbound\Fax($client, 12), [$client, 12, $response[0]]], 37 | [new Inbound\Fax($client, 40), [$client, 14, $response[1]]] 38 | ] 39 | ); 40 | 41 | $inbound = new Inbound($client, $factory); 42 | 43 | $faxes = $inbound->incoming(['unreadOnly' => false]); 44 | 45 | $this->assertCount(2, $faxes); 46 | $transaction = $container[0]; 47 | $this->assertEquals('GET', $transaction['request']->getMethod()); 48 | $this->assertEquals('/inbound/faxes', $transaction['request']->getUri()->getPath()); 49 | $this->assertEquals('unreadOnly=FALSE', $transaction['request']->getUri()->getQuery()); 50 | } 51 | 52 | public function test_find() 53 | { 54 | $response = ['messageId' => 12, 'phoneNumber' => '111']; 55 | $container = []; 56 | $client = $this->getClientWithResponses( 57 | $container, 58 | [ 59 | new Response('200', ['Content-type' => 'text/json'], json_encode($response)) 60 | ] 61 | ); 62 | 63 | $fax = new Inbound\Fax($client, 12); 64 | $factory = $this->getFactory( 65 | [ 66 | [$fax, [$client, 12, $response]], 67 | ] 68 | ); 69 | 70 | $inbound = new Inbound($client, $factory); 71 | 72 | $this->assertEquals($fax, $inbound->find(12)); 73 | $transaction = $container[0]; 74 | $this->assertEquals('GET', $transaction['request']->getMethod()); 75 | $this->assertEquals('/inbound/faxes/12', $transaction['request']->getUri()->getPath()); 76 | $this->assertEquals('', $transaction['request']->getUri()->getQuery()); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /tests/Interfax/Outbound/DeliveryTest.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Test\Interfax\Outbound; 16 | 17 | use Interfax\Outbound\Delivery; 18 | use PHPUnit\Framework\MockObject\MockObject; 19 | use Test\Interfax\BaseTest; 20 | use Interfax\Client; 21 | use GuzzleHttp\Psr7\Response; 22 | use Interfax\File; 23 | 24 | class DeliveryTest extends BaseTest 25 | { 26 | protected $client; 27 | 28 | public function setUp(): void 29 | { 30 | $this->client = new Client(['username' => 'test_user', 'password' => 'test_password']); 31 | } 32 | 33 | public function test_it_cant_be_constructed_without_params() 34 | { 35 | $this->expectException('InvalidArgumentException'); 36 | 37 | $delivery = new Delivery($this->client); 38 | } 39 | 40 | public function test_it_cant_be_constructed_without_a_file() 41 | { 42 | 43 | $this->expectException('InvalidArgumentException'); 44 | 45 | $delivery2 = new Delivery($this->client, ['faxNumber' => '12345']); 46 | } 47 | 48 | public function test_it_can_be_constructed_with_a_file_path() 49 | { 50 | $file = $this->getMockBuilder('Interfax\File')->disableOriginalConstructor()->getMock(); 51 | 52 | $factory = $this->getFactory( 53 | [ 54 | [$file, [$this->client, '/fake/file']], 55 | [$file, [$this->client, '/fake/file2']] 56 | ] 57 | ); 58 | 59 | $this->assertInstanceOf('Interfax\Outbound\Delivery', new Delivery($this->client, ['faxNumber' => '12345', 'file' => '/fake/file'], $factory)); 60 | $this->assertInstanceOf('Interfax\Outbound\Delivery', new Delivery($this->client, ['faxNumber' => '12345', 'files' => ['/fake/file2']], $factory)); 61 | } 62 | 63 | public function test_it_can_be_constructed_with_a_stream() 64 | { 65 | $stream = fopen(__DIR__ . '/../test.pdf', 'rb'); 66 | $file = $this->getMockBuilder('Interfax\File')->disableOriginalConstructor()->getMock(); 67 | $params = ['name' => 'test.pdf', 'mime_type' => 'application/pdf']; 68 | 69 | $factory = $this->getFactory([ 70 | [$file, [$this->client, $stream, $params]] 71 | ]); 72 | 73 | $this->assertInstanceOf('Interfax\Outbound\Delivery', new Delivery($this->client, ['faxNumber' => '12345', 'file' => [$stream, $params]], $factory)); 74 | } 75 | 76 | public function test_it_can_be_constructed_with_a_uri() 77 | { 78 | $this->assertInstanceOf('Interfax\Outbound\Delivery', new Delivery($this->client, ['faxNumber' => '12345', 'file' => 'https://test.com/foo/bar'])); 79 | } 80 | 81 | public function test_it_can_be_constructed_with_an_Interfax_File() 82 | { 83 | $client = $this->client; 84 | $file = new File($client, __DIR__ . '/../test.pdf'); 85 | $delivery = new Delivery($this->client, ['faxNumber' => '12345', 'file' => $file]); 86 | $this->assertInstanceOf('Interfax\Outbound\Delivery', $delivery); 87 | $r = new \ReflectionClass($delivery); 88 | $rp = $r->getProperty('files'); 89 | $rp->setAccessible(true); 90 | $this->assertEquals([$file], $rp->getValue($delivery)); 91 | } 92 | 93 | public function test_it_can_be_constructed_with_an_Interfax_Document() 94 | { 95 | $file = $this->getMockBuilder('Interfax\File')->disableOriginalConstructor()->getMock(); 96 | $factory = $this->getFactory([ 97 | [$file, [$this->client, 'http://test.com/foo']] 98 | ]); 99 | 100 | $document = $this->getMockBuilder('Interfax\Document') 101 | ->disableOriginalConstructor() 102 | ->setMethods(['getHeaderLocation']) 103 | ->getMock(); 104 | 105 | $document->expects($this->once()) 106 | ->method('getHeaderLocation') 107 | ->will($this->returnValue('http://test.com/foo')); 108 | 109 | $delivery = new Delivery($this->client, ['faxNumber' => '12345', 'file' => $document], $factory); 110 | $this->assertInstanceOf('Interfax\Outbound\Delivery', $delivery); 111 | } 112 | 113 | public function test_it_stores_provided_params_for_the_query_string() 114 | { 115 | $params = []; 116 | for ($i = 0; $i < 5; $i++) { 117 | $params[substr(md5(mt_rand()), 0, 7)] = substr(md5(mt_rand()), 0, 7); 118 | } 119 | $params['faxNumber'] = '12345'; 120 | $params['file'] = __DIR__ . '/../test.pdf'; 121 | 122 | $delivery = new Delivery($this->client, $params); 123 | $r = new \ReflectionClass('Interfax\Outbound\Delivery'); 124 | $r_qp = $r->getProperty('query_params'); 125 | $r_qp->setAccessible(true); 126 | $qp = $r_qp->getValue($delivery); 127 | 128 | // file is not a query param 129 | unset($params['file']); 130 | $this->assertEquals(count($params), count($qp)); 131 | foreach ($params as $k => $v) { 132 | $this->assertArrayHasKey($k, $qp); 133 | $this->assertEquals($v, $qp[$k]); 134 | } 135 | } 136 | 137 | public function test_it_uses_the_client_to_post_a_delivery_and_returns_fax() 138 | { 139 | $container = []; 140 | $client = $this->getClientWithResponses( 141 | $container, 142 | [ 143 | new Response(201, ['Location' => 'http://myfax.resource.uri/outbound/faxes/21'], '') 144 | ] 145 | ); 146 | 147 | // construct fake file to ensure it affects the request contents correctly 148 | $file = $this->getMockBuilder('Interfax\File') 149 | ->disableOriginalConstructor() 150 | ->setMethods(['getHeader', 'getBody']) 151 | ->getMock(); 152 | 153 | $file->expects($this->any()) 154 | ->method('getHeader') 155 | ->will($this->returnValue(['Content-Type' => 'app/foo'])); 156 | 157 | $file->expects($this->any()) 158 | ->method('getBody') 159 | ->will($this->returnValue('foo bar car')); 160 | 161 | // fake fax to be returned 162 | $fax = $this->getMockBuilder('Interfax\Outbound\Fax') 163 | ->disableOriginalConstructor() 164 | ->getMock(); 165 | 166 | $factory = $this->getFactory( 167 | [ 168 | [$file, [$client, 'fake/file']], 169 | [$fax, [$client, 21]] 170 | ] 171 | ); 172 | 173 | $delivery = new Delivery($client, ['faxNumber' => 12345, 'bar' => 'foo', 'file' => 'fake/file'], $factory); 174 | 175 | $this->assertEquals($fax, $delivery->send()); 176 | $transaction = $container[0]; 177 | $this->assertEquals('POST', $transaction['request']->getMethod()); 178 | $this->assertEquals('/outbound/faxes', $transaction['request']->getUri()->getPath()); 179 | $this->assertEquals('faxNumber=12345&bar=foo', $transaction['request']->getUri()->getQuery()); 180 | $this->assertEquals('app/foo', $transaction['request']->getHeaderLine('Content-Type')); 181 | $body = $transaction['request']->getBody(); 182 | //$this->assertInstanceOf('GuzzleHttp\Psr7\MultipartStream', $body); 183 | $contents = (string) $body; 184 | 185 | $this->assertEquals(1, preg_match('/foo bar car/', $contents)); 186 | } 187 | 188 | /** 189 | * Helper function to generate a mock Interfax\File 190 | * 191 | * @param $headers 192 | * @param $body 193 | * @return MockObject 194 | */ 195 | protected function getFakeFile($headers, $body, $name = 'fakeFaxFile') 196 | { 197 | // construct fake file to ensure it affects the request contents correctly 198 | $file = $this->getMockBuilder('Interfax\File') 199 | ->disableOriginalConstructor() 200 | ->onlyMethods(['getHeader', 'getBody', 'getName']) 201 | ->getMock(); 202 | 203 | $file->expects($this->any()) 204 | ->method('getHeader') 205 | ->will($this->returnValue($headers)); 206 | 207 | $file->expects($this->any()) 208 | ->method('getBody') 209 | ->will($this->returnValue($body)); 210 | 211 | $file->expects($this->any()) 212 | ->method('getName') 213 | ->will($this->returnValue($name)); 214 | 215 | return $file; 216 | } 217 | 218 | public function test_it_supports_multiple_file_delivery() 219 | { 220 | $container = []; 221 | $client = $this->getClientWithResponses( 222 | $container, 223 | [ 224 | new Response(201, ['Location' => 'http://myfax.resource.uri/outbound/faxes/21'], '') 225 | ] 226 | ); 227 | 228 | $file1 = $this->getFakeFile(['Content-Type' => 'app/foo'], 'foo bar car'); 229 | $file2 = $this->getFakeFile(['Content-Type' => 'app/bar'], 'test content'); 230 | 231 | // fake fax to be returned 232 | $fax = $this->getMockBuilder('Interfax\Outbound\Fax') 233 | ->disableOriginalConstructor() 234 | ->getMock(); 235 | 236 | $factory = $this->getFactory([ 237 | [$file1, [$client, 'fake/file1']], 238 | [$file2, [$client, 'fake/file2']], 239 | [$fax, [$client, 21]] 240 | ]); 241 | 242 | $delivery = new Delivery($client, ['faxNumber' => 12345, 'bar' => 'foo', 'files' => ['fake/file1', 'fake/file2']], $factory); 243 | 244 | $this->assertEquals($fax, $delivery->send()); 245 | $transaction = $container[0]; 246 | $this->assertEquals('POST', $transaction['request']->getMethod()); 247 | $this->assertEquals('/outbound/faxes', $transaction['request']->getUri()->getPath()); 248 | $this->assertEquals('faxNumber=12345&bar=foo', $transaction['request']->getUri()->getQuery()); 249 | $body = $transaction['request']->getBody(); 250 | $this->assertInstanceOf('GuzzleHttp\Psr7\MultipartStream', $body); 251 | $contents = (string) $body; 252 | $this->assertEquals(1, preg_match('/Content-Type: app\/foo/', $contents)); 253 | $this->assertEquals(1, preg_match('/foo bar car/', $contents)); 254 | $this->assertEquals(1, preg_match('/Content-Type: app\/bar/', $contents)); 255 | $this->assertEquals(1, preg_match('/test content/', $contents)); 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /tests/Interfax/Outbound/FaxTest.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Test\Interfax\Outbound; 16 | 17 | use GuzzleHttp\Psr7\Response; 18 | use Interfax\Outbound\Fax; 19 | use Test\Interfax\BaseTest; 20 | 21 | class FaxTest extends BaseTest 22 | { 23 | public function test_successful_construction() 24 | { 25 | $client = $this->getMockBuilder('Interfax\Client') 26 | ->disableOriginalConstructor() 27 | ->getMock(); 28 | 29 | $fax = new Fax($client, 854759652); 30 | 31 | $this->assertInstanceOf('Interfax\Outbound\Fax', $fax); 32 | $this->assertNotNull($fax->id); 33 | $this->assertEquals(854759652, $fax->id); 34 | } 35 | 36 | public function test_refresh() 37 | { 38 | $client = $this->getMockBuilder('Interfax\Client') 39 | ->disableOriginalConstructor() 40 | ->setMethods(array('get')) 41 | ->getMock(); 42 | 43 | $reload_response = ['id' => 854759652,'uri' => 'https://rest.interfax.net/outbound/faxes/279415116','status' => 0]; 44 | 45 | $client->expects($this->once()) 46 | ->method('get') 47 | ->with('/outbound/faxes/854759652') 48 | ->will($this->returnValue($reload_response)); 49 | 50 | $fax = new Fax($client, 854759652); 51 | 52 | $this->assertNull($fax->status); 53 | $this->assertEquals($fax, $fax->refresh()); 54 | $this->assertEquals(0, $fax->status); 55 | } 56 | 57 | public function test_getter_method_for_record_details() 58 | { 59 | $client = $this->getMockBuilder('Interfax\Client') 60 | ->disableOriginalConstructor() 61 | ->setMethods(array('get')) 62 | ->getMock(); 63 | 64 | $response = []; 65 | for ($i = 0; $i < 5; $i++) { 66 | $response[substr(md5(mt_rand()), 0, 7)] = substr(md5(mt_rand()), 0, 7); 67 | } 68 | $response['id'] = 82342453; 69 | $response['status'] = -2; 70 | 71 | $client->expects($this->once()) 72 | ->method('get') 73 | ->with('/outbound/faxes/82342453') 74 | ->will($this->returnValue($response)); 75 | 76 | $fax = new Fax($client, 82342453); 77 | $this->assertNull($fax->status); 78 | foreach ($response as $k => $v) { 79 | if ($k != 'id') { 80 | $this->assertNull($fax->$k); 81 | } 82 | } 83 | 84 | $this->assertInstanceOf('Interfax\Outbound\Fax', $fax->refresh()); 85 | 86 | foreach ($response as $k => $v) { 87 | $this->assertEquals($v, $fax->$k); 88 | } 89 | 90 | $this->assertNull($fax->undefined_property); 91 | } 92 | 93 | public function test_resend() 94 | { 95 | $container = []; 96 | 97 | $client = $this->getClientWithResponses( 98 | $container, 99 | [ 100 | new Response(201, ['Location' => 'http://myfax.resource.uri/outbound/faxes/21'], '') 101 | ] 102 | ); 103 | 104 | $resent_fax = $this->getMockBuilder('Interfax\Outbound\Fax') 105 | ->disableOriginalConstructor() 106 | ->getMock(); 107 | 108 | $factory = $this->getFactory([[$resent_fax, [$client, 21]]]); 109 | 110 | $fax = new Fax($client, 45, [], $factory); 111 | 112 | $this->assertEquals($resent_fax, $fax->resend()); 113 | $transaction = $container[0]; 114 | $this->assertEquals('POST', $transaction['request']->getMethod()); 115 | $this->assertEquals('/outbound/faxes/45/resend', $transaction['request']->getUri()->getPath()); 116 | } 117 | 118 | public function test_resend_with_param() 119 | { 120 | $container = []; 121 | 122 | $client = $this->getClientWithResponses( 123 | $container, 124 | [ 125 | new Response(201, ['Location' => 'http://myfax.resource.uri/outbound/faxes/21'], '') 126 | ] 127 | ); 128 | 129 | $resent_fax = $this->getMockBuilder('Interfax\Outbound\Fax') 130 | ->disableOriginalConstructor() 131 | ->getMock(); 132 | 133 | $factory = $this->getFactory([[$resent_fax, [$client, 21]]]); 134 | 135 | $fax = new Fax($client, 45, [], $factory); 136 | 137 | $resend_number = '+1111111111'; 138 | 139 | $this->assertEquals($resent_fax, $fax->resend($resend_number)); 140 | $transaction = $container[0]; 141 | $this->assertEquals('POST', $transaction['request']->getMethod()); 142 | $this->assertEquals('/outbound/faxes/45/resend', $transaction['request']->getUri()->getPath()); 143 | $this->assertEquals('faxNumber=' . urlencode($resend_number), $transaction['request']->getUri()->getQuery()); 144 | } 145 | 146 | public function test_attributes() 147 | { 148 | $client = $this->getMockBuilder('Interfax\Client')->disableOriginalConstructor()->getMock(); 149 | 150 | $definition = [ 151 | 'submitTime' => '2012-06-20T06:08:18', 152 | 'contact' => '', 153 | 'destinationFax' => '0081287282867', 154 | 'replyEmail' => 'nadya@interfax.net', 155 | 'subject' => 'test', 156 | 'pagesSubmitted' => 1, 157 | 'senderCSID' => 'INTERFAX', 158 | 'attemptsToPerform' => 4, 159 | 'pageSize' => 'A4', 160 | 'resolution' => 'Portrait', 161 | 'pageResolution' => 'Fine', 162 | 'pageOrientation' => 'Portrait', 163 | 'rendering' => 'Fine', 164 | 'pageHeade' => '0', 165 | 'userId' => 'nadya', 166 | 'pagesSent' => 1, 167 | 'completionTime' => '2012-06-20T06:09:08', 168 | 'remoteCSID' => '81287282867', 169 | 'duration' => 37, 170 | 'priority' => 2, 171 | 'units' => 1.00, 172 | 'costPerUnit' => 0.9500, 173 | 'attemptsMade' => 1, 174 | 'id' => 279415116, 175 | 'uri' => 'https://rest.interfax.net/outbound/faxes/279415116', 176 | 'status' => 0 177 | ]; 178 | 179 | $fax = new Fax($client, 279415116, $definition); 180 | 181 | $this->assertEquals($definition, $fax->attributes()); 182 | } 183 | 184 | public function test_cancel() 185 | { 186 | $container = []; 187 | $client = $this->getClientWithResponses( 188 | $container, 189 | [ 190 | new Response(200, [], '') 191 | ] 192 | ); 193 | 194 | $fax = new Fax($client, 21); 195 | 196 | $this->assertEquals($fax, $fax->cancel()); 197 | 198 | $transaction = $container[0]; 199 | $this->assertEquals('POST', $transaction['request']->getMethod()); 200 | $this->assertEquals('/outbound/faxes/21/cancel', $transaction['request']->getUri()->getPath()); 201 | } 202 | 203 | public function test_hide() 204 | { 205 | $container = []; 206 | $client = $this->getClientWithResponses( 207 | $container, 208 | [ 209 | new Response(200, [], '') 210 | ] 211 | ); 212 | 213 | $fax = new Fax($client, 21); 214 | 215 | $this->assertEquals($fax, $fax->hide()); 216 | $transaction = $container[0]; 217 | $this->assertEquals('POST', $transaction['request']->getMethod()); 218 | $this->assertEquals('/outbound/faxes/21/hide', $transaction['request']->getUri()->getPath()); 219 | } 220 | 221 | public function test_image() 222 | { 223 | $container = []; 224 | $resp_resource = fopen(__DIR__ . '/../test.pdf', 'r'); 225 | $stream = \GuzzleHttp\Psr7\Utils::streamFor($resp_resource); 226 | $client = $this->getClientWithResponses( 227 | $container, 228 | [ 229 | new Response(200, [], $stream) 230 | ] 231 | ); 232 | 233 | $result_image = $this->getMockBuilder('Interfax\Image')->disableOriginalConstructor()->getMock(); 234 | $factory = $this->getFactory([ 235 | [$result_image, [$stream]] 236 | ]); 237 | 238 | $fax = new Fax($client, 42, [], $factory); 239 | 240 | $this->assertEquals($result_image, $fax->image()); 241 | $transaction = $container[0]; 242 | $this->assertEquals('GET', $transaction['request']->getMethod()); 243 | $this->assertEquals('/outbound/faxes/42/image', $transaction['request']->getUri()->getPath()); 244 | 245 | fclose($resp_resource); 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /tests/Interfax/OutboundTest.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Mike Smith 11 | * @copyright Copyright (c) 2016, InterFAX 12 | * @license MIT 13 | */ 14 | 15 | namespace Test\Interfax; 16 | 17 | use GuzzleHttp\Psr7\Response; 18 | use Interfax\Outbound; 19 | 20 | class OutboundTest extends BaseTest 21 | { 22 | public function test_completed() 23 | { 24 | $response = [['id' => 12, 'senderCSID' => 'Interfax'],['id' => 14, 'senderCSID' => 'Interfax']]; 25 | $container = []; 26 | $client = $this->getClientWithResponses( 27 | $container, 28 | [ 29 | new Response('200', ['Content-type' => 'text/json'], json_encode($response)) 30 | ] 31 | ); 32 | 33 | $fax1 = $this->getMockBuilder('Interfax\Outbound\Fax')->disableOriginalConstructor()->getMock(); 34 | $fax2 = $this->getMockBuilder('Interfax\Outbound\Fax')->disableOriginalConstructor()->getMock(); 35 | $factory = $this->getFactory([ 36 | [$fax1, [$client, 12, $response[0]]], 37 | [$fax2, [$client, 14, $response[1]]] 38 | ]); 39 | 40 | $outbound = new Outbound($client, $factory); 41 | 42 | $res = $outbound->completed(['12', '14']); 43 | 44 | $this->assertEquals([$fax1, $fax2], $res); 45 | $transaction = $container[0]; 46 | $this->assertEquals('GET', $transaction['request']->getMethod()); 47 | $this->assertEquals('/outbound/faxes/completed', $transaction['request']->getUri()->getPath()); 48 | $this->assertEquals('ids=' . urlencode('12,14'), $transaction['request']->getUri()->getQuery()); 49 | } 50 | 51 | public function test_recent() 52 | { 53 | $response = [['id' => 21]]; 54 | $container = []; 55 | $client = $this->getClientWithResponses( 56 | $container, 57 | [ 58 | new Response('200', ['Content-type' => 'text/json'], json_encode($response)) 59 | ] 60 | ); 61 | 62 | $fax = $this->getMockBuilder('Interfax\Outbound\Fax')->disableOriginalConstructor()->getMock(); 63 | 64 | $factory = $this->getFactory([[$fax, [$client, 21, $response[0]]]]); 65 | 66 | $outbound = new Outbound($client, $factory); 67 | 68 | $res = $outbound->recent(['limit' => 5]); 69 | 70 | $this->assertEquals([$fax], $res); 71 | $transaction = $container[0]; 72 | $this->assertEquals('GET', $transaction['request']->getMethod()); 73 | $this->assertEquals('/outbound/faxes', $transaction['request']->getUri()->getPath()); 74 | $this->assertEquals('limit=5', $transaction['request']->getUri()->getQuery()); 75 | } 76 | 77 | public function test_resend_uses_outbound_fax() 78 | { 79 | $fax = $this->getMockBuilder('Interfax\Outbound\Fax') 80 | ->disableOriginalConstructor() 81 | ->setMethods(['resend']) 82 | ->getMock(); 83 | 84 | $resent_fax = $this->getMockBuilder('Interfax\Outbound\Fax')->disableOriginalConstructor()->getMock(); 85 | 86 | $fax->expects($this->once()) 87 | ->method('resend') 88 | ->will($this->returnValue($resent_fax)); 89 | 90 | $factory = $this->getFactory([$fax]); 91 | 92 | $client = $this->getMockBuilder('Interfax\Client')->disableOriginalConstructor()->getMock(); 93 | 94 | $outbound = new Outbound($client, $factory); 95 | 96 | $this->assertEquals($resent_fax, $outbound->resend(34552)); 97 | } 98 | 99 | public function test_resend_with_new_number_uses_outbound_fax() 100 | { 101 | $fax = $this->getMockBuilder('Interfax\Outbound\Fax') 102 | ->disableOriginalConstructor() 103 | ->setMethods(['resend']) 104 | ->getMock(); 105 | 106 | $fax_number = '+112122323'; 107 | 108 | $resent_fax = $this->getMockBuilder('Interfax\Outbound\Fax')->disableOriginalConstructor()->getMock(); 109 | 110 | $fax->expects($this->once()) 111 | ->method('resend') 112 | ->with($fax_number) 113 | ->will($this->returnValue($resent_fax)); 114 | 115 | $factory = $this->getFactory([$fax]); 116 | 117 | $client = $this->getMockBuilder('Interfax\Client')->disableOriginalConstructor()->getMock(); 118 | 119 | $outbound = new Outbound($client, $factory); 120 | 121 | $this->assertEquals($resent_fax, $outbound->resend(34552, $fax_number)); 122 | } 123 | 124 | public function test_search() 125 | { 126 | $container = []; 127 | 128 | $search_results = [ 129 | ['id' => 5, 'status' => -32], 130 | ['id' => 9, 'status' => 40], 131 | ]; 132 | 133 | $client = $this->getClientWithResponses( 134 | $container, 135 | [ 136 | new Response(200, ['Content-Type' => 'text/json'], json_encode($search_results)) 137 | ] 138 | ); 139 | 140 | $test_params = ['status' => 'Inprocess']; 141 | 142 | $factory = $this->getFactory([ 143 | [new Outbound\Fax($client, 5),[$client, 5, $search_results[0]] ], 144 | [new Outbound\Fax($client, 9),[$client, 9, $search_results[1]] ] 145 | ]); 146 | 147 | $outbound = new Outbound($client, $factory); 148 | 149 | $this->assertCount(2, $outbound->search($test_params)); 150 | $transaction = $container[0]; 151 | $this->assertEquals('GET', $transaction['request']->getMethod()); 152 | $this->assertEquals('/outbound/search', $transaction['request']->getUri()->getPath()); 153 | $this->assertEquals('status=Inprocess', $transaction['request']->getUri()->getQuery()); 154 | } 155 | 156 | public function test_find() 157 | { 158 | $response = ['id' => 42, 'status' => 0, 'duration' => 4]; 159 | $container = []; 160 | $client = $this->getClientWithResponses( 161 | $container, 162 | [ 163 | new Response('200', ['Content-type' => 'text/json'], json_encode($response)) 164 | ] 165 | ); 166 | 167 | $fax = new Outbound\Fax($client, 42, $response); 168 | $factory = $this->getFactory( 169 | [ 170 | [$fax, [$client, 42, $response]], 171 | ] 172 | ); 173 | 174 | $outbound = new Outbound($client, $factory); 175 | 176 | $this->assertEquals($fax, $outbound->find(12)); 177 | $transaction = $container[0]; 178 | $this->assertEquals('GET', $transaction['request']->getMethod()); 179 | $this->assertEquals('/outbound/faxes/12', $transaction['request']->getUri()->getPath()); 180 | $this->assertEquals('', $transaction['request']->getUri()->getQuery()); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /tests/Interfax/test.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/interfax/interfax-php/de0ebc06471c140935e54d7e642fb4149f858a63/tests/Interfax/test.pdf --------------------------------------------------------------------------------