From 8ca6cc32b2c789a3149861159ad258f2cb9491e3 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 14:39:39 +0200 Subject: Adding upstream version 2.11.4. Signed-off-by: Daniel Baumann --- .../File/Exception/FileReaderException.php | 12 ++ library/Icinga/Protocol/File/FileIterator.php | 81 ++++++++ library/Icinga/Protocol/File/FileQuery.php | 86 +++++++++ library/Icinga/Protocol/File/FileReader.php | 208 +++++++++++++++++++++ library/Icinga/Protocol/File/LogFileIterator.php | 149 +++++++++++++++ 5 files changed, 536 insertions(+) create mode 100644 library/Icinga/Protocol/File/Exception/FileReaderException.php create mode 100644 library/Icinga/Protocol/File/FileIterator.php create mode 100644 library/Icinga/Protocol/File/FileQuery.php create mode 100644 library/Icinga/Protocol/File/FileReader.php create mode 100644 library/Icinga/Protocol/File/LogFileIterator.php (limited to 'library/Icinga/Protocol/File') diff --git a/library/Icinga/Protocol/File/Exception/FileReaderException.php b/library/Icinga/Protocol/File/Exception/FileReaderException.php new file mode 100644 index 0000000..237352c --- /dev/null +++ b/library/Icinga/Protocol/File/Exception/FileReaderException.php @@ -0,0 +1,12 @@ + $value) + * + * @var array + */ + protected $currentData; + + public function __construct($filename, $fields) + { + $this->fields = $fields; + $f = new File($filename); + $f->setFlags( + File::DROP_NEW_LINE | + File::READ_AHEAD | + File::SKIP_EMPTY + ); + parent::__construct($f); + } + + /** + * Return the current data + * + * @return array + */ + public function current(): array + { + return $this->currentData; + } + + /** + * Accept lines matching the given PCRE pattern + * + * @return bool + * + * @throws FileReaderException If PHP failed parsing the PCRE pattern + */ + public function accept(): bool + { + $data = array(); + $matched = preg_match( + $this->fields, + $this->getInnerIterator()->current(), + $data + ); + + if ($matched === false) { + throw new FileReaderException('Failed parsing regular expression!'); + } elseif ($matched === 1) { + foreach ($data as $key => $value) { + if (is_int($key)) { + unset($data[$key]); + } + } + $this->currentData = $data; + return true; + } + return false; + } +} diff --git a/library/Icinga/Protocol/File/FileQuery.php b/library/Icinga/Protocol/File/FileQuery.php new file mode 100644 index 0000000..504de2e --- /dev/null +++ b/library/Icinga/Protocol/File/FileQuery.php @@ -0,0 +1,86 @@ +sortDir = ( + $direction === null || strtoupper(trim($direction)) === 'DESC' + ) ? self::SORT_DESC : self::SORT_ASC; + return $this; + } + + /** + * Return true if sorting descending, false otherwise + * + * @return bool + */ + public function sortDesc() + { + return $this->sortDir === self::SORT_DESC; + } + + /** + * Add an mandatory filter expression to be applied on this query + * + * @param string $expression the filter expression to be applied + * + * @return FileQuery + */ + public function andWhere($expression) + { + $this->filters[] = $expression; + return $this; + } + + /** + * Get filters currently applied on this query + * + * @return array + */ + public function getFilters() + { + return $this->filters; + } +} diff --git a/library/Icinga/Protocol/File/FileReader.php b/library/Icinga/Protocol/File/FileReader.php new file mode 100644 index 0000000..d160387 --- /dev/null +++ b/library/Icinga/Protocol/File/FileReader.php @@ -0,0 +1,208 @@ +{$key})) { + $this->{$key} = $config->{$key}; + } else { + throw new FileReaderException('The directive `%s\' is required', $key); + } + } + } + + /** + * Instantiate a FileIterator object with the target file + * + * @return FileIterator + */ + public function iterate() + { + return new LogFileIterator($this->filename, $this->fields); + } + + /** + * Instantiate a FileQuery object + * + * @return FileQuery + */ + public function select() + { + return new FileQuery($this); + } + + /** + * Fetch and return all rows of the given query's result set using an iterator + * + * @param FileQuery $query + * + * @return ArrayIterator + */ + public function query(FileQuery $query) + { + return new ArrayIterator($this->fetchAll($query)); + } + + /** + * Return the number of available valid lines. + * + * @return int + */ + public function count(): int + { + if ($this->count === null) { + $this->count = iterator_count($this->iterate()); + } + return $this->count; + } + + /** + * Fetch result as an array of objects + * + * @param FileQuery $query + * + * @return array + */ + public function fetchAll(FileQuery $query) + { + $all = array(); + foreach ($this->fetchPairs($query) as $index => $value) { + $all[$index] = (object) $value; + } + return $all; + } + + /** + * Fetch result as a key/value pair array + * + * @param FileQuery $query + * + * @return array + */ + public function fetchPairs(FileQuery $query) + { + $skip = $query->getOffset(); + $read = $query->getLimit(); + if ($skip === null) { + $skip = 0; + } + $lines = array(); + if ($query->sortDesc()) { + $count = $this->count($query); + if ($count <= $skip) { + return $lines; + } elseif ($count < ($skip + $read)) { + $read = $count - $skip; + $skip = 0; + } else { + $skip = $count - ($skip + $read); + } + } + foreach ($this->iterate() as $index => $line) { + if ($index >= $skip) { + if ($index >= $skip + $read) { + break; + } + $lines[] = $line; + } + } + if ($query->sortDesc()) { + $lines = array_reverse($lines); + } + return $lines; + } + + /** + * Fetch first result row + * + * @param FileQuery $query + * + * @return object + */ + public function fetchRow(FileQuery $query) + { + $all = $this->fetchAll($query); + if (isset($all[0])) { + return $all[0]; + } + return null; + } + + /** + * Fetch first result column + * + * @param FileQuery $query + * + * @return array + */ + public function fetchColumn(FileQuery $query) + { + $column = array(); + foreach ($this->fetchPairs($query) as $pair) { + foreach ($pair as $value) { + $column[] = $value; + break; + } + } + return $column; + } + + /** + * Fetch first column value from first result row + * + * @param FileQuery $query + * + * @return mixed + */ + public function fetchOne(FileQuery $query) + { + $pairs = $this->fetchPairs($query); + if (isset($pairs[0])) { + foreach ($pairs[0] as $value) { + return $value; + } + } + return null; + } +} diff --git a/library/Icinga/Protocol/File/LogFileIterator.php b/library/Icinga/Protocol/File/LogFileIterator.php new file mode 100644 index 0000000..67a4d99 --- /dev/null +++ b/library/Icinga/Protocol/File/LogFileIterator.php @@ -0,0 +1,149 @@ +file = new SplFileObject($filename); + $this->file->setFlags( + SplFileObject::DROP_NEW_LINE | + SplFileObject::READ_AHEAD + ); + $this->fields = $fields; + } + + public function rewind(): void + { + $this->file->rewind(); + $this->index = 0; + $this->nextMessage(); + } + + public function next(): void + { + $this->file->next(); + ++$this->index; + $this->nextMessage(); + } + + public function current(): array + { + return $this->current; + } + + public function key(): int + { + return $this->index; + } + + public function valid(): bool + { + return $this->valid; + } + + protected function nextMessage() + { + $message = $this->next === null ? array() : array($this->next); + $this->valid = null; + while ($this->file->valid()) { + if (false === ($res = preg_match( + $this->fields, + $current = $this->file->current() + ))) { + throw new IcingaException('Failed at preg_match()'); + } + if (empty($message)) { + if ($res === 1) { + $message[] = $current; + } + } elseif ($res === 1) { + $this->next = $current; + $this->valid = true; + break; + } else { + $message[] = $current; + } + + $this->file->next(); + } + if ($this->valid === null) { + $this->next = null; + $this->valid = ! empty($message); + } + + if ($this->valid) { + while (! empty($message)) { + $matches = array(); + if (false === ($res = preg_match( + $this->fields, + implode(PHP_EOL, $message), + $matches + ))) { + throw new IcingaException('Failed at preg_match()'); + } + if ($res === 1) { + $this->current = $matches; + return; + } + array_pop($message); + } + $this->valid = false; + } + } +} -- cgit v1.2.3