diff --git a/src/BroadcasttClient.php b/src/BroadcasttClient.php index 2275afc..45fc78a 100644 --- a/src/BroadcasttClient.php +++ b/src/BroadcasttClient.php @@ -5,24 +5,26 @@ use Broadcastt\Exception\InvalidArgumentException; use Broadcastt\Exception\InvalidChannelNameException; use Broadcastt\Exception\InvalidDataException; +use Broadcastt\Exception\InvalidHostException; use Broadcastt\Exception\InvalidSocketIdException; +use Broadcastt\Exception\JsonEncodeException; use Broadcastt\Exception\TooManyChannelsException; -use Broadcastt\Exception\InvalidHostException; use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Psr7\Request; -use GuzzleHttp\Psr7\Response; -use function GuzzleHttp\Psr7\stream_for; use GuzzleHttp\Psr7\Uri; use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\UriInterface; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; use Psr\Log\LogLevel; +use function GuzzleHttp\Psr7\stream_for; /** * Class BroadcasttClient + * * @package Broadcastt * * @property-read int $appId Id of your application @@ -74,7 +76,6 @@ class BroadcasttClient implements LoggerAwareInterface 'timeout' => null, ]; - /** * Initializes a new Broadcastt instance with key, secret and ID of an app. * @@ -101,6 +102,7 @@ public function __construct($appId, $appKey, $appSecret, $appCluster = 'eu') * Clients can be instantiated from a URI. For example: "http://key:secret@eu.broadcastt.com/apps/{appId}" * * @param string|UriInterface $uri + * * @return BroadcasttClient */ public static function fromUri($uri) @@ -126,7 +128,7 @@ public static function fromUri($uri) throw new InvalidArgumentException('Secret part of user info is missing from URI'); } - list($appKey, $appSecret) = $userInfo; + [$appKey, $appSecret] = $userInfo; $client = new BroadcasttClient($appId, $appKey, $appSecret); $client->scheme = $uri->getScheme(); @@ -233,7 +235,7 @@ private function buildRequest($domain, $path, $requestMethod = 'GET', $queryPara * * @param RequestInterface $request * - * @return Response + * @return ResponseInterface * @throws GuzzleException */ private function sendRequest($request) @@ -275,6 +277,7 @@ private function buildUri() * Check if the status code indicates the request was successful. * * @param $status + * * @return bool */ private function isSuccessStatusCode($status) @@ -345,7 +348,8 @@ public static function httpBuildQuery($array) * @param bool $jsonEncoded [optional] * * @return bool - * invalid + * @throws GuzzleException + * @throws JsonEncodeException on JSON encode failure. */ public function trigger($channels, $name, $data, $socketId = null, $jsonEncoded = false) { @@ -356,33 +360,28 @@ public function trigger($channels, $name, $data, $socketId = null, $jsonEncoded $this->validateChannels($channels); $this->validateSocketId($socketId); + $jsonData = $data; if (!$jsonEncoded) { - $data = json_encode($data); + $jsonData = json_encode($data); - // json_encode might return false on failure - if (!$data) { - $this->log('Failed to perform json_encode on the the provided data: {error}', [ - 'error' => $data, - ], LogLevel::ERROR); + // json_encode returns false on failure + if ($jsonData === false) { + throw new JsonEncodeException($data, json_last_error_msg(), json_last_error()); } } $postParams = []; $postParams['name'] = $name; - $postParams['data'] = $data; + $postParams['data'] = $jsonData; $postParams['channels'] = $channels; if ($socketId !== null) { $postParams['socket_id'] = $socketId; } - try { - $response = $this->post('/event', [], $postParams); + $response = $this->post('/event', [], $postParams); - return $this->isSuccessStatusCode($response->getStatusCode()); - } catch (GuzzleException $e) { - return false; - } + return $this->isSuccessStatusCode($response->getStatusCode()); } /** @@ -392,6 +391,8 @@ public function trigger($channels, $name, $data, $socketId = null, $jsonEncoded * @param bool $jsonEncoded [optional] Defines if the data is already encoded * * @return bool + * @throws GuzzleException + * @throws JsonEncodeException on JSON encode failure. */ public function triggerBatch($batch = [], $jsonEncoded = false) { @@ -404,21 +405,23 @@ public function triggerBatch($batch = [], $jsonEncoded = false) } if (!$jsonEncoded) { - $batch[$key]['data'] = json_encode($event['data']); + $jsonData = json_encode($event['data']); + + // json_encode returns false on failure + if ($jsonData === false) { + throw new JsonEncodeException($event['data'], json_last_error_msg(), json_last_error()); + } + + $batch[$key]['data'] = $jsonData; } } $postParams = []; $postParams['batch'] = $batch; + $response = $this->post('/events', [], $postParams); - try { - $response = $this->post('/events', [], $postParams); - - return $this->isSuccessStatusCode($response->getStatusCode()); - } catch (GuzzleException $e) { - return false; - } + return $this->isSuccessStatusCode($response->getStatusCode()); } /** @@ -429,7 +432,7 @@ public function triggerBatch($batch = [], $jsonEncoded = false) * @param array $queryParams API query params (see https://broadcastt.xyz/docs/References-‐-Rest-API) * @param array $postParams API post params (see https://broadcastt.xyz/docs/References-‐-Rest-API) * - * @return Response + * @return ResponseInterface * @throws GuzzleException */ private function post($path, $queryParams = [], $postParams = []) @@ -453,7 +456,7 @@ private function post($path, $queryParams = [], $postParams = []) * @param string $path Path excluding /apps/{appId} * @param array $queryParams API query params (see https://broadcastt.xyz/docs/References-‐-Rest-API) * - * @return Response See Broadcastt API docs + * @return ResponseInterface See Broadcastt API docs * @throws GuzzleException */ public function get($path, $queryParams = []) @@ -568,5 +571,4 @@ public function __set($name, $value) $this->modifiers[$name] = $value; } } - } diff --git a/src/Exception/JsonEncodeException.php b/src/Exception/JsonEncodeException.php new file mode 100644 index 0000000..1729cfd --- /dev/null +++ b/src/Exception/JsonEncodeException.php @@ -0,0 +1,22 @@ +data = $data; + } + + /** + * @return mixed + */ + public function getData() + { + return $this->data; + } +} diff --git a/tests/Feature/BroadcasttTriggerBatchTest.php b/tests/Feature/BroadcasttTriggerBatchTest.php index 3fe831a..9ce8c0c 100644 --- a/tests/Feature/BroadcasttTriggerBatchTest.php +++ b/tests/Feature/BroadcasttTriggerBatchTest.php @@ -7,7 +7,9 @@ use Broadcastt\Exception\InvalidDataException; use Broadcastt\Exception\InvalidHostException; use Broadcastt\Exception\InvalidSocketIdException; +use Broadcastt\Exception\JsonEncodeException; use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; use GuzzleHttp\Middleware; @@ -180,7 +182,7 @@ public function testCanNotTriggerBatchWithInvalidHost() $this->client->triggerBatch($batch); } - public function testCanTriggerBatchHandlePayloadTooLargeResponse() + public function testCanTriggerBatchThrowExceptionOnPayloadTooLargeResponse() { $mockHandler = new MockHandler([ new Response(413, [], '{}'), @@ -202,8 +204,8 @@ public function testCanTriggerBatchHandlePayloadTooLargeResponse() $batch = []; $batch[] = ['channel' => 'test-channel', 'name' => 'test-event', 'data' => ['test-key' => 'test-val']]; $batch[] = ['channel' => 'test-channel2', 'name' => 'test-event2', 'data' => ['test-key' => 'test-val2']]; - $response = $this->client->triggerBatch($batch); - $this->assertFalse($response); + $this->expectException(GuzzleException::class); + $this->client->triggerBatch($batch); } public function testCanTriggerBatchHandlePayloadTooLargeResponseWhenGuzzleExceptionsAreDisabled() @@ -233,4 +235,19 @@ public function testCanTriggerBatchHandlePayloadTooLargeResponseWhenGuzzleExcept $this->assertFalse($response); } + public function testCanTriggerBatchThrowExceptionOnJsonEncodeFailure() + { + // data from https://www.php.net/manual/en/function.json-last-error.php + $data = "\xB1\x31"; + + $batch = []; + $batch[] = ['channel' => 'test-channel', 'name' => 'test-event', 'data' => $data]; + $this->expectException(JsonEncodeException::class); + try { + $this->client->triggerBatch($batch); + } catch (JsonEncodeException $e) { + $this->assertEquals($e->getData(), $data); + throw $e; + } + } } diff --git a/tests/Feature/BroadcasttTriggerTest.php b/tests/Feature/BroadcasttTriggerTest.php index 8de648d..96c714a 100644 --- a/tests/Feature/BroadcasttTriggerTest.php +++ b/tests/Feature/BroadcasttTriggerTest.php @@ -4,9 +4,11 @@ use Broadcastt\BroadcasttClient; use Broadcastt\Exception\InvalidSocketIdException; +use Broadcastt\Exception\JsonEncodeException; use Broadcastt\Exception\TooManyChannelsException; use Broadcastt\Exception\InvalidHostException; use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; use GuzzleHttp\Middleware; @@ -165,7 +167,6 @@ public function testCanNotTriggerWithInvalidChannel($invalidChannel) $this->client->setGuzzleClient($guzzleClient); $this->expectException(InvalidArgumentException::class); - $this->client->trigger($invalidChannel, 'test-event', ''); } @@ -181,12 +182,11 @@ public function testCanNotTriggerWithMoreThanHundredChannel() $this->client->setGuzzleClient($guzzleClient); - $this->expectException(TooManyChannelsException::class); - $channels = []; for ($i = 0; $i < 101; $i++) { $channels[] = 'test-channel' . $i; } + $this->expectException(TooManyChannelsException::class); $this->client->trigger($channels, 'test-event', ''); } @@ -207,7 +207,6 @@ public function testCanNotTriggerWithInvalidSocketId($invalidSocketId) $this->client->setGuzzleClient($guzzleClient); $this->expectException(InvalidSocketIdException::class); - $this->client->trigger('test-channel', 'test-event', '', $invalidSocketId); } @@ -225,11 +224,10 @@ public function testCanNotTriggerWithInvalidHost() $this->client->host = 'http://test.xyz'; $this->expectException(InvalidHostException::class); - $this->client->trigger('test-channel', 'test-event', ''); } - public function testCanTriggerHandlePayloadTooLargeResponse() + public function testCanTriggerThrowExceptionOnPayloadTooLargeResponse() { $mockHandler = new MockHandler([ new Response(413, [], '{}'), @@ -248,8 +246,8 @@ public function testCanTriggerHandlePayloadTooLargeResponse() $this->client->setGuzzleClient($guzzleClient); - $response = $this->client->trigger('test-channel', 'test-event', ''); - $this->assertFalse($response); + $this->expectException(GuzzleException::class); + $this->client->trigger('test-channel', 'test-event', ''); } public function testCanTriggerHandlePayloadTooLargeResponseWhenGuzzleExceptionsAreDisabled() @@ -276,4 +274,17 @@ public function testCanTriggerHandlePayloadTooLargeResponseWhenGuzzleExceptionsA $this->assertFalse($response); } + public function testCanTriggerThrowExceptionOnJsonEncodeFailure() + { + // data from https://www.php.net/manual/en/function.json-last-error.php + $data = "\xB1\x31"; + + $this->expectException(JsonEncodeException::class); + try { + $this->client->trigger('test-channel', 'test-event', $data); + } catch (JsonEncodeException $e) { + $this->assertEquals($e->getData(), $data); + throw $e; + } + } }