Files
Fociz/includes/http.php
2026-09-16 13:53:54 +02:00

161 lines
5.6 KiB
PHP

<?php
/**
* Fociz Pure PHP HTTP Client Helper
* Ondersteunt cURL met automatische fallback naar stream wrappers (file_get_contents).
*/
if (!function_exists('fociz_http_request')) {
/**
* Voert een HTTP request uit en retourneert de statuscode, body en geparsede JSON.
*
* @param string $url
* @param array $options [
* 'method' => '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, min(4, max(1, $timeout - 1)));
curl_setopt($ch, CURLOPT_DNS_CACHE_TIMEOUT, 600);
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
];
}
}