v1.9.1: fix world map rendering and resolve eight small TODO items
World map: - Fix zero-padded ISO numeric ids leaving 31 countries unrendered (Brazil, Australia, Belgium, Austria, Algeria and more) - Fix Russia and Fiji smearing across the full map width at the antimeridian by unwrapping ring longitudes and drawing them at both edges - Crop to 84N-60S, add evenodd fill rule, 174 countries rendered Improvements: - Logger::tail() reads backwards in chunks instead of loading the whole file - External links get rel=noopener noreferrer in footer and Markdown content - formatDisplayName() cleaned up and guarded against empty input - Export statistics as CSV (Excel BOM) or JSON - GeoIP database auto-updates when older than 35 days - Editor shortcuts Ctrl/Cmd+S to save and Ctrl/Cmd+N for a new page - Live search filter in the admin content browser - Content versioning with timestamped .bak copies in content/-backups/ Also removes eight stale TODO entries that were already implemented
This commit is contained in:
@@ -123,20 +123,71 @@ class Logger {
|
||||
|
||||
/**
|
||||
* Get last N lines from log file
|
||||
*
|
||||
*
|
||||
* Reads the file backwards in chunks so large log files never have to be
|
||||
* loaded into memory in their entirety.
|
||||
*
|
||||
* @param int $lines Number of lines to retrieve
|
||||
* @return array Log lines
|
||||
* @return array Log lines (including trailing newlines, oldest first)
|
||||
*/
|
||||
public static function tail($lines = 100) {
|
||||
if (!self::$logFile || !file_exists(self::$logFile)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$file = @file(self::$logFile);
|
||||
if ($file === false) {
|
||||
|
||||
$lines = max(1, (int)$lines);
|
||||
|
||||
$handle = @fopen(self::$logFile, 'rb');
|
||||
if ($handle === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_slice($file, -$lines);
|
||||
|
||||
if (fseek($handle, 0, SEEK_END) !== 0) {
|
||||
fclose($handle);
|
||||
return [];
|
||||
}
|
||||
|
||||
$fileSize = ftell($handle);
|
||||
if ($fileSize === false || $fileSize === 0) {
|
||||
fclose($handle);
|
||||
return [];
|
||||
}
|
||||
|
||||
$chunkSize = 8192;
|
||||
$position = $fileSize;
|
||||
$buffer = '';
|
||||
$newlineCount = 0;
|
||||
|
||||
// Read backwards until we have enough newlines or reach the start
|
||||
while ($position > 0 && $newlineCount <= $lines) {
|
||||
$readSize = (int)min($chunkSize, $position);
|
||||
$position -= $readSize;
|
||||
|
||||
if (fseek($handle, $position, SEEK_SET) !== 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
$chunk = fread($handle, $readSize);
|
||||
if ($chunk === false) {
|
||||
break;
|
||||
}
|
||||
|
||||
$buffer = $chunk . $buffer;
|
||||
$newlineCount = substr_count($buffer, "\n");
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
|
||||
if ($buffer === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Split while keeping the newline characters, matching file() behaviour
|
||||
$result = preg_split('/(?<=\n)/', $buffer, -1, PREG_SPLIT_NO_EMPTY);
|
||||
if ($result === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_slice($result, -$lines);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user