%PDF- %PDF-
| Direktori : /www/varak.net/dmarc.varak.net/classes/ |
| Current File : /www/varak.net/dmarc.varak.net/classes/TextTable.php |
<?php
/**
* dmarc-srg - A php parser, viewer and summary report generator for incoming DMARC reports.
* Copyright (C) 2024 Aleksey Andreev (liuch)
*
* Available at:
* https://github.com/liuch/dmarc-srg
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* =========================
*
* This file contains the class TextTable
*
* @category Common
* @package DmarcSrg
* @author Aleksey Andreev (liuch)
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU/GPLv3
*/
namespace Liuch\DmarcSrg;
/**
* The class is intended for displaying the table as text
* Usage example:
* (new TextTable([ 'Organization', 'Reports' ]))
* ->appendRow([ 'Organization 1', 15 ])
* ->appendRow([ 'Organization 2', 12 ])
* ->setMinColumnWidth(0, 25)
* ->sortBy(1)
* ->output();
*
*/
class TextTable
{
private $columns = null;
private $sort_by = null;
private $row_list = [];
private $widths = null;
private $minimals = [];
private $alignment = [];
private $skeleton = [ 'border' => null, 'header' => null, 'data' => null ];
private $borders = [ 'horizontal' => '-', 'vertical' => '|', 'intersection' => '+' ];
/**
* Constructor
*
* @param array $columns Array of strings with column names
*/
public function __construct(array $columns)
{
$this->columns = $columns;
}
/**
* Adds a row of with data to the table
*
* @param array $row_data Array with row data
*
* @retrun self
*/
public function appendRow(array $row_data)
{
$this->row_list[] = $row_data;
return $this;
}
/**
* Sets the minimal width in characters for the specified column
*
* @param int $col Zero-based index of the column
* @param int $width Minimum column width
*
* @return self
*/
public function setMinColumnWidth(int $col, int $width)
{
if ($col >= 0 && $col < count($this->columns) && $width >= 0) {
$this->minimals[$col] = $width;
}
return $this;
}
/**
* Sets the minimal width for in characters for the all columns at once
*
* If the array size is not the same as the number of columns, the extra or missing coulumns are ignored.
*
* @param array $widths
*
* @return self
*/
public function setMinColumnsWidth(array $widths)
{
$c = min(count($widths), count($this->columns));
for ($i = 0; $i < $c; ++$i) {
if (is_int($widths[$i])) {
$this->minimals[$i] = $widths[$i];
}
}
return $this;
}
/**
* Adjusts data alignment in columns
*
* @param int $col Zero-based index of the column
* @param string $align Alignment value. Can be either `left` or `right`.
*
* @return self
*/
public function setColumnAlignment(int $col, string $align)
{
if ($col >= 0 && $col < count($this->columns)) {
switch ($align) {
case 'left':
case 'right':
$this->alignment[$col] = $align;
}
}
return $this;
}
/**
* Sets the characters for the table borders
*
* @param string $horizontal Horizontal border
* @param string $vertical Vertical border
* @param string $intersection Border at intersections
*
* @return self
*/
public function setBorders(string $horizontal, string $vertical, string $intersection)
{
$this->borders['horizontal'] = $horizontal;
$this->borders['vertical'] = $vertical;
$this->borders['intersection'] = $intersection;
return $this;
}
/**
* Specifies by which column the rows in the table should be sorted
*
* @param int $col Zero-based index of the column
*
* @return self
*/
public function sortBy(int $col)
{
if ($col >= 0 && $col < count($this->columns)) {
$this->sort_by = $col;
}
return $this;
}
/**
* Outputs the final table view to stdout
*
* @return void
*/
public function output(): void
{
foreach ($this->toArray() as $s) {
echo $s, PHP_EOL;
}
}
/**
* Returns the table as an array of text strings
*
* @return array
*/
public function toArray(): array
{
$res = [];
$cc = count($this->columns);
if ($cc) {
$this->calculateWidths();
$this->makeSkeleton();
$bf = !empty($this->skeleton['border']);
if ($bf) {
$res[] = $this->skeleton['border'];
}
$res[] = $this->generateHeader();
if ($bf) {
$res[] = $this->skeleton['border'];
}
if (count($this->row_list)) {
$sort_by = $this->sort_by;
if (!is_null($sort_by) && isset($this->columns[$sort_by])) {
usort($this->row_list, static function ($a, $b) use ($sort_by) {
return $a[$sort_by] <=> $b[$sort_by];
});
}
foreach ($this->row_list as &$row) {
$res[] = $this->generateRow($row);
}
unset($row);
}
if ($bf) {
$res[] = $this->skeleton['border'];
}
}
return $res;
}
/**
* Pre-calculates column widths
*
* @return void
*/
private function calculateWidths(): void
{
$this->widths = [];
for ($i = 0; $i < count($this->columns); ++$i) {
$width = max(mb_strlen(strval($this->columns[$i])), $this->minimals[$i] ?? 0);
foreach ($this->row_list as $row) {
$width = max($width, mb_strlen(strval($row[$i] ?? '')));
}
$this->widths[$i] = $width;
}
}
/**
* Creates templates for outputting table elements
*
* @return void
*/
private function makeSkeleton(): void
{
$vborder = $this->borders['vertical'];
$hborder = $this->borders['horizontal'];
$iborder = $this->borders['intersection'];
$border = $iborder;
$header = $vborder;
$data = $vborder;
for ($i = 0; $i < count($this->widths); ++$i) {
$w = $this->widths[$i];
$border .= str_repeat($hborder, $w + 2) . $iborder;
$d = $this->row_list[0][$i] ?? '';
switch ($this->alignment[$i] ?? (is_int($d) ? 'right' : 'left')) {
case 'right':
$header .= " %{$w}s {$vborder}";
$data .= " %{$w}s {$vborder}";
break;
case 'left':
default:
$header .= " %-{$w}s {$vborder}";
$data .= " %-{$w}s {$vborder}";
break;
}
}
$this->skeleton['border'] = trim($border);
$this->skeleton['header'] = trim($header);
$this->skeleton['data'] = trim($data);
}
/**
* Makess a string with titles
*
* @return string
*/
private function generateHeader(): string
{
return vsprintf($this->skeleton['header'], $this->columns);
}
/**
* Generates a table row
*
* @param array $columns Columns of the row
*
* @return string
*/
private function generateRow(array $columns): string
{
return vsprintf($this->skeleton['data'], $columns);
}
}