'GET'|'POST'|'PUT'|'DELETE', * 'headers' => ['Header-Name: Value'] of ['Header-Name' => 'Value'], * 'body' => string|array, * 'timeout' => int (seconden, default 12), * 'verify_ssl' => bool (default true) * ] * @return array ['status' => int, 'body' => string, 'json' => ?array, 'error' => ?string] */ function fociz_http_request(string $url, array $options = []): array { $method = strtoupper($options['method'] ?? 'GET'); $timeout = (int)($options['timeout'] ?? 12); $verifySsl = $options['verify_ssl'] ?? true; // Normaliseer headers naar 'Name: Value' regels $rawHeaders = $options['headers'] ?? []; $headerLines = []; foreach ($rawHeaders as $k => $v) { if (is_numeric($k)) { $headerLines[] = $v; } else { $headerLines[] = "{$k}: {$v}"; } } // Bereid payload voor $body = $options['body'] ?? null; if (is_array($body)) { $hasJsonHeader = false; foreach ($headerLines as $h) { if (stripos($h, 'application/json') !== false) { $hasJsonHeader = true; break; } } if ($hasJsonHeader) { $body = json_encode($body); } else { $body = http_build_query($body); $headerLines[] = 'Content-Type: application/x-www-form-urlencoded'; } } // Voeg standaard User-Agent toe als die er niet is $hasUserAgent = false; foreach ($headerLines as $h) { if (stripos($h, 'User-Agent:') !== false) { $hasUserAgent = true; break; } } if (!$hasUserAgent) { $headerLines[] = 'User-Agent: Fociz-Integrations/1.0'; } // 1. Probeer cURL indien beschikbaar if (extension_loaded('curl')) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 6); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($ch, CURLOPT_HTTPHEADER, $headerLines); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verifySsl); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verifySsl ? 2 : 0); if ($body !== null && in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'])) { curl_setopt($ch, CURLOPT_POSTFIELDS, $body); } $responseBody = curl_exec($ch); $curlError = curl_error($ch); $statusCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($responseBody === false && !empty($curlError)) { return [ 'status' => $statusCode ?: 0, 'body' => '', 'json' => null, 'error' => "cURL fout: {$curlError}" ]; } $parsedJson = json_decode((string)$responseBody, true); return [ 'status' => $statusCode, 'body' => (string)$responseBody, 'json' => is_array($parsedJson) ? $parsedJson : null, 'error' => null ]; } // 2. Fallback: PHP Streams (file_get_contents) $contextOptions = [ 'http' => [ 'method' => $method, 'header' => implode("\r\n", $headerLines), 'timeout' => $timeout, 'ignore_errors' => true // Zorgt dat we ook bij 4xx / 5xx responses de body en statuscode kunnen lezen ], 'ssl' => [ 'verify_peer' => $verifySsl, 'verify_peer_name' => $verifySsl ] ]; if ($body !== null && in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'])) { $contextOptions['http']['content'] = $body; } $context = stream_context_create($contextOptions); $responseBody = @file_get_contents($url, false, $context); $statusCode = 0; if (!empty($http_response_header)) { // Zoek de laatste HTTP status regel (bij eventuele redirects) foreach (array_reverse($http_response_header) as $hdr) { if (preg_match('#HTTP/\d\.\d\s+(\d{3})#i', $hdr, $matches)) { $statusCode = (int)$matches[1]; break; } } } if ($responseBody === false) { $lastErr = error_get_last(); return [ 'status' => $statusCode ?: 0, 'body' => '', 'json' => null, 'error' => 'Stream fout: ' . ($lastErr['message'] ?? 'Kan verbinding niet voltooien') ]; } $parsedJson = json_decode((string)$responseBody, true); return [ 'status' => $statusCode, 'body' => (string)$responseBody, 'json' => is_array($parsedJson) ? $parsedJson : null, 'error' => null ]; } }