Initial commit

This commit is contained in:
distinctm
2019-01-22 17:58:20 -05:00
commit d6ced07199
6519 changed files with 752220 additions and 0 deletions

15
vendor/nexmo/client/LICENSE.txt vendored Normal file
View File

@@ -0,0 +1,15 @@
The MIT License (MIT)
Copyright (c) 2016-2018 Nexmo, Inc
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

41
vendor/nexmo/client/composer.json vendored Normal file
View File

@@ -0,0 +1,41 @@
{
"name": "nexmo/client",
"description": "PHP Client for using Nexmo's API.",
"license": "MIT",
"type": "library",
"authors": [
{
"name": "Tim Lytle",
"email": "tim@nexmo.com",
"role": "Developer",
"homepage": "http://twitter.com/tjlytle"
}
],
"support": {
"email": "devrel@nexmo.com"
},
"require": {
"php": ">=5.6",
"php-http/client-implementation": "^1.0",
"zendframework/zend-diactoros": "^1.3",
"php-http/guzzle6-adapter": "^1.0",
"lcobucci/jwt": "^3.2"
},
"require-dev": {
"phpunit/phpunit": "^5.7",
"php-http/mock-client": "^0.3.0",
"estahn/phpunit-json-assertions": "^1.0.0",
"squizlabs/php_codesniffer": "^3.1"
},
"autoload": {
"psr-4": {
"Nexmo\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Nexmo\\": "test/",
"NexmoTest\\": "test/"
}
}
}

View File

@@ -0,0 +1,12 @@
<?php
require_once "vendor/autoload.php";
$recordingId = 'RECORDING_ID';
$keypair = new \Nexmo\Client\Credentials\Keypair(file_get_contents(__DIR__ . '/private.key'), 'APPLICATION_ID');
$client = new \Nexmo\Client($keypair);
$recording = 'https://api.nexmo.com/v1/files/'.$recordingId;
$data = $client->get($recording);
file_put_contents($recordingId.'.mp3', $data->getBody());

72
vendor/nexmo/client/examples/send.php vendored Normal file
View File

@@ -0,0 +1,72 @@
<?php
//example of sending an sms using an API key / secret
require_once '../vendor/autoload.php';
//create client with api key and secret
$client = new Nexmo\Client(new Nexmo\Client\Credentials\Basic(API_KEY, API_SECRET));
//send message using simple api params
$message = $client->message()->send([
'to' => NEXMO_TO,
'from' => NEXMO_FROM,
'text' => 'Test message from the Nexmo PHP Client'
]);
//array access provides response data
echo "Sent message to " . $message['to'] . ". Balance is now " . $message['remaining-balance'] . PHP_EOL;
sleep(1);
//send message using object support
$text = new \Nexmo\Message\Text(NEXMO_TO, NEXMO_FROM, 'Test message using PHP client library');
$text->setClientRef('test-message')
->setClass(\Nexmo\Message\Text::CLASS_FLASH);
$client->message()->send($text);
//method access
echo "Sent message to " . $text->getTo() . ". Balance is now " . $text->getRemainingBalance() . PHP_EOL;
sleep(1);
//sending a message over 160 characters
$longwinded = <<<EOF
But soft! What light through yonder window breaks?
It is the east, and Juliet is the sun.
Arise, fair sun, and kill the envious moon,
Who is already sick and pale with grief,
That thou, her maid, art far more fair than she.
EOF;
$text = new \Nexmo\Message\Text(NEXMO_TO, NEXMO_FROM, $longwinded);
$client->message()->send($text);
echo "Sent message to " . $text->getTo() . ". Balance is now " . $text->getRemainingBalance() . PHP_EOL;
echo "Message was split into " . count($text) . " messages, those message ids are: " . PHP_EOL;
for($i = 0; $i < count($text); $i++){
echo $text[$i]['message-id'] . PHP_EOL;
}
echo "The account balance after each message was: " . PHP_EOL;
for($i = 0; $i < count($text); $i++){
echo $text->getRemainingBalance($i) . PHP_EOL;
}
//easier iteration, can use methods or array access
foreach($text as $index => $data){
echo "Balance was " . $text->getRemainingBalance($index) . " after message " . $data['message-id'] . " was sent." . PHP_EOL;
}
//an invalid request
try{
$text = new \Nexmo\Message\Text('not valid', NEXMO_FROM, $longwinded);
$client->message()->send($text);
} catch (Nexmo\Client\Exception\Request $e) {
//can still get the API response
$text = $e->getEntity();
$request = $text->getRequest(); //PSR-7 Request Object
$response = $text->getResponse(); //PSR-7 Response Object
$data = $text->getResponseData(); //parsed response object
$code = $e->getCode(); //nexmo error code
error_log($e->getMessage()); //nexmo error message
}

5
vendor/nexmo/client/phpcs.xml vendored Normal file
View File

@@ -0,0 +1,5 @@
<?xml version="1.0"?>
<ruleset name="PSR2">
<rule ref="PSR2"/>
<file>./src</file>
</ruleset>

View File

@@ -0,0 +1,59 @@
<?php
namespace Nexmo\Account;
use ArrayAccess;
use Nexmo\Client\Exception\Exception;
use Nexmo\Entity\JsonSerializableInterface;
use Nexmo\Entity\JsonUnserializableInterface;
class Balance implements JsonSerializableInterface, JsonUnserializableInterface, ArrayAccess {
public function __construct($balance, $autoReload)
{
$this->data['balance'] = $balance;
$this->data['auto_reload'] = $autoReload;
}
public function getBalance()
{
return $this['balance'];
}
public function getAutoReload()
{
return $this['auto_reload'];
}
public function jsonUnserialize(array $json)
{
$this->data = [
'balance' => $json['value'],
'auto_reload' => $json['autoReload']
];
}
function jsonSerialize()
{
return $this->data;
}
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
public function offsetGet($offset)
{
return $this->data[$offset];
}
public function offsetSet($offset, $value)
{
throw new Exception('Balance is read only');
}
public function offsetUnset($offset)
{
throw new Exception('Balance is read only');
}
}

View File

