Page MenuHomec4science

PhutilConsoleTable.php
No OneTemporary

File Metadata

Created
Wed, Dec 18, 16:25

PhutilConsoleTable.php

<?php
/**
* Show a table in the console. Usage:
*
* $table = id(new PhutilConsoleTable())
* ->addColumn('id', array('title' => 'ID', 'align' => 'right'))
* ->addColumn('name', array('title' => 'Username', 'align' => 'center'))
* ->addColumn('email', array('title' => 'Email Address'))
*
* ->addRow(array(
* 'id' => 12345,
* 'name' => 'alicoln',
* 'email' => 'abraham@lincoln.com',
* ))
* ->addRow(array(
* 'id' => 99999999,
* 'name' => 'jbloggs',
* 'email' => 'joe@bloggs.com',
* ))
*
* ->setBorders(true)
* ->draw();
*/
final class PhutilConsoleTable extends PhutilConsoleView {
private $columns = array();
private $data = array();
private $widths = array();
private $borders = false;
private $padding = 1;
private $showHeader = true;
const ALIGN_LEFT = 'left';
const ALIGN_CENTER = 'center';
const ALIGN_RIGHT = 'right';
/* -( Configuration )------------------------------------------------------ */
public function setBorders($borders) {
$this->borders = $borders;
return $this;
}
public function setPadding($padding) {
$this->padding = $padding;
return $this;
}
public function setShowHeader($show_header) {
$this->showHeader = $show_header;
return $this;
}
/* -( Data )--------------------------------------------------------------- */
public function addColumn($key, array $column) {
PhutilTypeSpec::checkMap($column, array(
'title' => 'string',
'align' => 'optional string',
));
$this->columns[$key] = $column;
return $this;
}
public function addColumns(array $columns) {
foreach ($columns as $key => $column) {
$this->addColumn($key, $column);
}
return $this;
}
public function addRow(array $data) {
$this->data[] = $data;
foreach ($data as $key => $value) {
$this->widths[$key] = max(
idx($this->widths, $key, 0),
phutil_utf8_console_strlen($value));
}
return $this;
}
/* -( Drawing )------------------------------------------------------------ */
protected function drawView() {
return $this->drawLines(
array_merge(
$this->getHeader(),
$this->getBody(),
$this->getFooter()));
}
private function getHeader() {
$output = array();
if ($this->borders) {
$output[] = $this->formatSeparator('=');
}
if (!$this->showHeader) {
return $output;
}
$columns = array();
foreach ($this->columns as $key => $column) {
$title = tsprintf('**%s**', $column['title']);
if ($this->shouldAddSpacing($key, $column)) {
$title = $this->alignString(
$title,
$this->getWidth($key),
idx($column, 'align', self::ALIGN_LEFT));
}
$columns[] = $title;
}
$output[] = $this->formatRow($columns);
if ($this->borders) {
$output[] = $this->formatSeparator('=');
}
return $output;
}
private function getBody() {
$output = array();
foreach ($this->data as $data) {
$columns = array();
foreach ($this->columns as $key => $column) {
if (!$this->shouldAddSpacing($key, $column)) {
$columns[] = idx($data, $key, '');
} else {
$columns[] = $this->alignString(
idx($data, $key, ''),
$this->getWidth($key),
idx($column, 'align', self::ALIGN_LEFT));
}
}
$output[] = $this->formatRow($columns);
}
return $output;
}
private function getFooter() {
$output = array();
if ($this->borders) {
$columns = array();
foreach ($this->getColumns() as $column) {
$columns[] = str_repeat('=', $this->getWidth($column));
}
$output[] = array(
'+',
$this->implode('+', $columns),
'+',
);
}
return $output;
}
/* -( Internals )---------------------------------------------------------- */
/**
* Returns if the specified column should have spacing added.
*
* @return bool
*/
private function shouldAddSpacing($key, $column) {
if (!$this->borders) {
if (last_key($this->columns) === $key) {
if (idx($column, 'align', self::ALIGN_LEFT) === self::ALIGN_LEFT) {
// Don't add extra spaces to this column since it's the last column,
// left aligned, and we're not showing borders. This prevents
// unnecessary empty lines from appearing when the extra spaces
// wrap around the terminal.
return false;
}
}
}
return true;
}
/**
* Returns the column IDs.
*
* @return list<string>
*/
protected function getColumns() {
return array_keys($this->columns);
}
/**
* Get the width of a specific column, including padding.
*
* @param string
* @return int
*/
protected function getWidth($key) {
$width = max(
idx($this->widths, $key),
phutil_utf8_console_strlen(
idx(idx($this->columns, $key, array()), 'title', '')));
return $width + 2 * $this->padding;
}
protected function alignString($string, $width, $align) {
$num_padding = $width -
(2 * $this->padding) - phutil_utf8_console_strlen($string);
switch ($align) {
case self::ALIGN_LEFT:
$num_left_padding = 0;
$num_right_padding = $num_padding;
break;
case self::ALIGN_CENTER:
$num_left_padding = (int)($num_padding / 2);
$num_right_padding = $num_padding - $num_left_padding;
break;
case self::ALIGN_RIGHT:
$num_left_padding = $num_padding;
$num_right_padding = 0;
break;
}
$left_padding = str_repeat(' ', $num_left_padding);
$right_padding = str_repeat(' ', $num_right_padding);
return array(
$left_padding,
$string,
$right_padding,
);
}
/**
* Format cells into an entire row.
*
* @param list<string>
* @return string
*/
protected function formatRow(array $columns) {
$padding = str_repeat(' ', $this->padding);
if ($this->borders) {
$separator = $padding.'|'.$padding;
return array(
'|'.$padding,
$this->implode($separator, $columns),
$padding.'|',
);
} else {
return $this->implode($padding, $columns);
}
}
protected function formatSeparator($string) {
$columns = array();
if ($this->borders) {
$separator = '+';
} else {
$separator = '';
}
foreach ($this->getColumns() as $column) {
$columns[] = str_repeat($string, $this->getWidth($column));
}
return array(
$separator,
$this->implode($separator, $columns),
$separator,
);
}
}

Event Timeline