Skip to content
50 changes: 50 additions & 0 deletions src/Twilio/ApiV1Version.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace Twilio;

use Twilio\Exceptions\RestException;
use Twilio\Exceptions\RestExceptionV1;
use Twilio\Exceptions\TwilioException;
use Twilio\Http\Response;

class ApiV1Version extends Version {

protected $apiVersion;

/**
* @param Domain $domain
* @param ?string $version
*/
public function __construct(Domain $domain, ?string $version) {
parent::__construct($domain);
$this->version = $version;
$this->apiVersion = "V1";
}

/**
* Create the best possible exception for the response as per Twilio API Standard V1.
*
* Attempts to parse the response for Twilio Standard error as defined in Twilio API Standards V1
* and use those to populate the exception, falls back to generic error message and
* HTTP status code.
*
* @param Response $response Error response
* @param string $header Header for exception message
* @return TwilioException
*/
protected function exception(Response $response, string $header): TwilioException {
$message = '[HTTP ' . $response->getStatusCode() . '] ' . $header;

$content = $response->getContent();
if (\is_array($content)) {
$message .= isset($content['message']) ? ': ' . $content['message'] : '';
$code = $content['code'] ?? $response->getStatusCode();
$httpStatusCode = $content['httpStatusCode'] ?? $response->getStatusCode();
$params = $content['params'] ?? [];
$userError = $content['userError'] ?? false;
return new RestExceptionV1($code, $message, $httpStatusCode, $params, $userError);
}

return new RestExceptionV1($response->getStatusCode(), $message, $response->getStatusCode());
}
}
54 changes: 54 additions & 0 deletions src/Twilio/Exceptions/RestExceptionV1.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php


namespace Twilio\Exceptions;


class RestExceptionV1 extends TwilioException {
protected $code;
protected $message;
protected $httpStatusCode;
protected $params;
protected $userError;

/**
* Construct the exception.
* @param int $code A unique error code.
* @param string $message A human-readable error message.
* @param int $httpStatusCode The HTTP status code.
* @param array $params [optional] More information about the error.
* @param bool $userError [optional] true if it is an error that depends on the end users actions
*/
public function __construct(int $code, string $message, int $httpStatusCode, array $params = [], bool $userError = false) {
$this->code = $code;
$this->message = $message;
$this->httpStatusCode = $httpStatusCode;
$this->params = $params;
$this->userError = $userError;
parent::__construct($message, $code);
}

/**
* Get the HTTP Status Code of the RestException
* @return int HTTP Status Code
*/
public function getHttpStatusCode(): int {
return $this->httpStatusCode;
}

/**
* Get more information to additional information about the error
* @return array additional information about the error
*/
public function getParams(): array {
return $this->params;
}

/**
* Get the user error flag of the RestException
* @return bool user error flag
*/
public function getUserError(): bool {
return $this->userError;
}
}
67 changes: 38 additions & 29 deletions src/Twilio/Version.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
/**
* @var string $version
*/
protected $version;
public $version;

/**
* @param Domain $domain
Expand Down Expand Up @@ -82,7 +82,7 @@
$content = $response->getContent();
if (\is_array($content)) {
$message .= isset($content['message']) ? ': ' . $content['message'] : '';
$code = isset($content['code']) ? $content['code'] : $response->getStatusCode();
$code = $content['code'] ?? $response->getStatusCode();
$moreInfo = $content['more_info'] ?? '';
$details = $content['details'] ?? [];
return new RestException($message, $code, $response->getStatusCode(), $moreInfo, $details);
Expand All @@ -98,22 +98,17 @@
array $params = [], array $data = [], array $headers = [],
?string $username = null, ?string $password = null,
?int $timeout = null) {
$response = $this->request(
$response = $this->handleException(
$method,
$uri,
$params,
$data,
$headers,
$username,
$password,
$timeout
$timeout,
"fetch"
);

// 3XX response codes are allowed here to allow for 307 redirect from Deactivations API.
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 400) {
throw $this->exception($response, 'Unable to fetch record');
}

return $response->getContent();
}

Expand All @@ -134,21 +129,17 @@
array $params = [], array $data = [], array $headers = [],
?string $username = null, ?string $password = null,
?int $timeout = null) {
$response = $this->request(
$response = $this->handleException(
$method,
$uri,
$params,
$data,
$headers,
$username,
$password,
$timeout
$timeout,
"update"
);

if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
throw $this->exception($response, 'Unable to update record');
}

return $response->getContent();
}

Expand All @@ -159,23 +150,18 @@
array $params = [], array $data = [], array $headers = [],
?string $username = null, ?string $password = null,
?int $timeout = null): bool {
$response = $this->request(
$this->handleException(
$method,
$uri,
$params,
$data,
$headers,
$username,
$password,
$timeout
$timeout,
"delete"
);

// check for 2xx status code is already present here
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
throw $this->exception($response, 'Unable to delete record');
}

return true; // if response code is 2XX, deletion was successful
return true;
}

public function readLimits(?int $limit = null, ?int $pageSize = null): array {
Expand Down Expand Up @@ -219,6 +205,28 @@
array $params = [], array $data = [], array $headers = [],
?string $username = null, ?string $password = null,
?int $timeout = null) {
$response = $this->handleException(
$method,
$uri,
$params,
$data,
$headers,
$username,
$password,
$timeout,
"create"
);
return $response->getContent();
}

/**
* @throws TwilioException
*/
public function handleException(string $method, string $uri,
array $params = [], array $data = [], array $headers = [],
?string $username = null, ?string $password = null,
?int $timeout = null, ?string $operation = ""): Response

