265 lines
8.5 KiB
PHP
265 lines
8.5 KiB
PHP
<?php
|
|
ini_set('memory_limit', '512M');
|
|
|
|
// CORS headers for iframe support
|
|
header('Access-Control-Allow-Origin: *');
|
|
header('Access-Control-Allow-Methods: GET');
|
|
header('Content-Type: application/json');
|
|
|
|
// Custom error handler
|
|
function errorHandler($errno, $errstr, $errfile, $errline) {
|
|
echo json_encode(['error' => "$errstr in $errfile on line $errline"]);
|
|
exit;
|
|
}
|
|
set_error_handler("errorHandler");
|
|
|
|
/**
|
|
* Stock Symbol Checker API
|
|
* Usage: ?q=SYMBOL (max 6 chars) or ?q=ISIN (8+ chars)
|
|
*/
|
|
|
|
$input = isset($_GET['q']) ? strtoupper(trim($_GET['q'])) : '';
|
|
|
|
if (empty($input)) {
|
|
echo json_encode(['error' => 'No input provided. Use ?q=SYMBOL or ?q=ISIN']);
|
|
exit;
|
|
}
|
|
|
|
// Read stocks.json from same directory
|
|
$jsonFile = __DIR__ . '/stocks.json';
|
|
if (!file_exists($jsonFile)) {
|
|
echo json_encode(['error' => 'stocks.json not found', 'path' => $jsonFile]);
|
|
exit;
|
|
}
|
|
|
|
$stocks = json_decode(file_get_contents($jsonFile), true);
|
|
if (!$stocks) {
|
|
echo json_encode(['error' => 'stocks.json is invalid']);
|
|
exit;
|
|
}
|
|
|
|
// Determine input type
|
|
$isSymbol = strlen($input) <= 6;
|
|
$isIsin = strlen($input) >= 8;
|
|
|
|
$found = null;
|
|
$isinToCheck = null;
|
|
$inJson = false;
|
|
|
|
if ($isSymbol) {
|
|
// Search for symbol in stocks.json
|
|
foreach ($stocks as $item) {
|
|
if (strtoupper($item['stock']) === $input) {
|
|
$found = $item;
|
|
$inJson = true;
|
|
$isinToCheck = $found['isin'] ?? null;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!$found) {
|
|
echo json_encode([
|
|
'status' => 'not_found',
|
|
'symbol' => $input,
|
|
'injson' => false,
|
|
'gettex' => 'no'
|
|
]);
|
|
exit;
|
|
}
|
|
} elseif ($isIsin) {
|
|
// Input is ISIN
|
|
$isinToCheck = $input;
|
|
|
|
// Check if this ISIN exists in stocks.json
|
|
foreach ($stocks as $item) {
|
|
if (!empty($item['isin']) && strtoupper($item['isin']) === $input) {
|
|
$found = $item;
|
|
$inJson = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check Gettex for the ISIN
|
|
$inGettex = isIsinInGettex($isinToCheck);
|
|
|
|
if ($inJson && $found) {
|
|
echo json_encode([
|
|
'status' => 'found_in_json',
|
|
'symbol' => $found['stock'],
|
|
'isin' => $found['isin'],
|
|
'injson' => true,
|
|
'gettex' => $inGettex ? 'yes' : 'no',
|
|
'price' => $found['price'],
|
|
'currency' => $found['currency'],
|
|
'date' => $found['date'],
|
|
'exchange_market' => $found['exchange_market'],
|
|
'depot' => $found['depot'] ?? null,
|
|
'nrbght' => $found['nrbght'] ?? null
|
|
]);
|
|
} else {
|
|
echo json_encode([
|
|
'status' => 'isin_checked',
|
|
'submitted_isin' => $input,
|
|
'injson' => false,
|
|
'gettex' => $inGettex ? 'yes' : 'no',
|
|
'note' => 'ISIN checked against Gettex data'
|
|
]);
|
|
}
|
|
exit;
|
|
} else {
|
|
echo json_encode([
|
|
'error' => 'Ambiguous input length',
|
|
'input' => $input,
|
|
'length' => strlen($input),
|
|
'note' => 'Symbols should be max 6 chars, ISIN should be 8+ chars'
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// Symbol found in JSON
|
|
$inGettex = false;
|
|
if (!empty($isinToCheck)) {
|
|
$inGettex = isIsinInGettex($isinToCheck);
|
|
}
|
|
|
|
echo json_encode([
|
|
'status' => 'found',
|
|
'symbol' => $found['stock'],
|
|
'injson' => true,
|
|
'isin' => $found['isin'] ?? null,
|
|
'gettex' => $inGettex ? 'yes' : 'no',
|
|
'price' => $found['price'],
|
|
'currency' => $found['currency'],
|
|
'date' => $found['date'],
|
|
'exchange_market' => $found['exchange_market'],
|
|
'depot' => $found['depot'] ?? null,
|
|
'nrbght' => $found['nrbght'] ?? null
|
|
]);
|
|
|
|
/**
|
|
* Check if ISIN exists in Gettex data using cURL (iframe-safe)
|
|
*/
|
|
function isIsinInGettex($isin) {
|
|
$cacheFile = __DIR__ . '/gettex_cache.json';
|
|
$cacheExpiry = 3600; // 1 hour cache
|
|
|
|
// Check cache
|
|
if (file_exists($cacheFile)) {
|
|
$cache = json_decode(file_get_contents($cacheFile), true);
|
|
if ($cache && isset($cache['timestamp']) && (time() - $cache['timestamp']) < $cacheExpiry) {
|
|
if (isset($cache['data'][$isin])) {
|
|
return $cache['data'][$isin];
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Weekend check (German GMT = UTC+1/UTC+2) ---
|
|
$now = new DateTime('now', new DateTimeZone('Europe/Berlin'));
|
|
$dayOfWeek = (int)$now->format('N'); // 1=Mon, 7=Sun
|
|
$isWeekend = ($dayOfWeek >= 6); // Saturday or Sunday
|
|
|
|
if ($isWeekend) {
|
|
// Use the weekend URL directly
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, 'https://alcea-wisteria.de/z_files/trading/exchangeplatform/gettex.csv.gz');
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0');
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
|
$gzContent = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($httpCode === 200 && $gzContent) {
|
|
$csvContent = gzdecode($gzContent);
|
|
if ($csvContent && strpos($csvContent, $isin) !== false) {
|
|
// Cache the positive result
|
|
$cacheData = ['timestamp' => time(), 'data' => [$isin => true]];
|
|
file_put_contents($cacheFile, json_encode($cacheData));
|
|
return true;
|
|
} elseif ($csvContent) {
|
|
// Extract all ISINs from weekend CSV for caching
|
|
preg_match_all('/[A-Z]{2}[A-Z0-9]{9}[0-9]/', $csvContent, $isinMatches);
|
|
$allIsins = [];
|
|
if (!empty($isinMatches[0])) {
|
|
foreach ($isinMatches[0] as $foundIsin) {
|
|
$allIsins[$foundIsin] = true;
|
|
}
|
|
}
|
|
// Cache all found ISINs + negative result for this ISIN
|
|
$cacheData = ['timestamp' => time(), 'data' => $allIsins + [$isin => false]];
|
|
file_put_contents($cacheFile, json_encode($cacheData));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// If weekend fetch fails, return false (no fallback to weekday data)
|
|
return false;
|
|
}
|
|
|
|
// --- Normal weekday logic (unchanged) ---
|
|
// Fetch Gettex page with cURL (works in iframes)
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, 'https://www.gettex.de/handel/delayed-data/posttrade-data/');
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
|
$html = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($httpCode !== 200 || !$html) {
|
|
return false;
|
|
}
|
|
|
|
// Extract CSV file URLs
|
|
preg_match_all('/href="(https:\/\/erdk\.bayerische-boerse\.de:8000\/delayed-data\/[^"]+\.csv\.gz)"/', $html, $matches);
|
|
if (empty($matches[1])) {
|
|
return false;
|
|
}
|
|
|
|
$allIsins = [];
|
|
foreach ($matches[1] as $url) {
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0');
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
|
$gzContent = curl_exec($ch);
|
|
curl_close($ch);
|
|
|
|
if (!$gzContent) continue;
|
|
|
|
$csvContent = gzdecode($gzContent);
|
|
if (!$csvContent) continue;
|
|
|
|
// Check if our ISIN exists in this CSV
|
|
if (strpos($csvContent, $isin) !== false) {
|
|
// Cache the positive result
|
|
$cacheData = ['timestamp' => time(), 'data' => [$isin => true]];
|
|
file_put_contents($cacheFile, json_encode($cacheData));
|
|
return true;
|
|
}
|
|
|
|
// Extract all ISINs for caching
|
|
preg_match_all('/[A-Z]{2}[A-Z0-9]{9}[0-9]/', $csvContent, $isinMatches);
|
|
if (!empty($isinMatches[0])) {
|
|
foreach ($isinMatches[0] as $foundIsin) {
|
|
$allIsins[$foundIsin] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cache all found ISINs + negative result for this ISIN
|
|
$cacheData = ['timestamp' => time(), 'data' => $allIsins + [$isin => false]];
|
|
file_put_contents($cacheFile, json_encode($cacheData));
|
|
|
|
return false;
|
|
}
|
|
?>
|