Save all currently accumulated repository changes as a backup snapshot for Gitea so no local work is lost.
530 lines
25 KiB
PHP
530 lines
25 KiB
PHP
<?php
|
||
/**
|
||
* Обёртка для парсинга ответа от parscourt.php и создания события
|
||
*
|
||
* Принимает те же параметры что и parscourt.php
|
||
* Вызывает parscourt.php, получает JSON с last_event
|
||
* И создаёт событие через CreateCourtEvent.php
|
||
*
|
||
* Этот скрипт можно дёргать из workflow вместо parscourt.php
|
||
*/
|
||
|
||
// Устанавливаем рабочую директорию
|
||
chdir(__DIR__);
|
||
|
||
// Логирование
|
||
function log_wrapper($level, $message) {
|
||
$log_file = 'logs/parse_and_create_event.log';
|
||
$timestamp = date('Y-m-d H:i:s');
|
||
$log_entry = "{$timestamp} - {$level}: {$message}\n";
|
||
file_put_contents($log_file, $log_entry, FILE_APPEND | LOCK_EX);
|
||
}
|
||
|
||
try {
|
||
$scriptStartTime = microtime(true);
|
||
log_wrapper('INFO', '=== НАЧАЛО ОБРАБОТКИ ===');
|
||
log_wrapper('DEBUG', "Память на старте: " . round(memory_get_usage() / 1024 / 1024, 2) . " МБ");
|
||
|
||
// Получаем параметры (из POST, GET или argv)
|
||
$params = array_merge($_GET, $_POST);
|
||
|
||
// Если параметров нет, пробуем argv (для вызова через CLI)
|
||
if (empty($params) && !empty($argv)) {
|
||
for ($i = 1; $i < count($argv); $i++) {
|
||
if (strpos($argv[$i], '=') !== false) {
|
||
list($key, $value) = explode('=', $argv[$i], 2);
|
||
$params[$key] = $value;
|
||
}
|
||
}
|
||
}
|
||
|
||
log_wrapper('DEBUG', "Параметры: " . json_encode($params, JSON_UNESCAPED_UNICODE));
|
||
|
||
// Проверяем обязательные параметры
|
||
if (empty($params['project_id'])) {
|
||
// Если это прямой доступ через браузер без параметров, показываем информативную страницу
|
||
if (empty($params) && !empty($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'curl') === false) {
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>ParseAndCreateEvent - Тестирование</title>
|
||
<style>
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||
max-width: 800px;
|
||
margin: 50px auto;
|
||
padding: 20px;
|
||
background: #f5f5f5;
|
||
}
|
||
.container {
|
||
background: white;
|
||
padding: 30px;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||
}
|
||
h1 {
|
||
color: #333;
|
||
margin-top: 0;
|
||
}
|
||
.info {
|
||
background: #e3f2fd;
|
||
border-left: 4px solid #2196f3;
|
||
padding: 15px;
|
||
margin: 20px 0;
|
||
border-radius: 4px;
|
||
}
|
||
.form-group {
|
||
margin-bottom: 20px;
|
||
}
|
||
label {
|
||
display: block;
|
||
margin-bottom: 5px;
|
||
font-weight: 600;
|
||
color: #555;
|
||
}
|
||
input[type="text"], input[type="number"], textarea {
|
||
width: 100%;
|
||
padding: 10px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 4px;
|
||
font-size: 14px;
|
||
box-sizing: border-box;
|
||
}
|
||
textarea {
|
||
min-height: 80px;
|
||
font-family: monospace;
|
||
}
|
||
.required {
|
||
color: #f44336;
|
||
}
|
||
button {
|
||
background: #2196f3;
|
||
color: white;
|
||
padding: 12px 30px;
|
||
border: none;
|
||
border-radius: 4px;
|
||
font-size: 16px;
|
||
cursor: pointer;
|
||
margin-right: 10px;
|
||
}
|
||
button:hover {
|
||
background: #1976d2;
|
||
}
|
||
.result {
|
||
margin-top: 20px;
|
||
padding: 15px;
|
||
border-radius: 4px;
|
||
display: none;
|
||
}
|
||
.result.success {
|
||
background: #e8f5e9;
|
||
border-left: 4px solid #4caf50;
|
||
display: block;
|
||
}
|
||
.result.error {
|
||
background: #ffebee;
|
||
border-left: 4px solid #f44336;
|
||
display: block;
|
||
}
|
||
pre {
|
||
background: #f5f5f5;
|
||
padding: 15px;
|
||
border-radius: 4px;
|
||
overflow-x: auto;
|
||
font-size: 12px;
|
||
}
|
||
.help-text {
|
||
font-size: 12px;
|
||
color: #666;
|
||
margin-top: 5px;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<h1>🔍 ParseAndCreateEvent - Тестирование</h1>
|
||
|
||
<div class="info">
|
||
<strong>Назначение:</strong> Этот скрипт парсит ответ от parscourt.php и создаёт событие через CreateCourtEvent_v2.php.<br>
|
||
<strong>Использование:</strong> Обычно вызывается из n8n workflow или через API с параметрами.
|
||
</div>
|
||
|
||
<form id="testForm" method="POST">
|
||
<div class="form-group">
|
||
<label for="project_id">Project ID <span class="required">*</span></label>
|
||
<input type="number" id="project_id" name="project_id" required placeholder="Например: 12345">
|
||
<div class="help-text">Обязательный параметр. ID проекта в CRM.</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="case_number">Номер дела</label>
|
||
<input type="text" id="case_number" name="case_number" placeholder="02-15800/2025">
|
||
<div class="help-text">По умолчанию: 02-15800/2025</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="status">Статус</label>
|
||
<input type="text" id="status" name="status" placeholder="">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="link1">Ссылка 1</label>
|
||
<input type="text" id="link1" name="link1" placeholder="">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="link2">Ссылка 2</label>
|
||
<input type="text" id="link2" name="link2" placeholder="">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="link3">Ссылка 3</label>
|
||
<input type="text" id="link3" name="link3" placeholder="">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="uid">UID</label>
|
||
<input type="text" id="uid" name="uid" placeholder="">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>
|
||
<input type="checkbox" name="use_new_parser" value="true" checked> Использовать новый парсер
|
||
</label>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>
|
||
<input type="checkbox" name="skip_duplicate_check" value="true"> Пропустить проверку дубликатов
|
||
</label>
|
||
</div>
|
||
|
||
<button type="submit">🚀 Запустить парсинг и создание события</button>
|
||
<button type="button" onclick="window.location.reload()">🔄 Сбросить</button>
|
||
</form>
|
||
|
||
<div id="result" class="result"></div>
|
||
</div>
|
||
|
||
<script>
|
||
document.getElementById('testForm').addEventListener('submit', async function(e) {
|
||
e.preventDefault();
|
||
|
||
const formData = new FormData(this);
|
||
const resultDiv = document.getElementById('result');
|
||
resultDiv.className = 'result';
|
||
resultDiv.innerHTML = '<p>⏳ Обработка...</p>';
|
||
resultDiv.style.display = 'block';
|
||
|
||
try {
|
||
const response = await fetch(window.location.href, {
|
||
method: 'POST',
|
||
body: formData
|
||
});
|
||
|
||
const data = await response.json();
|
||
|
||
if (data.success) {
|
||
resultDiv.className = 'result success';
|
||
resultDiv.innerHTML = '<h3>✅ Успешно!</h3><pre>' + JSON.stringify(data, null, 2) + '</pre>';
|
||
} else {
|
||
resultDiv.className = 'result error';
|
||
resultDiv.innerHTML = '<h3>❌ Ошибка</h3><pre>' + JSON.stringify(data, null, 2) + '</pre>';
|
||
}
|
||
} catch (error) {
|
||
resultDiv.className = 'result error';
|
||
resultDiv.innerHTML = '<h3>❌ Ошибка запроса</h3><pre>' + error.message + '</pre>';
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|
||
<?php
|
||
exit(0);
|
||
}
|
||
// Для API вызовов (curl, n8n и т.д.) возвращаем JSON ошибку
|
||
throw new Exception('Параметр project_id обязателен');
|
||
}
|
||
|
||
$projectId = $params['project_id'];
|
||
|
||
// Формируем параметры для parscourt.php
|
||
// ВАЖНО: Проверяем не только на null, но и на пустые строки
|
||
// parscourt.php требует: status, link (из link1/link2/link3), case_number
|
||
$status = !empty($params['status']) ? $params['status'] : '';
|
||
$link1 = !empty($params['link1']) ? $params['link1'] : '';
|
||
$link2 = !empty($params['link2']) ? $params['link2'] : '';
|
||
$link3 = !empty($params['link3']) ? $params['link3'] : '';
|
||
$case_number = !empty($params['case_number']) ? $params['case_number'] : '02-15800/2025'; // Дефолтный номер дела для тестирования
|
||
$uid = !empty($params['uid']) ? $params['uid'] : '';
|
||
|
||
// Проверяем обязательные параметры для parscourt.php
|
||
$link = $link1 ?: ($link2 ?: $link3);
|
||
if (empty($status) || empty($link) || empty($case_number)) {
|
||
$missing = [];
|
||
if (empty($status)) $missing[] = 'status';
|
||
if (empty($link)) $missing[] = 'link1/link2/link3 (хотя бы одна ссылка)';
|
||
if (empty($case_number)) $missing[] = 'case_number';
|
||
|
||
log_wrapper('ERROR', 'Отсутствуют обязательные параметры для parscourt.php: ' . implode(', ', $missing));
|
||
throw new Exception('Отсутствуют обязательные параметры для парсинга: ' . implode(', ', $missing));
|
||
}
|
||
|
||
$parscourtParams = [
|
||
'project_id' => $projectId,
|
||
'status' => $status,
|
||
'link1' => $link1,
|
||
'link2' => $link2,
|
||
'link3' => $link3,
|
||
'case_number' => $case_number,
|
||
'uid' => $uid,
|
||
'use_new_parser' => !empty($params['use_new_parser']) ? $params['use_new_parser'] : 'true',
|
||
'skip_duplicate_check' => !empty($params['skip_duplicate_check']) ? $params['skip_duplicate_check'] : 'false'
|
||
];
|
||
|
||
log_wrapper('INFO', "Вызываем parscourt.php для проекта $projectId");
|
||
|
||
// Формируем URL для вызова parscourt.php
|
||
// ВСЕГДА используем прямой доступ к Apache через HTTP (порт 81) для внутренних вызовов
|
||
// Это полностью обходит проблему с SSL сертификатом
|
||
$domain = $_SERVER['HTTP_HOST'] ?? 'crm.clientright.ru';
|
||
|
||
// Для внутренних вызовов всегда используем HTTP на порту 81
|
||
// Это работает быстрее и обходит все проблемы с SSL
|
||
$curlUrl = 'http://127.0.0.1:81/parscourt.php';
|
||
|
||
log_wrapper('DEBUG', "Используем внутренний вызов: $curlUrl");
|
||
log_wrapper('DEBUG', "Домен: $domain");
|
||
log_wrapper('DEBUG', "Параметры для parscourt.php: " . json_encode($parscourtParams, JSON_UNESCAPED_UNICODE));
|
||
|
||
$postData = http_build_query($parscourtParams);
|
||
log_wrapper('DEBUG', "POST данные (http_build_query): $postData");
|
||
|
||
$ch = curl_init();
|
||
curl_setopt($ch, CURLOPT_URL, $curlUrl);
|
||
curl_setopt($ch, CURLOPT_POST, true);
|
||
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
|
||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||
curl_setopt($ch, CURLOPT_TIMEOUT, 180); // Увеличен таймаут до 3 минут
|
||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); // Таймаут на установку соединения
|
||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||
|
||
// ВАЖНО: Отключаем проверку SSL на всякий случай (если вдруг используется HTTPS)
|
||
// Это гарантирует, что SSL ошибки не возникнут
|
||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||
|
||
// Устанавливаем заголовок Host для правильной работы с виртуальными хостами Apache
|
||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Host: ' . $domain]);
|
||
|
||
log_wrapper('DEBUG', "Отправляем cURL запрос к parscourt.php...");
|
||
$startTime = microtime(true);
|
||
|
||
$output = curl_exec($ch);
|
||
$execTime = round(microtime(true) - $startTime, 2);
|
||
|
||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||
$curlError = curl_error($ch);
|
||
$curlErrno = curl_errno($ch);
|
||
$curlInfo = curl_getinfo($ch);
|
||
|
||
curl_close($ch);
|
||
|
||
log_wrapper('DEBUG', "cURL выполнен за {$execTime} сек, HTTP код: $httpCode");
|
||
|
||
if ($curlErrno !== 0) {
|
||
log_wrapper('ERROR', "cURL ошибка #{$curlErrno}: {$curlError}");
|
||
log_wrapper('DEBUG', "cURL info: " . json_encode($curlInfo, JSON_UNESCAPED_UNICODE));
|
||
throw new Exception("Ошибка cURL при вызове parscourt.php: [{$curlErrno}] {$curlError}");
|
||
}
|
||
|
||
if ($httpCode !== 200) {
|
||
log_wrapper('ERROR', "Неожиданный HTTP код: $httpCode");
|
||
log_wrapper('DEBUG', "Первые 500 символов ответа: " . substr($output, 0, 500));
|
||
throw new Exception("Ошибка вызова parscourt.php: HTTP $httpCode");
|
||
}
|
||
|
||
$outputSize = strlen($output);
|
||
log_wrapper('DEBUG', "Получен ответ от parscourt.php (размер: {$outputSize} байт)");
|
||
log_wrapper('DEBUG', "Ответ от parscourt.php: $output");
|
||
|
||
// Парсим JSON ответ
|
||
$parscourtResponse = json_decode($output, true);
|
||
|
||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||
$jsonError = json_last_error_msg();
|
||
log_wrapper('ERROR', "Ошибка декодирования JSON: {$jsonError}");
|
||
log_wrapper('DEBUG', "Первые 1000 символов проблемного ответа: " . substr($output, 0, 1000));
|
||
throw new Exception('Ошибка декодирования JSON от parscourt.php: ' . $jsonError);
|
||
}
|
||
|
||
log_wrapper('DEBUG', "Распарсенный ответ: " . json_encode($parscourtResponse, JSON_UNESCAPED_UNICODE));
|
||
|
||
// Проверяем наличие last_event и что он не пустой
|
||
if (empty($parscourtResponse['last_event']) ||
|
||
!isset($parscourtResponse['last_event']['Наименование']) ||
|
||
empty($parscourtResponse['last_event']['Наименование'])) {
|
||
log_wrapper('WARNING', 'Нет данных о событиях (last_event пустой или без названия)');
|
||
|
||
$response = [
|
||
'success' => true,
|
||
'message' => 'Парсинг выполнен, но нет новых событий',
|
||
'event_created' => false
|
||
];
|
||
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||
exit(0);
|
||
}
|
||
|
||
$lastEvent = $parscourtResponse['last_event'];
|
||
|
||
// Извлекаем данные события (пробуем оба варианта ключей)
|
||
$eventName = $lastEvent['Наименование'] ?? $lastEvent['name'] ?? 'Судебное заседание';
|
||
$eventDate = $lastEvent['Дата'] ?? $lastEvent['date'] ?? '';
|
||
$eventTime = $lastEvent['Время'] ?? $lastEvent['time'] ?? '';
|
||
$location = $lastEvent['Место'] ?? $lastEvent['location'] ?? '';
|
||
$result = $lastEvent['Результат'] ?? $lastEvent['result'] ?? '';
|
||
$basis = $lastEvent['Основание'] ?? $lastEvent['basis'] ?? '';
|
||
$note = $lastEvent['Примечание'] ?? $lastEvent['note'] ?? '';
|
||
$publicationDate = $lastEvent['Дата размещения'] ?? $lastEvent['publication_date'] ?? '';
|
||
|
||
log_wrapper('DEBUG', "Извлеченные данные: eventName='$eventName', eventDate='$eventDate', eventTime='$eventTime'");
|
||
log_wrapper('INFO', "Событие извлечено: $eventName ($eventDate $eventTime)");
|
||
|
||
// Проверяем что дата не пустая
|
||
if (empty($eventDate)) {
|
||
log_wrapper('WARNING', 'Дата события пустая, пропускаем создание');
|
||
|
||
$response = [
|
||
'success' => true,
|
||
'message' => 'Событие не создано: дата отсутствует',
|
||
'event_created' => false
|
||
];
|
||
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||
exit(0);
|
||
}
|
||
|
||
// Формируем данные для CreateCourtEvent.php
|
||
log_wrapper('DEBUG', "Перед формированием данных: eventName='$eventName', result='$result'");
|
||
|
||
$eventData = [
|
||
'project_id' => $projectId,
|
||
'event_name' => $eventName,
|
||
'event_date' => $eventDate,
|
||
'event_time' => $eventTime,
|
||
'location' => $location,
|
||
'result' => $result,
|
||
'basis' => $basis,
|
||
'note' => $note,
|
||
'publication_date' => $publicationDate
|
||
];
|
||
|
||
log_wrapper('INFO', "Создаём событие через CreateCourtEvent_v2.php");
|
||
log_wrapper('DEBUG', "Данные события: " . json_encode($eventData, JSON_UNESCAPED_UNICODE));
|
||
|
||
// Вызываем CreateCourtEvent_v2.php через CLI
|
||
$createEventCommand = 'php ' . __DIR__ . '/CreateCourtEvent_v2.php';
|
||
$eventDataJson = json_encode($eventData, JSON_UNESCAPED_UNICODE);
|
||
|
||
// Передаём данные через временный файл
|
||
$tempFile = tempnam(sys_get_temp_dir(), 'event_data_');
|
||
file_put_contents($tempFile, $eventDataJson);
|
||
log_wrapper('DEBUG', "Временный файл создан: $tempFile");
|
||
|
||
log_wrapper('DEBUG', "Запускаем команду: cat $tempFile | $createEventCommand");
|
||
$startTimeEvent = microtime(true);
|
||
|
||
$createEventOutput = shell_exec('cat ' . escapeshellarg($tempFile) . ' | ' . $createEventCommand . ' 2>&1');
|
||
|
||
$execTimeEvent = round(microtime(true) - $startTimeEvent, 2);
|
||
log_wrapper('DEBUG', "CreateCourtEvent_v2.php выполнен за {$execTimeEvent} сек");
|
||
|
||
// Удаляем временный файл
|
||
unlink($tempFile);
|
||
log_wrapper('DEBUG', "Временный файл удалён");
|
||
|
||
log_wrapper('DEBUG', "Ответ от CreateCourtEvent_v2.php (длина: " . strlen($createEventOutput) . " байт): $createEventOutput");
|
||
|
||
// Фильтруем PHP Notice из ответа
|
||
$cleanOutput = preg_replace('/^PHP Notice:.*$/m', '', $createEventOutput);
|
||
$cleanOutput = trim($cleanOutput);
|
||
|
||
log_wrapper('DEBUG', "Очищенный ответ (длина: " . strlen($cleanOutput) . " байт)");
|
||
|
||
$createEventResponse = json_decode($cleanOutput, true);
|
||
|
||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||
$jsonError = json_last_error_msg();
|
||
log_wrapper('ERROR', "Ошибка декодирования JSON от CreateCourtEvent_v2.php: {$jsonError}");
|
||
log_wrapper('DEBUG', "Проблемный ответ: $cleanOutput");
|
||
throw new Exception('Ошибка декодирования JSON от CreateCourtEvent_v2.php: ' . $jsonError);
|
||
}
|
||
|
||
if (empty($createEventResponse['success'])) {
|
||
$errorMsg = $createEventResponse['error'] ?? 'Неизвестная ошибка';
|
||
log_wrapper('ERROR', "CreateCourtEvent_v2.php вернул ошибку: $errorMsg");
|
||
throw new Exception('Ошибка создания события: ' . $errorMsg);
|
||
}
|
||
|
||
log_wrapper('SUCCESS', "Событие создано: " . $createEventResponse['event_id']);
|
||
|
||
// Формируем финальный ответ
|
||
$response = [
|
||
'success' => true,
|
||
'message' => 'Парсинг выполнен и событие создано',
|
||
'event_created' => true,
|
||
'event_id' => $createEventResponse['event_id'],
|
||
'event_name' => $eventName,
|
||
'event_date' => $eventDate,
|
||
'event_time' => $eventTime,
|
||
'project_id' => $projectId
|
||
];
|
||
|
||
$totalTime = round(microtime(true) - $scriptStartTime, 2);
|
||
$memoryUsed = round(memory_get_usage() / 1024 / 1024, 2);
|
||
$memoryPeak = round(memory_get_peak_usage() / 1024 / 1024, 2);
|
||
|
||
log_wrapper('SUCCESS', "=== ОБРАБОТКА ЗАВЕРШЕНА УСПЕШНО ===");
|
||
log_wrapper('INFO', "Общее время выполнения: {$totalTime} сек");
|
||
log_wrapper('DEBUG', "Память использовано: {$memoryUsed} МБ (пик: {$memoryPeak} МБ)");
|
||
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||
|
||
exit(0);
|
||
|
||
} catch (Exception $e) {
|
||
$error_message = $e->getMessage();
|
||
$totalTime = isset($scriptStartTime) ? round(microtime(true) - $scriptStartTime, 2) : 0;
|
||
$memoryUsed = round(memory_get_usage() / 1024 / 1024, 2);
|
||
|
||
log_wrapper('ERROR', "=== ОШИБКА ОБРАБОТКИ ===");
|
||
log_wrapper('ERROR', "Сообщение: $error_message");
|
||
log_wrapper('ERROR', "Файл: " . $e->getFile() . " (строка " . $e->getLine() . ")");
|
||
log_wrapper('ERROR', "Стек вызовов:");
|
||
log_wrapper('ERROR', $e->getTraceAsString());
|
||
log_wrapper('INFO', "Время до ошибки: {$totalTime} сек");
|
||
log_wrapper('DEBUG', "Память использовано: {$memoryUsed} МБ");
|
||
log_wrapper('ERROR', '=== КОНЕЦ ЛОГА ОШИБКИ ===');
|
||
|
||
$response = [
|
||
'success' => false,
|
||
'error' => $error_message,
|
||
'timestamp' => date('Y-m-d H:i:s'),
|
||
'execution_time' => $totalTime
|
||
];
|
||
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
http_response_code(500);
|
||
echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||
|
||
exit(1);
|
||
}
|
||
?>
|