config = new gatewayapi_c(); } /** * @inheritDoc */ public function send(array $to, string $message, bool $ignore_empty = false): bool { $this->requireEnabled(); // If there are no recipients, throw an exception if (empty($to)) { if ($ignore_empty) { return false; } throw new Exception('No recipients provided'); } $data = [ "recipients" => array_map(function ($recipient) { return [ 'msisdn' => $recipient, ]; }, $to), 'message' => $message, 'sender' => $this->config->sender->getVariableValue(), ]; try { self::sendRequest( '/rest/mtsms', 'POST', $data, ); } catch (Exception $exception) { throw new Exception('Failed to send SMS: ' . $exception->getMessage()); } return true; } /** * @inheritDoc */ public function requireEnabled(): void { if (!$this->isEnabled()) { throw new Exception('GatewayAPI is not enabled'); } } /** * @inheritDoc * @throws Exception If the enabled property is not set */ public function isEnabled(): bool { try { return $this->config->enabled->isTrue(); } catch (Exception $exception) { return false; } } /** * @inheritDoc * @throws Exception If the module is not enabled * @throws Exception If the API key is not set * @throws Exception If the sender is not set * @throws Exception If the API URL is not set * @throws Exception If the request fails * @throws Exception If the response is not valid JSON */ public function sendRequest(string $url, string $method, array $data = [], array $headers = []): array { self::requireEnabled(); $json_encode = json_encode($data); if ($json_encode === false) { throw new Exception('Failed to encode data to JSON: ' . json_last_error_msg()); } if (empty($url) || empty($method)) { throw new Exception('URL and method are required'); } // Add the token to the headers return self::curlRequest( $url, $method, $json_encode, [ ...$headers, ...self::getDefaultRequestHeaders(), ] ); } /** * @param string $endpoint The URL endpoint to send the request to (e.g. /rest/mtsms) * @param string $method The HTTP method to use (GET, POST, PUT, DELETE) * @param string $json_encode The JSON-encoded data to send * @param array $headers The headers to send with the request * @param bool $allow_fail If true, the response will be returned even if the HTTP code is not 200, otherwise an exception will be thrown * @throws Exception If the request fails * @throws Exception If the response is not valid JSON */ private function curlRequest(string $endpoint, string $method, string $json_encode, array $headers, bool $allow_fail = false): array { $curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => $this->api_url . $endpoint, CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => $method, CURLOPT_POSTFIELDS => $json_encode, CURLOPT_HTTPHEADER => $headers, ]); $response = curl_exec($curl); $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); if ($http_code !== 200 && !$allow_fail) { throw new Exception('Request failed with HTTP code ' . $http_code . ': ' . $response); } if (empty($response)) { throw new Exception('Empty response from API'); } $response = json_decode($response, true); if (json_last_error() !== JSON_ERROR_NONE) { throw new Exception('Failed to decode JSON response: ' . json_last_error_msg()); } if (!is_array($response)) { throw new Exception('Invalid response format: ' . gettype($response)); } // Check if the response is an array return $response; } /** * @inheritDoc */ public function getDefaultRequestHeaders(): array { return [ 'Authorization: Token ' . $this->config->api_token->getVariableValue(), 'Content-Type: application/json', ]; } }