├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── example ├── example.html └── example.php ├── lib └── HtmlPhpExcel │ ├── Elements │ ├── BaseElement.php │ ├── Cell.php │ ├── Document.php │ ├── Element.php │ ├── Row.php │ └── Table.php │ ├── Exception │ └── InexistentExcelObjectException.php │ ├── HtmlPhpExcel.php │ └── Parser │ └── Parser.php ├── phpunit.xml ├── rector.php └── tests ├── HtmlPhpExcel └── Tests │ ├── HtmlPhpExcelTest.php │ └── Parser │ └── ParserTest.php └── testfiles └── test.html /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | tests: 9 | runs-on: ubuntu-20.04 10 | 11 | strategy: 12 | matrix: 13 | php: ['8.2', '8.3', '8.4'] 14 | stability: ['prefer-lowest', 'prefer-stable'] 15 | 16 | name: PHP ${{ matrix.php }} - ${{ matrix.stability }} 17 | 18 | steps: 19 | - name: Checkout code 20 | uses: actions/checkout@v2 21 | 22 | - name: Setup PHP 23 | uses: shivammathur/setup-php@v2 24 | with: 25 | php-version: ${{ matrix.php }} 26 | extensions: dom, curl, libxml, mbstring, zip, gd 27 | tools: composer:v2 28 | coverage: none 29 | 30 | - name: Install dependencies 31 | run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress 32 | 33 | - name: Execute tests 34 | run: vendor/bin/phpunit --verbose 35 | 36 | 37 | code_checks: 38 | runs-on: ubuntu-20.04 39 | name: Static code analysis 40 | 41 | steps: 42 | - name: Checkout code 43 | uses: actions/checkout@v3 44 | 45 | - name: Setup PHP 46 | uses: shivammathur/setup-php@v2 47 | with: 48 | php-version: '8.3' 49 | extensions: dom, curl, libxml, mbstring, zip, bcmath, gd 50 | tools: composer 51 | coverage: none 52 | 53 | - name: Get composer cache directory 54 | id: composer-cache 55 | run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT 56 | 57 | - name: Cache dependencies 58 | uses: actions/cache@v3 59 | with: 60 | path: ${{ steps.composer-cache.outputs.dir }} 61 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} 62 | restore-keys: ${{ runner.os }}-composer- 63 | 64 | - name: Install dependencies 65 | run: composer install --prefer-dist --no-progress 66 | 67 | - name: Check composer files 68 | run: composer validate --strict 69 | 70 | - name: Check coding standard 71 | run: vendor/bin/php-cs-fixer --no-interaction --dry-run --diff -v fix lib/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /composer.lock 3 | .DS_Store 4 | /example/test.xlsx 5 | /example/~$test.xlsx 6 | /.phpunit.result.cache 7 | /.php-cs-fixer.cache 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 – 2019 Ticketpark GmbH / Manuel Reinhard 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HtmlPhpExcel 2 | 3 | [![Build Status](https://github.com/Ticketpark/HtmlPhpExcel/actions/workflows/ci.yml/badge.svg)](https://github.com/Ticketpark/HtmlPhpExcel/actions) 4 | 5 | This is a php library based on [FastExcelWriter](https://github.com/aVadim483/fast-excel-writer), simplifying converting html tables to excel files. It allows styling right within the html template with specific attributes. 6 | 7 | ## Installation 8 | 9 | Add HtmlPhpExcel to your composer.json: 10 | 11 | ``` 12 | composer require ticketpark/htmlphpexcel 13 | ``` 14 | 15 | ## Simple example 16 | ```php 17 | Column AColumn BValue AValue B'; 22 | $htmlPhpExcel = new \Ticketpark\HtmlPhpExcel\HtmlPhpExcel($html); 23 | 24 | $htmlPhpExcel->process()->save('myFile.xlsx'); 25 | 26 | ``` 27 | 28 | For a more complex example see [example directory](example). 29 | 30 | ## Styling 31 | Styles are set with an html attribute `_excel-styles`. The attribute expects the content to be in json format. 32 | 33 | Example: 34 | ```html 35 | 36 | 37 | 40 | 41 |
38 | Cell value 39 |
42 | ``` 43 | 44 | You can use any style supported by `fast-excel-writer`, of which the most common are: 45 | 46 | * border-color 47 | * border-style 48 | * fill-color 49 | * fill-pattern 50 | * font-color 51 | * font-size 52 | * format 53 | * format-text-wrap 54 | * height 55 | * hyperlink 56 | * text-align 57 | * text-color 58 | * text-rotation 59 | * text-wrap 60 | * vertical-align 61 | * width 62 | 63 | More information (though unfortunately limited) is available in the [docs of FastExcelWriter](https://github.com/aVadim483/fast-excel-writer/blob/master/docs/04-styles.md). 64 | 65 | ## Adding links to cells 66 | 67 | Links are treated like styles and added with the `hyperlink` key: 68 | 69 | Example: 70 | ```html 71 | 72 | 73 | 76 | 77 |
74 | Cell value 75 |
78 | ``` 79 | 80 | 81 | 82 | ## Adding comments to cells 83 | 84 | To add comments, use the `_excel-comment` attribute. 85 | 86 | Example: 87 | ```html 88 | 89 | 90 | 93 | 94 |
91 | Cell value 92 |
95 | ``` 96 | 97 | ## History 98 | 99 | * v2.x of this library is based on `FastExcelWriter` 100 | * v1.x of this library was based on `PhpSpreadsheet` 101 | * v0.x of this library was based on `PhpExcel` 102 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ticketpark/htmlphpexcel", 3 | "type": "library", 4 | "description": "A php library to convert html tables to Excel files, including styling.", 5 | "keywords": ["html", "tables", "excel", "phpexcel", "phpspreadsheet", "fastexcelwriter"], 6 | "homepage": "https://github.com/Ticketpark/HtmlPhpExcel", 7 | "license": "MIT", 8 | "authors": [ 9 | {"name": "Manuel Reinhard", "email": "manu@sprain.ch"} 10 | ], 11 | "require": { 12 | "php": "~8.2.0|~8.3.0|~8.4.0", 13 | "ext-json": "*", 14 | "ext-dom": "*", 15 | "ext-intl": "*", 16 | "avadim/fast-excel-helper": "^1.0.4", 17 | "avadim/fast-excel-writer": "^4.5|^5.0|^6.0" 18 | }, 19 | "require-dev": { 20 | "phpunit/phpunit": "^9.0", 21 | "friendsofphp/php-cs-fixer": "^3.35", 22 | "rector/rector": "^1.2" 23 | }, 24 | "autoload": { 25 | "psr-4": { "Ticketpark\\HtmlPhpExcel\\": "lib/HtmlPhpExcel" } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /example/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
An Excel worksheet
Column AColumn B
Left alignRight align
560022
=1+1=A5+B5=SUM(A5:B5)
251520.20
Merge cellsNo need to fill all cells.And no need to care about how many cells there are in each row.Ich bin ein Ümläøut.
58 | -------------------------------------------------------------------------------- /example/example.php: -------------------------------------------------------------------------------- 1 | process()->save(__DIR__ . '/test.xlsx'); 7 | -------------------------------------------------------------------------------- /lib/HtmlPhpExcel/Elements/BaseElement.php: -------------------------------------------------------------------------------- 1 | attributes[$key] = $value; 14 | } 15 | 16 | public function getAttribute(string $key): ?string 17 | { 18 | if (!isset($this->attributes[$key])) { 19 | return null; 20 | } 21 | 22 | return $this->attributes[$key]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/HtmlPhpExcel/Elements/Cell.php: -------------------------------------------------------------------------------- 1 | value = $value; 15 | } 16 | 17 | public function getValue(): ?string 18 | { 19 | return $this->value; 20 | } 21 | 22 | public function setIsHeader(bool $isHeader): void 23 | { 24 | $this->isHeader = $isHeader; 25 | } 26 | 27 | public function isHeader(): bool 28 | { 29 | return $this->isHeader; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/HtmlPhpExcel/Elements/Document.php: -------------------------------------------------------------------------------- 1 | tables[] = $table; 14 | } 15 | 16 | /** 17 | * @return array 18 | */ 19 | public function getTables(): array 20 | { 21 | return $this->tables; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/HtmlPhpExcel/Elements/Element.php: -------------------------------------------------------------------------------- 1 | cells[] = $cell; 14 | } 15 | 16 | /** 17 | * @return array 18 | */ 19 | public function getCells(): array 20 | { 21 | return $this->cells; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/HtmlPhpExcel/Elements/Table.php: -------------------------------------------------------------------------------- 1 | rows[] = $row; 14 | } 15 | 16 | /** 17 | * @return array 18 | */ 19 | public function getRows(): array 20 | { 21 | return $this->rows; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/HtmlPhpExcel/Exception/InexistentExcelObjectException.php: -------------------------------------------------------------------------------- 1 | ) must have to be parsed. 24 | * Default is none, which results in all rows of a parsed table. 25 | */ 26 | private ?string $rowClass = null; 27 | 28 | /** 29 | * The class attribute the rows (
or ) must have to be parsed. 30 | * Default is none, which results in all cells of a parsed row. 31 | */ 32 | private ?string $cellClass = null; 33 | 34 | /** 35 | * The default styles to be applied to all excel cells 36 | */ 37 | private array $defaultStyles = []; 38 | 39 | /** 40 | * The default styles additionally to be applied to header cells () 41 | */ 42 | private array $defaultHeaderStyles = []; 43 | 44 | /** 45 | * The document instance which contains the parsed html elements 46 | */ 47 | private HtmlPhpExcelElement\Document $document; 48 | 49 | /** 50 | * The instance of the Excel creator used within this library. 51 | */ 52 | private ?Excel $excel = null; 53 | 54 | public function __construct( 55 | private readonly string $htmlStringOrFile 56 | ) { 57 | } 58 | 59 | public function setTableClass(?string $class): self 60 | { 61 | $this->tableClass = $class; 62 | 63 | return $this; 64 | } 65 | 66 | public function setRowClass(?string $class): self 67 | { 68 | $this->rowClass = $class; 69 | 70 | return $this; 71 | } 72 | 73 | public function setCellClass(?string $class): self 74 | { 75 | $this->cellClass = $class; 76 | 77 | return $this; 78 | } 79 | 80 | public function setDefaultStyles(array $defaultStyles): self 81 | { 82 | $this->defaultStyles = $defaultStyles; 83 | 84 | return $this; 85 | } 86 | 87 | public function setDefaultHeaderStyles(array $defaultHeaderStyles): self 88 | { 89 | $this->defaultHeaderStyles = $defaultHeaderStyles; 90 | 91 | return $this; 92 | } 93 | 94 | public function process(?Excel $excel = null): self 95 | { 96 | $this->excel = $excel; 97 | if (null === $this->excel) { 98 | $this->excel = Excel::create(); 99 | } 100 | 101 | $this->parseHtml(); 102 | $this->createExcel(); 103 | 104 | return $this; 105 | } 106 | 107 | public function download(string $filename): void 108 | { 109 | $filename = str_ireplace('.xlsx', '', $filename); 110 | $this->getExcelObject()->download($filename . '.xlsx'); 111 | } 112 | 113 | public function save(string $filename): bool 114 | { 115 | $filename = str_ireplace('.xlsx', '', $filename); 116 | 117 | return $this->getExcelObject()->save($filename . '.xlsx'); 118 | } 119 | 120 | public function getExcelObject(): Excel 121 | { 122 | if (null === $this->excel) { 123 | throw new InexistentExcelObjectException('You must run process() before handling the excel object. '); 124 | } 125 | 126 | return $this->excel; 127 | } 128 | 129 | private function parseHtml(): void 130 | { 131 | $parser = new Parser($this->htmlStringOrFile); 132 | $document = $parser->setTableClass($this->tableClass) 133 | ->setRowClass($this->rowClass) 134 | ->setCellClass($this->cellClass) 135 | ->parse(); 136 | 137 | $this->document = $document; 138 | } 139 | 140 | private function createExcel(): void 141 | { 142 | // Remove 1st sheet, which might be created automatically with new excel 143 | try { 144 | $this->excel->removeSheet(1); 145 | } catch (\Exception) { 146 | } 147 | 148 | // Loop over all tables in document 149 | foreach ($this->document->getTables() as $table) { 150 | 151 | // Handle worksheets 152 | $sheet = $this->excel->makeSheet($table->getAttribute('_excel-name')); 153 | 154 | // Loop over all rows 155 | $rowIndex = 1; 156 | foreach ($table->getRows() as $row) { 157 | $rowStyles = $this->getStyles($row); 158 | if (!empty($rowStyles)) { 159 | $sheet->setRowStyles( 160 | $rowIndex, 161 | $rowStyles 162 | ); 163 | } 164 | 165 | // Loop over all cells in a row 166 | $colIndex = 1; 167 | foreach ($row->getCells() as $cell) { 168 | 169 | // Skip cells within merged range 170 | $excelCellIndex = Helper::colLetter($colIndex).$rowIndex; 171 | while ($this->isMerged($sheet, $excelCellIndex)) { 172 | $colIndex++; 173 | $excelCellIndex = Helper::colLetter($colIndex).$rowIndex; 174 | $sheet->cell($excelCellIndex); 175 | } 176 | 177 | // Write cell 178 | $cellStyles = $this->getStyles($cell); 179 | $sheet->writeCell( 180 | trim((string) $cell->getValue()), 181 | empty($cellStyles) ? null : $cellStyles 182 | ); 183 | 184 | if (isset($cellStyles['width'])) { 185 | $sheet->setColWidth($colIndex, $cellStyles['width']); 186 | } 187 | 188 | if (isset($cellStyles['height'])) { 189 | $sheet->setRowHeight($rowIndex, $cellStyles['height']); 190 | } 191 | 192 | $cellComment = $cell->getAttribute('_excel-comment'); 193 | if ($cellComment) { 194 | $sheet->addNote(Excel::cellAddress($rowIndex, $colIndex), $cellComment); 195 | } 196 | 197 | // Merge cells, if necessary 198 | $colspan = $cell->getAttribute('colspan'); 199 | $rowspan = $cell->getAttribute('rowspan'); 200 | 201 | if ($colspan || $rowspan) { 202 | $colspan = $colspan ? $colspan - 1 : 0; 203 | $rowspan = $rowspan ? $rowspan - 1 : 0; 204 | 205 | $mergeCellsTargetCellIndex = Helper::colLetter($colIndex + $colspan).($rowIndex + $rowspan); 206 | $sheet->mergeCells($excelCellIndex . ':' . $mergeCellsTargetCellIndex); 207 | } 208 | 209 | $colIndex++; 210 | } 211 | 212 | $sheet->nextRow(); 213 | $rowIndex++; 214 | } 215 | } 216 | } 217 | 218 | private function isMerged(Sheet $sheet, string $cellAddress): bool 219 | { 220 | foreach ($sheet->getMergedCells() as $range) { 221 | if (Helper::inRange($cellAddress, $range)) { 222 | return true; 223 | } 224 | } 225 | 226 | return false; 227 | } 228 | 229 | private function getStyles(HtmlPhpExcelElement\Element $documentElement): array 230 | { 231 | $styles = []; 232 | if ($attributeStyles = $documentElement->getAttribute('_excel-styles')) { 233 | if (!is_array($attributeStyles)) { 234 | try { 235 | $attributeStyles = json_decode($attributeStyles, true, 512, JSON_THROW_ON_ERROR); 236 | } catch (\JsonException) { 237 | } 238 | } 239 | } 240 | 241 | if (is_array($attributeStyles)) { 242 | $styles = $attributeStyles; 243 | } 244 | 245 | return array_merge( 246 | $this->defaultStyles, 247 | ($documentElement instanceof HtmlPhpExcelElement\Cell && $documentElement->isHeader()) ? $this->defaultHeaderStyles : [], 248 | $styles 249 | ); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /lib/HtmlPhpExcel/Parser/Parser.php: -------------------------------------------------------------------------------- 1 | ) must have to be parsed. 25 | * Default is none, which results in all rows of a parsed table. 26 | */ 27 | private ?string $rowClass = null; 28 | 29 | /** 30 | * The class attribute the rows ( or ) must have to be parsed. 31 | * Default is none, which results in all cells of a parsed row. 32 | */ 33 | private ?string $cellClass = null; 34 | 35 | public function __construct(string $htmlStringOrFile) 36 | { 37 | if (PHP_MAXPATHLEN >= strlen($htmlStringOrFile) && is_file($htmlStringOrFile)) { 38 | $this->html = file_get_contents($htmlStringOrFile); 39 | } else { 40 | $this->html = $htmlStringOrFile; 41 | } 42 | } 43 | 44 | public function setTableClass(?string $class): self 45 | { 46 | $this->tableClass = $class; 47 | 48 | return $this; 49 | } 50 | 51 | public function setRowClass(?string $class): self 52 | { 53 | $this->rowClass = $class; 54 | 55 | return $this; 56 | } 57 | 58 | public function setCellClass(?string $class): self 59 | { 60 | $this->cellClass = $class; 61 | 62 | return $this; 63 | } 64 | 65 | public function parse(): Document 66 | { 67 | $dom = new \DOMDocument(); 68 | $dom->loadHTML($this->html); 69 | 70 | $xpath = new \DOMXPath($dom); 71 | $htmlTables = $xpath->query('.//table[contains(concat(" ", normalize-space(@class), " "), "'.$this->tableClass.'")]'); 72 | 73 | $document = new Document(); 74 | 75 | foreach ($htmlTables as $htmlTable) { 76 | 77 | $table = new Table(); 78 | 79 | $htmlRows = $xpath->query('.//tr[contains(concat(" ", normalize-space(@class), " "), "'.$this->rowClass.'")]', $htmlTable); 80 | foreach ($htmlRows as $htmlRow) { 81 | 82 | $row = new Row(); 83 | $htmlCells = $xpath->query( 84 | './/td[contains(concat(" ", normalize-space(@class), " "), "'.$this->cellClass.'")] 85 | | .//th[contains(concat(" ", normalize-space(@class), " "), "'.$this->cellClass.'")]', 86 | $htmlRow 87 | ); 88 | 89 | foreach ($htmlCells as $htmlCell) { 90 | $cell = new Cell(); 91 | $cell->setValue($htmlCell->nodeValue); 92 | 93 | foreach ($htmlCell->attributes as $attribute) { 94 | $cell->addAttribute($attribute->name, $attribute->value); 95 | } 96 | 97 | if ('th' == $htmlCell->nodeName) { 98 | $cell->setIsHeader(true); 99 | } 100 | 101 | $row->addCell($cell); 102 | } 103 | 104 | foreach ($htmlRow->attributes as $attribute) { 105 | $row->addAttribute($attribute->name, $attribute->value); 106 | } 107 | 108 | $table->addRow($row); 109 | } 110 | 111 | foreach ($htmlTable->attributes as $attribute) { 112 | $table->addAttribute($attribute->name, $attribute->value); 113 | } 114 | 115 | $document->addTable($table); 116 | } 117 | 118 | return $document; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./tests 6 | 7 | 8 | 9 | 10 | ./tests 11 | 12 | 13 | -------------------------------------------------------------------------------- /rector.php: -------------------------------------------------------------------------------- 1 | paths([ 10 | __DIR__ . '/lib', 11 | __DIR__ . '/tests', 12 | ]); 13 | 14 | $rectorConfig->sets([ 15 | LevelSetList::UP_TO_PHP_84 16 | ]); 17 | }; 18 | -------------------------------------------------------------------------------- /tests/HtmlPhpExcel/Tests/HtmlPhpExcelTest.php: -------------------------------------------------------------------------------- 1 | pathToTestfiles = __DIR__.'/../../testfiles/'; 20 | } 21 | 22 | public function testSave() 23 | { 24 | if (!is_writable($this->pathToTestfiles)) { 25 | $this->markTestSkipped( 26 | sprintf('The directory %s must be writable for this test to run.', realpath($this->pathToTestfiles)) 27 | ); 28 | } 29 | 30 | $file = $this->pathToTestfiles.'test.xlsx'; 31 | if (file_exists($file)) { 32 | if (!is_writable($file)) { 33 | $this->markTestSkipped( 34 | sprintf('The file %s must be writable for this test to run.', realpath($file)) 35 | ); 36 | } 37 | unlink($file); 38 | } 39 | 40 | $htmlphpexcel = new HtmlPhpExcel('
'); 41 | $htmlphpexcel->process()->save($file); 42 | 43 | $this->assertTrue(file_exists($file)); 44 | 45 | unlink($file); 46 | } 47 | 48 | public function testItReturnsExcelInstance() 49 | { 50 | $htmlphpexcel = new HtmlPhpExcel('
'); 51 | $this->assertInstanceOf(Excel::class, $htmlphpexcel->process()->getExcelObject()); 52 | } 53 | 54 | public function testItThrowsExceptionIfProcessIsNotRunBeforeGettingExcelObject() 55 | { 56 | $this->expectException(InexistentExcelObjectException::class); 57 | 58 | $htmlphpexcel = new HtmlPhpExcel('
'); 59 | $htmlphpexcel->getExcelObject(); 60 | } 61 | 62 | public function testItThrowsExceptionIfProcessIsNotRunBeforeSave() 63 | { 64 | $this->expectException(InexistentExcelObjectException::class); 65 | 66 | $htmlphpexcel = new HtmlPhpExcel('
'); 67 | $htmlphpexcel->save('foo'); 68 | } 69 | 70 | public function testItThrowsExceptionIfProcessIsNotRunBeforeDownload() 71 | { 72 | $this->expectException(InexistentExcelObjectException::class); 73 | 74 | $htmlphpexcel = new HtmlPhpExcel('
'); 75 | $htmlphpexcel->download('foo'); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tests/HtmlPhpExcel/Tests/Parser/ParserTest.php: -------------------------------------------------------------------------------- 1 |
row1cell1row1cell2
row2cell1row2cell2
'); 15 | $document = $parser->parse(); 16 | 17 | $this->assertEquals(1, count($document->getTables())); 18 | $this->assertEquals(2, count($document->getTables()[0]->getRows())); 19 | 20 | foreach($document->getTables()[0]->getRows() as $row){ 21 | $this->assertEquals(2, count($row->getCells())); 22 | } 23 | } 24 | 25 | public function testMultipleTables() 26 | { 27 | $parser = new Parser(' 28 |
row1cell1row1cell2
row2cell1row2cell2
29 |

someotherstuff

30 |
row1cell1row1cell2
row2cell1row2cell2
31 | '); 32 | $document = $parser->parse(); 33 | 34 | $this->assertEquals(2, count($document->getTables())); 35 | foreach($document->getTables() as $table){ 36 | $this->assertEquals(2, count($table->getRows())); 37 | 38 | foreach($table->getRows() as $row){ 39 | $this->assertEquals(2, count($row->getCells())); 40 | } 41 | } 42 | } 43 | 44 | public function testMultipleTablesWithTableClass() 45 | { 46 | $parser = new Parser(' 47 |
row1cell1row1cell2
row2cell1row2cell2
48 |

someotherstuff

49 |
row1cell1row1cell2
row2cell1row2cell2
50 | '); 51 | $document = $parser->setTableClass('pickme')->parse(); 52 | 53 | $this->assertEquals(1, count($document->getTables())); 54 | } 55 | 56 | public function testMultipleTablesWithRowClass() 57 | { 58 | $parser = new Parser(' 59 |
row1cell1row1cell2
row2cell1row2cell2
60 |

someotherstuff

61 |
row1cell1row1cell2
row2cell1row2cell2
62 | '); 63 | $document = $parser->setRowClass('pickme')->parse(); 64 | 65 | foreach($document->getTables() as $table){ 66 | $this->assertEquals(1, count($table->getRows())); 67 | } 68 | } 69 | 70 | public function testMultipleTablesWithCellClass() 71 | { 72 | $parser = new Parser(' 73 |
row1cell1row1cell2
row2cell1row2cell2
74 |

someotherstuff

75 |
row1cell1row1cell2
row2cell1row2cell2
76 | '); 77 | $document = $parser->setCellClass('pickme')->parse(); 78 | 79 | foreach($document->getTables() as $table){ 80 | foreach($table->getRows() as $row){ 81 | $this->assertEquals(1, count($row->getCells())); 82 | } 83 | } 84 | } 85 | 86 | public function testMultipleTablesWithMixedClasses() 87 | { 88 | $parser = new Parser(' 89 |
row1cell1row1cell2
row2cell1row2cell2
90 |

someotherstuff

91 |
row1cell1row1cell2
row2cell1row2cell2
92 | '); 93 | $document = $parser 94 | ->setTableClass('pickme') 95 | ->setRowClass('pickme') 96 | ->setCellClass('pickme') 97 | ->parse(); 98 | 99 | $this->assertEquals(1, count($document->getTables())); 100 | foreach($document->getTables() as $table){ 101 | $this->assertEquals(1, count($table->getRows())); 102 | 103 | foreach($table->getRows() as $row){ 104 | $this->assertEquals(1, count($row->getCells())); 105 | } 106 | } 107 | } 108 | 109 | public function testMultipleTablesWithMixedClassesAndOtherClasses() 110 | { 111 | $parser = new Parser(' 112 |
row1cell1row1cell2
row2cell1row2cell2
113 |

someotherstuff

114 |
row1cell1row1cell2
row2cell1row2cell2
115 | '); 116 | $document = $parser 117 | ->setTableClass('pickme') 118 | ->setRowClass('pickme') 119 | ->setCellClass('pickme') 120 | ->parse(); 121 | 122 | $this->assertEquals(1, count($document->getTables())); 123 | foreach($document->getTables() as $table){ 124 | $this->assertEquals(1, count($table->getRows())); 125 | 126 | foreach($table->getRows() as $row){ 127 | $this->assertEquals(1, count($row->getCells())); 128 | } 129 | } 130 | } 131 | 132 | public function testFindsHeaders() 133 | { 134 | $parser = new Parser('
row1cell1row1cell2
row2cell1row2cell2
'); 135 | $document = $parser->parse(); 136 | 137 | foreach($document->getTables()[0]->getRows() as $key => $row){ 138 | foreach($row->getCells() as $cell){ 139 | if (0 === $key) { 140 | $this->assertTrue($cell->isHeader()); 141 | } elseif (1 === $key) { 142 | $this->assertFalse($cell->isHeader()); 143 | } 144 | } 145 | } 146 | } 147 | 148 | public function testFindsAttributes() 149 | { 150 | $parser = new Parser('
row1cell1row1cell2
row2cell1row2cell2
'); 151 | $document = $parser->parse(); 152 | 153 | foreach($document->getTables() as $table){ 154 | $this->assertEquals('foo', $table->getAttribute('bar')); 155 | 156 | foreach($table->getRows() as $rowKey => $row){ 157 | if (0 === $rowKey) { 158 | $this->assertEquals('foo', $row->getAttribute('bar')); 159 | } else { 160 | $this->assertEquals(null, $row->getAttribute('bar')); 161 | } 162 | 163 | foreach($row->getCells() as $cellKey => $cell){ 164 | if (0 === $rowKey && 0 == $cellKey) { 165 | $this->assertEquals('foo', $cell->getAttribute('bar')); 166 | } else { 167 | $this->assertEquals(null, $cell->getAttribute('bar')); 168 | } 169 | } 170 | } 171 | } 172 | } 173 | 174 | public function testWithFile() 175 | { 176 | $parser = new Parser($this->pathToTestfiles . '/test.html'); 177 | $document = $parser->parse(); 178 | $this->assertEquals(1, count($document->getTables())); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /tests/testfiles/test.html: -------------------------------------------------------------------------------- 1 |
--------------------------------------------------------------------------------