Skip to content

Commit

Permalink
Merge master->develop for 1.0.0 release
Browse files Browse the repository at this point in the history
  • Loading branch information
mheap committed Aug 29, 2017
2 parents 51a4340 + 610fba3 commit 4a197ea
Show file tree
Hide file tree
Showing 29 changed files with 732 additions and 75 deletions.
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ language: php
php:
- '5.6'
- '7.0'
- '7.1'
- '7.2'

before_script:
- composer install

script: ./vendor/bin/phpunit -v --configuration ./phpunit.xml.dist
script: ./vendor/bin/phpunit -v --configuration ./phpunit.xml.dist
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
This is a high level list of changes for each version of the Nexmo PHP client.

### 1.0.0 (Not released yet) [Show Diff](https://github.com/Nexmo/nexmo-php/compare/develop)

* Renamed `Nexmo\Client\Credentials\SharedSecret` to `Nexmo\Client\Credentials\SignatureSecret` [#39](https://github.com/Nexmo/nexmo-php/pull/39)
* Added ability to set callback URL via `setCallback()` [#34](https://github.com/Nexmo/nexmo-php/pull/34)
* Rate limit error when searching messages [#37](https://github.com/Nexmo/nexmo-php/pull/37)
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ Nexmo Client Library for PHP
============================
[![Build Status](https://api.travis-ci.org/Nexmo/nexmo-php.svg?branch=master)](https://travis-ci.org/Nexmo/nexmo-php)

*This library requires a minimum PHP version of 5.6*

This is the PHP client library for use Nexmo's API. To use this, you'll need a Nexmo account. Sign up [for free at
nexmo.com][signup]. This is currently a beta release, see [contributing](#contributing) for more information.

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"email": "[email protected]"
},
"require": {
"php": ">=5.4",
"php": ">=5.6",
"php-http/client-implementation": "^1.0",
"zendframework/zend-diactoros": "^1.3",
"php-http/guzzle6-adapter": "^1.0",
Expand Down
19 changes: 17 additions & 2 deletions src/Call/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,25 @@ 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($body['error_title'], $status);
$e = new Exception\Request($errorTitle, $status);
} elseif($status >= 500 AND $status < 600) {
$e = new Exception\Server($body['error_title'], $status);
$e = new Exception\Server($errorTitle, $status);
} else {
$e = new Exception\Exception('Unexpected HTTP Status Code');
throw $e;
Expand Down
94 changes: 86 additions & 8 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
use Nexmo\Client\Credentials\CredentialsInterface;
use Nexmo\Client\Credentials\Keypair;
use Nexmo\Client\Credentials\OAuth;
use Nexmo\Client\Credentials\SharedSecret;
use Nexmo\Client\Credentials\SignatureSecret;
use Nexmo\Client\Factory\FactoryInterface;
use Nexmo\Client\Factory\MapFactory;
use Nexmo\Client\Response\Response;
Expand All @@ -22,6 +22,7 @@
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.
Expand Down Expand Up @@ -76,7 +77,7 @@ public function __construct(CredentialsInterface $credentials, $options = array(
$this->setHttpClient($client);

//make sure we know how to use the credentials
if(!($credentials instanceof Container) && !($credentials instanceof Basic) && !($credentials instanceof SharedSecret) && !($credentials instanceof OAuth)){
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));
}

Expand Down Expand Up @@ -135,7 +136,7 @@ public function setFactory(FactoryInterface $factory)
* @param Signature $signature
* @return RequestInterface
*/
public static function signRequest(RequestInterface $request, SharedSecret $credentials)
public static function signRequest(RequestInterface $request, SignatureSecret $credentials)
{
switch($request->getHeaderLine('content-type')){
case 'application/json':
Expand All @@ -144,7 +145,7 @@ public static function signRequest(RequestInterface $request, SharedSecret $cred
$content = $body->getContents();
$params = json_decode($content, true);
$params['api_key'] = $credentials['api_key'];
$signature = new Signature($params, $credentials['shared_secret']);
$signature = new Signature($params, $credentials['signature_secret']);
$body->rewind();
$body->write(json_encode($signature->getSignedParams()));
break;
Expand All @@ -155,7 +156,7 @@ public static function signRequest(RequestInterface $request, SharedSecret $cred
$params = [];
parse_str($content, $params);
$params['api_key'] = $credentials['api_key'];
$signature = new Signature($params, $credentials['shared_secret']);
$signature = new Signature($params, $credentials['signature_secret']);
$params = $signature->getSignedParams();
$body->rewind();
$body->write(http_build_query($params, null, '&'));
Expand All @@ -164,7 +165,7 @@ public static function signRequest(RequestInterface $request, SharedSecret $cred
$query = [];
parse_str($request->getUri()->getQuery(), $query);
$query['api_key'] = $credentials['api_key'];
$signature = new Signature($query, $credentials['shared_secret']);
$signature = new Signature($query, $credentials['signature_secret']);
$request = $request->withUri($request->getUri()->withQuery(http_build_query($signature->getSignedParams())));
break;
}
Expand Down Expand Up @@ -206,6 +207,83 @@ public static function authRequest(RequestInterface $request, Basic $credentials
}

/**
* 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 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
Expand All @@ -220,8 +298,8 @@ public function send(\Psr\Http\Message\RequestInterface $request)
$request = self::authRequest($request, $this->credentials->get(Basic::class));
}
} elseif($this->credentials instanceof Keypair){
$request = $request->withHeader('Authorization', 'Bearer ' . $this->credentials->get(Keypair::class)->generateJwt());
} elseif($this->credentials instanceof SharedSecret){
$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);
Expand Down
2 changes: 1 addition & 1 deletion src/Client/Credentials/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Container extends AbstractCredentials implements CredentialsInterface
{
protected $types = [
Basic::class,
SharedSecret::class,
SignatureSecret::class,
Keypair::class
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@

namespace Nexmo\Client\Credentials;

class SharedSecret extends AbstractCredentials implements CredentialsInterface
class SignatureSecret extends AbstractCredentials implements CredentialsInterface
{
/**
* Create a credential set with an API key and shared secret.
* Create a credential set with an API key and signature secret.
*
* @param string $key API Key
* @param string $secret Shared Secret
* @param string $signature_secret Signature Secret
*/
public function __construct($key, $secret)
public function __construct($key, $signature_secret)
{
$this->credentials['api_key'] = $key;
$this->credentials['shared_secret'] = $secret;
$this->credentials['signature_secret'] = $signature_secret;
}
}
}
7 changes: 7 additions & 0 deletions src/Entity/RequestArrayTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ public function getRequestData($sent = true)
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;
}

Expand Down
38 changes: 38 additions & 0 deletions src/Message/AutoDetect.php
Original file line number Diff line number Diff line change
@@ -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;
}
}
11 changes: 6 additions & 5 deletions src/Message/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,19 +171,20 @@ public function search($idOrMessage)
$response = $this->client->send($request);

$response->getBody()->rewind();

$data = json_decode($response->getBody()->getContents(), true);

if(!$data){
throw new Exception\Request('no message found for `' . $id . '`');
}
$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':
Expand Down
48 changes: 48 additions & 0 deletions src/Message/EncodingDetector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?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 = [
0x0040, 0x00A3, 0x0024, 0x00A5, 0x00E8, 0x00E9, 0x00F9, 0x00EC, 0x00F2, 0x00E7, 0x000A, 0x00D8, 0x00F8, 0x000D, 0x00C5, 0x00E5, 0x0394,
0x005F, 0x03A6, 0x0393, 0x039B, 0x03A9, 0x03A0, 0x03A8, 0x03A3, 0x0398, 0x039E, 0x00A0, 0x000C, 0x005E, 0x007B, 0x007D, 0x005C, 0x005B,
0x007E, 0x005D, 0x007C, 0x20AC, 0x00C6, 0x00E6, 0x00DF, 0x00C9, 0x0020, 0x0021, 0x0022, 0x0023, 0x00A4, 0x0025, 0x0026, 0x0027, 0x0028,
0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039,
0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x00A1, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A,
0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00C4,
0x00D6, 0x00D1, 0x00DC, 0x00A7, 0x00BF, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C,
0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00E4, 0x00F6, 0x00F1,
0x00FC, 0x00E0
];

// 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(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;
}, $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);
}


}

Loading

0 comments on commit 4a197ea

Please sign in to comment.