composer package updates
[openemr.git] / vendor / zendframework / zend-text / src / Table / Table.php
blob415836197f7dbd21e8f580d7f648baf5357afde5
1 <?php
2 /**
3 * @see https://github.com/zendframework/zend-text for the canonical source repository
4 * @copyright Copyright (c) 2005-2018 Zend Technologies USA Inc. (https://www.zend.com)
5 * @license https://github.com/zendframework/zend-text/blob/master/LICENSE.md New BSD License
6 */
8 namespace Zend\Text\Table;
10 use Traversable;
11 use Zend\ServiceManager\ServiceManager;
12 use Zend\Stdlib\ArrayUtils;
13 use Zend\Text\Table\Decorator\DecoratorInterface as Decorator;
15 /**
16 * Zend\Text\Table\Table enables developers to create tables out of characters
18 class Table
20 /**
21 * Auto separator settings
23 const AUTO_SEPARATE_NONE = 0x0;
24 const AUTO_SEPARATE_HEADER = 0x1;
25 const AUTO_SEPARATE_FOOTER = 0x2;
26 const AUTO_SEPARATE_ALL = 0x4;
28 /**
29 * Decorator used for the table borders
31 * @var Decorator
33 protected $decorator = null;
35 /**
36 * List of all column widths
38 * @var array
40 protected $columnWidths = null;
42 /**
43 * Rows of the table
45 * @var array
47 protected $rows = [];
49 /**
50 * Auto separation mode
52 * @var int
54 protected $autoSeparate = self::AUTO_SEPARATE_ALL;
56 /**
57 * Padding for columns
59 * @var int
61 protected $padding = 0;
63 /**
64 * Default column aligns for rows created by appendRow(array $data)
66 * @var array
68 protected $defaultColumnAligns = [];
70 /**
71 * Plugin loader for decorators
73 * @var DecoratorManager
75 protected $decoratorManager = null;
77 /**
78 * Charset which is used for input by default
80 * @var string
82 protected static $inputCharset = 'utf-8';
84 /**
85 * Charset which is used internally
87 * @var string
89 protected static $outputCharset = 'utf-8';
91 /**
92 * Option keys to skip when calling setOptions()
94 * @var array
96 protected $skipOptions = [
97 'options',
98 'config',
99 'defaultColumnAlign',
103 * Create a basic table object
105 * @param array|Traversable $options Configuration options
106 * @throws Exception\UnexpectedValueException When no columns widths were set
108 public function __construct($options = null)
110 // Set options
111 if ($options instanceof Traversable) {
112 $options = ArrayUtils::iteratorToArray($options);
114 if (is_array($options)) {
115 $this->setOptions($options);
118 // If no decorator was given, use default unicode decorator
119 if ($this->decorator === null) {
120 if (static::getOutputCharset() === 'utf-8') {
121 $this->setDecorator('unicode');
122 } else {
123 $this->setDecorator('ascii');
129 * Set options from array
131 * @param array $options Configuration for Table
132 * @return Table
134 public function setOptions(array $options)
136 foreach ($options as $key => $value) {
137 if (in_array(strtolower($key), $this->skipOptions)) {
138 continue;
141 $method = 'set' . ucfirst($key);
142 if (method_exists($this, $method)) {
143 $this->$method($value);
147 return $this;
151 * Set column widths
153 * @param array $columnWidths Widths of all columns
154 * @throws Exception\InvalidArgumentException When no columns were supplied
155 * @throws Exception\InvalidArgumentException When a column has an invalid width
156 * @return Table
158 public function setColumnWidths(array $columnWidths)
160 if (count($columnWidths) === 0) {
161 throw new Exception\InvalidArgumentException('You must supply at least one column');
164 foreach ($columnWidths as $columnNum => $columnWidth) {
165 if (is_int($columnWidth) === false or $columnWidth < 1) {
166 throw new Exception\InvalidArgumentException('Column ' . $columnNum . ' has an invalid column width');
170 $this->columnWidths = $columnWidths;
172 return $this;
176 * Set auto separation mode
178 * @param int $autoSeparate Auto separation mode
179 * @return Table
181 public function setAutoSeparate($autoSeparate)
183 $this->autoSeparate = (int) $autoSeparate;
184 return $this;
188 * Set decorator
190 * @param Decorator|string $decorator Decorator to use
191 * @return Table
193 public function setDecorator($decorator)
195 if (! $decorator instanceof Decorator) {
196 $decorator = $this->getDecoratorManager()->get($decorator);
199 $this->decorator = $decorator;
201 return $this;
205 * Set the column padding
207 * @param int $padding The padding for the columns
208 * @return Table
210 public function setPadding($padding)
212 $this->padding = max(0, (int) $padding);
213 return $this;
217 * Get the plugin manager for decorators
219 * @return DecoratorManager
221 public function getDecoratorManager()
223 if ($this->decoratorManager instanceof DecoratorManager) {
224 return $this->decoratorManager;
227 $this->setDecoratorManager(new DecoratorManager(new ServiceManager()));
228 return $this->decoratorManager;
232 * Set the plugin manager instance for decorators
234 * @param DecoratorManager $decoratorManager
235 * @return Table
237 public function setDecoratorManager(DecoratorManager $decoratorManager)
239 $this->decoratorManager = $decoratorManager;
240 return $this;
244 * Set default column align for rows created by appendRow(array $data)
246 * @param int $columnNum
247 * @param string $align
248 * @return Table
250 public function setDefaultColumnAlign($columnNum, $align)
252 $this->defaultColumnAligns[$columnNum] = $align;
254 return $this;
258 * Set the input charset for column contents
260 * @param string $charset
262 public static function setInputCharset($charset)
264 static::$inputCharset = strtolower($charset);
268 * Get the input charset for column contents
270 * @return string
272 public static function getInputCharset()
274 return static::$inputCharset;
278 * Set the output charset for column contents
280 * @param string $charset
282 public static function setOutputCharset($charset)
284 static::$outputCharset = strtolower($charset);
288 * Get the output charset for column contents
290 * @return string
292 public static function getOutputCharset()
294 return static::$outputCharset;
298 * Append a row to the table
300 * @param array|Row $row The row to append to the table
301 * @throws Exception\InvalidArgumentException When $row is neither an array nor Zend\Text\Table\Row
302 * @throws Exception\OverflowException When a row contains too many columns
303 * @return Table
305 public function appendRow($row)
307 if (! is_array($row) && ! ($row instanceof Row)) {
308 throw new Exception\InvalidArgumentException('$row must be an array or instance of Zend\Text\Table\Row');
311 if (is_array($row)) {
312 if (count($row) > count($this->columnWidths)) {
313 throw new Exception\OverflowException('Row contains too many columns');
316 $data = $row;
317 $row = new Row();
318 $colNum = 0;
319 foreach ($data as $columnData) {
320 if (isset($this->defaultColumnAligns[$colNum])) {
321 $align = $this->defaultColumnAligns[$colNum];
322 } else {
323 $align = null;
326 $row->appendColumn(new Column($columnData, $align));
327 $colNum++;
331 $this->rows[] = $row;
333 return $this;
337 * Render the table
339 * @throws Exception\UnexpectedValueException When no rows were added to the table
340 * @return string
342 public function render()
344 // There should be at least one row
345 if (count($this->rows) === 0) {
346 throw new Exception\UnexpectedValueException('No rows were added to the table yet');
349 // Initiate the result variable
350 $result = '';
352 // Count total columns
353 $totalNumColumns = count($this->columnWidths);
355 // Check if we have a horizontal character defined
356 $hasHorizontal = $this->decorator->getHorizontal() !== '';
358 // Now render all rows, starting from the first one
359 $numRows = count($this->rows);
360 foreach ($this->rows as $rowNum => $row) {
361 // Get all column widths
362 if (isset($columnWidths) === true) {
363 $lastColumnWidths = $columnWidths;
366 $renderedRow = $row->render($this->columnWidths, $this->decorator, $this->padding);
367 $columnWidths = $row->getColumnWidths();
368 $numColumns = count($columnWidths);
370 // Check what we have to draw
371 if ($rowNum === 0 && $hasHorizontal) {
372 // If this is the first row, draw the table top
373 $result .= $this->decorator->getTopLeft();
375 foreach ($columnWidths as $columnNum => $columnWidth) {
376 $result .= str_repeat($this->decorator->getHorizontal(), $columnWidth);
378 if (($columnNum + 1) === $numColumns) {
379 $result .= $this->decorator->getTopRight();
380 } else {
381 $result .= $this->decorator->getHorizontalDown();
385 $result .= "\n";
386 } else {
387 // Else check if we have to draw the row separator
388 if (! $hasHorizontal) {
389 $drawSeparator = false; // there is no horizontal character;
390 } elseif ($this->autoSeparate & self::AUTO_SEPARATE_ALL) {
391 $drawSeparator = true;
392 } elseif ($rowNum === 1 && $this->autoSeparate & self::AUTO_SEPARATE_HEADER) {
393 $drawSeparator = true;
394 } elseif ($rowNum === ($numRows - 1) && $this->autoSeparate & self::AUTO_SEPARATE_FOOTER) {
395 $drawSeparator = true;
396 } else {
397 $drawSeparator = false;
400 if ($drawSeparator) {
401 $result .= $this->decorator->getVerticalRight();
403 $currentUpperColumn = 0;
404 $currentLowerColumn = 0;
405 $currentUpperWidth = 0;
406 $currentLowerWidth = 0;
408 // Add horizontal lines
409 // Loop through all column widths
410 foreach ($this->columnWidths as $columnNum => $columnWidth) {
411 // Add the horizontal line
412 $result .= str_repeat($this->decorator->getHorizontal(), $columnWidth);
414 // If this is the last line, break out
415 if (($columnNum + 1) === $totalNumColumns) {
416 break;
419 // Else check, which connector style has to be used
420 $connector = 0x0;
421 $currentUpperWidth += $columnWidth;
422 $currentLowerWidth += $columnWidth;
424 if ($lastColumnWidths[$currentUpperColumn] === $currentUpperWidth) {
425 $connector |= 0x1;
426 $currentUpperColumn += 1;
427 $currentUpperWidth = 0;
428 } else {
429 $currentUpperWidth += 1;
432 if ($columnWidths[$currentLowerColumn] === $currentLowerWidth) {
433 $connector |= 0x2;
434 $currentLowerColumn += 1;
435 $currentLowerWidth = 0;
436 } else {
437 $currentLowerWidth += 1;
440 switch ($connector) {
441 case 0x0:
442 $result .= $this->decorator->getHorizontal();
443 break;
445 case 0x1:
446 $result .= $this->decorator->getHorizontalUp();
447 break;
449 case 0x2:
450 $result .= $this->decorator->getHorizontalDown();
451 break;
453 case 0x3:
454 $result .= $this->decorator->getCross();
455 break;
457 default:
458 // This can never happen, but the CS tells I have to have it ...
459 break;
463 $result .= $this->decorator->getVerticalLeft() . "\n";
467 // Add the rendered row to the result
468 $result .= $renderedRow;
470 // If this is the last row, draw the table bottom
471 if (($rowNum + 1) === $numRows && $hasHorizontal) {
472 $result .= $this->decorator->getBottomLeft();
474 foreach ($columnWidths as $columnNum => $columnWidth) {
475 $result .= str_repeat($this->decorator->getHorizontal(), $columnWidth);
477 if (($columnNum + 1) === $numColumns) {
478 $result .= $this->decorator->getBottomRight();
479 } else {
480 $result .= $this->decorator->getHorizontalUp();
484 $result .= "\n";
488 return $result;
492 * Magic method which returns the rendered table
494 * @return string
496 public function __toString()
498 try {
499 return $this->render();
500 } catch (\Exception $e) {
501 trigger_error($e->getMessage(), E_USER_ERROR);