<?php
namespace PHPFUI\MySQLSlowQuery;
class Parser
{
private const PORT = 'TCP Port: ';
private const TIME = '# Time: ';
private array $entries = [];
private array $extraLines = [];
private $handle;
private array $sessions = [];
private string $sortColumn = 'Query_time';
private string $sortOrder = 'desc';
public function __construct(private string $fileName)
{
}
public function getEntries(int $session = -1) : array
{
if (! $this->entries)
{
$this->parse();
}
if ($session > -1)
{
$entries = [];
foreach ($this->entries as $entry)
{
if ($entry->Session == $session)
{
$entries[] = $entry;
}
}
return $entries;
}
return $this->entries;
}
public function getSessions() : array
{
if (! $this->sessions)
{
$this->parse();
}
return $this->sessions;
}
public function sortEntries(string $sortColumn = 'Query_time', string $sortOrder = 'desc') : self
{
$this->sortColumn = $sortColumn;
$this->sortOrder = $sortOrder;
if (! $this->entries)
{
$this->parse();
}
\usort($this->entries, [$this, 'entrySort']);
return $this;
}
protected function entrySort(\PHPFUI\MySQLSlowQuery\Entry $lhs, \PHPFUI\MySQLSlowQuery\Entry $rhs) : int
{
$column = $this->sortColumn;
if ('desc' == $this->sortOrder)
{
return $rhs->{$column} <=> $lhs->{$column};
}
return $lhs->{$column} <=> $rhs->{$column};
}
private function getNextLine() : string
{
if ($this->extraLines)
{
return \array_shift($this->extraLines);
}
$line = \fgets($this->handle);
return $line;
}
private function getParseMode(string $sessionHeaderFirstLine) : string
{
return \stripos($sessionHeaderFirstLine, 'MariaDB') ? 'mariadb' : '';
}
private function parse() : void
{
$this->handle = @\fopen($this->fileName, 'r');
if (! $this->handle)
{
throw new Exception\EmptyLog(self::class . ': ' . $this->fileName . ' appears to not exist or is empty');
}
$newSessionHeader = [];
$parseMode = '';
$previousTimeLine = '';
$processingSessionHeader = true;
while (\strlen($line = $this->getNextLine()))
{
if (0 === \stripos($line, self::PORT))
{
$newSessionHeader[] = $line;
$parseMode = $this->getParseMode($newSessionHeader[0]);
$this->getNextLine();
$this->sessions[] = new \PHPFUI\MySQLSlowQuery\Session($newSessionHeader, $parseMode);
$newSessionHeader = [];
$processingSessionHeader = false;
if ('mariadb' === $parseMode && \strlen($line = $this->getNextLine()) > 0)
{
if ('#' !== $line[0])
{
$processingSessionHeader = true;
}
$this->pushLine($line);
}
}
elseif ($processingSessionHeader)
{
$newSessionHeader[] = $line;
}
elseif ('#' === $line[0])
{
$entry = new \PHPFUI\MySQLSlowQuery\Entry(['parse_mode' => $parseMode]);
$query = [];
if ('' === $parseMode)
{
if (! \str_starts_with($line, self::TIME))
{
continue;
}
$entry->setFromLine($line);
$entry->setFromLine(\fgets($this->handle));
$entry->setFromLine(\fgets($this->handle));
}
else
{
$timeLineFound = false;
do
{
$entry->setFromLine($line);
if ('mariadb' == $parseMode && \str_starts_with($line, self::TIME))
{
$timeLineFound = true;
$previousTimeLine = $line;
}
}
while (\strlen($line = $this->getNextLine()) > 0 && '#' === $line[0]);
if ('mariadb' == $parseMode && ! $timeLineFound && $previousTimeLine)
{
$entry->setFromLine($previousTimeLine);
}
$query[] = \trim($line);
}
while (\strlen($line = $this->getNextLine()) > 0 && '#' !== $line[0])
{
if (0 === \stripos($line, self::PORT))
{
$this->pushLine($line);
if (\count($query))
{
$this->pushLine(\array_pop($query));
}
$line = '';
$newSessionHeader = [];
$processingSessionHeader = true;
break;
}
$query[] = \trim($line);
}
if (\strlen($line) && '#' === $line[0])
{
$this->pushLine($line);
}
$entry->Query = $query;
$entry->Session = \count($this->sessions) - 1;
$this->entries[] = $entry;
}
}
\fclose($this->handle);
}
private function pushLine(string $line) : self
{
\array_unshift($this->extraLines, $line);
return $this;
}
}