phplibrary/src/Excel/Table.php

357 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Клетка таблицы
*/
namespace ctiso\Excel;
use XMLWriter,
ctiso\Excel\DateTime as Excel_DateTime,
ctiso\Excel\Number as Excel_Number;
class TableCell
{
/** @var string|false */
public $style = false;
/** @var string */
public $value;
/** @var bool */
public $merge = false;
/**
* @param string $value Значение клетки
*/
function __construct ($value)
{
$this->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();
}
}