Check warning on line 228 in src/Twilio/Version.php

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This function has 9 parameters, which is greater than the 7 authorized.

See more on https://sonarcloud.io/project/issues?id=twilio_twilio-php&issues=AZsC5F-h0wOFECHZVdD9&open=AZsC5F-h0wOFECHZVdD9&pullRequest=898
{
$response = $this->request(
$method,
$uri,
Expand All @@ -230,11 +238,12 @@
$timeout
);

if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
throw $this->exception($response, 'Unable to create record');
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 400) {
$exceptionHeader = 'Unable to ' . $operation . ' record';
throw $this->exception($response, $exceptionHeader);
}

return $response->getContent();
return $response;
}

public function getDomain(): Domain {
Expand Down
46 changes: 46 additions & 0 deletions tests/Twilio/Unit/VersionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@

namespace Twilio\Tests\Unit;

use Twilio\ApiV1Version;
use Twilio\Domain;
use Twilio\Exceptions\RestException;
use Twilio\Exceptions\RestExceptionV1;
use Twilio\Exceptions\TwilioException;
use Twilio\Http\CurlClient;
use Twilio\Http\Response;
use Twilio\Page;
Expand Down Expand Up @@ -440,6 +443,49 @@ public function testRestException(): void {
}
}

public function testRestExceptionV1(): void {
$this->curlClient
->expects(self::once())
->method('request')
->willReturn(new Response(404, '{
"code": 20404,
"message": "The requested resource was not found",
"httpStatusCode": 404,
"userError": true,
"params": {
"twilioErrorCodeUrl": "https://www.twilio.com/docs/errors/20404" }
}'));
try {
$this->version = new ApiV1Version($this->version->getDomain(), $this->version->version);
$this->version->fetch('get', 'http://foo.bar');
self::fail();
}catch (RestExceptionV1 $rex){
self::assertEquals(20404, $rex->getCode());
self::assertEquals(404, $rex->getHttpStatusCode());
self::assertEquals('[HTTP 404] Unable to fetch record: The requested resource was not found', $rex->getMessage());
self::assertEquals(['twilioErrorCodeUrl' => 'https://www.twilio.com/docs/errors/20404'], $rex->getParams());
self::assertTrue($rex->getUserError());
}
}

public function testRestExceptionV1WithoutParams(): void {
$this->curlClient
->expects(self::once())
->method('request')
->willReturn(new Response(400, ''));
try {
$this->version = new ApiV1Version($this->version->getDomain(), $this->version->version);
$this->version->fetch('get', 'http://foo.bar');
self::fail();
}catch (RestExceptionV1 $rex){
self::assertEquals(400, $rex->getCode());
self::assertEquals(400, $rex->getHttpStatusCode());
self::assertEquals('[HTTP 400] Unable to fetch record', $rex->getMessage());
self::assertEmpty($rex->getParams());
self::assertFalse($rex->getUserError());
}
}

public function absoluteUrlProvider(): array {
$cases = $this->relativeUriProvider();
foreach ($cases as &$case) {
Expand Down
Loading