Completed
Pull Request — master (#76)
by Marcel
33:21
created

MarkdownTable::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace PHPSemVerChecker\Console;
4
5
use Symfony\Component\Console\Helper\Helper;
6
use Symfony\Component\Console\Helper\TableSeparator;
7
use Symfony\Component\Console\Output\OutputInterface;
8
9
/**
10
 * Renders a Markdown compatible table.
11
 */
12
class MarkdownTable
13
{
14
	/**
15
	 * @var array
16
	 */
17
	private $headers = [];
18
	/**
19
	 * @var array
20
	 */
21
	private $rows = [];
22
	/**
23
	 * @var \Symfony\Component\Console\Output\OutputInterface
24
	 */
25
	private $output;
26
	/**
27
	 * @var int[]
28
	 */
29
	private $columnWidths;
30
31
	/**
32
	 * @param \Symfony\Component\Console\Output\OutputInterface $output
33
	 */
34
	public function __construct(OutputInterface $output)
35
	{
36
		$this->output = $output;
37
	}
38
39
	/**
40
	 * Set the column headers.
41
	 *
42
	 * @param array $headers
43
	 * @return $this
44
	 */
45
	public function setHeaders(array $headers)
46
	{
47
		// Ensure zero-indexed array
48
		$this->headers = array_values($headers);
49
		return $this;
50
	}
51
52
	/**
53
	 * Sets all rows, replacing any existing.
54
	 *
55
	 * @param array $rows
56
	 * @return MarkdownTable
57
	 */
58
	public function setRows(array $rows)
59
	{
60
		$this->rows = [];
61
		return $this->addRows($rows);
62
	}
63
64
	/**
65
	 * @param array $rows
66
	 * @return $this
67
	 */
68
	public function addRows(array $rows)
69
	{
70
		foreach ($rows as $row) {
71
			$this->addRow($row);
72
		}
73
		return $this;
74
	}
75
76
	/**
77
	 * @param $row
78
	 * @return $this
79
	 */
80
	public function addRow($row)
81
	{
82
		if ($row instanceof TableSeparator) {
83
			$this->rows[] = $row;
84
85
			return $this;
86
		}
87
		if (!is_array($row)) {
88
			throw new \InvalidArgumentException('A row must be an array or a TableSeparator instance.');
89
		}
90
		$this->rows[] = array_values($row);
91
		return $this;
92
	}
93
94
	/**
95
	 * @param int $index
96
	 * @param array $row
97
	 */
98
	public function setRow($index, array $row)
99
	{
100
		$this->rows[$index] = $row;
101
	}
102
103
	/**
104
	 * Renders table to output.
105
	 */
106
	public function render()
107
	{
108
		$this->prepare();
109
		$this->output->writeln('');
110
		$this->renderRow($this->headers);
111
		$this->renderRowSeparator();
112
		foreach ($this->rows as $row) {
113
			$this->renderRow($row);
114
		}
115
	}
116
117
	/**
118
	 * Renders a single row.
119
	 *
120
	 * @param array|TableSeparator $row
121
	 */
122
	private function renderRow($row)
123
	{
124
		if ($row instanceof TableSeparator) {
125
			$this->renderRowSeparator();
126
			return;
127
		}
128
		$this->output->write('| ');
129
		$cells = [];
130
		foreach ($row as $index => $content) {
131
			$cell = $content;
132
			$padding = $this->columnWidths[$index] - Helper::strlenWithoutDecoration($this->output->getFormatter(), $content);
133
			$cell .= str_repeat(' ', $padding);
134
			$cells[] = $cell;
135
		}
136
		$this->output->writeln(implode(' | ', $cells).' |');
137
	}
138
139
	/**
140
	 * Renders the row separator. In this case it should only be used as a header separator.
141
	 */
142
	private function renderRowSeparator()
143
	{
144
		$this->output->write('|');
145
		foreach ($this->columnWidths as $columnWidth) {
146
			$this->output->write(str_repeat('-',  $columnWidth + 1));
147
			$this->output->write('-|');
148
		}
149
		$this->output->writeln('');
150
	}
151
152
	/**
153
	 * Prepare for rendering
154
	 */
155
	private function prepare()
156
	{
157
		$this->columnWidths = [];
158
		$this->prepareColumnWidths($this->headers);
159
		foreach ($this->rows as $row) {
160
			$this->prepareColumnWidths($row);
161
		}
162
	}
163
164
	/**
165
	 * Extracts maximum column widths from a row.
166
	 *
167
	 * @param $row
168
	 */
169
	private function prepareColumnWidths($row)
170
	{
171
		if ($row instanceof TableSeparator) {
172
			return;
173
		}
174
		foreach ($row as $index => $content) {
175
			$currentMaximum = 0;
176
			if (isset($this->columnWidths[$index])) {
177
				$currentMaximum = $this->columnWidths[$index];
178
			}
179
			$width = Helper::strlenWithoutDecoration($this->output->getFormatter(), $content);
180
			$this->columnWidths[$index] = max($currentMaximum, $width);
181
		}
182
	}
183
}
184