load(); $this->baseUrl = $_ENV['API_BASE_URL']; $this->username = $_ENV['API_USERNAME']; $this->password = $_ENV['API_PASSWORD']; } public static function getInstance() { if (self::$instance === null) { self::$instance = new VisualLimsApiClient(); } return self::$instance; } private function authenticate($retryCount = 0, $maxRetries = 3) { $ch = curl_init("{$this->baseUrl}/api/authentication/authenticate"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'Username' => $this->username, 'Password' => $this->password ])); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'Accept: application/json' ]); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_VERBOSE, true); $log = fopen(__DIR__ . '/curl_auth_debug.log', 'a') ?: fopen('php://stderr', 'w'); curl_setopt($ch, CURLOPT_STDERR, $log); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curl_error = curl_error($ch); $log_message = date('Y-m-d H:i:s') . " - Auth attempt {$retryCount}: HTTP {$http_code}, Error: {$curl_error}, Response: " . substr($response, 0, 1000) . "\n"; fwrite($log, $log_message); fclose($log); curl_close($ch); if ($response === false || $http_code != 200) { if ($http_code === 400 && strpos($response, 'Cannot persist the object') !== false && $retryCount < $maxRetries) { usleep(500000); // Ritardo di 500ms return $this->authenticate($retryCount + 1, $maxRetries); // Riprova } throw new Exception("Autenticazione fallita: HTTP {$http_code}, Errore cURL: {$curl_error}, Risposta: " . substr($response, 0, 1000)); } $token_data = json_decode($response, true); $this->token = null; if (is_array($token_data) && isset($token_data['token'])) { $this->token = $token_data['token']; } elseif (is_string($token_data) && !empty($token_data)) { $this->token = trim($token_data, '"'); } elseif (is_string($response) && !empty($response)) { $this->token = trim($response, '"'); } if (empty($this->token)) { throw new Exception("Token non ricevuto: " . substr($response, 0, 1000)); } } private function getToken() { if ($this->token === null) { $this->authenticate(); } return $this->token; } public function get($endpoint) { $token = $this->getToken(); $url = "{$this->baseUrl}/api/odata/{$endpoint}"; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Authorization: Bearer {$token}", "Accept: application/json" ]); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_VERBOSE, true); $log = fopen(__DIR__ . '/curl_request_debug.log', 'w') ?: fopen('php://stderr', 'w'); curl_setopt($ch, CURLOPT_STDERR, $log); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curl_error = curl_error($ch); fclose($log); curl_close($ch); if ($response === false) { throw new Exception("Errore nella richiesta: {$curl_error}"); } if ($http_code !== 200) { throw new Exception("Errore nel recupero dati: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000)); } $data = json_decode($response, true); if (json_last_error() !== JSON_ERROR_NONE) { throw new Exception("Risposta non JSON valida: " . substr($response, 0, 1000)); } return $data; } public function post($endpoint, $payload) { $token = $this->getToken(); $url = "{$this->baseUrl}/api/odata/{$endpoint}"; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Authorization: Bearer {$token}", "Content-Type: application/json", "Accept: application/json" ]); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curl_error = curl_error($ch); curl_close($ch); if ($response === false) { throw new Exception("Errore nella richiesta POST: {$curl_error}"); } if ($http_code < 200 || $http_code >= 300) { throw new Exception("POST fallito: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000)); } return json_decode($response, true); } public function patch($endpoint, $payload) { $token = $this->getToken(); $url = "{$this->baseUrl}/api/odata/{$endpoint}"; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH"); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Authorization: Bearer {$token}", "Content-Type: application/json", "Accept: application/json" ]); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curl_error = curl_error($ch); curl_close($ch); if ($response === false) { throw new Exception("Errore nella richiesta PATCH: {$curl_error}"); } if ($http_code < 200 || $http_code >= 300) { throw new Exception("PATCH fallito: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000)); } return json_decode($response, true); } public function getBaseUrl() { return $this->baseUrl; } }