$entry * * @return array{success:bool, driver:?string, error:?string, path:?string} */ function control_log_write(array $entry): array { static $tableName; static $fallbackPath; /** @var array> $columnsCache */ static $columnsCache = []; $result = [ 'success' => false, 'driver' => null, 'error' => null, 'path' => null, ]; $settings = include __DIR__ . '/../config/control-settings.php'; if ($tableName === null) { $configuredTable = $settings['log_table'] ?? null; if (!is_string($configuredTable) || trim($configuredTable) === '') { $result['error'] = 'Control log table name is not configured.'; error_log('Control write log insert failed: ' . $result['error']); return $result; } $tableName = trim($configuredTable); } if ($fallbackPath === null) { $configuredFallback = $settings['log_fallback_path'] ?? ''; if (is_string($configuredFallback) && trim($configuredFallback) !== '') { $fallbackPath = $configuredFallback; } else { $fallbackPath = ''; } } try { $connection = controls_db_connect(); $tableKey = $tableName; if (!isset($columnsCache[$tableKey])) { $tableEscaped = $connection->real_escape_string($tableName); $columnsResult = $connection->query('SHOW COLUMNS FROM `' . $tableEscaped . '`'); if ($columnsResult === false) { throw new RuntimeException('Unable to determine columns for control write log table.'); } $columns = []; while ($column = $columnsResult->fetch_assoc()) { if (isset($column['Field'])) { $columns[] = $column['Field']; } } $columnsResult->free(); $columnsCache[$tableKey] = $columns; } $availableColumns = $columnsCache[$tableKey] ?? []; if ($availableColumns === []) { throw new RuntimeException('No columns available on control write log table.'); } $insertData = []; foreach ($entry as $column => $value) { if (in_array($column, $availableColumns, true)) { $insertData[$column] = $value; } } if ($insertData === []) { $result['error'] = 'None of the provided fields matched the log table columns.'; error_log('Control write log insert skipped: ' . $result['error']); } else { $fields = []; $values = []; foreach ($insertData as $column => $value) { $fields[] = '`' . $connection->real_escape_string($column) . '`'; if ($value === null) { $values[] = 'NULL'; continue; } if (is_bool($value)) { $values[] = $value ? "'1'" : "'0'"; continue; } $values[] = "'" . $connection->real_escape_string((string) $value) . "'"; } $tableEscaped = $connection->real_escape_string($tableName); $sql = sprintf( 'INSERT INTO `%s` (%s) VALUES (%s)', $tableEscaped, implode(', ', $fields), implode(', ', $values) ); $connection->query($sql); $result['success'] = true; $result['driver'] = 'database'; $result['error'] = null; return $result; } } catch (Throwable $exception) { $result['error'] = $exception->getMessage(); error_log('Control write log insert failed: ' . $exception->getMessage()); } if ($fallbackPath === '') { return $result; } try { $directory = dirname($fallbackPath); if ($directory !== '' && !is_dir($directory)) { if (!mkdir($directory, 0775, true) && !is_dir($directory)) { throw new RuntimeException('Unable to create fallback log directory: ' . $directory); } } $record = [ 'timestamp' => gmdate('c'), 'entry' => $entry, 'error' => $result['error'], ]; $json = json_encode($record, JSON_THROW_ON_ERROR); file_put_contents($fallbackPath, $json . PHP_EOL, FILE_APPEND | LOCK_EX); $result['success'] = true; $result['driver'] = 'file'; $result['path'] = $fallbackPath; error_log('Control write log stored in fallback file: ' . $fallbackPath); } catch (Throwable $fallbackException) { $result['driver'] = 'none'; $result['path'] = null; $fallbackMessage = $fallbackException->getMessage(); if ($result['error'] !== null) { $result['error'] .= '; ' . $fallbackMessage; } else { $result['error'] = $fallbackMessage; } error_log('Control write log fallback failed: ' . $fallbackMessage); } return $result; } }