config = new bird_c(); } /** * Ensure module is enabled * @throws Exception */ function requireModuleEnabled(): void { if (!$this->config->enabled->isTrue()) { throw new Exception('The bird module is not enabled'); } } /** * Ensure API key is present * @throws Exception */ function requireValidApiKey(): void { $k = $this->config->api_key->getVariableValue(); if ($k === null || $k === '') { throw new Exception('Invalid API key defined in the config (bird_api_key_c)'); } } /** * Ensure base URL is defined * @throws Exception */ function requireValidServerURL(): void { $u = $this->config->server_url->getVariableValue(); if ($u === null || $u === '') { throw new Exception('Invalid server URL defined in the config (bird_server_url_c)'); } } /** * Send a request to Bird API * @param string $endpoint e.g. "/v1/devices" * @param array $data request body * @param string $method HTTP method (currently only POST supported) * @return object|array|null * @throws Exception */ function sendRequest(string $endpoint, array $data = [], string $method = 'POST'): object|array|null { $this->requireModuleEnabled(); $this->requireValidApiKey(); $this->requireValidServerURL(); return match (strtoupper($method)) { 'POST' => $this->sendPostRequest($endpoint, $data), 'GET' => $this->sendGetRequest($endpoint, $data), default => throw new Exception('Invalid request method'), }; } /** * Send POST request * @param string $endpoint * @param array $data * @return array|object|null * @throws Exception */ function sendPostRequest(string $endpoint, array $data): array|object|null { $this->requireModuleEnabled(); $this->requireValidApiKey(); $this->requireValidServerURL(); $url = rtrim($this->config->server_url->getVariableValue(), '/') . $endpoint; $headers = [ 'Content-Type: application/json', 'Authorization: Bearer ' . $this->config->api_key->getVariableValue(), ]; $body = json_encode($data); $result = $this->doHttpRequest('POST', $url, $headers, $body); $status = (int)($result['status_code'] ?? 0); $response = $result['body'] ?? ''; if ($status >= 400) { throw new Exception('Bird API request failed with status ' . $status); } if ($response === '' || $response === false || $response === null) { return null; } $decoded = json_decode($response); return $decoded ?? $response; } /** * Low-level HTTP transport (curl). Tests can override this to stub network. * @param string $method * @param string $url * @param array $headers * @param string $body * @return array{status_code:int, body:string|false} * @throws Exception */ protected function doHttpRequest(string $method, string $url, array $headers, string $body = ''): array { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $upper = strtoupper($method); if ($upper === 'POST') { curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $body); } else { curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $upper); } curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); $resp = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); if (curl_errno($ch)) { $err = curl_error($ch); curl_close($ch); throw new Exception('cURL error: ' . $err); } curl_close($ch); return [ 'status_code' => (int)$code, 'body' => $resp, ]; } /** * Send GET request * @param string $endpoint * @param array $query * @return array|object|null * @throws Exception */ function sendGetRequest(string $endpoint, array $query = []): array|object|null { $this->requireModuleEnabled(); $this->requireValidApiKey(); $this->requireValidServerURL(); $base = rtrim($this->config->server_url->getVariableValue(), '/'); $url = $base . $endpoint; if (!empty($query)) { $qs = http_build_query($query); $url .= (str_contains($url, '?') ? '&' : '?') . $qs; } $headers = [ 'Content-Type: application/json', 'Authorization: Bearer ' . $this->config->api_key->getVariableValue(), ]; $result = $this->doHttpRequest('GET', $url, $headers); $status = (int)($result['status_code'] ?? 0); $response = $result['body'] ?? ''; if ($status >= 400) { throw new Exception('Bird API request failed with status ' . $status); } if ($response === '' || $response === false || $response === null) { return null; } $decoded = json_decode($response); return $decoded ?? $response; } }