Reorganize PHP internals and static assets

Move shared PHP code into private/, move JavaScript files into js/, and block direct access to private/. Remove unused API key and cache artifacts from the working tree.
This commit is contained in:
www-data
2026-05-26 11:32:36 +02:00
parent 97f23260ed
commit 2f2e8869d6
30 changed files with 48 additions and 48 deletions
+228
View File
@@ -0,0 +1,228 @@
<?php
function catalog_http_normalize_header_name($name)
{
return strtolower(trim($name));
}
function catalog_http_ensure_dir($dir)
{
if (!is_dir($dir)) {
if (!mkdir($dir, 0755, true)) {
throw new RuntimeException('Kan cache directory niet maken: ' . $dir);
}
}
if (!is_writable($dir)) {
throw new RuntimeException('Cache directory is niet schrijfbaar: ' . $dir);
}
}
function catalog_http_read_meta($metaFile)
{
if ($metaFile === null || !is_file($metaFile)) {
return array();
}
$json = file_get_contents($metaFile);
if ($json === false || $json === '') {
return array();
}
$meta = json_decode($json, true);
return is_array($meta) ? $meta : array();
}
function catalog_http_write_meta($metaFile, $meta)
{
if ($metaFile === null) {
return;
}
$json = json_encode($meta, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
if ($json === false) {
throw new RuntimeException('Kan catalogus-cache metadata niet coderen.');
}
if (file_put_contents($metaFile, $json . "\n", LOCK_EX) === false) {
throw new RuntimeException('Kan catalogus-cache metadata niet schrijven: ' . $metaFile);
}
}
function catalog_http_validator_headers($meta)
{
$headers = array();
if (!empty($meta['etag'])) {
$headers[] = 'If-None-Match: ' . $meta['etag'];
}
if (!empty($meta['last_modified'])) {
$headers[] = 'If-Modified-Since: ' . $meta['last_modified'];
}
return $headers;
}
function catalog_http_fetch($url, $requestHeaders = array(), $userAgent = 'rktsndbx-catalog/1.0', $timeout = 180)
{
if (function_exists('curl_init')) {
$ch = curl_init($url);
$responseHeaders = array();
curl_setopt_array($ch, array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_CONNECTTIMEOUT => 20,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_USERAGENT => $userAgent,
CURLOPT_FAILONERROR => false,
CURLOPT_HTTPHEADER => $requestHeaders,
CURLOPT_HEADERFUNCTION => function ($ch, $line) use (&$responseHeaders) {
$pos = strpos($line, ':');
if ($pos !== false) {
$name = catalog_http_normalize_header_name(substr($line, 0, $pos));
$value = trim(substr($line, $pos + 1));
$responseHeaders[$name] = $value;
}
return strlen($line);
},
));
$body = curl_exec($ch);
if ($body === false) {
$err = curl_error($ch);
curl_close($ch);
throw new RuntimeException('Catalogus ophalen mislukt: ' . $err);
}
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 304 && ($status < 200 || $status >= 300)) {
throw new RuntimeException('Catalogus gaf HTTP status ' . $status . ' voor ' . $url);
}
return array(
'status' => $status,
'headers' => $responseHeaders,
'body' => $body,
);
}
$headerLines = array('User-Agent: ' . $userAgent);
foreach ($requestHeaders as $header) {
$headerLines[] = $header;
}
$ctx = stream_context_create(array(
'http' => array(
'method' => 'GET',
'timeout' => $timeout,
'header' => implode("\r\n", $headerLines) . "\r\n",
'ignore_errors' => true,
),
));
$body = @file_get_contents($url, false, $ctx);
if ($body === false) {
throw new RuntimeException('Catalogus ophalen mislukt: ' . $url);
}
$status = 0;
$responseHeaders = array();
$rawHeaders = isset($http_response_header) && is_array($http_response_header)
? $http_response_header
: array();
foreach ($rawHeaders as $line) {
if (preg_match('/^HTTP\/\S+\s+(\d+)/', $line, $m)) {
$status = (int)$m[1];
continue;
}
$pos = strpos($line, ':');
if ($pos !== false) {
$name = catalog_http_normalize_header_name(substr($line, 0, $pos));
$value = trim(substr($line, $pos + 1));
$responseHeaders[$name] = $value;
}
}
if ($status !== 304 && ($status < 200 || $status >= 300)) {
throw new RuntimeException('Catalogus gaf HTTP status ' . $status . ' voor ' . $url);
}
return array(
'status' => $status,
'headers' => $responseHeaders,
'body' => $body,
);
}
function catalog_http_fetch_cached($url, $cacheFile, $metaFile, $ttl, $userAgent, $timeout = 180)
{
catalog_http_ensure_dir(dirname($cacheFile));
if (is_file($cacheFile)) {
$mtime = filemtime($cacheFile);
if ($mtime !== false && $mtime >= time() - $ttl) {
$data = file_get_contents($cacheFile);
if ($data !== false && $data !== '') {
return $data;
}
}
}
$meta = catalog_http_read_meta($metaFile);
$requestHeaders = is_file($cacheFile) ? catalog_http_validator_headers($meta) : array();
$response = catalog_http_fetch($url, $requestHeaders, $userAgent, $timeout);
if ($response['status'] === 304) {
$data = file_get_contents($cacheFile);
if ($data !== false && $data !== '') {
touch($cacheFile);
$meta['checked_at'] = time();
catalog_http_write_meta($metaFile, $meta);
return $data;
}
}
$data = $response['body'];
if (file_put_contents($cacheFile, $data, LOCK_EX) === false) {
throw new RuntimeException('Kan catalogus-cache niet schrijven: ' . $cacheFile);
}
$headers = $response['headers'];
$now = time();
$meta = array(
'url' => $url,
'fetched_at' => $now,
'checked_at' => $now,
);
if (!empty($headers['etag'])) {
$meta['etag'] = $headers['etag'];
}
if (!empty($headers['last-modified'])) {
$meta['last_modified'] = $headers['last-modified'];
}
catalog_http_write_meta($metaFile, $meta);
return $data;
}