@@ -0,0 +1,228 @@
<?php
namespace Nexmo\Account;
use Nexmo\ApiErrorHandler;
use Nexmo\Client\ClientAwareInterface;
use Nexmo\Client\ClientAwareTrait;
use Nexmo\Network;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Request;
use Nexmo\Client\Exception;
class Client implements ClientAwareInterface
{
use ClientAwareTrait;
public function getPrefixPricing($prefix)
{
$queryString = http_build_query([
'prefix' => $prefix
]);
$request = new Request(
$this->getClient()->getRestUrl() . '/account/get-prefix-pricing/outbound?'.$queryString,
'GET',
'php://temp'
);
$response = $this->client->send($request);
$rawBody = $response->getBody()->getContents();
$body = json_decode($rawBody, true);
$codeCategory = (int) ($response->getStatusCode()/100);
if ($codeCategory != 2) {
if ($codeCategory == 4) {
throw new Exception\Request($body['error-code-label']);
}else if ($codeCategory == 5) {
throw new Exception\Server($body['error-code-label']);
}
}
if ($body['count'] == 0) {
return [];
}
// Multiple countries can match each prefix
$prices = [];
foreach ($body['prices'] as $p) {
$prefixPrice = new PrefixPrice();
$prefixPrice->jsonUnserialize($p);
$prices[] = $prefixPrice;
}
return $prices;
}
public function getSmsPrice($country)
{
$body = $this->makePricingRequest($country, 'sms');
$smsPrice = new SmsPrice();
$smsPrice->jsonUnserialize($body);
return $smsPrice;
}
public function getVoicePrice($country)
{
$body = $this->makePricingRequest($country, 'voice');
$voicePrice = new VoicePrice();
$voicePrice->jsonUnserialize($body);
return $voicePrice;
}
protected function makePricingRequest($country, $pricingType)
{
$queryString = http_build_query([
'country' => $country
]);
$request = new Request(
$this->getClient()->getRestUrl() . '/account/get-pricing/outbound/'.$pricingType.'?'.$queryString,
'GET',
'php://temp'
);
$response = $this->client->send($request);
$rawBody = $response->getBody()->getContents();
if ($rawBody === '') {
throw new Exception\Server('No results found');
}
return json_decode($rawBody, true);
}
public function getBalance()
{
$request = new Request(
$this->getClient()->getRestUrl() . '/account/get-balance',
'GET',
'php://temp'
);
$response = $this->client->send($request);
$rawBody = $response->getBody()->getContents();
if ($rawBody === '') {
throw new Exception\Server('No results found');
}
$body = json_decode($rawBody, true);
$balance = new Balance($body['value'], $body['autoReload']);
return $balance;
}
public function topUp($trx)
{
$body = [
'trx' => $trx
];
$request = new Request(
$this->getClient()->getRestUrl() . '/account/top-up'
,'POST'
, 'php://temp'
, ['content-type' => 'application/x-www-form-urlencoded']
);
$request->getBody()->write(http_build_query($body));
$response = $this->client->send($request);
if($response->getStatusCode() != '200'){
throw $this->getException($response);
}
}
public function listSecrets($apiKey)
{
$body = $this->get( $this->getClient()->getApiUrl() . '/accounts/'.$apiKey.'/secrets');
return SecretCollection::fromApi($body);
}
public function getSecret($apiKey, $secretId)
{
$body = $this->get( $this->getClient()->getApiUrl() . '/accounts/'.$apiKey.'/secrets/'. $secretId);
return Secret::fromApi($body);
}
public function createSecret($apiKey, $newSecret)
{
$body = [
'secret' => $newSecret
];
$request = new Request(
$this->getClient()->getApiUrl() . '/accounts/'.$apiKey.'/secrets'
,'POST'
, 'php://temp'
, ['content-type' => 'application/json']
);
$request->getBody()->write(json_encode($body));
$response = $this->client->send($request);
$rawBody = $response->getBody()->getContents();
$responseBody = json_decode($rawBody, true);
ApiErrorHandler::check($responseBody, $response->getStatusCode());
return Secret::fromApi($responseBody);
}
public function deleteSecret($apiKey, $secretId)
{
$request = new Request(
$this->getClient()->getApiUrl() . '/accounts/'.$apiKey.'/secrets/'. $secretId
,'DELETE'
, 'php://temp'
, ['content-type' => 'application/json']
);
$response = $this->client->send($request);
$rawBody = $response->getBody()->getContents();
$body = json_decode($rawBody, true);
// This will throw an exception on any error
ApiErrorHandler::check($body, $response->getStatusCode());
// This returns a 204, so no response body
}
protected function get($url) {
$request = new Request(
$url
,'GET'
, 'php://temp'
, ['content-type' => 'application/json']
);
$response = $this->client->send($request);
$rawBody = $response->getBody()->getContents();
$body = json_decode($rawBody, true);
// This will throw an exception on any error
ApiErrorHandler::check($body, $response->getStatusCode());
return $body;
}
protected function getException(ResponseInterface $response, $application = null)
{
$body = json_decode($response->getBody()->getContents(), true);
$status = $response->getStatusCode();
if($status >= 400 AND $status < 500) {
$e = new Exception\Request($body['error_title'], $status);
} elseif($status >= 500 AND $status < 600) {
$e = new Exception\Server($body['error_title'], $status);
} else {
$e = new Exception\Exception('Unexpected HTTP Status Code');
}
return $e;
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Nexmo\Account;
class PrefixPrice extends Price {
protected $priceMethod = 'getPrefixPrice';
public function getCurrency()
{
throw new Exception('Currency is unavailable from this endpoint');
}
}

View File

@@ -0,0 +1,144 @@
<?php
namespace Nexmo\Account;
use ArrayAccess;
use Nexmo\Client\Exception\Exception;
use Nexmo\Network;
use Nexmo\Entity\EntityInterface;
use Nexmo\Entity\JsonSerializableInterface;
use Nexmo\Entity\JsonResponseTrait;
use Nexmo\Entity\JsonSerializableTrait;
use Nexmo\Entity\NoRequestResponseTrait;
use Nexmo\Entity\JsonUnserializableInterface;
abstract class Price implements EntityInterface, JsonSerializableInterface, JsonUnserializableInterface, ArrayAccess {
use JsonSerializableTrait;
use NoRequestResponseTrait;
use JsonResponseTrait;
protected $data = [];
public function getCountryCode()
{
return $this['country_code'];
}
public function getCountryDisplayName()
{
return $this['country_display_name'];
}
public function getCountryName()
{
return $this['country_name'];
}
public function getDialingPrefix()
{
return $this['dialing_prefix'];
}
public function getDefaultPrice()
{
if (isset($this['default_price'])) {
return $this['default_price'];
}
return $this['mt'];
}
public function getCurrency()
{
return $this['currency'];
}
public function getNetworks()
{
return $this['networks'];
}
public function getPriceForNetwork($networkCode)
{
$networks = $this->getNetworks();
if (isset($networks[$networkCode]))
{
return $networks[$networkCode]->{$this->priceMethod}();
}
return $this->getDefaultPrice();
}
public function jsonUnserialize(array $json)
{
// Convert CamelCase to snake_case as that's how we use array access in every other object
$data = [];
foreach ($json as $k => $v){
$k = ltrim(strtolower(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '_$0', $k)), '_');
// PrefixPrice fixes
if ($k == 'country') {
$k = 'country_code';
}
if ($k == 'name') {
$data['country_display_name'] = $v;
$data['country_name'] = $v;
}
if ($k == 'prefix') {
$k = 'dialing_prefix';
}
$data[$k] = $v;
}
// Create objects for all the nested networks too
$networks = [];
if (isset($json['networks'])) {
foreach ($json['networks'] as $n){
if (isset($n['code'])) {
$n['networkCode'] = $n['code'];
unset ($n['code']);
}
if (isset($n['network'])) {
$n['networkName'] = $n['network'];
unset ($n['network']);
}
$network = new Network($n['networkCode'], $n['networkName']);
$network->jsonUnserialize($n);
$networks[$network->getCode()] = $network;
}
}
$data['networks'] = $networks;
$this->data = $data;
}
function jsonSerialize()
{
return $this->data;
}
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
public function offsetGet($offset)
{
return $this->data[$offset];
}
public function offsetSet($offset, $value)
{
throw new Exception('Price is read only');
}
public function offsetUnset($offset)
{
throw new Exception('Price is read only');
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Nexmo\Account;
use Nexmo\InvalidResponseException;
class Secret implements \ArrayAccess {
protected $data;
public function __construct($data) {
$this->data = $data;
}
public function getId() {
return $this['id'];
}
public function getCreatedAt() {
return $this['created_at'];
}
public function getLinks() {
return $this['_links'];
}
public static function fromApi($data) {
if (!isset($data['id'])) {
throw new InvalidResponseException("Missing key: 'id");
}
if (!isset($data['created_at'])) {
throw new InvalidResponseException("Missing key: 'created_at");
}
return new self($data);
}
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
public function offsetGet($offset)
{
return $this->data[$offset];
}
public function offsetSet($offset, $value)
{
throw new \Exception('Secret::offsetSet is not implemented');
}
public function offsetUnset($offset)
{
throw new \Exception('Secret::offsetUnset is not implemented');
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Nexmo\Account;
class SecretCollection implements \ArrayAccess {
protected $data;
public function __construct($secrets, $links) {
$this->data = [
'secrets' => $secrets,
'_links' => $links
];
}
public function getSecrets() {
return $this['secrets'];
}
public function getLinks() {
return $this['_links'];
}
public static function fromApi($data) {
$secrets = [];
foreach ($data['_embedded']['secrets'] as $s) {
$secrets[] = Secret::fromApi($s);
}
return new self($secrets, $data['_links']);
}
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
public function offsetGet($offset)
{
return $this->data[$offset];
}
public function offsetSet($offset, $value)
{
throw new \Exception('SecretCollection::offsetSet is not implemented');
}
public function offsetUnset($offset)
{
throw new \Exception('SecretCollection::offsetUnset is not implemented');
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Nexmo\Account;
class SmsPrice extends Price {
protected $priceMethod = 'getOutboundSmsPrice';
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Nexmo\Account;
class VoicePrice extends Price {
protected $priceMethod = 'getOutboundVoicePrice';
}

View File

@@ -0,0 +1,39 @@
<?php
namespace Nexmo;
use Nexmo\Client\Exception;
class ApiErrorHandler {
public static function check($body, $statusCode) {
$statusCodeType = (int) ($statusCode / 100);
// If it's ok, we can continue
if ($statusCodeType == 2) {
return;
}
// Build up our error message
$errorMessage = $body['title'];
if (isset($body['detail']) && $body['detail']) {
$errorMessage .= ': '.$body['detail'].'.';
} else {
$errorMessage .= '.';
}
$errorMessage .= ' See '.$body['type'].' for more information';
// If it's a 5xx error, throw an exception
if ($statusCodeType == 5) {
throw new Exception\Server($errorMessage, $statusCode);
}
// Otherwise it's a 4xx, so we may have more context for the user
// If it's a validation error, share that information
if (isset($body['invalid_parameters'])) {
throw new Exception\Validation($errorMessage, $statusCode, null, $body['invalid_parameters']);
}
// Otherwise throw a normal error
throw new Exception\Request($errorMessage, $statusCode);
}
}

View File

@@ -0,0 +1,121 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Application;
use Nexmo\Entity\JsonUnserializableInterface;
use Nexmo\Entity\EntityInterface;
use Nexmo\Entity\JsonResponseTrait;
use Nexmo\Entity\JsonSerializableTrait;
use Nexmo\Entity\Psr7Trait;
class Application implements EntityInterface, \JsonSerializable, JsonUnserializableInterface
{
use JsonSerializableTrait;
use Psr7Trait;
use JsonResponseTrait;
protected $voiceConfig;
protected $name;
protected $keys = [];
protected $id;
public function __construct($id = null)
{
$this->id = $id;
}
public function getId()
{
return $this->id;
}
public function setVoiceConfig(VoiceConfig $config)
{
$this->voiceConfig = $config;
return $this;
}
/**
* @return VoiceConfig
*/
public function getVoiceConfig()
{
if(!isset($this->voiceConfig)){
$this->setVoiceConfig(new VoiceConfig());
$data = $this->getResponseData();
if(isset($data['voice']) AND isset($data['voice']['webhooks'])){
foreach($data['voice']['webhooks'] as $webhook){
$this->voiceConfig->setWebhook($webhook['endpoint_type'], $webhook['endpoint'], $webhook['http_method']);
}
}
}
return $this->voiceConfig;
}
public function getPublicKey()
{
if(isset($this->keys['public_key'])){
return $this->keys['public_key'];
}
}
public function getPrivateKey()
{
if(isset($this->keys['private_key'])){
return $this->keys['private_key'];
}
}
public function setName($name)
{
$this->name = $name;
return $this;
}
public function getName()
{
return $this->name;
}
public function jsonUnserialize(array $json)
{
$this->name = $json['name'];
$this->id = $json['id'];
$this->keys = $json['keys'];
//todo: make voice hydrate-able
$this->voiceConfig = new VoiceConfig();
if(isset($json['voice']) AND isset($json['voice']['webhooks'])){
foreach($json['voice']['webhooks'] as $webhook){
$this->voiceConfig->setWebhook($webhook['endpoint_type'], new Webhook($webhook['endpoint'], $webhook['http_method']));
}
}
}
public function jsonSerialize()
{
return [
'name' => $this->getName(),
//currently, the request data does not match the response data
'event_url' => (string) $this->getVoiceConfig()->getWebhook(VoiceConfig::EVENT),
'answer_url' => (string) $this->getVoiceConfig()->getWebhook(VoiceConfig::ANSWER),
'type' => 'voice' //currently the only type
];
}
public function __toString()
{
return (string) $this->getId();
}
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Application;
use Nexmo\Entity\EntityInterface;
interface ApplicationInterface extends EntityInterface
{
public function getId();
}

View File

@@ -0,0 +1,207 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Application;
use Nexmo\Client\ClientAwareInterface;
use Nexmo\Client\ClientAwareTrait;
use Nexmo\Entity\CollectionInterface;
use Nexmo\Entity\CollectionTrait;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Request;
use Nexmo\Client\Exception;
class Client implements ClientAwareInterface, CollectionInterface
{
use ClientAwareTrait;
use CollectionTrait;
public static function getCollectionName()
{
return 'applications';
}
public static function getCollectionPath()
{
return '/v1/' . self::getCollectionName();
}
public function hydrateEntity($data, $id)
{
$application = new Application($id);
$application->jsonUnserialize($data);
return $application;
}
public function get($application)
{
if(!($application instanceof Application)){
$application = new Application($application);
}
$request = new Request(
$this->getClient()->getApiUrl() . $this->getCollectionPath() . '/' . $application->getId()
,'GET'
);
$application->setRequest($request);
$response = $this->client->send($request);
$application->setResponse($response);
if($response->getStatusCode() != '200'){
throw $this->getException($response, $application);
}
return $application;
}
public function create($application)
{
return $this->post($application);
}
public function post($application)
{
if(!($application instanceof Application)){
$application = $this->createFromArray($application);
}
$body = $application->getRequestData(false);
$request = new Request(
$this->getClient()->getApiUrl() . $this->getCollectionPath()
,'POST',
'php://temp',
['content-type' => 'application/json']
);
$request->getBody()->write(json_encode($body));
$application->setRequest($request);
$response = $this->client->send($request);
$application->setResponse($response);
if($response->getStatusCode() != '201'){
throw $this->getException($response, $application);
}
return $application;
}
public function update($application, $id = null)
{
return $this->put($application, $id);
}
public function put($application, $id = null)
{
if(!($application instanceof Application)){
$application = $this->createFromArray($application);
}
if(is_null($id)){
$id = $application->getId();
}
$body = $application->getRequestData(false);
$request = new Request(
$this->getClient()->getApiUrl() . $this->getCollectionPath() . '/' . $id,
'PUT',
'php://temp',
['content-type' => 'application/json']
);
$request->getBody()->write(json_encode($body));
$application->setRequest($request);
$response = $this->client->send($request);
$application->setResponse($response);
if($response->getStatusCode() != '200'){
throw $this->getException($response, $application);
}
return $application;
}
public function delete($application)
{
if(($application instanceof Application)){
$id = $application->getId();
} else {
$id = $application;
}
$request = new Request(
$this->getClient()->getApiUrl(). $this->getCollectionPath() . '/' . $id
,'DELETE'
);
if($application instanceof Application){
$application->setRequest($request);
}
$response = $this->client->send($request);
if($application instanceof Application){
$application->setResponse($response);
}
if($response->getStatusCode() != '204'){
throw $this->getException($response, $application);
}
return true;
}
protected function getException(ResponseInterface $response, $application = null)
{
$body = json_decode($response->getBody()->getContents(), true);
$status = $response->getStatusCode();
if($status >= 400 AND $status < 500) {
$e = new Exception\Request($body['error_title'], $status);
} elseif($status >= 500 AND $status < 600) {
$e = new Exception\Server($body['error_title'], $status);
} else {
$e = new Exception\Exception('Unexpected HTTP Status Code');
throw $e;
}
//todo use interfaces here
if(($application instanceof Application) AND (($e instanceof Exception\Request) OR ($e instanceof Exception\Server))){
$e->setEntity($application);
}
return $e;
}
protected function createFromArray($array)
{
if(!is_array($array)){
throw new \RuntimeException('application must implement `' . ApplicationInterface::class . '` or be an array`');
}
foreach(['name',] as $param){
if(!isset($array[$param])){
throw new \InvalidArgumentException('missing expected key `' . $param . '`');
}
}
$application = new Application();
$application->setName($array['name']);
foreach(['event', 'answer'] as $type){
if(isset($array[$type . '_url'])){
$method = isset($array[$type . '_method']) ? $array[$type . '_method'] : null;
$application->getVoiceConfig()->setWebhook($type . '_url', new Webhook($array[$type . '_url'], $method));
}
}
return $application;
}
}

View File

@@ -0,0 +1,39 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Application;
use Nexmo\Entity\FilterInterface;
/**
* Simple value object for application filtering.
*/
class Filter implements FilterInterface
{
const FORMAT = 'Y:m:d:H:i:s';
protected $start;
protected $end;
public function __construct(\DateTime $start, \DateTime $end)
{
if($start < $end){
$this->start = $start;
$this->end = $end;
} else {
$this->start = $end;
$this->end = $start;
}
}
public function getQuery()
{
return [
'date' => $this->start->format(self::FORMAT) . '-' . $this->end->format(self::FORMAT)
];
}
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Application;
class VoiceConfig
{
const EVENT = 'event_url';
const ANSWER = 'answer_url';
protected $webhooks = [];
public function setWebhook($type, $url, $method = null)
{
if(!($url instanceof Webhook)){
$url = new Webhook($url, $method);
}
$this->webhooks[$type] = $url;
return $this;
}
public function getWebhook($type)
{
if(isset($this->webhooks[$type])){
return $this->webhooks[$type];
}
}
}

View File

@@ -0,0 +1,46 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Application;
class Webhook
{
const METHOD_POST = 'POST';
const METHOD_GET = 'GET';
/**
* @var string;
*/
protected $method;
/**
* @var string
*/
protected $url;
public function __construct($url, $method = self::METHOD_POST)
{
$this->url = $url;
$this->method = $method;
}
public function getMethod()
{
return $this->method;
}
public function getUrl()
{
return $this->url;
}
public function __toString()
{
return $this->getUrl();
}
}

301
vendor/nexmo/client/src/Call/Call.php vendored Normal file
View File

@@ -0,0 +1,301 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Call;
use Nexmo\Client\ClientAwareInterface;
use Nexmo\Client\ClientAwareTrait;
use Nexmo\Conversations\Conversation;
use Nexmo\Entity\EntityInterface;
use Nexmo\Entity\JsonResponseTrait;
use Nexmo\Entity\JsonSerializableTrait;
use Nexmo\Entity\JsonUnserializableInterface;
use Nexmo\Entity\NoRequestResponseTrait;
use Psr\Http\Message\ResponseInterface;
use Nexmo\Client\Exception;
use Zend\Diactoros\Request;
/**
* Class Call
*
* @property \Nexmo\Call\Stream $stream
* @property \Nexmo\Call\Talk $talk
* @property \Nexmo\Call\Dtmf $dtmf
*
* @method \Nexmo\Call\Stream stream()
* @method \Nexmo\Call\Talk talk()
* @method \Nexmo\Call\Dtmf dtmf()
*/
class Call implements EntityInterface, \JsonSerializable, JsonUnserializableInterface, ClientAwareInterface
{
use NoRequestResponseTrait;
use JsonSerializableTrait;
use JsonResponseTrait;
use ClientAwareTrait;
const WEBHOOK_ANSWER = 'answer';
const WEBHOOK_EVENT = 'event';
const TIMER_LENGTH = 'length';
const TIMER_RINGING = 'ringing';
const TIMEOUT_MACHINE = 'machine';
protected $id;
protected $to;
protected $from;
/**
* @var Webhook[]
*/
protected $webhooks = [];
protected $data = [];
protected $subresources = [];
public function __construct($id = null)
{
$this->id = $id;
}
public function get()
{
$request = new Request(
$this->getClient()->getApiUrl() . Collection::getCollectionPath() . '/' . $this->getId()
,'GET'
);
$response = $this->getClient()->send($request);
if($response->getStatusCode() != '200'){
throw $this->getException($response);
}
$data = json_decode($response->getBody()->getContents(), true);
$this->jsonUnserialize($data);
return $this;
}
protected function getException(ResponseInterface $response)
{
$body = json_decode($response->getBody()->getContents(), true);
$status = $response->getStatusCode();
if($status >= 400 AND $status < 500) {
$e = new Exception\Request($body['error_title'], $status);
} elseif($status >= 500 AND $status < 600) {
$e = new Exception\Server($body['error_title'], $status);
} else {
$e = new Exception\Exception('Unexpected HTTP Status Code');
}
return $e;
}
public function put($payload)
{
$request = new Request(
$this->getClient()->getApiUrl() . Collection::getCollectionPath() . '/' . $this->getId()
,'PUT',
'php://temp',
['content-type' => 'application/json']
);
$request->getBody()->write(json_encode($payload));
$response = $this->client->send($request);
$responseCode = $response->getStatusCode();
if($responseCode != '200' && $responseCode != '204'){
throw $this->getException($response);
}
return $this;
}
public function getId()
{
return $this->id;
}
public function setTo($endpoint)
{
if(!($endpoint instanceof Endpoint)){
$endpoint = new Endpoint($endpoint);
}
$this->to = $endpoint;
return $this;
}
/**
* @return Endpoint
*/
public function getTo()
{
if($this->lazyLoad()){
return new Endpoint($this->data['to']['number'], $this->data['to']['type']);
}
return $this->to;
}
public function setFrom($endpoint)
{
if(!($endpoint instanceof Endpoint)){
$endpoint = new Endpoint($endpoint);
}
$this->from = $endpoint;
return $this;
}
/**
* @return Endpoint
*/
public function getFrom()
{
if($this->lazyLoad()){
return new Endpoint($this->data['from']['number'], $this->data['from']['type']);
}
return $this->from;
}
public function setWebhook($type, $url = null, $method = null)
{
if($type instanceof Webhook){
$this->webhooks[$type->getType()] = $type;
return $this;
}
if(is_null($url)){
throw new \InvalidArgumentException('must provide `Nexmo\Call\Webhook` object, or a type and url: missing url' );
}
$this->webhooks[$type] = new Webhook($type, $url, $method);
return $this;
}
public function setTimer($type, $length)
{
$this->data[$type . '_timer'] = $length;
}
public function setTimeout($type, $length)
{
$this->data[$type . '_timeout'] = $length;
}
public function getStatus()
{
if($this->lazyLoad()){
return $this->data['status'];
}
}
public function getDirection()
{
if($this->lazyLoad()){
return $this->data['direction'];
}
}
public function getConversation()
{
if($this->lazyLoad()){
return new Conversation($this->data['conversation_uuid']);
}
}
/**
* Returns true if the resource data is loaded.
*
* Will attempt to load the data if it's not already.
*
* @return bool
*/
protected function lazyLoad()
{
if(!empty($this->data)){
return true;
}
if(isset($this->id)){
$this->get($this);
return true;
}
return false;
}
public function __get($name)
{
switch($name){
case 'stream':
case 'talk':
case 'dtmf':
return $this->lazySubresource(ucfirst($name));
default:
throw new \RuntimeException('property does not exist: ' . $name);
}
}
public function __call($name, $arguments)
{
switch($name){
case 'stream':
case 'talk':
case 'dtmf':
$entity = $this->lazySubresource(ucfirst($name));
return call_user_func_array($entity, $arguments);
default:
throw new \RuntimeException('method does not exist: ' . $name);
}
}
protected function lazySubresource($type)
{
if(!isset($this->subresources[$type])){
$class = 'Nexmo\Call\\' . $type;
$instance = new $class($this->getId());
$instance->setClient($this->getClient());
$this->subresources[$type] = $instance;
}
return $this->subresources[$type];
}
public function jsonSerialize()
{
$data = $this->data;
if(isset($this->to)){
$data['to'] = [$this->to->jsonSerialize()];
}
if(isset($this->from)){
$data['from'] = $this->from->jsonSerialize();
}
foreach($this->webhooks as $webhook){
$data = array_merge($data, $webhook->jsonSerialize());
}
return $data;
}
public function jsonUnserialize(array $json)
{
$this->data = $json;
$this->id = $json['uuid'];
}
}

View File

@@ -0,0 +1,203 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Call;
use Nexmo\Client\ClientAwareInterface;
use Nexmo\Client\ClientAwareTrait;
use Nexmo\Conversations\Conversation;
use Nexmo\Entity\CollectionInterface;
use Nexmo\Entity\CollectionTrait;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Request;
use Nexmo\Client\Exception;
class Collection implements ClientAwareInterface, CollectionInterface, \ArrayAccess
{
use ClientAwareTrait;
use CollectionTrait;
public static function getCollectionName()
{
return 'calls';
}
public static function getCollectionPath()
{
return '/v1/' . self::getCollectionName();
}
public function hydrateEntity($data, $idOrCall)
{
if(!($idOrCall instanceof Call)){
$idOrCall = new Call($idOrCall);
}
$idOrCall->setClient($this->getClient());
$idOrCall->jsonUnserialize($data);
return $idOrCall;
}
/**
* @param null $callOrFilter
* @return $this|Call
*/
public function __invoke(Filter $filter = null)
{
if(!is_null($filter)){
$this->setFilter($filter);
}
return $this;
}
public function create($call)
{
return $this->post($call);
}
public function put($payload, $idOrCall)
{
if(!($idOrCall instanceof Call)){
$idOrCall = new Call($idOrCall);
}
$idOrCall->setClient($this->getClient());
$idOrCall->put($payload);
return $idOrCall;
}
public function delete($call = null, $type)
{
if(is_object($call) AND is_callable([$call, 'getId'])){
$call = $call->getId();
}
if(!($call instanceof Call)){
$call = new Call($call);
}
$request = new Request(
$this->getClient()->getApiUrl() . $this->getCollectionPath() . '/' . $call->getId() . '/' . $type
,'DELETE'
);
$response = $this->client->send($request);
if($response->getStatusCode() != '204'){
throw $this->getException($response);
}
return $call;
}
public function post($call)
{
if($call instanceof Call){
$body = $call->getRequestData();
} else {
$body = $call;
}
$request = new Request(
$this->getClient()->getApiUrl() . $this->getCollectionPath()
,'POST',
'php://temp',
['content-type' => 'application/json']
);
$request->getBody()->write(json_encode($body));
$response = $this->client->send($request);
if($response->getStatusCode() != '201'){
throw $this->getException($response);
}
$body = json_decode($response->getBody()->getContents(), true);
$call = new Call($body['uuid']);
$call->jsonUnserialize($body);
$call->setClient($this->getClient());
return $call;
}
public function get($call)
{
if(!($call instanceof Call)){
$call = new Call($call);
}
$call->setClient($this->getClient());
$call->get();
return $call;
}
protected function getException(ResponseInterface $response)
{
$body = json_decode($response->getBody()->getContents(), true);
$status = $response->getStatusCode();
// Error responses aren't consistent. Some are generated within the
// proxy and some are generated within voice itself. This handles
// both cases
// This message isn't very useful, but we shouldn't ever see it
$errorTitle = 'Unexpected error';
if (isset($body['title'])) {
$errorTitle = $body['title'];
}
if (isset($body['error_title'])) {
$errorTitle = $body['error_title'];
}
if($status >= 400 AND $status < 500) {
$e = new Exception\Request($errorTitle, $status);
} elseif($status >= 500 AND $status < 600) {
$e = new Exception\Server($errorTitle, $status);
} else {
$e = new Exception\Exception('Unexpected HTTP Status Code');
throw $e;
}
return $e;
}
public function offsetExists($offset)
{
//todo: validate form of id
return true;
}
/**
* @param mixed $call
* @return Call
*/
public function offsetGet($call)
{
if(!($call instanceof Call)){
$call = new Call($call);
}
$call->setClient($this->getClient());
return $call;
}
public function offsetSet($offset, $value)
{
throw new \RuntimeException('can not set collection properties');
}
public function offsetUnset($offset)
{
throw new \RuntimeException('can not unset collection properties');
}
}

139
vendor/nexmo/client/src/Call/Dtmf.php vendored Normal file
View File

@@ -0,0 +1,139 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2017 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Call;
use Nexmo\Call\Collection;
use Nexmo\Client\ClientAwareInterface;
use Nexmo\Client\ClientAwareTrait;
use Nexmo\Entity\JsonSerializableInterface;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Request;
use Nexmo\Client\Exception;
/**
* Lightweight resource, only has put / delete.
*/
class Dtmf implements JsonSerializableInterface, ClientAwareInterface, \ArrayAccess
{
use ClientAwareTrait;
protected $id;
protected $data = [];
protected $params= [
'digits'
];
public function __construct($id = null)
{
$this->id = $id;
}
public function __invoke(self $entity = null)
{
if(is_null($entity)){
return $this;
}
return $this->put($entity);
}
public function getId()
{
return $this->id;
}
public function setDigits($digits)
{
$this->data['digits'] = (string) $digits;
}
public function put($dtmf = null)
{
if(!$dtmf){
$dtmf = $this;
}
$request = new Request(
$this->getClient()->getApiUrl() . Collection::getCollectionPath() . '/' . $this->getId() . '/dtmf',
'PUT',
'php://temp',
['content-type' => 'application/json']
);
$request->getBody()->write(json_encode($dtmf));
$response = $this->client->send($request);
return $this->parseEventResponse($response);
}
protected function parseEventResponse(ResponseInterface $response)
{
if($response->getStatusCode() != '200'){
throw $this->getException($response);
}
$json = json_decode($response->getBody()->getContents(), true);
if(!$json){
throw new Exception\Exception('Unexpected Response Body Format');
}
return new Event($json);
}
protected function getException(ResponseInterface $response)
{
$body = json_decode($response->getBody()->getContents(), true);
$status = $response->getStatusCode();
if($status >= 400 AND $status < 500) {
$e = new Exception\Request($body['error_title'], $status);
} elseif($status >= 500 AND $status < 600) {
$e = new Exception\Server($body['error_title'], $status);
} else {
$e = new Exception\Exception('Unexpected HTTP Status Code');
throw $e;
}
return $e;
}
function jsonSerialize()
{
return $this->data;
}
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
public function offsetGet($offset)
{
return $this->data[$offset];
}
public function offsetSet($offset, $value)
{
if(!in_array($offset, $this->params)){
throw new \RuntimeException('invalid parameter: ' . $offset);
}
$this->data[$offset] = $value;
}
public function offsetUnset($offset)
{
if(!in_array($offset, $this->params)){
throw new \RuntimeException('invalid parameter: ' . $offset);
}
unset($this->data[$offset]);
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2017 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Call;
class Earmuff implements \JsonSerializable
{
function jsonSerialize()
{
return [
'action' => 'earmuff'
];
}
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Call;
/**
* Class Endpoint
* Represents a call destination / origin.
*
* TODO: Try to unify this and other (message, etc) endpoint identifiers.
*/
class Endpoint implements \JsonSerializable
{
const PHONE = 'phone';
protected $id;
protected $type;
protected $additional;
public function __construct($id, $type = self::PHONE, $additional = [])
{
$this->id = $id;
$this->type = $type;
$this->additional = $additional;
}
public function getType()
{
return $this->type;
}
public function getId()
{
return $this->id;
}
public function set($property, $value)
{
$this->additional[$property] = $value;
return $this;
}
public function get($property)
{
if(isset($this->additional[$property])){
return $this->additional[$property];
}
}
public function getNumber()
{
if(!self::PHONE == $this->type){
throw new \RuntimeException('number not defined for this type');
}
return $this->getId();
}
public function __toString()
{
return (string) $this->getId();
}
function jsonSerialize()
{
switch($this->type){
case 'phone':
return array_merge(
$this->additional,
[
'type' => $this->type,
'number' => $this->id
]
);
default:
throw new \RuntimeException('unknown type: ' . $this->type);
}
}
}

53
vendor/nexmo/client/src/Call/Event.php vendored Normal file
View File

@@ -0,0 +1,53 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2017 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Call;
class Event implements \ArrayAccess
{
protected $data;
public function __construct($data)
{
if(!isset($data['uuid']) || !isset($data['message'])){
throw new \InvalidArgumentException('missing message or uuid');
}
$this->data = $data;
}
public function getId()
{
return $this->data['uuid'];
}
public function getMessage()
{
return $this->data['message'];
}
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
public function offsetGet($offset)
{
return $this->data[$offset];
}
public function offsetSet($offset, $value)
{
throw new \RuntimeException('can not set properties directly');
}
public function offsetUnset($offset)
{
throw new \RuntimeException('can not set properties directly');
}
}

81
vendor/nexmo/client/src/Call/Filter.php vendored Normal file
View File

@@ -0,0 +1,81 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Call;
use Nexmo\Conversations\Conversation;
use Nexmo\Entity\FilterInterface;
class Filter implements FilterInterface
{
protected $query = [];
public function getQuery()
{
return $this->query;
}
public function sortAscending()
{
return $this->setOrder('asc');
}
public function sortDescending()
{
return $this->setOrder('desc');
}
public function setStatus($status)
{
$this->query['status'] = (string) $status;
return $this;
}
public function setStart(\DateTime $start)
{
$start->setTimezone(new \DateTimeZone("UTC"));
$this->query['date_start'] = $start->format('Y-m-d\TH:i:s\Z');
return $this;
}
public function setEnd(\DateTime $end)
{
$end->setTimezone(new \DateTimeZone("UTC"));
$this->query['date_end'] = $end->format('Y-m-d\TH:i:s\Z');
return $this;
}
public function setSize($size)
{
$this->query['page_size'] = (int) $size;
return $this;
}
public function setIndex($index)
{
$this->query['record_index'] = (int) $index;
return $this;
}
public function setOrder($order)
{
$this->query['order'] = (string) $order;
return $this;
}
public function setConversation($conversation)
{
if($conversation instanceof Conversation){
$conversation = $conversation->getId();
}
$this->query['conversation_uuid'] = $conversation;
return $this;
}
}

21
vendor/nexmo/client/src/Call/Hangup.php vendored Normal file
View File

@@ -0,0 +1,21 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2017 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Call;
class Hangup implements \JsonSerializable
{
function jsonSerialize()
{
return [
'action' => 'hangup'
];
}
}

20
vendor/nexmo/client/src/Call/Mute.php vendored Normal file
View File

@@ -0,0 +1,20 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2017 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Call;
class Mute implements \JsonSerializable
{
function jsonSerialize()
{
return [
'action' => 'mute'
];
}
}

127
vendor/nexmo/client/src/Call/Stream.php vendored Normal file
View File

@@ -0,0 +1,127 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2017 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Call;
use Nexmo\Call\Collection;
use Nexmo\Client\ClientAwareInterface;
use Nexmo\Client\ClientAwareTrait;
use Nexmo\Entity\JsonSerializableInterface;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Request;
use Nexmo\Client\Exception;
/**
* Lightweight resource, only has put / delete.
*/
class Stream implements JsonSerializableInterface, ClientAwareInterface
{
use ClientAwareTrait;
protected $id;
protected $data = [];
public function __construct($id = null)
{
$this->id = $id;
}
public function __invoke(Stream $stream = null)
{
if(is_null($stream)){
return $this;
}
return $this->put($stream);
}
public function getId()
{
return $this->id;
}
public function setUrl($url)
{
if(!is_array($url)){
$url = array($url);
}
$this->data['stream_url'] = $url;
}
public function setLoop($times)
{
$this->data['loop'] = (int) $times;
}
public function put($stream = null)
{
if(!$stream){
$stream = $this;
}
$request = new Request(
$this->getClient()->getApiUrl() . Collection::getCollectionPath() . '/' . $this->getId() . '/stream',
'PUT',
'php://temp',
['content-type' => 'application/json']
);
$request->getBody()->write(json_encode($stream));
$response = $this->client->send($request);
return $this->parseEventResponse($response);
}
public function delete()
{
$request = new Request(
$this->getClient()->getApiUrl() . Collection::getCollectionPath() . '/' . $this->getId() . '/stream',
'DELETE'
);
$response = $this->client->send($request);
return $this->parseEventResponse($response);
}
protected function parseEventResponse(ResponseInterface $response)
{
if($response->getStatusCode() != '200'){
throw $this->getException($response);
}
$json = json_decode($response->getBody()->getContents(), true);
if(!$json){
throw new Exception\Exception('Unexpected Response Body Format');
}
return new Event($json);
}
protected function getException(ResponseInterface $response)
{
$body = json_decode($response->getBody()->getContents(), true);
$status = $response->getStatusCode();
if($status >= 400 AND $status < 500) {
$e = new Exception\Request($body['error_title'], $status);
} elseif($status >= 500 AND $status < 600) {
$e = new Exception\Server($body['error_title'], $status);
} else {
$e = new Exception\Exception('Unexpected HTTP Status Code');
throw $e;
}
return $e;
}
function jsonSerialize()
{
return $this->data;
}
}

162
vendor/nexmo/client/src/Call/Talk.php vendored Normal file
View File

@@ -0,0 +1,162 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2017 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Call;
use Nexmo\Call\Collection;
use Nexmo\Client\ClientAwareInterface;
use Nexmo\Client\ClientAwareTrait;
use Nexmo\Entity\JsonSerializableInterface;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Request;
use Nexmo\Client\Exception;
/**
* Lightweight resource, only has put / delete.
*/
class Talk implements JsonSerializableInterface, ClientAwareInterface, \ArrayAccess
{
use ClientAwareTrait;
protected $id;
protected $data = [];
protected $params= [
'text',
'voice_name',
'loop'
];
public function __construct($id = null)
{
$this->id = $id;
}
public function __invoke(self $entity = null)
{
if(is_null($entity)){
return $this;
}
return $this->put($entity);
}
public function getId()
{
return $this->id;
}
public function setText($text)
{
$this->data['text'] = (string) $text;
}
public function setVoiceName($name)
{
$this->data['voice_name'] = (string) $name;
}
public function setLoop($times)
{
$this->data['loop'] = (int) $times;
}
public function put($talk = null)
{
if(!$talk){
$talk = $this;
}
$request = new Request(
$this->getClient()->getApiUrl() . Collection::getCollectionPath() . '/' . $this->getId() . '/talk',
'PUT',
'php://temp',
['content-type' => 'application/json']
);
$request->getBody()->write(json_encode($talk));
$response = $this->client->send($request);
return $this->parseEventResponse($response);
}
public function delete()
{
$request = new Request(
$this->getClient()->getApiUrl() . Collection::getCollectionPath() . '/' . $this->getId() . '/talk',
'DELETE'
);
$response = $this->client->send($request);
return $this->parseEventResponse($response);
}
protected function parseEventResponse(ResponseInterface $response)
{
if($response->getStatusCode() != '200'){
throw $this->getException($response);
}
$json = json_decode($response->getBody()->getContents(), true);
if(!$json){
throw new Exception\Exception('Unexpected Response Body Format');
}
return new Event($json);
}
protected function getException(ResponseInterface $response)
{
$body = json_decode($response->getBody()->getContents(), true);
$status = $response->getStatusCode();
if($status >= 400 AND $status < 500) {
$e = new Exception\Request($body['error_title'], $status);
} elseif($status >= 500 AND $status < 600) {
$e = new Exception\Server($body['error_title'], $status);
} else {
$e = new Exception\Exception('Unexpected HTTP Status Code');
throw $e;
}
return $e;
}
function jsonSerialize()
{
return $this->data;
}
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
public function offsetGet($offset)
{
return $this->data[$offset];
}
public function offsetSet($offset, $value)
{
if(!in_array($offset, $this->params)){
throw new \RuntimeException('invalid parameter: ' . $offset);
}
$this->data[$offset] = $value;
}
public function offsetUnset($offset)
{
if(!in_array($offset, $this->params)){
throw new \RuntimeException('invalid parameter: ' . $offset);
}
unset($this->data[$offset]);
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2017 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Call;
class Transfer implements \JsonSerializable
{
protected $urls;
public function __construct($urls)
{
if(!is_array($urls)){
$urls = array($urls);
}
$this->urls = $urls;
}
function jsonSerialize()
{
return [
'action' => 'transfer',
'destination' => [
'type' => 'ncco',
'url' => $this->urls
]
];
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2017 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Call;
class Unearmuff implements \JsonSerializable
{
function jsonSerialize()
{
return [
'action' => 'unearmuff'
];
}
}

20
vendor/nexmo/client/src/Call/Unmute.php vendored Normal file
View File

@@ -0,0 +1,20 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2017 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Call;
class Unmute implements \JsonSerializable
{
function jsonSerialize()
{
return [
'action' => 'unmute'
];
}
}

View File

@@ -0,0 +1,55 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Call;
class Webhook implements \JsonSerializable
{
protected $urls;
protected $method;
protected $type;
public function __construct($type, $urls, $method = null)
{
if(!is_array($urls)){
$urls = [$urls];
}
$this->urls = $urls;
$this->type = $type;
$this->method = $method;
}
public function getType()
{
return $this->type;
}
public function add($url)
{
$this->urls[] = $url;
}
function jsonSerialize()
{
$data = [
$this->type . '_url' => $this->urls
];
if(isset($this->method)){
$data[$this->type . '_method'] = $this->method;
}
return $data;
}
}

510
vendor/nexmo/client/src/Client.php vendored Normal file
View File

@@ -0,0 +1,510 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo;
use Http\Client\HttpClient;
use Nexmo\Client\Credentials\Basic;
use Nexmo\Client\Credentials\Container;
use Nexmo\Client\Credentials\CredentialsInterface;
use Nexmo\Client\Credentials\Keypair;
use Nexmo\Client\Credentials\OAuth;
use Nexmo\Client\Credentials\SignatureSecret;
use Nexmo\Client\Exception\Exception;
use Nexmo\Client\Factory\FactoryInterface;
use Nexmo\Client\Factory\MapFactory;
use Nexmo\Client\Response\Response;
use Nexmo\Client\Signature;
use Nexmo\Entity\EntityInterface;
use Nexmo\Verify\Verification;
use Psr\Http\Message\RequestInterface;
use Zend\Diactoros\Uri;
use Zend\Diactoros\Request;
/**
* Nexmo API Client, allows access to the API from PHP.
*
* @property \Nexmo\Message\Client $message
* @property \Nexmo\Call\Collection|\Nexmo\Call\Call[] $calls
*
* @method \Nexmo\Message\Client message()
* @method \Nexmo\Verify\Client verify()
* @method \Nexmo\Application\Client applications()
* @method \Nexmo\Call\Collection calls()
* @method \Nexmo\Numbers\Client numbers()
*/
class Client
{
const VERSION = '1.2.0';
const BASE_API = 'https://api.nexmo.com';
const BASE_REST = 'https://rest.nexmo.com';
/**
* API Credentials
* @var CredentialsInterface
*/
protected $credentials;
/**
* Http Client
* @var HttpClient
*/
protected $client;
/**
* @var FactoryInterface
*/
protected $factory;
/**
* @var array
*/
protected $options = [];
/**
* Create a new API client using the provided credentials.
*/
public function __construct(CredentialsInterface $credentials, $options = array(), HttpClient $client = null)
{
if(is_null($client)){
$client = new \Http\Adapter\Guzzle6\Client();
}
$this->setHttpClient($client);
//make sure we know how to use the credentials
if(!($credentials instanceof Container) && !($credentials instanceof Basic) && !($credentials instanceof SignatureSecret) && !($credentials instanceof OAuth) && !($credentials instanceof Keypair)){
throw new \RuntimeException('unknown credentials type: ' . get_class($credentials));
}
$this->credentials = $credentials;
$this->options = $options;
// If they've provided an app name, validate it
if (isset($options['app'])) {
$this->validateAppOptions($options['app']);
}
// Set the default URLs. Keep the constants for
// backwards compatibility
$this->apiUrl = static::BASE_API;
$this->restUrl = static::BASE_REST;
// If they've provided alternative URLs, use that instead
// of the defaults
if (isset($options['base_rest_url'])) {
$this->restUrl = $options['base_rest_url'];
}
if (isset($options['base_api_url'])) {
$this->apiUrl = $options['base_api_url'];
}
$this->setFactory(new MapFactory([
'account' => 'Nexmo\Account\Client',
'insights' => 'Nexmo\Insights\Client',
'message' => 'Nexmo\Message\Client',
'verify' => 'Nexmo\Verify\Client',
'applications' => 'Nexmo\Application\Client',
'numbers' => 'Nexmo\Numbers\Client',
'calls' => 'Nexmo\Call\Collection',
'conversion' => 'Nexmo\Conversion\Client',
'conversation' => 'Nexmo\Conversations\Collection',
'user' => 'Nexmo\User\Collection',
'redact' => 'Nexmo\Redact\Client',
], $this));
}
public function getRestUrl() {
return $this->restUrl;
}
public function getApiUrl() {
return $this->apiUrl;
}
/**
* Set the Http Client to used to make API requests.
*
* This allows the default http client to be swapped out for a HTTPlug compatible
* replacement.
*
* @param HttpClient $client
* @return $this
*/
public function setHttpClient(HttpClient $client)
{
$this->client = $client;
return $this;
}
/**
* Get the Http Client used to make API requests.
*
* @return HttpClient
*/
public function getHttpClient()
{
return $this->client;
}
/**
* Set the factory used to create API specific clients.
*
* @param FactoryInterface $factory
* @return $this
*/
public function setFactory(FactoryInterface $factory)
{
$this->factory = $factory;
return $this;
}
/**
* @param RequestInterface $request
* @param Signature $signature
* @return RequestInterface
*/
public static function signRequest(RequestInterface $request, SignatureSecret $credentials)
{
switch($request->getHeaderLine('content-type')){
case 'application/json':
$body = $request->getBody();
$body->rewind();
$content = $body->getContents();
$params = json_decode($content, true);
$params['api_key'] = $credentials['api_key'];
$signature = new Signature($params, $credentials['signature_secret'], $credentials['signature_method']);
$body->rewind();
$body->write(json_encode($signature->getSignedParams()));
break;
case 'application/x-www-form-urlencoded':
$body = $request->getBody();
$body->rewind();
$content = $body->getContents();
$params = [];
parse_str($content, $params);
$params['api_key'] = $credentials['api_key'];
$signature = new Signature($params, $credentials['signature_secret'], $credentials['signature_method']);
$params = $signature->getSignedParams();
$body->rewind();
$body->write(http_build_query($params, null, '&'));
break;
default:
$query = [];
parse_str($request->getUri()->getQuery(), $query);
$query['api_key'] = $credentials['api_key'];
$signature = new Signature($query, $credentials['signature_secret'], $credentials['signature_method']);
$request = $request->withUri($request->getUri()->withQuery(http_build_query($signature->getSignedParams())));
break;
}
return $request;
}
public static function authRequest(RequestInterface $request, Basic $credentials)
{
switch($request->getHeaderLine('content-type')) {
case 'application/json':
if (static::requiresBasicAuth($request)) {
$c = $credentials->asArray();
$request = $request->withHeader('Authorization', 'Basic ' . base64_encode($c['api_key'] . ':' . $c['api_secret']));
} else if (static::requiresAuthInUrlNotBody($request)) {
$query = [];
parse_str($request->getUri()->getQuery(), $query);
$query = array_merge($query, $credentials->asArray());
$request = $request->withUri($request->getUri()->withQuery(http_build_query($query)));
} else {
$body = $request->getBody();
$body->rewind();
$content = $body->getContents();
$params = json_decode($content, true);
$params = array_merge($params, $credentials->asArray());
$body->rewind();
$body->write(json_encode($params));
}
break;
case 'application/x-www-form-urlencoded':
$body = $request->getBody();
$body->rewind();
$content = $body->getContents();
$params = [];
parse_str($content, $params);
$params = array_merge($params, $credentials->asArray());
$body->rewind();
$body->write(http_build_query($params, null, '&'));
break;
default:
$query = [];
parse_str($request->getUri()->getQuery(), $query);
$query = array_merge($query, $credentials->asArray());
$request = $request->withUri($request->getUri()->withQuery(http_build_query($query)));
break;
}
return $request;
}
/**
* @param array $claims
* @return \Lcobucci\JWT\Token
*/
public function generateJwt($claims = [])
{
if (method_exists($this->credentials, "generateJwt")) {
return $this->credentials->generateJwt($claims);
}
throw new Exception(get_class($this->credentials).' does not support JWT generation');
}
/**
* Takes a URL and a key=>value array to generate a GET PSR-7 request object
*
* @param string $url The URL to make a request to
* @param array $params Key=>Value array of data to use as the query string
* @return \Psr\Http\Message\ResponseInterface
*/
public function get($url, array $params = [])
{
$queryString = '?' . http_build_query($params);
$url = $url . $queryString;
$request = new Request(
$url,
'GET'
);
return $this->send($request);
}
/**
* Takes a URL and a key=>value array to generate a POST PSR-7 request object
*
* @param string $url The URL to make a request to
* @param array $params Key=>Value array of data to send
* @return \Psr\Http\Message\ResponseInterface
*/
public function post($url, array $params)
{
$request = new Request(
$url,
'POST',
'php://temp',
['content-type' => 'application/json']
);
$request->getBody()->write(json_encode($params));
return $this->send($request);
}
/**
* Takes a URL and a key=>value array to generate a POST PSR-7 request object
*
* @param string $url The URL to make a request to
* @param array $params Key=>Value array of data to send
* @return \Psr\Http\Message\ResponseInterface
*/
public function postUrlEncoded($url, array $params)
{
$request = new Request(
$url,
'POST',
'php://temp',
['content-type' => 'application/x-www-form-urlencoded']
);
$request->getBody()->write(http_build_query($params));
return $this->send($request);
}
/**
* Takes a URL and a key=>value array to generate a PUT PSR-7 request object
*
* @param string $url The URL to make a request to
* @param array $params Key=>Value array of data to send
* @return \Psr\Http\Message\ResponseInterface
*/
public function put($url, array $params)
{
$request = new Request(
$url,
'PUT',
'php://temp',
['content-type' => 'application/json']
);
$request->getBody()->write(json_encode($params));
return $this->send($request);
}
/**
* Takes a URL and a key=>value array to generate a DELETE PSR-7 request object
*
* @param string $url The URL to make a request to
* @return \Psr\Http\Message\ResponseInterface
*/
public function delete($url)
{
$request = new Request(
$url,
'DELETE'
);
return $this->send($request);
}
/**
* Wraps the HTTP Client, creates a new PSR-7 request adding authentication, signatures, etc.
*
* @param \Psr\Http\Message\RequestInterface $request
* @return \Psr\Http\Message\ResponseInterface
*/
public function send(\Psr\Http\Message\RequestInterface $request)
{
if($this->credentials instanceof Container) {
if ($this->needsKeypairAuthentication($request)) {
$request = $request->withHeader('Authorization', 'Bearer ' . $this->credentials->get(Keypair::class)->generateJwt());
} else {
$request = self::authRequest($request, $this->credentials->get(Basic::class));
}
} elseif($this->credentials instanceof Keypair){
$request = $request->withHeader('Authorization', 'Bearer ' . $this->credentials->generateJwt());
} elseif($this->credentials instanceof SignatureSecret){
$request = self::signRequest($request, $this->credentials);
} elseif($this->credentials instanceof Basic){
$request = self::authRequest($request, $this->credentials);
}
//todo: add oauth support
//allow any part of the URI to be replaced with a simple search
if(isset($this->options['url'])){
foreach($this->options['url'] as $search => $replace){
$uri = (string) $request->getUri();
$new = str_replace($search, $replace, $uri);
if($uri !== $new){
$request = $request->withUri(new Uri($new));
}
}
}
// The user agent must be in the following format:
// LIBRARY-NAME/LIBRARY-VERSION LANGUAGE-NAME/LANGUAGE-VERSION [APP-NAME/APP-VERSION]
// See https://github.com/Nexmo/client-library-specification/blob/master/SPECIFICATION.md#reporting
$userAgent = [];
// Library name
$userAgent[] = 'nexmo-php/'.self::VERSION;
// Language name
$userAgent[] = 'php/'.PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION;
// If we have an app set, add that to the UA
if (isset($this->options['app'])) {
$app = $this->options['app'];
$userAgent[] = $app['name'].'/'.$app['version'];
}
// Set the header. Build by joining all the parts we have with a space
$request = $request->withHeader('User-Agent', implode(" ", $userAgent));
$response = $this->client->sendRequest($request);
return $response;
}
protected function validateAppOptions($app) {
$disallowedCharacters = ['/', ' ', "\t", "\n"];
foreach (['name', 'version'] as $key) {
if (!isset($app[$key])) {
throw new \InvalidArgumentException('app.'.$key.' has not been set');
}
foreach ($disallowedCharacters as $char) {
if (strpos($app[$key], $char) !== false) {
throw new \InvalidArgumentException('app.'.$key.' cannot contain the '.$char.' character');
}
}
}
}
public function serialize(EntityInterface $entity)
{
if($entity instanceof Verification){
return $this->verify()->serialize($entity);
}
throw new \RuntimeException('unknown class `' . get_class($entity) . '``');
}
public function unserialize($entity)
{
if(is_string($entity)){
$entity = unserialize($entity);
}
if($entity instanceof Verification){
return $this->verify()->unserialize($entity);
}
throw new \RuntimeException('unknown class `' . get_class($entity) . '``');
}
public function __call($name, $args)
{
if(!$this->factory->hasApi($name)){
throw new \RuntimeException('no api namespace found: ' . $name);
}
$collection = $this->factory->getApi($name);
if(empty($args)){
return $collection;
}
return call_user_func_array($collection, $args);
}
public function __get($name)
{
if(!$this->factory->hasApi($name)){
throw new \RuntimeException('no api namespace found: ' . $name);
}
return $this->factory->getApi($name);
}
protected static function requiresBasicAuth(\Psr\Http\Message\RequestInterface $request)
{
$path = $request->getUri()->getPath();
$isSecretManagementEndpoint = strpos($path, '/accounts') === 0 && strpos($path, '/secrets') !== false;
return $isSecretManagementEndpoint;
}
protected static function requiresAuthInUrlNotBody(\Psr\Http\Message\RequestInterface $request)
{
$path = $request->getUri()->getPath();
$isRedactEndpoint = strpos($path, '/v1/redact') === 0;
return $isRedactEndpoint;
}
protected function needsKeypairAuthentication(\Psr\Http\Message\RequestInterface $request)
{
$path = $request->getUri()->getPath();
$isCallEndpoint = strpos($path, '/v1/calls') === 0;
$isRecordingUrl = strpos($path, '/v1/files') === 0;
$isStitchEndpoint = strpos($path, '/beta/conversation') === 0;
$isUserEndpoint = strpos($path, '/beta/users') === 0;
return $isCallEndpoint || $isRecordingUrl || $isStitchEndpoint || $isUserEndpoint;
}
}

View File

@@ -0,0 +1,57 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Callback;
class Callback implements CallbackInterface
{
const ENV_ALL = 'all';
const ENV_POST = 'post';
const ENV_GET = 'get';
protected $expected = array();
protected $data;
public function __construct(array $data)
{
$keys = array_keys($data);
$missing = array_diff($this->expected, $keys);
if($missing){
throw new \RuntimeException('missing expected callback keys: ' . implode(', ', $missing));
}
$this->data = $data;
}
public function getData()
{
return $this->data;
}
public static function fromEnv($source = self::ENV_ALL)
{
switch(strtolower($source)){
case 'post':
$data = $_POST;
break;
case 'get':
$data = $_GET;
break;
case 'all':
$data = array_merge($_GET, $_POST);
break;
default:
throw new \InvalidArgumentException('invalid source: ' . $source);
}
return new static($data);
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Callback;
interface CallbackInterface
{
public function getData();
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client;
use Nexmo\Client;
interface ClientAwareInterface
{
public function setClient(Client $client);
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client;
use Nexmo\Client;
trait ClientAwareTrait
{
/**
* @var Client
*/
protected $client;
public function setClient(Client $client)
{
$this->client = $client;
}
protected function getClient()
{
if(isset($this->client)){
return $this->client;
}
throw new \RuntimeException('Nexmo\Client not set');
}
}

View File

@@ -0,0 +1,53 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Credentials;
abstract class AbstractCredentials implements CredentialsInterface
{
protected $credentials = array();
public function offsetExists($offset)
{
return isset($this->credentials[$offset]);
}
public function offsetGet($offset)
{
return $this->credentials[$offset];
}
public function offsetSet($offset, $value)
{
throw $this->readOnlyException();
}
public function offsetUnset($offset)
{
throw $this->readOnlyException();
}
public function __get($name)
{
return $this->credentials[$name];
}
public function asArray()
{
return $this->credentials;
}
protected function readOnlyException()
{
return new \RuntimeException(sprintf(
'%s is read only, cannot modify using array access.',
get_class($this)
));
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Credentials;
/**
* Class Basic
* Read-only container for api key and secret.
*/
class Basic extends AbstractCredentials implements CredentialsInterface
{
/**
* Create a credential set with an API key and secret.
*
* @param string $key
* @param string $secret
*/
public function __construct($key, $secret)
{
$this->credentials['api_key'] = $key;
$this->credentials['api_secret'] = $secret;
}
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Credentials;
class Container extends AbstractCredentials implements CredentialsInterface
{
protected $types = [
Basic::class,
SignatureSecret::class,
Keypair::class
];
protected $credentials;
public function __construct($credentials)
{
if(!is_array($credentials)){
$credentials = func_get_args();
}
foreach($credentials as $credential){
$this->addCredential($credential);
}
}
protected function addCredential(CredentialsInterface $credential)
{
$type = $this->getType($credential);
if(isset($this->credentials[$type])){
throw new \RuntimeException('can not use more than one of a single credential type');
}
$this->credentials[$type] = $credential;
}
protected function getType(CredentialsInterface $credential)
{
foreach ($this->types as $type) {
if($credential instanceof $type){
return $type;
}
}
}
public function get($type)
{
if(!isset($this->credentials[$type])){
throw new \RuntimeException('credental not set');
}
return $this->credentials[$type];
}
public function has($type)
{
return isset($this->credentials[$type]);
}
public function generateJwt($claims) {
return $this->credentials[Keypair::class]->generateJwt($claims);
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Credentials;
interface CredentialsInterface extends \ArrayAccess
{
public function asArray();
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Credentials;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Rsa\Sha256;
use Nexmo\Application\Application;
class Keypair extends AbstractCredentials implements CredentialsInterface
{
protected $key;
protected $signer;
public function __construct($privateKey, $application = null)
{
$this->credentials['key'] = $privateKey;
if($application){
if($application instanceof Application){
$application = $application->getId();
}
$this->credentials['application'] = $application;
}
$this->key = new Key($privateKey);
$this->signer = new Sha256();
}
public function generateJwt(array $claims = [])
{
$exp = time() + 60;
$iat = time();
$jti = base64_encode(mt_rand());
if(isset($claims['exp'])){
$exp = $claims['exp'];
unset($claims['exp']);
}
if(isset($claims['iat'])){
$iat = $claims['iat'];
unset($claims['iat']);
}
if(isset($claims['jti'])){
$jti = $claims['jti'];
unset($claims['jti']);
}
$builder = new Builder();
$builder->setIssuedAt($iat)
->setExpiration($exp)
->setId($jti);
if(isset($claims['nbf'])){
$builder->setNotBefore($claims['nbf']);
unset($claims['nbf']);
}
if(isset($this->credentials['application'])){
$builder->set('application_id', $this->credentials['application']);
}
if(!empty($claims)){
foreach($claims as $claim => $value){
$builder->set($claim, $value);
}
}
return $builder->sign($this->signer, $this->key)->getToken();
}
}

View File

@@ -0,0 +1,26 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Credentials;
class OAuth extends AbstractCredentials implements CredentialsInterface
{
/**
* Create a credential set with OAuth credentials.
*
* @param string $consumerToken
* @param string $consumerSecret
* @param string $token
* @param string $secret
*/
public function __construct($consumerToken, $consumerSecret, $token, $secret)
{
//using keys that match guzzle
$this->credentials = array_combine(array('consumer_key', 'consumer_secret', 'token', 'token_secret'), func_get_args());
}
}

View File

@@ -0,0 +1,25 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Credentials;
class SignatureSecret extends AbstractCredentials implements CredentialsInterface
{
/**
* Create a credential set with an API key and signature secret.
*
* @param string $key API Key
* @param string $signature_secret Signature Secret
*/
public function __construct($key, $signature_secret, $method='md5hash')
{
$this->credentials['api_key'] = $key;
$this->credentials['signature_secret'] = $signature_secret;
$this->credentials['signature_method'] = $method;
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Exception;
class Exception extends \Exception
{
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Exception;
use Nexmo\Entity\HasEntityTrait;
class Request extends Exception
{
use HasEntityTrait;
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Exception;
use Nexmo\Entity\HasEntityTrait;
class Server extends Exception
{
use HasEntityTrait;
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Exception;
class Transport extends Exception
{
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Exception;
use Throwable;
class Validation extends Request
{
public function __construct($message = "", $code = 0, Throwable $previous = null, $errors)
{
$this->errors = $errors;
parent::__construct($message, $code, $previous);
}
public function getValidationErrors() {
return $this->errors;
}
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Factory;
/**
* Interface FactoryInterface
*
* Factor create API clients (clients specific to single API, that leverages Nexmo\Client for HTTP communication and
* common functionality).
*/
interface FactoryInterface
{
/**
* @param $api
* @return bool
*/
public function hasApi($api);
/**
* @param $api
* @return mixed
*/
public function getApi($api);
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Factory;
use Nexmo\Client;
class MapFactory implements FactoryInterface
{
/**
* Map of api namespaces to classes.
*
* @var array
*/
protected $map = [];
/**
* Map of instances.
*
* @var array
*/
protected $cache = [];
/**
* Nexmo Client
*
* @var Client
*/
protected $client;
public function __construct($map, Client $client)
{
$this->map = $map;
$this->client = $client;
}
public function hasApi($api)
{
return isset($this->map[$api]);
}
public function getApi($api)
{
if(isset($this->cache[$api])){
return $this->cache[$api];
}
if(!$this->hasApi($api)){
throw new \RuntimeException(sprintf(
'no map defined for `%s`',
$api
));
}
$class = $this->map[$api];
$instance = new $class();
if($instance instanceof Client\ClientAwareInterface){
$instance->setClient($this->client);
}
$this->cache[$api] = $instance;
return $instance;
}
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Request;
abstract class AbstractRequest implements RequestInterface
{
protected $params = array();
/**
* @return array
*/
public function getParams()
{
return array_filter($this->params, 'is_scalar');
}
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Request;
interface RequestInterface
{
/**
* @return array
*/
public function getParams();
/**
* @return string
*/
public function getURI();
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Request;
use Nexmo\Client\Response\ResponseInterface;
interface WrapResponseInterface
{
/**
* @param ResponseInterface $response
* @return ResponseInterface
*/
public function wrapResponse(ResponseInterface $response);
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Response;
abstract class AbstractResponse implements ResponseInterface
{
protected $data;
public function getData()
{
return $this->data;
}
public function isSuccess()
{
return isset($this->data['status']) AND $this->data['status'] == 0;
}
public function isError()
{
return !$this->isSuccess();
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Response;
class Error extends Response
{
public function __construct($data)
{
//normalize the data
if(isset($data['error_text'])){
$data['error-text'] = $data['error_text'];
}
$this->expected = ['status', 'error-text'];
return parent::__construct($data);
}
public function isError()
{
return true;
}
public function isSuccess()
{
return false;
}
public function getCode()
{
return $this->data['status'];
}
public function getMessage()
{
return $this->data['error-text'];
}
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Response;
class Response extends AbstractResponse implements ResponseInterface
{
/**
* Allow specific responses to easily define required parameters.
* @var array
*/
protected $expected = array();
public function __construct(array $data)
{
$keys = array_keys($data);
$missing = array_diff($this->expected, $keys);
if($missing){
throw new \RuntimeException('missing expected response keys: ' . implode(', ', $missing));
}
$this->data = $data;
}
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client\Response;
interface ResponseInterface
{
public function getData();
public function isError();
public function isSuccess();
}

View File

@@ -0,0 +1,136 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Client;
use Nexmo\Client\Exception\Exception;
class Signature
{
/**
* Params to Sign
* @var array
*/
protected $params;
/**
* Params with Signature (and timestamp if not present)
* @var array
*/
protected $signed;
/**
* Create a signature from a set of parameters.
*
* @param array $params
* @param $secret
*/
public function __construct(array $params, $secret, $signatureMethod)
{
$this->params = $params;
$this->signed = $params;
if(!isset($this->signed['timestamp'])){
$this->signed['timestamp'] = time();
}
//remove signature if present
unset($this->signed['sig']);
//sort params
ksort($this->signed);
$signed = [];
foreach ($this->signed as $key => $value) {
$signed[$key] = str_replace(array("&", "="), "_", $value);
}
//create base string
$base = '&'.urldecode(http_build_query($signed));
$this->signed['sig'] = $this->sign($signatureMethod, $base, $secret);
}
protected function sign($signatureMethod, $data, $secret) {
switch($signatureMethod) {
case 'md5hash':
// md5hash needs the secret appended
$data .= $secret;
return md5($data);
break;
case 'md5':
case 'sha1':
case 'sha256':
case 'sha512':
return hash_hmac($signatureMethod, $data, $secret);
break;
default:
throw new Exception('Unknown signature algorithm: '.$signatureMethod.'. Expected: md5hash, md5, sha1, sha256, or sha512');
}
}
/**
* Get the original parameters.
*
* @return array
*/
public function getParams()
{
return $this->params;
}
/**
* Get the signature for the parameters.
*
* @return string
*/
public function getSignature()
{
return $this->signed['sig'];
}
/**
* Get a full set of parameters including the signature and timestamp.
*
* @return array
*/
public function getSignedParams()
{
return $this->signed;
}
/**
* Check that a signature (or set of parameters) is valid.
*
* @param array| string $signature
* @return bool
* @throws \InvalidArgumentException
*/
public function check($signature)
{
if(is_array($signature) AND isset($signature['sig'])){
$signature = $signature['sig'];
}
if(!is_string($signature)){
throw new \InvalidArgumentException('signature must be string, or present in array or parameters');
}
return $signature == $this->signed['sig'];
}
/**
* Allow easy comparison.
*
* @return string
*/
public function __toString()
{
return $this->getSignature();
}
}

View File

@@ -0,0 +1,178 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2018 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Conversations;
use Nexmo\Client\ClientAwareInterface;
use Nexmo\Client\ClientAwareTrait;
use Nexmo\Entity\CollectionInterface;
use Nexmo\Entity\CollectionTrait;
use Nexmo\Entity\JsonResponseTrait;
use Nexmo\Entity\JsonSerializableTrait;
use Nexmo\Entity\NoRequestResponseTrait;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Request;
use Nexmo\Client\Exception;
class Collection implements ClientAwareInterface, CollectionInterface, \ArrayAccess
{
use ClientAwareTrait;
use CollectionTrait;
use JsonSerializableTrait;
use NoRequestResponseTrait;
use JsonResponseTrait;
public static function getCollectionName()
{
return 'conversations';
}
public static function getCollectionPath()
{
return '/beta/' . self::getCollectionName();
}
public function hydrateEntity($data, $idOrConversation)
{
if(!($idOrConversation instanceof Conversation)){
$idOrConversation = new Conversation($idOrConversation);
}
$idOrConversation->setClient($this->getClient());
$idOrConversation->jsonUnserialize($data);
return $idOrConversation;
}
public function hydrateAll($conversations)
{
$hydrated = [];
foreach ($conversations as $conversation) {
$hydrated[] = $this->hydrateEntity($conversation, $conversation['id']);
}
return $hydrated;
}
/**
* @param null $conversation
* @return $this|Conversation
*/
public function __invoke(Filter $filter = null)
{
if(!is_null($filter)){
$this->setFilter($filter);
}
return $this;
}
public function create($conversation)
{
return $this->post($conversation);
}
public function post($conversation)
{
if($conversation instanceof Conversation){
$body = $conversation->getRequestData();
} else {
$body = $conversation;
}
$request = new Request(
$this->getClient()->getApiUrl() . $this->getCollectionPath()
,'POST',
'php://temp',
['content-type' => 'application/json']
);
$request->getBody()->write(json_encode($body));
$response = $this->getClient()->send($request);
if($response->getStatusCode() != '200'){
throw $this->getException($response);
}
$body = json_decode($response->getBody()->getContents(), true);
$conversation = new Conversation($body['id']);
$conversation->jsonUnserialize($body);
$conversation->setClient($this->getClient());
return $conversation;
}
public function get($conversation)
{
if(!($conversation instanceof Conversation)){
$conversation = new Conversation($conversation);
}
$conversation->setClient($this->getClient());
$conversation->get();
return $conversation;
}
protected function getException(ResponseInterface $response)
{
$body = json_decode($response->getBody()->getContents(), true);
$status = $response->getStatusCode();
// This message isn't very useful, but we shouldn't ever see it
$errorTitle = 'Unexpected error';
if (isset($body['description'])) {
$errorTitle = $body['description'];
}
if (isset($body['error_title'])) {
$errorTitle = $body['error_title'];
}
if($status >= 400 AND $status < 500) {
$e = new Exception\Request($errorTitle, $status);
} elseif($status >= 500 AND $status < 600) {
$e = new Exception\Server($errorTitle, $status);
} else {
$e = new Exception\Exception('Unexpected HTTP Status Code');
throw $e;
}
return $e;
}
public function offsetExists($offset)
{
return true;
}
/**
* @param mixed $conversation
* @return Conversation
*/
public function offsetGet($conversation)
{
if(!($conversation instanceof Conversation)){
$conversation = new Conversation($conversation);
}
$conversation->setClient($this->getClient());
return $conversation;
}
public function offsetSet($offset, $value)
{
throw new \RuntimeException('can not set collection properties');
}
public function offsetUnset($offset)
{
throw new \RuntimeException('can not unset collection properties');
}
}

View File

@@ -0,0 +1,182 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Conversations;
use Nexmo\Client\ClientAwareInterface;
use Nexmo\Client\ClientAwareTrait;
use Nexmo\Entity\EntityInterface;
use Nexmo\Entity\JsonResponseTrait;
use Nexmo\Entity\JsonSerializableTrait;
use Nexmo\Entity\JsonUnserializableInterface;
use Nexmo\Entity\NoRequestResponseTrait;
use Nexmo\User\Collection as UserCollection;
use Nexmo\User\User;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Request;
use Nexmo\Client\Exception;
class Conversation implements EntityInterface, \JsonSerializable, JsonUnserializableInterface, ClientAwareInterface
{
use NoRequestResponseTrait;
use JsonSerializableTrait;
use JsonResponseTrait;
use ClientAwareTrait;
protected $data = [];
public function __construct($id = null)
{
$this->data['id'] = $id;
}
public function setName($name)
{
$this->data['name'] = $name;
return $this;
}
public function setDisplayName($name)
{
$this->data['display_name'] = $name;
return $this;
}
public function getId()
{
if (isset($this->data['uuid'])) {
return $this->data['uuid'];
}
return $this->data['id'];
}
public function __toString()
{
return (string)$this->getId();
}
public function get()
{
$request = new Request(
$this->getClient()->getApiUrl() . Collection::getCollectionPath() . '/' . $this->getId()
,'GET'
);
$response = $this->getClient()->send($request);
if($response->getStatusCode() != '200'){
throw $this->getException($response);
}
$data = json_decode($response->getBody()->getContents(), true);
$this->jsonUnserialize($data);
return $this;
}
public function jsonSerialize()
{
return $this->data;
}
public function jsonUnserialize(array $json)
{
$this->data = $json;
}
public function members()
{
$response = $this->getClient()->get($this->getClient()->getApiUrl() . Collection::getCollectionPath() . '/' . $this->getId() .'/members');
if($response->getStatusCode() != '200'){
throw $this->getException($response);
}
$data = json_decode($response->getBody()->getContents(), true);
$memberCollection = new UserCollection();
return $memberCollection->hydrateAll($data);
}
public function addMember(User $user)
{
return $this->sendPostAction($user, 'join');
}
public function inviteMember(User $user)
{
return $this->sendPostAction($user, 'invite');
}
public function removeMember(User $user)
{
$response = $this->getClient()->delete(
$this->getClient()->getApiUrl() . Collection::getCollectionPath() . '/' . $this->getId() .'/members/'. $user->getId()
);
if($response->getStatusCode() != '200'){
throw $this->getException($response);
}
}
public function sendPostAction(User $user, $action, $channel = 'app') {
$body = $user->getRequestDataForConversation();
$body['action'] = $action;
$body['channel'] = ['type' => $channel];
$response = $this->getClient()->post(
$this->getClient()->getApiUrl() . Collection::getCollectionPath() . '/' . $this->getId() .'/members',
$body
);
if($response->getStatusCode() != '200'){
throw $this->getException($response);
}
$body = json_decode($response->getBody()->getContents(), true);
$user = new User($body['user_id']);
$user->jsonUnserialize($body);
$user->setClient($this->getClient());
return $user;
}
protected function getException(ResponseInterface $response)
{
$body = json_decode($response->getBody()->getContents(), true);
$status = $response->getStatusCode();
// This message isn't very useful, but we shouldn't ever see it
$errorTitle = 'Unexpected error';
if (isset($body['description'])) {
$errorTitle = $body['description'];
}
if (isset($body['error_title'])) {
$errorTitle = $body['error_title'];
}
if($status >= 400 AND $status < 500) {
$e = new Exception\Request($errorTitle, $status);
} elseif($status >= 500 AND $status < 600) {
$e = new Exception\Server($errorTitle, $status);
} else {
$e = new Exception\Exception('Unexpected HTTP Status Code');
throw $e;
}
return $e;
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace Nexmo\Conversion;
use Nexmo\Client\ClientAwareInterface;
use Nexmo\Client\ClientAwareTrait;
use Nexmo\Client\Exception;
class Client implements ClientAwareInterface
{
use ClientAwareTrait;
public function sms($message_id, $delivered, $timestamp=null)
{
return $this->sendConversion('sms', $message_id, $delivered, $timestamp);
}
public function voice($message_id, $delivered, $timestamp=null)
{
return $this->sendConversion('voice', $message_id, $delivered, $timestamp);
}
protected function sendConversion($type, $message_id, $delivered, $timestamp=null)
{
$params = [
'message-id' => $message_id,
'delivered' => $delivered
];
if ($timestamp) {
$params['timestamp'] = $timestamp;
}
$response = $this->client->postUrlEncoded(
$this->getClient()->getApiUrl() . '/conversions/'.$type.'?'.http_build_query($params),
[]
);
if($response->getStatusCode() != '200'){
throw $this->getException($response);
}
}
protected function getException(ResponseInterface $response)
{
$body = json_decode($response->getBody()->getContents(), true);
$status = $response->getStatusCode();
if($status === 402) {
$e = new Exception\Request("This endpoint may need activating on your account. Please email support@nexmo.com for more information", $status);
} elseif($status >= 400 AND $status < 500) {
$e = new Exception\Request($body['error_title'], $status);
} elseif($status >= 500 AND $status < 600) {
$e = new Exception\Server($body['error_title'], $status);
} else {
$e = new Exception\Exception('Unexpected HTTP Status Code');
}
return $e;
}
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Entity;
trait ArrayAccessTrait
{
public abstract function getResponseData();
public abstract function getRequestData();
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Entity;
interface CollectionAwareInterface
{
/**
* @param CollectionInterface $collection
* @return mixed
*/
public function setCollection(CollectionInterface $collection);
/**
* @return CollectionInterface
*/
public function getCollection();
}

View File

@@ -0,0 +1,31 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Entity;
trait CollectionAwareTrait
{
/**
* @var CollectionInterface
*/
protected $collection;
public function setCollection(CollectionInterface $collection)
{
$this->collection = $collection;
}
public function getCollection()
{
if(!isset($this->collection)){
throw new \RuntimeException('missing collection');
}
return $this->collection;
}
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Entity;
interface CollectionInterface extends \Countable, \Iterator
{
/**
* @return string
*/
public static function getCollectionName();
/**
* @return string
*/
public static function getCollectionPath();
/**
* @param $data
* @param $idOrEntity
* @return mixed
*/
public function hydrateEntity($data, $idOrEntity);
}

View File

@@ -0,0 +1,250 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Entity;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Request;
use Nexmo\Application\Application;
/**
* Common code for iterating over a collection, and using the collection class to discover the API path.
*/
trait CollectionTrait
{
/**
* Index of the current resource of the current page
* @var int
*/
protected $current;
/**
* Current page data.
* @var array
*/
protected $page;
/**
* Last API Response
* @var ResponseInterface
*/
protected $response;
/**
* User set page index.
* @var int
*/
protected $index;
/**
* User set pgge sixe.
* @var int
*/
protected $size;
/**
* @var FilterInterface
*/
protected $filter;
abstract public function getCollectionName();
abstract public function getCollectionPath();
abstract public function hydrateEntity($data, $id);
/**
* Return the current item, expects concrete collection to handle creating the object.
* @return mixed
*/
public function current()
{
return $this->hydrateEntity($this->page['_embedded'][$this->getCollectionName()][$this->current], $this->key());
}
/**
* No checks here, just advance the index.
*/
public function next()
{
$this->current++;
}
/**
* Return the ID of the resource, in some cases this is `id`, in others `uuid`.
* @return string
*/
public function key()
{
if(isset($this->page['_embedded'][$this->getCollectionName()][$this->current]['id'])){
return $this->page['_embedded'][$this->getCollectionName()][$this->current]['id'];
} elseif(isset($this->page['_embedded'][$this->getCollectionName()][$this->current]['uuid'])) {
return $this->page['_embedded'][$this->getCollectionName()][$this->current]['uuid'];
}
return $this->current;
}
/**
* Handle pagination automatically (unless configured not to).
* @return bool
*/
public function valid()
{
//can't be valid if there's not a page (rewind sets this)
if(!isset($this->page)){
return false;
}
//all hal collections have an `_embedded` object, we expect there to be a property matching the collection name
if(!isset($this->page['_embedded']) OR !isset($this->page['_embedded'][$this->getCollectionName()])){
return false;
}
//if we have a page with no items, we've gone beyond the end of the collection
if(!count($this->page['_embedded'][$this->getCollectionName()])){
return false;
}
//index the start of a page at 0
if(is_null($this->current)){
$this->current = 0;
}
//if our current index is past the current page, fetch the next page if possible and reset the index
if(!isset($this->page['_embedded'][$this->getCollectionName()][$this->current])){
if(isset($this->page['_links']) AND isset($this->page['_links']['next'])){
$this->fetchPage($this->page['_links']['next']['href']);
$this->current = 0;
return true;
}
return false;
}
return true;
}
/**
* Fetch the initial page
*/
public function rewind()
{
$this->fetchPage($this->getCollectionPath());
}
/**
* Count of total items
* @return integer
*/
public function count()
{
if(isset($this->page)){
return (int) $this->page['count'];
}
}
public function setPage($index)
{
$this->index = (int) $index;
return $this;
}
public function getPage()
{
if(isset($this->page)){
return $this->page['page_index'];
}
if(isset($this->index)){
return $this->index;
}
throw new \RuntimeException('page not set');
}
public function getSize()
{
if(isset($this->page)){
return $this->page['page_size'];
}
if(isset($this->size)){
return $this->size;
}
throw new \RuntimeException('size not set');
}
public function setSize($size)
{
$this->size = (int) $size;
return $this;
}
/**
* Filters reduce to query params and include paging settings.
*
* @param FilterInterface $filter
* @return $this
*/
public function setFilter(FilterInterface $filter)
{
$this->filter = $filter;
return $this;
}
public function getFilter()
{
if(!isset($this->filter)){
$this->setFilter(new EmptyFilter());
}
return $this->filter;
}
/**
* Fetch a page using the current filter if no query is provided.
*
* @param $absoluteUri
*/
protected function fetchPage($absoluteUri)
{
//use filter if no query provided
if(false === strpos($absoluteUri, '?')){
$query = [];
if(isset($this->size)){
$query['page_size'] = $this->size;
}
if(isset($this->index)){
$query['page_index'] = $this->index;
}
if(isset($this->filter)){
$query = array_merge($this->filter->getQuery(), $query);
}
$absoluteUri .= '?' . http_build_query($query);
}
//
$request = new Request(
$this->getClient()->getApiUrl() . $absoluteUri,
'GET'
);
$response = $this->client->send($request);
if($response->getStatusCode() != '200'){
throw $this->getException($response);
}
$this->response = $response;
$this->page = json_decode($this->response->getBody()->getContents(), true);
}
}

View File

@@ -0,0 +1,18 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Entity;
class EmptyFilter implements FilterInterface
{
public function getQuery()
{
return [];
}
}

View File

@@ -0,0 +1,26 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Entity;
interface EntityInterface
{
public function getRequest();
public function getRequestData($sent = true);
public function getResponse();
public function getResponseData();
public function setResponse(\Psr\Http\Message\ResponseInterface $response);
public function setRequest(\Psr\Http\Message\RequestInterface $request);
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Entity;
interface FilterInterface
{
public function getQuery();
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Entity;
trait HasEntityTrait
{
protected $entity;
public function setEntity($entity)
{
$this->entity = $entity;
}
public function getEntity()
{
return $this->entity;
}
}

View File

@@ -0,0 +1,39 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Entity;
use Psr\Http\Message\ResponseInterface;
trait JsonResponseTrait
{
protected $responseJson;
public function getResponseData()
{
if(!($this instanceof EntityInterface)){
throw new \Exception(sprintf(
'%s can only be used if the class implements %s',
__TRAIT__,
EntityInterface::class
));
}
if(($response = $this->getResponse()) && ($response instanceof ResponseInterface)){
if($response->getBody()->isSeekable()){
$response->getBody()->rewind();
}
$body = $response->getBody()->getContents();
$this->responseJson = json_decode($body, true);
return $this->responseJson;
}
return [];
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Entity;
/**
* Identifies the Entity as using JsonSerializable to prepare request data.
*/
interface JsonSerializableInterface extends \JsonSerializable {}

View File

@@ -0,0 +1,45 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Entity;
/**
* Implements getRequestData from EntityInterface based on the entity's jsonSerialize().
*
* @see EntityInterface::getRequestData()
*/
trait JsonSerializableTrait
{
/**
* Get an array of params to use in an API request.
*/
public function getRequestData($sent = true)
{
if(!($this instanceof EntityInterface)){
throw new \Exception(sprintf(
'%s can only be used if the class implements %s',
__TRAIT__,
EntityInterface::class
));
}
if(!($this instanceof \JsonSerializable)){
throw new \Exception(sprintf(
'%s can only be used if the class implements %s',
__TRAIT__,
\JsonSerializable::class
));
}
if($sent && ($request = $this->getRequest())){
//TODO, figure out what the request data actually was
}
return $this->jsonSerialize();
}
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Entity;
/**
* Identifies the Entity as using JsonSerializable to prepare request data.
*/
interface JsonUnserializableInterface
{
/**
* Update the object state with the json data (as an array)
* @param $json
* @return null
*/
public function jsonUnserialize(array $json);
}

View File

@@ -0,0 +1,37 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Entity;
/**
* Class Psr7Trait
*
* Allow an entity to contain last request / response objects.
*/
trait NoRequestResponseTrait
{
public function setResponse(\Psr\Http\Message\ResponseInterface $response)
{
throw new \RuntimeException(__CLASS__ . ' does not support request / response');
}
public function setRequest(\Psr\Http\Message\RequestInterface $request)
{
throw new \RuntimeException(__CLASS__ . ' does not support request / response');
}
public function getRequest()
{
return null;
}
public function getResponse()
{
return null;
}
}

View File

@@ -0,0 +1,53 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Entity;
/**
* Class Psr7Trait
*
* Allow an entity to contain last request / response objects.
*/
trait Psr7Trait
{
/**
* @var \Psr\Http\Message\RequestInterface
*/
protected $request;
/**
* @var \Psr\Http\Message\ResponseInterface
*/
protected $response;
public function setResponse(\Psr\Http\Message\ResponseInterface $response)
{
$this->response = $response;
$status = $response->getStatusCode();
if($this instanceof JsonUnserializableInterface AND ((200 == $status) OR (201 == $status))){
$this->jsonUnserialize($this->getResponseData());
}
}
public function setRequest(\Psr\Http\Message\RequestInterface $request)
{
$this->request = $request;
}
public function getRequest()
{
return $this->request;
}
public function getResponse()
{
return $this->response;
}
}

View File

@@ -0,0 +1,74 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Entity;
/**
* Implements getRequestData from EntityInterface with a simple array. Request data stored in an array, and locked once
* a request object has been set.
*
* @see EntityInterface::getRequestData()
*/
trait RequestArrayTrait
{
/**
* @var array
*/
protected $requestData = [];
/**
* Get an array of params to use in an API request.
*/
public function getRequestData($sent = true)
{
if(!($this instanceof EntityInterface)){
throw new \Exception(sprintf(
'%s can only be used if the class implements %s',
__TRAIT__,
EntityInterface::class
));
}
if($sent && ($request = $this->getRequest())){
$query = [];
parse_str($request->getUri()->getQuery(), $query);
return $query;
}
// Trigger a pre-getRequestData() hook for any last minute
// decision making that needs to be done, but only if
// it hasn't been sent already
if (method_exists($this, 'preGetRequestDataHook')) {
$this->preGetRequestDataHook();
}
return $this->requestData;
}
protected function setRequestData($name, $value)
{
if(!($this instanceof EntityInterface)){
throw new \Exception(sprintf(
'%s can only be used if the class implements %s',
__TRAIT__,
EntityInterface::class
));
}
if($this->getResponse()){
throw new \RuntimeException(sprintf(
'can not set request parameter `%s` for `%s` after API request has be made',
$name,
get_class($this)
));
}
$this->requestData[$name] = $value;
return $this;
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Nexmo\Insights;
class Advanced extends Standard
{
public function getValidNumber()
{
return $this['valid_number'];
}
public function getReachable()
{
return $this['reachable'];
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Nexmo\Insights;
class AdvancedCnam extends Advanced
{
use CnamTrait;
}

View File

@@ -0,0 +1,102 @@
<?php
namespace Nexmo\Insights;
use Nexmo\Client\Exception\Exception;
use Nexmo\Entity\JsonUnserializableInterface;
class Basic implements \JsonSerializable, JsonUnserializableInterface, \ArrayAccess {
protected $data = [];
public function __construct($number)
{
$this->data['national_format_number'] = $number;
}
/**
* @return string
*/
public function getRequestId()
{
return $this['request_id'];
}
/**
* @return string
*/
public function getNationalFormatNumber()
{
return $this['national_format_number'];
}
/**
* @return string
*/
public function getInternationalFormatNumber()
{
return $this['international_format_number'];
}
/**
* @return string
*/
public function getCountryCode()
{
return $this['country_code'];
}
/**
* @return string
*/
public function getCountryCodeISO3()
{
return $this['country_code_iso3'];
}
/**
* @return string
*/
public function getCountryName()
{
return $this['country_name'];
}
/**
* @return integer
*/
public function getCountryPrefix()
{
return $this['country_prefix'];
}
public function jsonSerialize()
{
return $this->data;
}
public function jsonUnserialize(array $json)
{
$this->data = $json;
}
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
public function offsetGet($offset)
{
return $this->data[$offset];
}
public function offsetSet($offset, $value)
{
throw new Exception('Number insights results are read only');
}
public function offsetUnset($offset)
{
throw new Exception('Number insights results are read only');
}
}

View File

@@ -0,0 +1,118 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Insights;
use Nexmo\Client\ClientAwareInterface;
use Nexmo\Client\ClientAwareTrait;
use Nexmo\Client\Exception;
use Zend\Diactoros\Request;
/**
* Class Client
*/
class Client implements ClientAwareInterface
{
use ClientAwareTrait;
public function basic($number)
{
$insightsResults = $this->makeRequest('/ni/basic/json', $number);
$basic = new Basic($insightsResults['national_format_number']);
$basic->jsonUnserialize($insightsResults);
return $basic;
}
public function standardCNam($number)
{
$insightsResults = $this->makeRequest('/ni/standard/json', $number, ['cnam' => 'true']);
$standard = new StandardCnam($insightsResults['national_format_number']);
$standard->jsonUnserialize($insightsResults);
return $standard;
}
public function advancedCnam($number)
{
$insightsResults = $this->makeRequest('/ni/advanced/json', $number, ['cnam' => 'true']);
$standard = new AdvancedCnam($insightsResults['national_format_number']);
$standard->jsonUnserialize($insightsResults);
return $standard;
}
public function standard($number, $useCnam=false)
{
$insightsResults = $this->makeRequest('/ni/standard/json', $number);
$standard = new Standard($insightsResults['national_format_number']);
$standard->jsonUnserialize($insightsResults);
return $standard;
}
public function advanced($number)
{
$insightsResults = $this->makeRequest('/ni/advanced/json', $number);
$advanced = new Advanced($insightsResults['national_format_number']);
$advanced->jsonUnserialize($insightsResults);
return $advanced;
}
public function advancedAsync($number, $webhook)
{
// This method does not have a return value as it's async. If there is no exception thrown
// We can assume that everything is fine
$this->makeRequest('/ni/advanced/async/json', $number, ['callback' => $webhook]);
}
public function makeRequest($path, $number, $additionalParams = [])
{
if ($number instanceof Number)
{
$number = $number->getMsisdn();
}
$queryString = http_build_query([
'number' => $number,
] + $additionalParams);
$request = new Request(
$this->getClient()->getApiUrl(). $path.'?'.$queryString,
'GET',
'php://temp',
[
'Accept' => 'application/json'
]
);
$response = $this->client->send($request);
$insightsResults = json_decode($response->getBody()->getContents(), true);
if('200' != $response->getStatusCode()){
throw $this->getException($response);
}
return $insightsResults;
}
protected function getException(ResponseInterface $response)
{
$body = json_decode($response->getBody()->getContents(), true);
$status = $response->getStatusCode();
if($status >= 400 AND $status < 500) {
$e = new Exception\Request($body['error-code-label'], $status);
} elseif($status >= 500 AND $status < 600) {
$e = new Exception\Server($body['error-code-label'], $status);
} else {
$e = new Exception\Exception('Unexpected HTTP Status Code');
throw $e;
}
return $e;
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Nexmo\Insights;
trait CnamTrait {
public function getCallerName()
{
return $this['caller_name'];
}
public function getFirstName()
{
return $this['first_name'];
}
public function getLastName()
{
return $this['last_name'];
}
public function getCallerType()
{
return $this['caller_type'];
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Nexmo\Insights;
class Standard extends Basic
{
public function getCurrentCarrier()
{
return $this['current_carrier'];
}
public function getOriginalCarrier()
{
return $this['original_carrier'];
}
public function getPorted()
{
return $this['ported'];
}
public function getRoaming()
{
return $this['roaming'];
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Nexmo\Insights;
class StandardCnam extends Standard
{
use CnamTrait;
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Nexmo;
class InvalidResponseException extends \Exception {
}

View File

@@ -0,0 +1,38 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Message;
/**
* SMS Text Message
*/
class AutoDetect extends Message
{
const TYPE = 'text';
/**
* Message Body
* @var string
*/
protected $text;
/**
* Create a new SMS text message.
*
* @param string $to
* @param string $from
* @param string $text
* @param array $additional
*/
public function __construct($to, $from, $text, $additional = [])
{
parent::__construct($to, $from, $additional);
$this->enableEncodingDetection();
$this->requestData['text'] = (string) $text;
}
}

View File

@@ -0,0 +1,55 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Message;
/**
* SMS Binary Message
*/
class Binary extends Message
{
const TYPE = 'binary';
/**
* Message Body
* @var string
*/
protected $body;
/**
* Message UDH
* @var string
*/
protected $udh;
/**
* Create a new SMS text message.
*
* @param string $to
* @param string $from
* @param string $body
* @param string $udh
*/
public function __construct($to, $from, $body, $udh)
{
parent::__construct($to, $from);
$this->body = (string) $body;
$this->udh = (string) $udh;
}
/**
* Get an array of params to use in an API request.
*/
public function getRequestData($sent = true)
{
return array_merge(parent::getRequestData($sent), array(
'body' => $this->body,
'udh' => $this->udh,
));
}
}

View File

@@ -0,0 +1,139 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Message\Callback;
use Nexmo\Client\Callback\Callback;
class Receipt extends Callback
{
protected $expected = array(
'err-code',
'message-timestamp',
'msisdn',
'network-code',
'price',
'scts',
'status',
//'timestamp',
'to'
);
public function __construct(array $data)
{
//default value
$data = array_merge(array('client-ref' => null), $data);
parent::__construct($data);
}
/**
* @return int
*/
public function getErrorCode()
{
return (int) $this->data['err-code'];
}
/**
* @return string
*/
public function getNetwork()
{
return (string) $this->data['network-code'];
}
/**
* @return string
*/
public function getId()
{
return (string) $this->data['messageId'];
}
/**
* @return string
*/
public function getReceiptFrom()
{
return (string) $this->data['msisdn'];
}
/**
* @return string
*/
public function getTo()
{
return $this->getReceiptFrom();
}
/**
* @return string
*/
public function getReceiptTo()
{
return (string) $this->data['to'];
}
/**
* @return string
*/
public function getFrom()
{
return $this->getReceiptTo();
}
/**
* @return string
*/
public function getStatus()
{
return (string) $this->data['status'];
}
/**
* @return string
*/
public function getPrice()
{
return (string) $this->data['price'];
}
/**
* @return \DateTime
*/
public function getTimestamp()
{
$date = \DateTime::createFromFormat('ymdHi', $this->data['scts']);
if($date){
return $date;
}
throw new \UnexpectedValueException('could not parse message timestamp');
}
/**
* @return \DateTime
*/
public function getSent()
{
$date = \DateTime::createFromFormat('Y-m-d H:i:s', $this->data['message-timestamp']);
if($date){
return $date;
}
throw new \UnexpectedValueException('could not parse message timestamp');
}
/**
* @return string|null
*/
public function getClientRef()
{
return $this->data['client-ref'];
}
}

View File

@@ -0,0 +1,324 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Message;
use Nexmo\Client\ClientAwareInterface;
use Nexmo\Client\ClientAwareTrait;
use Nexmo\Client\Exception;
use Zend\Diactoros\Request;
/**
* Class Client
* @method Text sendText(string $to, string $from, string $text, array $additional = []) Send a Test Message
*/
class Client implements ClientAwareInterface
{
use ClientAwareTrait;
/**
* @param Message|array $message
* @return Message
* @throws Exception\Exception
* @throws Exception\Request
* @throws Exception\Server
*/
public function send($message)
{
if(!($message instanceof MessageInterface)){
$message = $this->createMessageFromArray($message);
}
$params = $message->getRequestData(false);
$request = new Request(
$this->getClient()->getRestUrl() . '/sms/json'
,'POST',
'php://temp',
['content-type' => 'application/json']
);
$request->getBody()->write(json_encode($params));
$message->setRequest($request);
$response = $this->client->send($request);
$message->setResponse($response);
//check for valid data, as well as an error response from the API
$data = $message->getResponseData();
if(!isset($data['messages'])){
throw new Exception\Exception('unexpected response from API');
}
//normalize errors (client vrs server)
foreach($data['messages'] as $part){
switch($part['status']){
case '0':
break; //all okay
case '1':
if(preg_match('#\[\s+(\d+)\s+\]#', $part['error-text'], $match)){
usleep($match[1] + 1);
} else {
sleep(1);
}
return $this->send($message);
case '5':
$e = new Exception\Server($part['error-text'], $part['status']);
$e->setEntity($message);
throw $e;
default:
$e = new Exception\Request($part['error-text'], $part['status']);
$e->setEntity($message);
throw $e;
}
}
return $message;
}
/**
* @param $query
* @return MessageInterface[]
* @throws Exception\Exception
* @throws Exception\Request
*/
public function get($query)
{
if($query instanceof Query){
$params = $query->getParams();
} else if($query instanceof MessageInterface){
$params = ['ids' => [$query->getMessageId()]];
} else if(is_string($query)) {
$params = ['ids' => [$query]];
} else if(is_array($query)){
$params = ['ids' => $query];
} else {
throw new \InvalidArgumentException('query must be an instance of Query, MessageInterface, string ID, or array of IDs.');
}
$request = new Request(
$this->getClient()->getRestUrl() . '/search/messages?' . http_build_query($params),
'GET',
'php://temp',
['Accept' => 'application/json']
);
$response = $this->client->send($request);
$response->getBody()->rewind();
$data = json_decode($response->getBody()->getContents(), true);
if($response->getStatusCode() != '200' && isset($data['error-code'])){
throw new Exception\Request($data['error-code-label'], $data['error-code']);
} elseif($response->getStatusCode() != '200'){
throw new Exception\Request('error status from API', $response->getStatusCode());
}
if(!isset($data['items'])){
throw new Exception\Exception('unexpected response from API');
}
if(count($data['items']) == 0){
return [];
}
$collection = [];
foreach($data['items'] as $index => $item){
switch($item['type']){
case 'MT':
$new = new Message($item['message-id']);
break;
case 'MO':
$new = new InboundMessage($item['message-id']);
break;
default:
throw new Exception\Exception('unexpected response from API');
}
$new->setResponse($response);
$new->setIndex($index);
$collection[] = $new;
}
return $collection;
}
/**
* @param string|MessageInterface $idOrMessage
*/
public function search($idOrMessage)
{
if($idOrMessage instanceof MessageInterface){
$id = $idOrMessage->getMessageId();
$message = $idOrMessage;
} else {
$id = $idOrMessage;
}
$request = new Request(
$this->getClient()->getRestUrl() . '/search/message?' . http_build_query(['id' => $id]),
'GET',
'php://temp',
['Accept' => 'application/json']
);
$response = $this->client->send($request);
$response->getBody()->rewind();
$data = json_decode($response->getBody()->getContents(), true);
if($response->getStatusCode() != '200' && isset($data['error-code'])){
throw new Exception\Request($data['error-code-label'], $data['error-code']);
} elseif($response->getStatusCode() == '429'){
throw new Exception\Request('too many concurrent requests', $response->getStatusCode());
} elseif($response->getStatusCode() != '200'){
throw new Exception\Request('error status from API', $response->getStatusCode());
}
if(!$data){
throw new Exception\Request('no message found for `' . $id . '`');
}
switch($data['type']){
case 'MT':
$new = new Message($data['message-id']);
break;
case 'MO':
$new = new InboundMessage($data['message-id']);
break;
default:
throw new Exception\Exception('unexpected response from API');
}
if(isset($message) && !($message instanceof $new)){
throw new Exception\Exception(sprintf(
'searched for message with type `%s` but message of type `%s`',
get_class($message),
get_class($new)
));
}
if(!isset($message)){
$message = $new;
}
$message->setResponse($response);
return $message;
}
public function searchRejections(Query $query) {
$params = $query->getParams();
$request = new Request(
$this->getClient()->getRestUrl() . '/search/rejections?' . http_build_query($params),
'GET',
'php://temp',
['Accept' => 'application/json']
);
$response = $this->client->send($request);
$response->getBody()->rewind();
$data = json_decode($response->getBody()->getContents(), true);
if($response->getStatusCode() != '200' && isset($data['error-code'])){
throw new Exception\Request($data['error-code-label'], $data['error-code']);
} elseif($response->getStatusCode() != '200'){
throw new Exception\Request('error status from API', $response->getStatusCode());
}
if(!isset($data['items'])){
throw new Exception\Exception('unexpected response from API');
}
if(count($data['items']) == 0){
return [];
}
$collection = [];
foreach($data['items'] as $index => $item){
switch($item['type']){
case 'MT':
$new = new Message($item['message-id']);
break;
case 'MO':
$new = new InboundMessage($item['message-id']);
break;
default:
throw new Exception\Exception('unexpected response from API');
}
$new->setResponse($response);
$new->setIndex($index);
$collection[] = $new;
}
return $collection;
}
/**
* @param array $message
* @return Message
*/
protected function createMessageFromArray($message)
{
if(!is_array($message)){
throw new \RuntimeException('message must implement `' . MessageInterface::class . '` or be an array`');
}
foreach(['to', 'from'] as $param){
if(!isset($message[$param])){
throw new \InvalidArgumentException('missing expected key `' . $param . '`');
}
}
$to = $message['to'];
$from = $message['from'];
unset($message['to']);
unset($message['from']);
return new Message($to, $from, $message);
}
/**
* Convenience feature allowing messages to be sent without creating a message object first.
*
* @param $name
* @param $arguments
* @return MessageInterface
*/
public function __call($name, $arguments)
{
if(!(strstr($name, 'send') !== 0)){
throw new \RuntimeException(sprintf(
'`%s` is not a valid method on `%s`',
$name,
get_class($this)
));
}
$class = substr($name, 4);
$class = 'Nexmo\\Message\\' . ucfirst(strtolower($class));
if(!class_exists($class)){
throw new \RuntimeException(sprintf(
'`%s` is not a valid method on `%s`',
$name,
get_class($this)
));
}
$reflection = new \ReflectionClass($class);
$message = $reflection->newInstanceArgs($arguments);
return $this->send($message);
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Nexmo\Message;
trait CollectionTrait
{
protected $index = null;
public function setIndex($index)
{
$this->index = (int) $index;
}
}

View File

@@ -0,0 +1,53 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Message;
class EncodingDetector {
public function requiresUnicodeEncoding($content)
{
$gsmCodePoints = array_map(
$this->convertIntoUnicode(),
[ // See: https://en.wikipedia.org/wiki/GSM_03.38#GSM_7-bit_default_alphabet_and_extension_table_of_3GPP_TS_23.038_/_GSM_03.38
'@', '£', '$', '¥', 'è', 'é', 'ù', 'ì', 'ò', 'ç', "\r", 'Ø', 'ø', "\n", 'Å', 'å',
'Δ', '_', 'Φ', 'Γ', 'Λ', 'Ω', 'Π', 'Ψ', 'Σ', 'Θ', 'Ξ', 'Æ', 'æ', 'ß', 'É',
' ', '!', '"', '#', '¤', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
'¡', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'Ä', 'Ö', 'Ñ', 'Ü', '§',
'¿', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'ä', 'ö', 'ñ', 'ü', 'à',
"\f", '^', '{', '}', '\\', '[', '~', ']', '|', '€',
]
);
// Split $text into an array in a way that respects multibyte characters.
$textChars = preg_split('//u', $content, null, PREG_SPLIT_NO_EMPTY);
// Array of codepoint values for characters in $text.
$textCodePoints = array_map($this->convertIntoUnicode(), $textChars);
// Filter the array to contain only codepoints from $text that are not in the set of valid GSM codepoints.
$nonGsmCodePoints = array_diff($textCodePoints, $gsmCodePoints);
// The text contains unicode if the result is not empty.
return !empty($nonGsmCodePoints);
}
private function convertIntoUnicode()
{
return function ($char) {
$k = mb_convert_encoding($char, 'UTF-16LE', 'UTF-8');
$k1 = ord(substr($k, 0, 1));
$k2 = ord(substr($k, 1, 1));
return $k2 * 256 + $k1;
};
}
}

View File

@@ -0,0 +1,237 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Message;
use Nexmo\Entity\JsonResponseTrait;
use Nexmo\Entity\Psr7Trait;
use Psr\Http\Message\ServerRequestInterface;
class InboundMessage implements MessageInterface, \ArrayAccess
{
use Psr7Trait;
use JsonResponseTrait;
use CollectionTrait;
protected $id;
/**
* InboundMessage constructor.
* @param string|ServerRequestInterface $idOrRequest Message ID, or inbound HTTP request.
*/
public function __construct($idOrRequest)
{
if($idOrRequest instanceof ServerRequestInterface){
$this->setRequest($idOrRequest);
return;
}
if(is_string($idOrRequest)){
$this->id = $idOrRequest;
return;
}
throw new \RuntimeException(sprintf(
'`%s` must be constructed with a server request or a message id',
self::class
));
}
public static function createFromGlobals()
{
$serverRequest = \Zend\Diactoros\ServerRequestFactory::fromGlobals();
return new self($serverRequest);
}
/**
* Create a matching reply to the inbound message. Currently only supports text replies.
*
* @param string $body
* @return Text
*/
public function createReply($body)
{
return new Text($this->getFrom(), $this->getTo(), $body);
}
public function getRequestData($sent = true)
{
$request = $this->getRequest();
if(is_null($request)){
return [];
}
if(!($request instanceof ServerRequestInterface)){
throw new \RuntimeException('inbound message request should only ever be `' . ServerRequestInterface::class . '`');
}
// Check our incoming content type
$isApplicationJson = false;
$contentTypes = $request->getHeader('Content-Type');
// We only respect application/json if it's the first entry without any preference weighting
// as that's what Nexmo send
if (count($contentTypes) && $contentTypes[0] === 'application/json') {
$isApplicationJson = true;
}
switch($request->getMethod()){
case 'POST':
$params = $isApplicationJson ? json_decode((string)$request->getBody(), true) : $request->getParsedBody();
break;
case 'GET':
$params = $request->getQueryParams();
break;
default:
$params = [];
break;
}
return $params;
}
public function getFrom()
{
if($this->getRequest()){
return $this['msisdn'];
} else {
return $this['from'];
}
}
public function getTo()
{
return $this['to'];
}
public function getMessageId()
{
if(isset($this->id)){
return $this->id;
}
return $this['messageId'];
}
public function isValid()
{
return (bool) $this->getMessageId();
}
public function getBody()
{
if($this->getRequest()){
return $this['text'];
} else {
return $this['body'];
}
}
public function getType()
{
return $this['type'];
}
public function getAccountId()
{
return $this['account-id'];
}
public function getNetwork()
{
return $this['network'];
}
/**
* Allow the object to access the data from the API response, a sent API request, or the user set data that the
* request will be created from - in that order.
*
* @param mixed $offset
* @return bool
* @throws \Exception
*/
public function offsetExists($offset)
{
$response = $this->getResponseData();
if(isset($this->index)){
$response = $response['items'][$this->index];
}
$request = $this->getRequestData();
$dirty = $this->getRequestData(false);
return isset($response[$offset]) || isset($request[$offset]) || isset($dirty[$offset]);
}
/**
* Allow the object to access the data from the API response, a sent API request, or the user set data that the
* request will be created from - in that order.
*
* @param mixed $offset
* @return mixed
* @throws \Exception
*/
public function offsetGet($offset)
{
$response = $this->getResponseData();
if(isset($this->index)){
$response = $response['items'][$this->index];
}
$request = $this->getRequestData();
$dirty = $this->getRequestData(false);
if(isset($response[$offset])){
return $response[$offset];
}
if(isset($request[$offset])){
return $request[$offset];
}
if(isset($dirty[$offset])){
return $dirty[$offset];
}
}
/**
* All properties are read only.
*
* @param mixed $offset
* @param mixed $value
*/
public function offsetSet($offset, $value)
{
throw $this->getReadOnlyException($offset);
}
/**
* All properties are read only.
*
* @param mixed $offset
*/
public function offsetUnset($offset)
{
throw $this->getReadOnlyException($offset);
}
/**
* All properties are read only.
*
* @param $offset
* @return \RuntimeException
*/
protected function getReadOnlyException($offset)
{
return new \RuntimeException(sprintf(
'can not modify `%s` using array access',
$offset
));
}
}

View File

@@ -0,0 +1,375 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Message;
use Nexmo\Message\EncodingDetector;
use Nexmo\Entity\JsonResponseTrait;
use Nexmo\Entity\Psr7Trait;
use Nexmo\Entity\RequestArrayTrait;
/**
* Abstract Message
*
* Extended by concrete message types (text, binary, etc).
*/
class Message implements MessageInterface, \Countable, \ArrayAccess, \Iterator
{
use Psr7Trait;
use JsonResponseTrait;
use RequestArrayTrait;
use CollectionTrait;
const TYPE = null;
const CLASS_FLASH = 0;
protected $responseParams = [
'status',
'message-id',
'to',
'remaining-balance',
'message-price',
'network'
];
protected $current = 0;
protected $id;
protected $autodetectEncoding = false;
/**
* @param string $idOrTo Message ID or E.164 (international) formatted number to send the message
* @param null|string $from Number or name the message is from
* @param array $additional Additional API Params
*/
public function __construct($idOrTo, $from = null, $additional = [])
{
if(is_null($from)){
$this->id = $idOrTo;
return;
}
$this->requestData['to'] = (string) $idOrTo;
$this->requestData['from'] = (string) $from;
if(static::TYPE){
$this->requestData['type'] = static::TYPE;
}
$this->requestData = array_merge($this->requestData, $additional);
}
public function requestDLR($dlr = true)
{
return $this->setRequestData('status-report-req', $dlr ? 1 : 0);
}
public function setCallback($callback) {
return $this->setRequestData('callback', (string) $callback);
}
public function setClientRef($ref)
{
return $this->setRequestData('client-ref', (string) $ref);
}
public function setNetwork($network)
{
return $this->setRequestData('network-code', (string) $network);
}
public function setTTL($ttl)
{
return $this->setRequestData('ttl', (int) $ttl);
}
public function setClass($class)
{
return $this->setRequestData('message-class', $class);
}
public function enableEncodingDetection()
{
$this->autodetectEncoding = true;
}
public function disableEncodingDetection()
{
$this->autodetectEncoding = false;
}
public function count()
{
$data = $this->getResponseData();
if(!isset($data['messages'])){
return 0;
}
return count($data['messages']);
}
public function getMessageId($index = null)
{
if(isset($this->id)){
return $this->id;
}
return $this->getMessageData('message-id', $index);
}
public function getStatus($index = null)
{
return $this->getMessageData('status', $index);
}
public function getFinalStatus($index = null)
{
return $this->getMessageData('final-status', $index);
}
public function getTo($index = null)
{
$data = $this->getResponseData();
//check if this is data from a send request
//(which also has a status, but it's not the same)
if(isset($data['messages'])){
return $this->getMessageData('to', $index);
}
return $this['to'];
}
public function getRemainingBalance($index = null)
{
return $this->getMessageData('remaining-balance', $index);
}
public function getPrice($index = null)
{
$data = $this->getResponseData();
//check if this is data from a send request
//(which also has a status, but it's not the same)
if(isset($data['messages'])){
return $this->getMessageData('message-price', $index);
}
return $this['price'];
}
public function getNetwork($index = null)
{
return $this->getMessageData('network', $index);
}
public function getDeliveryStatus()
{
$data = $this->getResponseData();
//check if this is data from a send request
//(which also has a status, but it's not the same)
if(isset($data['messages'])){
return;
}
return $this['status'];
}
public function getFrom()
{
return $this['from'];
}
public function getBody()
{
return $this['body'];
}
public function getDateReceived()
{
return new \DateTime($this['date-received']);
}
public function getDeliveryError()
{
return $this['error-code'];
}
public function getDeliveryLabel()
{
return $this['error-code-label'];
}
public function isEncodingDetectionEnabled()
{
return $this->autodetectEncoding;
}
protected function getMessageData($name, $index = null)
{
if(!isset($this->response)){
return null;
}
$data = $this->getResponseData();
if(is_null($index)){
$index = $this->count() -1;
}
if (isset($data['messages'])) {
return $data['messages'][$index][$name];
}
return isset($data[$name]) ? $data[$name] : null;
}
protected function preGetRequestDataHook()
{
// If $autodetectEncoding is true, we want to set the `type`
// field in our payload
if ($this->isEncodingDetectionEnabled()) {
$this->requestData['type'] = $this->detectEncoding();
}
}
protected function detectEncoding()
{
if (!isset($this->requestData['text'])) {
return static::TYPE;
}
// Auto detect unicode messages
$detector = new EncodingDetector;
if ($detector->requiresUnicodeEncoding($this->requestData['text'])){
return Unicode::TYPE;
}
return static::TYPE;
}
public function offsetExists($offset)
{
$response = $this->getResponseData();
if(isset($this->index)){
$response = $response['items'][$this->index];
}
$request = $this->getRequestData();
$dirty = $this->getRequestData(false);
if(isset($response[$offset]) || isset($request[$offset]) || isset($dirty[$offset])){
return true;
}
//provide access to split messages by virtual index
if(is_int($offset) && $offset < $this->count()){
return true;
}
return false;
}
public function offsetGet($offset)
{
$response = $this->getResponseData();
if(isset($this->index)){
$response = $response['items'][$this->index];
}
$request = $this->getRequestData();
$dirty = $this->getRequestData(false);
if(isset($response[$offset])){
return $response[$offset];
}
//provide access to split messages by virtual index, if there is data
if(isset($response['messages'])){
if(is_int($offset) && isset($response['messages'][$offset])){
return $response['messages'][$offset];
}
$index = $this->count() -1;
if(isset($response['messages'][$index]) && isset($response['messages'][$index][$offset])){
return $response['messages'][$index][$offset];
}
}
if(isset($request[$offset])){
return $request[$offset];
}
if(isset($dirty[$offset])){
return $dirty[$offset];
}
}
public function offsetSet($offset, $value)
{
throw $this->getReadOnlyException($offset);
}
public function offsetUnset($offset)
{
throw $this->getReadOnlyException($offset);
}
protected function getReadOnlyException($offset)
{
return new \RuntimeException(sprintf(
'can not modify `%s` using array access',
$offset
));
}
public function current()
{
if(!isset($this->response)){
return null;
}
$data = $this->getResponseData();
return $data['messages'][$this->current];
}
public function next()
{
$this->current++;
}
public function key()
{
if(!isset($this->response)){
return null;
}
return $this->current;
}
public function valid()
{
if(!isset($this->response)){
return null;
}
$data = $this->getResponseData();
return isset($data['messages'][$this->current]);
}
public function rewind()
{
$this->current = 0;
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Message;
interface MessageInterface extends \Nexmo\Entity\EntityInterface
{
public function getMessageId();
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Nexmo\Message;
class Query
{
protected $params = [];
public function __construct(\DateTime $date, $to)
{
$this->params['date'] = $date->format('Y-m-d');
$this->params['to'] = $to;
}
public function getParams()
{
return $this->params;
}
}

View File

@@ -0,0 +1,125 @@
<?php
/**
* Nexmo Client Library for PHP
*
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
*/
namespace Nexmo\Message\Response;
use Nexmo\Client\Response\Response;
use Nexmo\Client\Response\Error;
use Nexmo\Client\Response\ResponseInterface;
class Collection extends Response implements ResponseInterface, \Countable, \Iterator
{
/**
* @var int
*/
protected $count;
/**
* @var array
*/
protected $data;
/**
* @var Message[]
*/
protected $messages = array();
/**
* @var int
*/
protected $position = 0;
public function __construct(array $data)
{
$this->expected = array('message-count', 'messages');
$return = parent::__construct($data);
$this->count = $data['message-count'];
if(count($data['messages']) != $data['message-count']){
throw new \RuntimeException('invalid message count');
}
foreach($data['messages'] as $message){
if(0 != $message['status']){
$this->messages[] = new Error($message);
} else {
$this->messages[] = new Message($message);
}
}
$this->data = $data;
return $return;
}
public function getMessages()
{
return $this->messages;
}
public function isSuccess()
{
foreach($this->messages as $message){
if($message instanceof Error){
return false;
}
}
return true;
}
public function count()
{
return $this->count;
}
/**
* @link http://php.net/manual/en/iterator.current.php
* @return Message
*/
public function current()
{
return $this->messages[$this->position];
}
/**
* @link http://php.net/manual/en/iterator.next.php
* @return void
*/
public function next()
{
$this->position++;
}
/**
* @link http://php.net/manual/en/iterator.key.php
* @return int
*/
public function key()
{
return $this->position;
}
/**
* @link http://php.net/manual/en/iterator.valid.php
* @return boolean
*/
public function valid()
{
return $this->position < $this->count;
}
/**
* @link http://php.net/manual/en/iterator.rewind.php
* @return void
*/
public function rewind()
{
$this->position = 0;
}
}

Some files were not shown because too many files have changed in this diff Show More