src/Debug/Dump/Html/Table.php
<?php
/**
* This file is part of PHPDebugConsole
*
* @package PHPDebugConsole
* @author Brad Kent <bkfake-github@yahoo.com>
* @license http://opensource.org/licenses/MIT MIT
* @copyright 2014-2024 Brad Kent
* @since 2.3
*/
namespace bdk\Debug\Dump\Html;
use bdk\Debug\Dump\Html as Dumper;
/**
* build a table
*/
class Table
{
/** @var Dumper */
protected $debug;
/** @var Dumper */
protected $dumper;
/** @var array<string,mixed> */
protected $options;
/**
* Constructor
*
* @param Dumper $dumper html dumper
*/
public function __construct(Dumper $dumper)
{
$this->debug = $dumper->debug;
$this->dumper = $dumper;
}
/**
* Formats an array as a table
*
* @param mixed $rows array of \Traversable or Abstraction
* @param array $options options
* 'attribs' : key/val array (or string - interpreted as class value)
* 'caption' : optional caption
* 'tableInfo':
* 'columns' : list of columns info
*
* @return string
*/
public function build($rows, $options = array())
{
$this->options = \array_merge(array(
'attribs' => array(),
'caption' => '',
'onBuildRow' => null, // callable (or array of callables)
'tableInfo' => array(),
), $options);
return $this->debug->html->buildTag(
'table',
$this->options['attribs'],
"\n"
. $this->buildCaption()
. $this->buildHeader()
. $this->buildBody($rows)
. $this->buildFooter()
);
}
/**
* Builds table's body
*
* @param array $rows array of arrays or Traversable
*
* @return string
*/
protected function buildBody($rows)
{
$tBody = '';
$this->options['onBuildRow'] = \is_callable($this->options['onBuildRow'])
? [$this->options['onBuildRow']]
: (array) $this->options['onBuildRow'];
foreach ($rows as $k => $row) {
$rowInfo = \array_merge(
array(
'attribs' => array(),
'class' => null,
'key' => null,
'summary' => '',
),
isset($this->options['tableInfo']['rows'][$k])
? $this->options['tableInfo']['rows'][$k]
: array()
);
$tBody .= $this->buildRow($row, $rowInfo, $k);
}
return '<tbody>' . "\n" . $tBody . '</tbody>' . "\n";
}
/**
* Build table caption
*
* @return string
*/
private function buildCaption()
{
$caption = \htmlspecialchars((string) $this->options['caption']);
if (!$this->options['tableInfo']['class']) {
return $caption
? '<caption>' . $caption . '</caption>' . "\n"
: '';
}
$class = $this->dumper->valDumper->markupIdentifier(
$this->options['tableInfo']['class'],
'className',
'span',
array(
'title' => $this->options['tableInfo']['summary'] ?: null,
)
);
$caption = $caption
? $caption . ' (' . $class . ')'
: $class;
return '<caption>' . $caption . '</caption>' . "\n";
}
/**
* Builds table's tfoot
*
* @return string
*/
protected function buildFooter()
{
$haveTotal = false;
$cells = array();
foreach ($this->options['tableInfo']['columns'] as $colInfo) {
if (isset($colInfo['total']) === false) {
$cells[] = '<td></td>';
continue;
}
$totalVal = $colInfo['total'];
if (\is_float($totalVal)) {
$totalVal = \round($totalVal, 6);
}
$cells[] = $this->dumper->valDumper->dump($totalVal, array('tagName' => 'td'));
$haveTotal = true;
}
if (!$haveTotal) {
return '';
}
return '<tfoot>' . "\n"
. '<tr><td> </td>'
. ($this->options['tableInfo']['haveObjRow'] ? '<td> </td>' : '')
. \implode('', $cells)
. '</tr>' . "\n"
. '</tfoot>' . "\n";
}
/**
* Returns table's thead
*
* @return string
*/
protected function buildHeader()
{
$labels = array();
foreach ($this->options['tableInfo']['columns'] as $colInfo) {
$label = $colInfo['key'];
if (isset($colInfo['class'])) {
$label .= ' ' . $this->dumper->valDumper->markupIdentifier($colInfo['class'], 'className');
}
$labels[] = $label;
}
return '<thead>' . "\n"
. '<tr>'
. ($this->options['tableInfo']['indexLabel']
? '<th class="text-right">' . $this->options['tableInfo']['indexLabel'] . '</th>'
: '<th> </th>')
. ($this->options['tableInfo']['haveObjRow']
? '<th> </th>'
: '')
. '<th scope="col">' . \implode('</th><th scope="col">', $labels) . '</th>'
. '</tr>' . "\n"
. '</thead>' . "\n";
}
/**
* Returns table row
*
* @param mixed $row should be array or object abstraction
* @param array $rowInfo row info / meta
* @param string|int $rowKey row key
*
* @return string
*/
protected function buildRow($row, array $rowInfo, $rowKey)
{
$str = '';
$rowKey = $rowInfo['key'] ?: $rowKey;
$rowKeyParsed = $this->debug->html->parseTag($this->dumper->valDumper->dump($rowKey));
$str .= '<tr' . $this->debug->html->buildAttribString($rowInfo['attribs']) . '>';
/*
Output key
*/
$str .= $this->debug->html->buildTag(
'th',
$this->debug->arrayUtil->mergeDeep($rowKeyParsed['attribs'], array(
'class' => ['t_key', 'text-right'],
'scope' => 'row',
)),
$rowKeyParsed['innerhtml']
);
/*
Output row's classname
*/
if ($this->options['tableInfo']['haveObjRow']) {
$str .= $rowInfo['class']
? $this->dumper->valDumper->markupIdentifier($rowInfo['class'], 'className', 'td', array(
'title' => $rowInfo['summary'] ?: null,
))
: '<td class="t_undefined"></td>';
}
/*
Output values
*/
foreach ($row as $v) {
$str .= $this->dumper->valDumper->dump($v, array('tagName' => 'td'));
}
$str .= '</tr>' . "\n";
return $this->onBuildRow($str, $row, $rowInfo, $rowKey);
}
/**
* Call any onBuildRow callbacks
*
* @param string $html built row
* @param mixed $row should be array or object abstraction
* @param array $rowInfo row info / meta
* @param string|int $rowKey row key
*
* @return string html fragment
*/
private function onBuildRow($html, $row, $rowInfo, $rowKey)
{
foreach ($this->options['onBuildRow'] as $callable) {
if (\is_callable($callable)) {
$html = $callable($html, $row, $rowInfo, $rowKey);
}
}
return $html;
}
}