value = $value; } function getString() { return $this->value; } } class Excel_DateTime { public $value; function __construct($value) { $this->value = intval($value); } function getString() { return date('Y-m-d\TH:i:s.u', $this->value); } } /** * Клетка таблицы */ class TableCell { public $style = false; public $value; public $merge = false; function __construct ($value) { $this->value = $value; } } /** * Ряд таблицы */ class TableRow { public $style = false; public $cells = array(); public $height = false; function setCell($y, $value) { $this->cells[$y] = new TableCell($value); } function setCellStyle($y, $name) { $this->cells[$y]->style = $name; } } /** * Таблица */ class ExcelTable { static $index; private $name; private $style; protected $rows = array(); protected $splitVertical = false; protected $splitHorizontal = false; function __construct() { $this->name = "Page " . intval(self::$index ++); } /** * Записать значение в клетку с заданными координатами */ function setCell($x, $y, $value) { assert(is_numeric($x) && $x > 0); assert(is_numeric($y) && $y > 0); if(! isset($this->rows[$x])) { $this->rows[$x] = new TableRow(); } $this->rows[$x]->setCell($y, $value); } /** * Заполняет ряд начиная с указанного столбца значениями из массива */ function setRow($row, $index, array $data) { assert(is_numeric($index) && $index > 0); assert(is_numeric($row) && $row > 0); reset($data); for ($i = $index; $i < $index + count($data); $i++) { $this->setCell($row, $i, current($data)); next($data); } } /** * Устанавливает высоту ряда * @param $row integer Номер ряда * @parma $value real Высота ряда */ function setRowHeight ($row, $value) { assert(is_numeric($row) && $row > 0); $this->rows[$row]->height = $value; } /** * Устанавливает стиль ряда * @param $row integer Номер ряда * @parma $name string Имя стиля */ function setRowStyle ($row, $name) { assert(is_numeric($row) && $row > 0); $this->rows[$row]->style = $name; } /** * Обьединяет клетки в строке * @param $row Номер ряда * @param $cell Номер столбца * @param $merge Количество клеток для обьединения */ function setCellMerge ($row, $cell, $merge) { assert(is_numeric($row) && $row > 0); assert(is_numeric($cell) && $cell > 0); $this->rows[$row]->cells[$cell]->merge = $merge; } /** * Устанавливает стиль для клеток ряда * @param $row integer Номер ряда * @param $y integer Номер столбца * @parma $name string Имя стиля */ function setCellStyle ($row, $y, $name) { if (isset($this->rows[$row])) $this->rows[$row]->setCellStyle($y, $name); } /** * Добавляет строку к таблице */ function addRow($index = 1, array $data = array("")) { assert(is_numeric($index) && $index > 0); $offset = $this->getRows() + 1; $this->setRow($offset, $index, $data); return $offset; } /** * Количество строк в таблице * * @return int */ function getRows() { return max(array_keys($this->rows)); } /** * Количество столбцов в строке * * @return int */ function getRowCells($row) { return max(array_keys($row->cells)); } /** * Разделяет таблицу на две части по вертикали * @param $n integer Количество столбцов слева */ function splitVertical($n) { $this->splitVertical = $n; } /** * Разделяет таблицу на две части по горизонтали * @param $n integer Количество столбцов сверху */ function splitHorizontal($n) { $this->splitHorizontal = $n; } /** * Количество столбцов в таблице * * @return int */ function getColumns() { return max(array_map(array($this, 'getRowCells'), $this->rows)); } function encode($s) { return $s; } /** * Генерация клетки таблицы (Переработать) */ function createCell ($ncell, XMLWriter $doc, $j, $value, $setIndex) { $doc->startElement("Cell"); if ($ncell->style) { $doc->writeAttribute('ss:StyleID', $ncell->style); } if ($ncell->merge) { $doc->writeAttribute('ss:MergeAcross', $ncell->merge); } if ($setIndex) { $doc->writeAttribute('ss:Index', $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($this->encode($value)); } $doc->endElement(); $doc->endElement(); } /** * Генерация таблицы */ public function createTable (XMLWriter $doc) { $doc->startElement('Worksheet'); $doc->writeAttribute('ss:Name', $this->name); $columns = $this->getColumns(); $rows = $this->getRows(); $doc->startElement('Table'); $doc->writeAttribute('ss:ExpandedColumnCount', $columns); $doc->writeAttribute('ss:ExpandedRowCount', $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', $this->rows[$i]->height); } $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) { $doc->startElement('WorksheetOptions'); $doc->writeAttribute('xmlns', 'urn:schemas-microsoft-com:office:excel'); $doc->writeElement('FrozenNoSplit'); if ($this->splitVertical) { $doc->writeElement('SplitVertical', $this->splitVertical); $doc->writeElement('LeftColumnRightPane', $this->splitVertical); } if ($this->splitHorizontal) { $doc->writeElement('SplitHorizontal', $this->splitHorizontal); $doc->writeElement('TopRowBottomPane', $this->splitHorizontal); } if ($this->splitHorizontal && $this->splitVertical) { $doc->writeElement('ActivePane', 0); } else if($this->splitHorizontal) { $doc->writeElement('ActivePane', 2); } $doc->endElement(); } } /** * Документ */ class ExcelDocument { static $ns = "urn:schemas-microsoft-com:office:spreadsheet"; private $table = array (); protected $styles = array(); function addTable($table) { $this->table [] = $table; } /** * Добавление стиля к документу * @param $name string Имя стиля * @param $values array Параметры стиля * @param $type Тип стиля */ function setStyle ($name, array $values, $type = 'Interior') { if(!isset($this->styles[$name])) { $this->styles[$name] = array(); } $this->styles[$name][$type] = $values; } /** * Генерация стилей */ private function createStyles (XMLWriter $doc) { $doc->startElement('Styles'); foreach ($this->styles as $name => $sn) { $doc->startElement('Style'); $doc->writeAttribute('ss:ID', $name); foreach ($sn as $type => $s) { // Стиль Borders - составной if ($type == 'Borders') { $doc->startElement('Borders'); foreach ($s as $border) { $doc->startElement('Border'); foreach ($border as $key => $value) { $doc->writeAttribute('ss:' . $key, $value); } $doc->endElement(); } $doc->endElement(); } else { $doc->startElement($type); foreach ($s as $key => $value) { $doc->writeAttribute('ss:' . $key, $value); } $doc->endElement(); } } $doc->endElement(); } $doc->endElement(); } /** * Преобразует переводы строки в спец символы */ function clean ($s) { assert(is_string($s)); return strtr($s, array ("\n" => ' ')); } /** * Сохраняет таблицу в формате Office 2003 XML * http://en.wikipedia.org/wiki/Microsoft_Office_XML_formats */ function save($filename) { $doc = new xmlWriter(); $doc->openURI($filename); $doc->setIndent(false); $doc->startDocument('1.0','utf-8'); $doc->startElement('Workbook'); $doc->writeAttribute('xmlns', self::$ns); $doc->writeAttribute('xmlns:ss', self::$ns); $this->createStyles($doc); foreach ($this->table as $table) { if ($table instanceof ExcelTable) { $table->createTable($doc); } else { $table_data = call_user_func($table); $table_data->createTable($doc); unset($table_data); } } $doc->endElement(); } }