value = $value; } } /** * Ряд таблицы */ class TableRow { /** @var string|false */ public $style = false; /** @var TableCell[] */ public $cells = []; /** @var int|false */ public $height = false; /** * Устанавливает значение для клетки * @param int $y Номер столбца * @param string $value Значение клетки */ function setCell($y, $value): void { $this->cells[$y] = new TableCell($value); } /** * Устанавливает стиль для клетки * @param int $y Номер столбца * @param string $name Имя стиля */ function setCellStyle($y, $name): void { $this->cells[$y]->style = $name; } } /** * Таблица */ class Table { /** @var int */ static $index; /** @var string */ private $name; /** @var TableRow[] */ protected $rows = []; /** @var int|false */ protected $_splitVertical = false; /** @var int|false */ protected $_splitHorizontal = false; function __construct() { $this->name = "Page " . ((int)self::$index ++); } /** * Записать значение в клетку с заданными координатами * @param int $x Номер ряда * @param int $y Номер столбца * @param string $value Значение клетки */ function setCell(int $x, int $y, $value): void { assert($x > 0); assert($y > 0); if(! isset($this->rows[$x])) { $this->rows[$x] = new TableRow(); } $row = $this->rows[$x]; $row->setCell($y, $value); } /** * Заполняет ряд начиная с указанного столбца значениями из массива */ function setRow(int $row, int $index, array $data): void { assert($index > 0); assert($row > 0); reset($data); for ($i = $index; $i < $index + count($data); $i++) { $this->setCell($row, $i, current($data)); next($data); } } /** * Устанавливает высоту ряда * @param int $row Номер ряда * @param int $value Высота ряда */ function setRowHeight (int $row, $value): void { assert($row > 0); $this->rows[$row]->height = $value; } /** * Устанавливает стиль ряда * @param int $row Номер ряда * @param string $name Имя стиля */ function setRowStyle(int $row, $name): void { assert($row > 0); $this->rows[$row]->style = $name; } /** * Обьединяет клетки в строке * @param int $x Номер ряда * @param int $cell Номер столбца * @param bool $merge Количество клеток для обьединения */ function setCellMerge(int $x, int $cell, $merge): void { assert($x > 0); assert($cell > 0); $row = $this->rows[$x]; $row->cells[$cell]->merge = $merge; } /** * Устанавливает стиль для клеток ряда * @param int $row Номер ряда * @param int $y Номер столбца * @param string $name Имя стиля */ function setCellStyle ($row, $y, $name): void { if (isset($this->rows[$row])) { $this->rows[$row]->setCellStyle($y, $name); } } /** * Добавляет строку к таблице * @return int Номер добавленной строки */ function addRow(int $index = 1, array $data = [""]) { assert($index > 0); $offset = $this->getRows() + 1; $this->setRow($offset, $index, $data); return $offset; } /** * Количество строк в таблице * * @return int */ function getRows() { // Высчитываем максимальный индекс, массив может быть разрежен поэтому используем array_keys $keys = array_keys($this->rows); return $keys === [] ? 0 : max($keys); } /** * Количество столбцов в строке * * @return int */ function getRowCells(TableRow $row) { $keys = array_keys($row->cells); return $keys === [] ? 0 :max($keys); } /** * Разделяет таблицу на две части по вертикали * @param int $n Количество столбцов слева */ function splitVertical($n): void { $this->_splitVertical = $n; } /** * Разделяет таблицу на две части по горизонтали * @param int $n Количество столбцов сверху */ function splitHorizontal($n): void { $this->_splitHorizontal = $n; } /** * Количество столбцов в таблице * * @return int */ function getColumns() { $columns = array_map($this->getRowCells(...), $this->rows); return $columns === [] ? 0 : max($columns); } /** * Кодирование строки * @deprecated Можно заменить на значение * @param string $s Строка * @return string */ function encode($s) { return $s; } /** * Генерация клетки таблицы (Переработать) * @param TableCell $ncell Клетка таблицы * @param XMLWriter $doc XMLWriter * @param int $j Индекс клетки * @param mixed $value Значение клетки * @param bool $setIndex Устанавливать индекс клетки в атрибут ss:Index */ function createCell (TableCell $ncell, XMLWriter $doc, $j, mixed $value, $setIndex): void { $doc->startElement("Cell"); if ($ncell->style) { $doc->writeAttribute('ss:StyleID', $ncell->style); } if ($ncell->merge) { $doc->writeAttribute('ss:MergeAcross', (string)$ncell->merge); } if ($setIndex) { $doc->writeAttribute('ss:Index', (string)$j); } $doc->startElement("Data"); if ($value instanceof Excel_DateTime) { $doc->writeAttribute('ss:Type', "DateTime"); $doc->text($value->getString()); } else if ($value instanceof Excel_Number) { $doc->writeAttribute('ss:Type', "Number"); $doc->text($value->getString()); } else { if (is_string($value)) { $doc->writeAttribute('ss:Type', "String"); } else { $doc->writeAttribute('ss:Type', "Number"); } $doc->writeCdata($value); } $doc->endElement(); $doc->endElement(); } /** * Генерация таблицы */ public function createTable (XMLWriter $doc): void { $doc->startElement('Worksheet'); $doc->writeAttribute('ss:Name', $this->name); $columns = $this->getColumns(); $rows = $this->getRows(); $doc->startElement('Table'); $doc->writeAttribute('ss:ExpandedColumnCount', (string)$columns); $doc->writeAttribute('ss:ExpandedRowCount', (string)$rows); // Переписать цыкл !!!!!!! for ($i = 1; $i <= $rows; $i++) { $doc->startElement('Row'); if (isset($this->rows[$i])) { if ($this->rows[$i]->style) { $doc->writeAttribute('ss:StyleID', $this->rows[$i]->style); } if ($this->rows[$i]->height) { $doc->writeAttribute('ss:Height', (string)$this->rows[$i]->height); } /** @var TableRow $nrow */ $nrow = $this->rows[$i]; // Флаг индикатор подстановки номера столбца $setIndex = false; for ($j = 1; $j <= $columns; $j++) { $value = null; if (isset($nrow->cells[$j])) { $value = $nrow->cells[$j]->value; } if (!empty($value)) { $this->createCell($nrow->cells[$j], $doc, $j, $value, $setIndex); $setIndex = false; } else { $setIndex = true; } } } $doc->endElement(); } $doc->endElement(); $this->splitPane ($doc); $doc->endElement(); } protected function splitPane (XMLWriter $doc): void { $doc->startElement('WorksheetOptions'); $doc->writeAttribute('xmlns', 'urn:schemas-microsoft-com:office:excel'); $doc->writeElement('FrozenNoSplit'); if ($this->_splitVertical) { $doc->writeElement('SplitVertical', (string) $this->_splitVertical); $doc->writeElement('LeftColumnRightPane', (string) $this->_splitVertical); } if ($this->_splitHorizontal) { $doc->writeElement('SplitHorizontal', (string) $this->_splitHorizontal); $doc->writeElement('TopRowBottomPane', (string) $this->_splitHorizontal); } if ($this->_splitHorizontal && $this->_splitVertical) { $doc->writeElement('ActivePane', (string) 0); } else if($this->_splitHorizontal) { $doc->writeElement('ActivePane', (string) 2); } $doc->endElement(); } }