diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..9476f34d9d844bccd738a22c7751912c450af2f9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,22 @@ +dist: bionic +sudo: required + +language: php + +php: + - 7.4.28 + +install: + - composer validate --no-check-all --no-check-publish + - composer install + +script: ./vendor/bin/phpunit Test/ + +after_success: + - bash <(curl -s https://codecov.io/bash) + +cache: + apt: true + directories: + - $TRAVIS_BUILD_DIR/vendor + - $HOME/.composer/cache diff --git a/Test/FilePropertyTest.php b/Test/FilePropertyTest.php new file mode 100644 index 0000000000000000000000000000000000000000..57c45ef63dfa4dadc63d79e8397e04808d507d48 --- /dev/null +++ b/Test/FilePropertyTest.php @@ -0,0 +1,60 @@ +<?php + +namespace Test; + + +use PHPUnit\Framework\TestCase; + +class FilePropertyTest extends TestCase { + + public function testAuthor() { + $expected_author = "John Doe"; + $writer = new \XLSXWriter(); + $writer->setAuthor($expected_author); + + $xlsx_properties = $writer->getFileProperties(); + + $this->assertEquals($expected_author, $xlsx_properties["author"]); + } + + public function testTitle() { + $expected_title = "My Spreadsheet"; + $writer = new \XLSXWriter(); + $writer->setTitle($expected_title); + + $xlsx_properties = $writer->getFileProperties(); + + $this->assertEquals($expected_title, $xlsx_properties["title"]); + } + + public function testSubject() { + $expected_subject = "My Spreadsheet is Wonderful"; + $writer = new \XLSXWriter(); + $writer->setSubject($expected_subject); + + $xlsx_properties = $writer->getFileProperties(); + + $this->assertEquals($expected_subject, $xlsx_properties["subject"]); + } + + public function testCompany() { + $expected_company = "EBANX"; + $writer = new \XLSXWriter(); + $writer->setCompany($expected_company); + + $xlsx_properties = $writer->getFileProperties(); + + $this->assertEquals($expected_company, $xlsx_properties["company"]); + } + + public function testKeywords() { + $expected_keywords = ["spreadsheet", "php", "EBANX"]; + $writer = new \XLSXWriter(); + $writer->setKeywords($expected_keywords); + + $xlsx_properties = $writer->getFileProperties(); + + $this->assertEquals($expected_keywords, $xlsx_properties["keywords"]); + } + +} diff --git a/Test/XLSXWriterTest.php b/Test/XLSXWriterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..36b163640ce1f68bf129d42825305e87cd097bbc --- /dev/null +++ b/Test/XLSXWriterTest.php @@ -0,0 +1,523 @@ +<?php + +namespace Test; + +include_once __DIR__.'/../vendor/autoload.php'; + +use PHPUnit\Framework\TestCase; +use SimpleXMLElement; +use XLSXWriter; +use XLSXWriter_BuffererWriter; +use ZipArchive; + +class _XLSXWriter_ extends XLSXWriter +{ + public function writeCell(XLSXWriter_BuffererWriter &$file, $row_number, $column_number, $value, $cell_format, $cell_style_idx = null) { + return call_user_func_array('parent::writeCell', [&$file, $row_number, $column_number, $value, $cell_format, $cell_style_idx = null]); + } +} +//Just a simple test, by no means comprehensive + +class XLSXWriterTest extends TestCase +{ + /** + * @covers XLSXWriter::writeCell + */ + public function testWriteCell() { + $filename = tempnam("/tmp", "xlsx_writer"); + $file_writer = new XLSXWriter_BuffererWriter($filename); + + $xlsx_writer = new _XLSXWriter_(); + $xlsx_writer->writeCell($file_writer, 0, 0, '0123', 'string'); + $file_writer->close(); + $cell_xml = file_get_contents($filename); + $this->assertNotEquals('<c r="A1" s="0" t="n"><v>123</v></c>', $cell_xml); + $this->assertEquals('<c r="A1" s="0" t="s"><v>0</v></c>', $cell_xml);//0123 should be the 0th index of the shared string array + @unlink($filename); + } + + /** + * @covers XLSXWriter::writeToFile + */ + public function testWriteToFile() { + $filename = tempnam("/tmp", "xlsx_writer"); + + $header = ['0'=>'string','1'=>'string','2'=>'string','3'=>'string']; + $sheet = [ + ['55','66','77','88'], + ['10','11','12','13'], + ]; + + $xlsx_writer = new XLSXWriter(); + $xlsx_writer->writeSheet($sheet,'mysheet',$header); + $xlsx_writer->writeToFile($filename); + + $zip = new ZipArchive(); + $r = $zip->open($filename); + $this->assertTrue($r); + + $this->assertNotEmpty(($zip->numFiles)); + + $out_sheet = []; + for($z=0; $z < $zip->numFiles; $z++) { + $inside_zip_filename = $zip->getNameIndex($z); + + if (preg_match("/sheet(\d+).xml/", basename($inside_zip_filename))) { + $out_sheet = $this->stripCellsFromSheetXML($zip->getFromName($inside_zip_filename)); + array_shift($out_sheet); + $out_sheet = array_values($out_sheet); + } + } + + $zip->close(); + @unlink($filename); + + $r1 = self::array_diff_assoc_recursive($out_sheet, $sheet); + $r2 = self::array_diff_assoc_recursive($sheet, $out_sheet); + $this->assertEmpty($r1); + $this->assertEmpty($r2); + } + + public function testMarkMergedCells() { + $filename = tempnam("/tmp", "xlsx_writer"); + + $header = ['0'=>'string','1'=>'string','2'=>'string','3'=>'string']; + $sheet = [ + ['55','66','77','88'], + ['10','11','12','13'], + ]; + + $expected_merged_range = "B2:C3"; + + $xlsx_writer = new XLSXWriter(); + $xlsx_writer->writeSheetHeader('mysheet', $header); + $xlsx_writer->writeSheetRow('mysheet', $sheet[0]); + $xlsx_writer->writeSheetRow('mysheet', $sheet[1]); + $xlsx_writer->markMergedCell('mysheet', 1, 1, 2, 2); + $xlsx_writer->writeToFile($filename); + + $zip = new ZipArchive(); + $r = $zip->open($filename); + $xml = $this->extractSheetXml($zip); + + $this->assertTrue($r); + $this->assertNotEmpty(($zip->numFiles)); + $this->assertNotEmpty($xml); + + $merged_cell_range = $xml->mergeCells->mergeCell["ref"][0]; + + $this->assertEquals($expected_merged_range, $merged_cell_range); + + $zip->close(); + @unlink($filename); + } + + /** + * @dataProvider getFreezeCellsScenarios + */ + public function testFreezeCells($freeze_cols, $freeze_rows, $expected_active_cells, $expected_pane) { + $filename = tempnam("/tmp", "xlsx_writer"); + + $header = ['0'=>'string','1'=>'string','2'=>'string','3'=>'string']; + $sheet = [ + ['55','66','77','88'], + ['10','11','12','13'], + ]; + + $col_options = ['freeze_columns' => $freeze_cols, 'freeze_rows' => $freeze_rows]; + + $xlsx_writer = new XLSXWriter(); + $xlsx_writer->writeSheetHeader('mysheet', $header, $format = 'xlsx', $delimiter = ';', $subheader = NULL, $col_options); + $xlsx_writer->writeSheetRow('mysheet', $sheet[0]); + $xlsx_writer->writeSheetRow('mysheet', $sheet[1]); + $xlsx_writer->writeToFile($filename); + + $zip = new ZipArchive(); + $r = $zip->open($filename); + $xml = $this->extractSheetXml($zip); + + $this->assertTrue($r); + $this->assertNotEmpty(($zip->numFiles)); + $this->assertNotEmpty($xml); + + $sheet_view = $xml->sheetViews->sheetView; + + if (!empty($expected_pane)) { + $pane = $sheet_view->pane; + foreach ($expected_pane as $expected_key => $expected_value) { + $attribute = (string) $pane[0][$expected_key]; + $this->assertEquals($expected_value, $attribute); + } + } + + $selections = $sheet_view->selection; + for ($i = 0; $i < count($expected_active_cells); $i++) { + $this->assertEquals($expected_active_cells[$i]['cell'], $selections[$i]['activeCell']); + $this->assertEquals($expected_active_cells[$i]['cell'], $selections[$i]['sqref']); + $this->assertEquals($expected_active_cells[$i]['pane'], $selections[$i]['pane']); + } + + $zip->close(); + @unlink($filename); + } + + public static function getFreezeCellsScenarios() { + return [ + "Not frozen" => [ + $freeze_cols = false, + $freeze_rows = false, + $expected_active_cells = [["cell" => "A1", "pane" => "topLeft"]], + $expected_pane = [], + ], + "Frozen Col B and Row 2" => [ + $freeze_cols = 1, + $freeze_rows = 1, + $expected_active_cells = [["cell" => "A2", "pane" => "topRight"], ["cell" => "B1", "pane" => "bottomLeft"], ["cell" => "B2", "pane" => "bottomRight"]], + $expected_pane = ["ySplit" => $freeze_rows, "xSplit" => $freeze_cols, "topLeftCell" => "B2", "activePane" => "bottomRight"], + ], + "Frozen Col B" => [ + $freeze_cols = 1, + $freeze_rows = false, + $expected_active_cells = [["cell" => "B1", "pane" => "topRight"]], + $expected_pane = ["xSplit" => $freeze_cols, "topLeftCell" => "B1", "activePane" => "topRight"], + ], + "Frozen Row 2" => [ + $freeze_cols = false, + $freeze_rows = 1, + $expected_active_cells = [["cell" => "A2", "pane" => "bottomLeft"]], + $expected_pane = ["ySplit" => $freeze_rows, "topLeftCell" => "A2", "activePane" => "bottomLeft"], + ], + "Frozen Col A and Row 1" => [ + $freeze_cols = 0, + $freeze_rows = 0, + $expected_active_cells = [["cell" => "A1", "pane" => "topRight"], ["cell" => "A1", "pane" => "bottomLeft"], ["cell" => "A1", "pane" => "bottomRight"]], + $expected_pane = ["ySplit" => $freeze_rows, "xSplit" => $freeze_cols, "topLeftCell" => "A1", "activePane" => "bottomRight"], + ], + "Frozen Col A" => [ + $freeze_cols = 0, + $freeze_rows = false, + $expected_active_cells = [["cell" => "A1", "pane" => "topRight"]], + $expected_pane = ["xSplit" => $freeze_cols, "topLeftCell" => "A1", "activePane" => "topRight"], + ], + "Frozen Row 1" => [ + $freeze_cols = false, + $freeze_rows = 0, + $expected_active_cells = [["cell" => "A1", "pane" => "bottomLeft"]], + $expected_pane = ["ySplit" => $freeze_rows, "topLeftCell" => "A1", "activePane" => "bottomLeft"], + ], + ]; + } + + public function testColumnsWidths() { + $filename = tempnam("/tmp", "xlsx_writer"); + + $header = ['0'=>'string','1'=>'string','2'=>'string','3'=>'string']; + $sheet = [ + ['55','66','77','88'], + ['10','11','12','13'], + ]; + + $widths = [10, 20, 30, 40]; + + $col_options = ['widths' => $widths]; + + $xlsx_writer = new XLSXWriter(); + $xlsx_writer->writeSheetHeader('mysheet', $header, $format = 'xlsx', $delimiter = ';', $subheader = NULL, $col_options); + $xlsx_writer->writeSheetRow('mysheet', $sheet[0]); + $xlsx_writer->writeSheetRow('mysheet', $sheet[1]); + $xlsx_writer->writeToFile($filename); + + $zip = new ZipArchive(); + $r = $zip->open($filename); + $xml = $this->extractSheetXml($zip); + + $this->assertTrue($r); + $this->assertNotEmpty(($zip->numFiles)); + $this->assertNotEmpty($xml); + + $cols = $xml->cols->col; + foreach ($widths as $col_index => $col_width) { + $col = $cols[$col_index]; + $this->assertFalse(filter_var($col["collapsed"], FILTER_VALIDATE_BOOLEAN)); + $this->assertFalse(filter_var($col["hidden"], FILTER_VALIDATE_BOOLEAN)); + $this->assertTrue(filter_var($col["customWidth"], FILTER_VALIDATE_BOOLEAN)); + $this->assertEquals($col_index + 1, (string) $col["max"]); + $this->assertEquals($col_index + 1, (string) $col["min"]); + $this->assertEquals("0", (string) $col["style"]); + $this->assertEquals($col_width, (string) $col["width"]); + } + $last_col_index = count($widths); + $last_col = $cols[$last_col_index]; + $this->assertFalse(filter_var($last_col["collapsed"], FILTER_VALIDATE_BOOLEAN)); + $this->assertFalse(filter_var($last_col["hidden"], FILTER_VALIDATE_BOOLEAN)); + $this->assertFalse(filter_var($last_col["customWidth"], FILTER_VALIDATE_BOOLEAN)); + $this->assertEquals("1024", (string) $last_col["max"]); + $this->assertEquals($last_col_index + 1, (string) $last_col["min"]); + $this->assertEquals("0", (string) $last_col["style"]); + $this->assertEquals("11.5", (string) $last_col["width"]); + + $zip->close(); + @unlink($filename); + } + + public function testRowHeight() { + $filename = tempnam("/tmp", "xlsx_writer"); + + $sheet = [ + ['55','66','77','88'], + ['10','11','12','13'], + ]; + + $custom_height = 20.5; + + $row_options = ['height' => $custom_height]; + + $xlsx_writer = new XLSXWriter(); + $xlsx_writer->writeSheetRow('mysheet', $sheet[0], $format = 'xlsx', $delimiter = ';', $row_options); + $xlsx_writer->writeSheetRow('mysheet', $sheet[1]); + $xlsx_writer->writeToFile($filename); + + $zip = new ZipArchive(); + $r = $zip->open($filename); + $xml = $this->extractSheetXml($zip); + + $this->assertTrue($r); + $this->assertNotEmpty(($zip->numFiles)); + $this->assertNotEmpty($xml); + + $rows = $xml->sheetData->row; + $this->assertRowProperties($custom_height, $expected_custom_height = true, $expected_hidden = false, $expected_collapsed = false, $rows[0]); + $this->assertRowProperties($expected_height = 12.1, $expected_custom_height = false, $expected_hidden = false, $expected_collapsed = false, $rows[1]); + + $zip->close(); + @unlink($filename); + } + + public function testRowHidden() { + $filename = tempnam("/tmp", "xlsx_writer"); + + $sheet = [ + ['55','66','77','88'], + ['10','11','12','13'], + ]; + + $row_options = ['hidden' => true]; + + $expected_height = 12.1; + $expected_custom_height = false; + $expected_collapsed = false; + + $xlsx_writer = new XLSXWriter(); + $xlsx_writer->writeSheetRow('mysheet', $sheet[0], $format = 'xlsx', $delimiter = ';', $row_options); + $xlsx_writer->writeSheetRow('mysheet', $sheet[1]); + $xlsx_writer->writeToFile($filename); + + $zip = new ZipArchive(); + $r = $zip->open($filename); + $xml = $this->extractSheetXml($zip); + + $this->assertTrue($r); + $this->assertNotEmpty(($zip->numFiles)); + $this->assertNotEmpty($xml); + + $rows = $xml->sheetData->row; + $this->assertRowProperties($expected_height, $expected_custom_height, $expected_hidden = true, $expected_collapsed, $rows[0]); + $this->assertRowProperties($expected_height, $expected_custom_height, $expected_hidden = false, $expected_collapsed, $rows[1]); + + $zip->close(); + @unlink($filename); + } + + public function testRowCollapsed() { + $filename = tempnam("/tmp", "xlsx_writer"); + + $sheet = [ + ['55','66','77','88'], + ['10','11','12','13'], + ]; + + $row_options = ['collapsed' => true]; + + $expected_height = 12.1; + $expected_custom_height = false; + $expected_hidden = false; + + $xlsx_writer = new XLSXWriter(); + $xlsx_writer->writeSheetRow('mysheet', $sheet[0], $format = 'xlsx', $delimiter = ';', $row_options); + $xlsx_writer->writeSheetRow('mysheet', $sheet[1]); + $xlsx_writer->writeToFile($filename); + + $zip = new ZipArchive(); + $r = $zip->open($filename); + $xml = $this->extractSheetXml($zip); + + $this->assertTrue($r); + $this->assertNotEmpty(($zip->numFiles)); + $this->assertNotEmpty($xml); + + $rows = $xml->sheetData->row; + $this->assertRowProperties($expected_height, $expected_custom_height, $expected_hidden, $expected_collapsed = true, $rows[0]); + $this->assertRowProperties($expected_height, $expected_custom_height, $expected_hidden, $expected_collapsed = false, $rows[1]); + + $zip->close(); + @unlink($filename); + } + + public function testAddBorder() { + $filename = tempnam("/tmp", "xlsx_writer"); + + $header = ["0"=>"string", "1"=>"string", "2"=>"string", "3"=>"string"]; + $sheet = [ + ["55", "66", "77", "88"], + ["10", "11", "12", "13"], + ]; + + $expected_borders = ["right", "left", "top", "bottom"]; + $expected_border_style = "thick"; + $expected_border_color_base = "ff99cc"; + $expected_border_color = "FFFF99CC"; + + $row_options = [ + "border" => implode(",", $expected_borders), + "border-style" => $expected_border_style, + "border-color" => "#$expected_border_color_base" , + ]; + + $xlsx_writer = new XLSXWriter(); + $xlsx_writer->writeSheetHeader("mysheet", $header); + $xlsx_writer->writeSheetRow("mysheet", $sheet[0], $format = "xlsx", $delimiter = ";", $row_options); + $xlsx_writer->writeSheetRow("mysheet", $sheet[1]); + $xlsx_writer->writeToFile($filename); + + $zip = new ZipArchive(); + $r = $zip->open($filename); + $xml = $this->extractSheetXml($zip); + $styles = $this->extractStyleXml($zip); + + $this->assertTrue($r); + $this->assertNotEmpty(($zip->numFiles)); + $this->assertNotEmpty($xml); + $this->assertNotEmpty($styles); + + $border_styles = $styles->borders; + $this->assertBorderStyle($expected_border_style, $expected_border_color, $border = $border_styles->border[1]); + $this->assertFillStyle($expected_pattern = "solid", $expected_bg_color = "FF003300", $styles->fills->fill[2]); + $this->assertFontStyle($expected_font_name = "Arial", $expected_is_bold = "true", $styles->fonts->font[4]); + + $cell_styles = $styles->cellXfs->xf; + $this->assertCellStyle($expected_apply_border_string = "false", $expected_border_id = 0, $expected_fill_id = 2, $expected_font_id = 4, $cell_styles[6]); + $this->assertCellStyle($expected_apply_border_string = "true", $expected_border_id = 1, $expected_fill_id = 0, $expected_font_id = 0, $cell_styles[7]); + + $rows = $xml->sheetData->row; + $this->assertRowHasStyleIndex($rows[0], $expected_header_style = 6); + $this->assertRowHasStyleIndex($rows[1], $expected_style = 7); + + $zip->close(); + @unlink($filename); + } + + private function stripCellsFromSheetXML($sheet_xml) { + $output = []; + + $xml = new SimpleXMLElement($sheet_xml); + + for ($i = 0; $i < count($xml->sheetData->row); $i++) { + $row = $xml->sheetData->row[$i]; + for ($j = 0; $j < count($row->c); $j ++) { + $output[$i][$j] = (string)$row->c[$j]->v; + } + } + + return $output; + } + + public static function array_diff_assoc_recursive($array1, $array2) { + $difference = []; + foreach($array1 as $key => $value) { + if(is_array($value)) { + if(!isset($array2[$key]) || !is_array($array2[$key])) { + $difference[$key] = $value; + } else { + $new_diff = self::array_diff_assoc_recursive($value, $array2[$key]); + if(!empty($new_diff)) { + $difference[$key] = $new_diff; + } + } + } else if(!isset($array2[$key]) || $array2[$key] != $value) { + $difference[$key] = $value; + } + } + + return empty($difference) ? [] : $difference; + } + + private function extractSheetXml($zip) { + for($z=0; $z < $zip->numFiles; $z++) { + $inside_zip_filename = $zip->getNameIndex($z); + $sheet_xml = $zip->getFromName($inside_zip_filename); + if (preg_match("/sheet(\d+).xml/", basename($inside_zip_filename))) { + return new SimpleXMLElement($sheet_xml); + } + } + + return null; + } + + private function extractStyleXml($zip) { + for($z=0; $z < $zip->numFiles; $z++) { + $inside_zip_filename = $zip->getNameIndex($z); + $xml = $zip->getFromName($inside_zip_filename); + if (preg_match("/styles.xml/", basename($inside_zip_filename))) { + return new SimpleXMLElement($xml); + } + } + + return null; + } + + private function assertRowProperties($expected_height, $expected_custom_height, $expected_hidden, $expected_collapsed, $row) { + $this->assertEquals($expected_height, (string)$row['ht']); + $this->assertEquals($expected_custom_height, filter_var($row['customHeight'], FILTER_VALIDATE_BOOLEAN)); + $this->assertEquals($expected_hidden, filter_var($row['hidden'], FILTER_VALIDATE_BOOLEAN)); + $this->assertEquals($expected_collapsed, filter_var($row['collapsed'], FILTER_VALIDATE_BOOLEAN)); + } + + private function assertCellStyle($expected_apply_border_string, $expected_border_id, $expected_fill_id, $expected_font_id, $cell_style) { + $this->assertEquals($expected_apply_border_string, $cell_style["applyBorder"]); + $this->assertEquals($expected_border_id, (int)$cell_style["borderId"]); + $this->assertEquals($expected_fill_id, (int)$cell_style["fillId"]); + $this->assertEquals($expected_font_id, (int)$cell_style["fontId"]); + } + + private function assertBorderStyle($expected_border_style, $expected_border_color, $border) { + $this->assertEquals($expected_border_style, $border->left["style"]); + $this->assertEquals($expected_border_style, $border->right["style"]); + $this->assertEquals($expected_border_style, $border->top["style"]); + $this->assertEquals($expected_border_style, $border->bottom["style"]); + + $this->assertEquals($expected_border_color, $border->left->color["rgb"]); + $this->assertEquals($expected_border_color, $border->right->color["rgb"]); + $this->assertEquals($expected_border_color, $border->top->color["rgb"]); + $this->assertEquals($expected_border_color, $border->bottom->color["rgb"]); + } + + private function assertFillStyle($expected_pattern, $expected_bg_color, $fill) { + $this->assertEquals($expected_pattern, $fill->patternFill["patternType"]); + if (!empty($expected_bg_color)) { + $this->assertEquals($expected_bg_color, $fill->patternFill->bgColor["rgb"]); + } + } + + private function assertFontStyle($expected_font_name, $expected_is_bold, $font) { + $this->assertEquals($expected_font_name, $font->name["val"]); + if (!empty($expected_is_bold)) { + $this->assertEquals($expected_is_bold, $font->b["val"]); + } + } + + private function assertRowHasStyleIndex($row, $expected_style) { + foreach ($row->c as $cell) { + $this->assertEquals($expected_style, (int)$cell["s"]); + } + } +} diff --git a/composer.json b/composer.json index c963afe2e217ffc9cfacd04401f7933b03f67319..b6a6b4512bca70bc59806ba89074f9b121b163a1 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,6 @@ "classmap": ["xlsxwriter.class.php"] }, "require-dev": { - "phpunit/phpunit": "4.3.*" + "phpunit/phpunit": "^7.5" } } diff --git a/composer.lock b/composer.lock index 532dd85d9aef7e984d75f0af8fa2c12512a593c3..87737532d49722e0501e386e3d12e9a5e78a265f 100644 --- a/composer.lock +++ b/composer.lock @@ -1,45 +1,47 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "hash": "89c1f54283c6b465c6b1a751a5270d78", + "content-hash": "9c5833be9ba3f93c4ddfb4ac7316b78a", "packages": [], "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.0.4", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119" + "reference": "a2c590166b2133a4633738648b6b064edae0814a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f976e5de371104877ebc89bd8fecb0019ed9c119", - "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", + "reference": "a2c590166b2133a4633738648b6b064edae0814a", "shasum": "" }, "require": { - "php": ">=5.3,<8.0-DEV" + "php": "^7.1" }, "require-dev": { - "athletic/athletic": "~0.1.8", + "doctrine/coding-standard": "^6.0", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "2.0.*@ALPHA" + "phpbench/phpbench": "^0.13", + "phpstan/phpstan-phpunit": "^0.11", + "phpstan/phpstan-shim": "^0.11", + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { - "psr-0": { - "Doctrine\\Instantiator\\": "src" + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" } }, "notification-url": "https://packagist.org/downloads/", @@ -54,48 +56,414 @@ } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", "keywords": [ "constructor", "instantiate" ], - "time": "2014-10-13 12:58:55" + "time": "2019-03-17T17:37:11+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.9.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", + "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "replace": { + "myclabs/deep-copy": "self.version" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "time": "2019-04-07T13:18:21+00:00" + }, + { + "name": "phar-io/manifest", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "phar-io/version": "^2.0", + "php": "^5.6 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "time": "2018-07-08T19:23:20+00:00" + }, + { + "name": "phar-io/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "time": "2018-07-08T19:19:57+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2017-09-11T18:02:19+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "4.3.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", + "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", + "shasum": "" + }, + "require": { + "php": "^7.0", + "phpdocumentor/reflection-common": "^1.0.0", + "phpdocumentor/type-resolver": "^0.4.0", + "webmozart/assert": "^1.0" + }, + "require-dev": { + "doctrine/instantiator": "~1.0.5", + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2019-04-30T17:48:53+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "phpdocumentor/reflection-common": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "time": "2017-07-14T14:27:02+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "1.8.1", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76", + "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", + "sebastian/comparator": "^1.1|^2.0|^3.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.5|^3.2", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2019-06-13T12:50:23+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "2.0.11", + "version": "6.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "53603b3c995f5aab6b59c8e08c3a663d2cc810b7" + "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/53603b3c995f5aab6b59c8e08c3a663d2cc810b7", - "reference": "53603b3c995f5aab6b59c8e08c3a663d2cc810b7", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", + "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", "shasum": "" }, "require": { - "php": ">=5.3.3", - "phpunit/php-file-iterator": "~1.3", - "phpunit/php-text-template": "~1.2", - "phpunit/php-token-stream": "~1.3", - "sebastian/environment": "~1.0", - "sebastian/version": "~1.0" + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^7.1", + "phpunit/php-file-iterator": "^2.0", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-token-stream": "^3.0", + "sebastian/code-unit-reverse-lookup": "^1.0.1", + "sebastian/environment": "^3.1 || ^4.0", + "sebastian/version": "^2.0.1", + "theseer/tokenizer": "^1.1" }, "require-dev": { - "ext-xdebug": ">=2.1.4", - "phpunit/phpunit": "~4.1" + "phpunit/phpunit": "^7.0" }, "suggest": { - "ext-dom": "*", - "ext-xdebug": ">=2.2.1", - "ext-xmlwriter": "*" + "ext-xdebug": "^2.6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "6.1-dev" } }, "autoload": { @@ -104,186 +472,610 @@ ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2018-10-31T16:06:48+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "050bedf145a257b1ff02746c31894800e5122946" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", + "reference": "050bedf145a257b1ff02746c31894800e5122946", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2018-09-13T20:33:42+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21T13:50:34+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "2.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", + "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2019-06-07T04:22:29+00:00" + }, + { + "name": "phpunit/php-token-stream", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c99e3be9d3e85f60646f152f9002d46ed7770d18", + "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2018-10-30T05:52:18+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "7.5.13", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "b9278591caa8630127f96c63b598712b699e671c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b9278591caa8630127f96c63b598712b699e671c", + "reference": "b9278591caa8630127f96c63b598712b699e671c", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "myclabs/deep-copy": "^1.7", + "phar-io/manifest": "^1.0.2", + "phar-io/version": "^2.0", + "php": "^7.1", + "phpspec/prophecy": "^1.7", + "phpunit/php-code-coverage": "^6.0.7", + "phpunit/php-file-iterator": "^2.0.1", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-timer": "^2.1", + "sebastian/comparator": "^3.0", + "sebastian/diff": "^3.0", + "sebastian/environment": "^4.0", + "sebastian/exporter": "^3.1", + "sebastian/global-state": "^2.0", + "sebastian/object-enumerator": "^3.0.3", + "sebastian/resource-operations": "^2.0", + "sebastian/version": "^2.0.1" + }, + "conflict": { + "phpunit/phpunit-mock-objects": "*" + }, + "require-dev": { + "ext-pdo": "*" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*", + "phpunit/php-invoker": "^2.0" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.5-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2019-06-19T12:01:51+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "time": "2017-03-04T06:30:41+00:00" + }, + { + "name": "sebastian/comparator", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "shasum": "" + }, + "require": { + "php": "^7.1", + "sebastian/diff": "^3.0", + "sebastian/exporter": "^3.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", "keywords": [ - "coverage", - "testing", - "xunit" + "comparator", + "compare", + "equality" ], - "time": "2014-08-31 06:33:04" + "time": "2018-07-12T15:12:46+00:00" }, { - "name": "phpunit/php-file-iterator", - "version": "1.3.4", + "name": "sebastian/diff", + "version": "3.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb" + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb", - "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8.0", + "symfony/process": "^2 || ^3.3 || ^4" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, "autoload": { "classmap": [ - "File/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], "license": [ "BSD-3-Clause" ], "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "filesystem", - "iterator" + "diff", + "udiff", + "unidiff", + "unified diff" ], - "time": "2013-10-10 15:34:57" + "time": "2019-02-04T06:01:07+00:00" }, { - "name": "phpunit/php-text-template", - "version": "1.2.0", + "name": "sebastian/environment", + "version": "4.2.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a" + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", - "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/f2a2c8e1c97c11ace607a7a667d73d47c19fe404", + "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.5" + }, + "suggest": { + "ext-posix": "*" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.2-dev" + } + }, "autoload": { "classmap": [ - "Text/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", "keywords": [ - "template" + "Xdebug", + "environment", + "hhvm" ], - "time": "2014-01-30 17:20:04" + "time": "2019-05-05T09:05:15+00:00" }, { - "name": "phpunit/php-timer", - "version": "1.0.5", + "name": "sebastian/exporter", + "version": "3.1.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", - "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.0", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^6.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.x-dev" + } + }, "autoload": { "classmap": [ - "PHP/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], "license": [ "BSD-3-Clause" ], "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", "keywords": [ - "timer" + "export", + "exporter" ], - "time": "2013-08-02 07:42:54" + "time": "2017-04-03T13:19:02+00:00" }, { - "name": "phpunit/php-token-stream", - "version": "1.3.0", + "name": "sebastian/global-state", + "version": "2.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "f8d5d08c56de5cfd592b3340424a81733259a876" + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/f8d5d08c56de5cfd592b3340424a81733259a876", - "reference": "f8d5d08c56de5cfd592b3340424a81733259a876", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": ">=5.3.3" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.2" + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-uopz": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -301,56 +1093,39 @@ "email": "sebastian@phpunit.de" } ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", "keywords": [ - "tokenizer" + "global state" ], - "time": "2014-08-31 06:12:13" + "time": "2017-04-27T15:39:26+00:00" }, { - "name": "phpunit/phpunit", - "version": "4.3.5", + "name": "sebastian/object-enumerator", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "2dab9d593997db4abcf58d0daf798eb4e9cecfe1" + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2dab9d593997db4abcf58d0daf798eb4e9cecfe1", - "reference": "2dab9d593997db4abcf58d0daf798eb4e9cecfe1", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=5.3.3", - "phpunit/php-code-coverage": "~2.0", - "phpunit/php-file-iterator": "~1.3.2", - "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": "~1.0.2", - "phpunit/phpunit-mock-objects": "~2.3", - "sebastian/comparator": "~1.0", - "sebastian/diff": "~1.1", - "sebastian/environment": "~1.0", - "sebastian/exporter": "~1.0", - "sebastian/version": "~1.0", - "symfony/yaml": "~2.0" + "php": "^7.0", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" }, - "suggest": { - "phpunit/php-invoker": "~1.1" + "require-dev": { + "phpunit/phpunit": "^6.0" }, - "bin": [ - "phpunit" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3.x-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -359,58 +1134,43 @@ ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "", - "../../symfony/yaml/" - ], "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "The PHP Unit Testing framework.", - "homepage": "http://www.phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "time": "2014-11-11 10:11:09" + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "time": "2017-08-03T12:35:26+00:00" }, { - "name": "phpunit/phpunit-mock-objects", - "version": "2.3.0", + "name": "sebastian/object-reflector", + "version": "1.1.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "c63d2367247365f688544f0d500af90a11a44c65" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "773f97c67f28de00d397be301821b06708fca0be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/c63d2367247365f688544f0d500af90a11a44c65", - "reference": "c63d2367247365f688544f0d500af90a11a44c65", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", + "reference": "773f97c67f28de00d397be301821b06708fca0be", "shasum": "" }, "require": { - "doctrine/instantiator": "~1.0,>=1.0.1", - "php": ">=5.3.3", - "phpunit/php-text-template": "~1.2" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.3" - }, - "suggest": { - "ext-soap": "*" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3.x-dev" + "dev-master": "1.1-dev" } }, "autoload": { @@ -425,44 +1185,37 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "time": "2014-10-03 05:12:11" + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "time": "2017-03-29T09:07:27+00:00" }, { - "name": "sebastian/comparator", - "version": "1.0.1", + "name": "sebastian/recursion-context", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "e54a01c0da1b87db3c5a3c4c5277ddf331da4aef" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e54a01c0da1b87db3c5a3c4c5277ddf331da4aef", - "reference": "e54a01c0da1b87db3c5a3c4c5277ddf331da4aef", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", "shasum": "" }, "require": { - "php": ">=5.3.3", - "sebastian/diff": "~1.1", - "sebastian/exporter": "~1.0" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.1" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -479,52 +1232,40 @@ "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "http://www.github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "time": "2014-05-11 23:00:21" + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2017-03-03T06:23:57+00:00" }, { - "name": "sebastian/diff", - "version": "1.2.0", + "name": "sebastian/resource-operations", + "version": "2.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "5843509fed39dee4b356a306401e9dd1a931fec7" + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/5843509fed39dee4b356a306401e9dd1a931fec7", - "reference": "5843509fed39dee4b356a306401e9dd1a931fec7", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", "shasum": "" }, "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" + "php": "^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -537,46 +1278,36 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], - "description": "Diff implementation", - "homepage": "http://www.github.com/sebastianbergmann/diff", - "keywords": [ - "diff" - ], - "time": "2014-08-15 10:29:00" + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "time": "2018-10-04T04:07:39+00:00" }, { - "name": "sebastian/environment", - "version": "1.2.0", + "name": "sebastian/version", + "version": "2.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "0d9bf79554d2a999da194a60416c15cf461eb67d" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/0d9bf79554d2a999da194a60416c15cf461eb67d", - "reference": "0d9bf79554d2a999da194a60416c15cf461eb67d", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", "shasum": "" }, "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.3" + "php": ">=5.6" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -591,97 +1322,92 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "time": "2014-10-22 06:38:05" + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2016-10-03T07:35:21+00:00" }, { - "name": "sebastian/exporter", - "version": "1.0.2", + "name": "symfony/polyfill-ctype", + "version": "v1.11.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "c7d59948d6e82818e1bdff7cadb6c34710eb7dc0" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "82ebae02209c21113908c229e9883c419720738a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c7d59948d6e82818e1bdff7cadb6c34710eb7dc0", - "reference": "c7d59948d6e82818e1bdff7cadb6c34710eb7dc0", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", + "reference": "82ebae02209c21113908c229e9883c419720738a", "shasum": "" }, "require": { "php": ">=5.3.3" }, - "require-dev": { - "phpunit/phpunit": "~4.0" + "suggest": { + "ext-ctype": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.11-dev" } }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" }, { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" } ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", "keywords": [ - "export", - "exporter" + "compatibility", + "ctype", + "polyfill", + "portable" ], - "time": "2014-09-10 00:51:36" + "time": "2019-02-06T07:57:58+00:00" }, { - "name": "sebastian/version", - "version": "1.0.3", + "name": "theseer/tokenizer", + "version": "1.1.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43" + "url": "https://github.com/theseer/tokenizer.git", + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43", - "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", "shasum": "" }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.0" + }, "type": "library", "autoload": { "classmap": [ @@ -694,42 +1420,45 @@ ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2014-03-07 15:35:33" + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "time": "2019-06-13T22:48:21+00:00" }, { - "name": "symfony/yaml", - "version": "v2.5.6", - "target-dir": "Symfony/Component/Yaml", + "name": "webmozart/assert", + "version": "1.4.0", "source": { "type": "git", - "url": "https://github.com/symfony/Yaml.git", - "reference": "2d9f527449cabfa8543dd7fa3a466d6ae83d6726" + "url": "https://github.com/webmozart/assert.git", + "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/2d9f527449cabfa8543dd7fa3a466d6ae83d6726", - "reference": "2d9f527449cabfa8543dd7fa3a466d6ae83d6726", + "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9", + "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.3.3 || ^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "1.3-dev" } }, "autoload": { - "psr-0": { - "Symfony\\Component\\Yaml\\": "" + "psr-4": { + "Webmozart\\Assert\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -738,23 +1467,24 @@ ], "authors": [ { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "Symfony Yaml Component", - "homepage": "http://symfony.com", - "time": "2014-10-01 05:50:18" + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2018-12-25T11:19:39+00:00" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, + "prefer-lowest": false, "platform": [], "platform-dev": [] } diff --git a/testbench/xlsxwriter.class.Test.php b/testbench/xlsxwriter.class.Test.php index 6660e6b87c6fcea411d6ea3fd4c09a08ef23c456..c6ea5efc46bb9d32f8421522ef71175b220621ea 100644 --- a/testbench/xlsxwriter.class.Test.php +++ b/testbench/xlsxwriter.class.Test.php @@ -2,6 +2,8 @@ include_once __DIR__.'/../vendor/autoload.php'; +use PHPUnit\Framework\TestCase; + //TODO test double:writeSheetHeader //TODO test invalid UTF8 //TODO test outoforder writeSheetRow('Sheet1',()); @@ -9,12 +11,12 @@ include_once __DIR__.'/../vendor/autoload.php'; class _XLSXWriter_ extends XLSXWriter { public function writeCell(XLSXWriter_BuffererWriter &$file, $row_number, $column_number, $value, $cell_format) { - return call_user_func_array('parent::writeCell', func_get_args()); + return call_user_func_array('parent::writeCell', [&$file, $row_number, $column_number, $value, $cell_format]); } } //Just a simple test, by no means comprehensive -class XLSXWriterTest extends PHPUnit_Framework_TestCase +class XLSXWriterTest extends TestCase { /** * @covers XLSXWriter::writeCell diff --git a/xlsxwriter.class.php b/xlsxwriter.class.php index 6cdc077cfca4664b3d1a00137c2c65c05111ee42..ee1ae299879f76e470bcf9dbb514566468fe70dc 100644 --- a/xlsxwriter.class.php +++ b/xlsxwriter.class.php @@ -9,7 +9,7 @@ class XLSXWriter { //------------------------------------------------------------------ //http://office.microsoft.com/en-us/excel-help/excel-specifications-and-limits-HP010073849.aspx - const EXCEL_2007_MAX_ROW=1048576; + const EXCEL_2007_MAX_ROW=1048576; const EXCEL_2007_MAX_COL=16384; //------------------------------------------------------------------ protected $author ='Doc Author'; @@ -20,6 +20,46 @@ class XLSXWriter protected $current_sheet = ''; + protected $title; + protected $subject; + protected $company; + protected $description; + protected $keywords = []; + + protected $cell_styles = []; + + private const CELL_STYLES = [ + 'money' => 1, + 'dollar' => 1, + 'datetime' => 2, + 'date' => 3, + 'string' => 0, + 'number.2' => 4, + 'number.4' => 5, + 'blackheader' => 6 + ]; + private const ALLOWED_BORDERS = [ + 'left', + 'right', + 'top', + 'bottom', + ]; + private const ALLOWED_BORDER_STYLES = [ + 'thin', + 'medium', + 'thick', + 'dashDot', + 'dashDotDot', + 'dashed', + 'dotted', + 'double', + 'hair', + 'mediumDashDot', + 'mediumDashDotDot', + 'mediumDashed', + 'slantDashDot', + ]; + public function __construct() { if(!ini_get('date.timezone')) @@ -27,9 +67,38 @@ class XLSXWriter //using date functions can kick out warning if this isn't set date_default_timezone_set('UTC'); } + + + $this->addCellStyle($number_format='GENERAL', $style_string=null); + $this->addCellStyle($number_format='GENERAL', $style_string=null); + $this->addCellStyle($number_format='GENERAL', $style_string=null); + $this->addCellStyle($number_format='GENERAL', $style_string=null); + $this->addCellStyle($number_format='string', $style_string=null); + $this->addCellStyle($number_format='money', $style_string=null); + $this->addCellStyle($number_format='dollar', $style_string=null); + $this->addCellStyle($number_format='datetime', $style_string=null); + $this->addCellStyle($number_format='date', $style_string=null); + $this->addCellStyle($number_format='number.2', $style_string=null); + $this->addCellStyle($number_format='number.4', $style_string=null); } public function setAuthor($author='') { $this->author=$author; } + public function setTitle($title='') { $this->title=$title; } + public function setSubject($subject='') { $this->subject=$subject; } + public function setCompany($company='') { $this->company=$company; } + public function setKeywords($keywords=[]) { $this->keywords=$keywords; } + public function setDescription($description='') { $this->description=$description; } + + public function getFileProperties(): array { + return [ + "author" => $this->author, + "title" => $this->title, + "subject" => $this->subject, + "company" => $this->company, + "keywords" => $this->keywords, + "description" => $this->description, + ]; + } public function __destruct() { @@ -72,7 +141,7 @@ class XLSXWriter $zip = new ZipArchive(); if (empty($this->sheets)) { self::log("Error in ".__CLASS__."::".__FUNCTION__.", no worksheets defined."); return; } if (!$zip->open($filename, ZipArchive::CREATE)) { self::log("Error in ".__CLASS__."::".__FUNCTION__.", unable to create zip."); return; } - + $zip->addEmptyDir("docProps/"); $zip->addFromString("docProps/app.xml" , self::buildAppXML() ); $zip->addFromString("docProps/core.xml", self::buildCoreXML()); @@ -96,7 +165,7 @@ class XLSXWriter $zip->close(); } - protected function initializeSheet($sheet_name) + protected function initializeSheet($sheet_name, $col_widths = [], $freeze_rows=false, $freeze_columns=false) { //if already initialized if ($this->current_sheet==$sheet_name || isset($this->sheets[$sheet_name])) @@ -104,17 +173,20 @@ class XLSXWriter $sheet_filename = $this->tempFilename(); $sheet_xmlname = 'sheet' . (count($this->sheets) + 1).".xml"; - $this->sheets[$sheet_name] = (object)array( - 'filename' => $sheet_filename, - 'sheetname' => $sheet_name, + $this->sheets[$sheet_name] = (object)[ + 'filename' => $sheet_filename, + 'sheetname' => $sheet_name, 'xmlname' => $sheet_xmlname, 'row_count' => 0, 'file_writer' => new XLSXWriter_BuffererWriter($sheet_filename), - 'cell_formats' => array(), + 'cell_formats' => [], + 'merge_cells' => [], 'max_cell_tag_start' => 0, 'max_cell_tag_end' => 0, + 'freeze_rows' => $freeze_rows, + 'freeze_columns' => $freeze_columns, 'finalized' => false, - ); + ]; $sheet = &$this->sheets[$sheet_name]; $tabselected = count($this->sheets) == 1 ? 'true' : 'false';//only first sheet is selected $max_cell=XLSXWriter::xlsCell(self::EXCEL_2007_MAX_ROW, self::EXCEL_2007_MAX_COL);//XFE1048577 @@ -128,55 +200,118 @@ class XLSXWriter $sheet->max_cell_tag_end = $sheet->file_writer->ftell(); $sheet->file_writer->write( '<sheetViews>'); $sheet->file_writer->write( '<sheetView colorId="64" defaultGridColor="true" rightToLeft="false" showFormulas="false" showGridLines="true" showOutlineSymbols="true" showRowColHeaders="true" showZeros="true" tabSelected="' . $tabselected . '" topLeftCell="A1" view="normal" windowProtection="false" workbookViewId="0" zoomScale="100" zoomScaleNormal="100" zoomScalePageLayoutView="100">'); - $sheet->file_writer->write( '<selection activeCell="A1" activeCellId="0" pane="topLeft" sqref="A1"/>'); + + if ($sheet->freeze_rows !== false && $sheet->freeze_columns !== false) { + $sheet->file_writer->write( '<pane ySplit="'.$sheet->freeze_rows.'" xSplit="'.$sheet->freeze_columns.'" topLeftCell="'.self::xlsCell($sheet->freeze_rows, $sheet->freeze_columns).'" activePane="bottomRight" state="frozen"/>'); + $sheet->file_writer->write( '<selection activeCell="'.self::xlsCell($sheet->freeze_rows, 0).'" activeCellId="0" pane="topRight" sqref="'.self::xlsCell($sheet->freeze_rows, 0).'"/>'); + $sheet->file_writer->write( '<selection activeCell="'.self::xlsCell(0, $sheet->freeze_columns).'" activeCellId="0" pane="bottomLeft" sqref="'.self::xlsCell(0, $sheet->freeze_columns).'"/>'); + $sheet->file_writer->write( '<selection activeCell="'.self::xlsCell($sheet->freeze_rows, $sheet->freeze_columns).'" activeCellId="0" pane="bottomRight" sqref="'.self::xlsCell($sheet->freeze_rows, $sheet->freeze_columns).'"/>'); + } elseif ($sheet->freeze_rows !== false) { + $sheet->file_writer->write( '<pane ySplit="'.$sheet->freeze_rows.'" topLeftCell="'.self::xlsCell($sheet->freeze_rows, 0).'" activePane="bottomLeft" state="frozen"/>'); + $sheet->file_writer->write( '<selection activeCell="'.self::xlsCell($sheet->freeze_rows, 0).'" activeCellId="0" pane="bottomLeft" sqref="'.self::xlsCell($sheet->freeze_rows, 0).'"/>'); + } elseif ($sheet->freeze_columns !== false) { + $sheet->file_writer->write( '<pane xSplit="'.$sheet->freeze_columns.'" topLeftCell="'.self::xlsCell(0, $sheet->freeze_columns).'" activePane="topRight" state="frozen"/>'); + $sheet->file_writer->write( '<selection activeCell="'.self::xlsCell(0, $sheet->freeze_columns).'" activeCellId="0" pane="topRight" sqref="'.self::xlsCell(0, $sheet->freeze_columns).'"/>'); + } else { // not frozen + $sheet->file_writer->write( '<selection activeCell="A1" activeCellId="0" pane="topLeft" sqref="A1"/>'); + } + $sheet->file_writer->write( '</sheetView>'); $sheet->file_writer->write( '</sheetViews>'); $sheet->file_writer->write( '<cols>'); - $sheet->file_writer->write( '<col collapsed="false" hidden="false" max="1025" min="1" style="0" width="11.5"/>'); + + $cols_count = count($col_widths); + + if (!empty($col_widths)) { + foreach($col_widths as $i => $column_width) { + $sheet->file_writer->write( '<col collapsed="false" hidden="false" max="'. ($i + 1) .'" min="'. ($i + 1) .'" style="0" customWidth="true" width="'.floatval($column_width).'"/>'); + } + } + $sheet->file_writer->write( '<col collapsed="false" hidden="false" max="1024" min="'. ($cols_count + 1) .'" style="0" customWidth="false" width="11.5"/>'); + $sheet->file_writer->write( '</cols>'); $sheet->file_writer->write( '<sheetData>'); } - public function writeSheetHeader($sheet_name, array $header_types) - { - if (empty($sheet_name) || empty($header_types) || !empty($this->sheets[$sheet_name])) + public function writeSheetHeader($sheet_name, array $header_types, $format = 'xlsx', $delimiter = ';', $subheader = NULL, $col_options = []) { + if (empty($sheet_name) || empty($header_types) || !empty($this->sheets[$sheet_name])) { return; + } + if ($format == 'csv') { + if (!empty($subheader)) { + $return = ''; + $return .= $this->writeCSVLine([' '], NULL, $delimiter); + $return .= $this->writeCSVLine([$subheader], NULL, $delimiter); + $return .= $this->writeCSVLine([' '], NULL, $delimiter); + $return .= $this->writeCSVLine($header_types, true, $delimiter); + return $return; + } + return $this->writeCSVLine($header_types, true, $delimiter); + } + if (!empty($subheader)) { + $this->writeSheetRow($sheet_name, [' '], $format, $delimiter); + $this->writeSheetRow($sheet_name, [$subheader], $format, $delimiter); + $this->writeSheetRow($sheet_name, [' '], $format, $delimiter); + $start = 3; + } else { + $start = 0; + } - self::initializeSheet($sheet_name); + $col_widths = (!empty($col_options['widths'])) ? (array)$col_options['widths'] : []; + $freeze_rows = (array_key_exists('freeze_rows', $col_options) && $col_options['freeze_rows'] !== false) ? intval($col_options['freeze_rows']) : false; + $freeze_columns = (array_key_exists('freeze_columns', $col_options) && $col_options['freeze_columns'] !== false) ? intval($col_options['freeze_columns']) : false; + + self::initializeSheet($sheet_name, $col_widths, $freeze_rows, $freeze_columns); $sheet = &$this->sheets[$sheet_name]; $sheet->cell_formats = array_values($header_types); $header_row = array_keys($header_types); - $sheet->file_writer->write('<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="' . (1) . '">'); + $sheet->file_writer->write('<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="' . ($start + 1) . '">'); + + $header_style = ['fill_idx' => '2', 'font_idx' => '4']; + $cell_style_index = $this->addCellStyle($number_format='blackheader', json_encode($header_style)); foreach ($header_row as $k => $v) { - $this->writeCell($sheet->file_writer, 0, $k, $v, $cell_format = 'string'); + $this->writeCell($sheet->file_writer, $start, $k, $v, 'blackheader', $cell_style_index); } $sheet->file_writer->write('</row>'); $sheet->row_count++; $this->current_sheet = $sheet_name; } - public function writeSheetRow($sheet_name, array $row) - { - if (empty($sheet_name) || empty($row)) + public function writeSheetRow($sheet_name, array $row, $format = 'xlsx', $delimiter = ';', array $row_options = []) { + if (empty($sheet_name) || empty($row)) { return; + } + if ($format == 'csv') { + return $this->writeCSVLine($row, NULL, $delimiter); + } self::initializeSheet($sheet_name); $sheet = &$this->sheets[$sheet_name]; - if (empty($sheet->cell_formats)) - { + if (empty($sheet->cell_formats)) { $sheet->cell_formats = array_fill(0, count($row), 'string'); } - $sheet->file_writer->write('<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="' . ($sheet->row_count + 1) . '">'); - foreach ($row as $k => $v) { - $this->writeCell($sheet->file_writer, $sheet->row_count, $k, $v, $sheet->cell_formats[$k]); + $ht = array_key_exists('height', $row_options) ? floatval($row_options['height']) : 12.1; + $customHt = array_key_exists('height', $row_options) ? 'true' : 'false'; + $hidden = (array_key_exists('hidden', $row_options) && $row_options['hidden']) ? 'true' : 'false'; + $collapsed = (array_key_exists('collapsed', $row_options) && $row_options['collapsed']) ? 'true' : 'false'; + $sheet->file_writer->write('<row collapsed="'.($collapsed).'" customFormat="false" customHeight="'.($customHt).'" hidden="'.($hidden).'" ht="'.($ht).'" outlineLevel="0" r="' . ($sheet->row_count + 1) . '">'); + + $style = &$row_options; + foreach ($row as $column => $content) { + $cell_value = (is_array($content)) ? $content[0] : $content; + $number_format_type = (is_array($content)) ? $content[1] : $sheet->cell_formats[$column]; + $style_string = (!empty($style)) ? json_encode((isset($style[0])) ? $style[$column] : $style) : null; + $cell_style_idx = $this->addCellStyle($number_format_type, $style_string); + $this->writeCell($sheet->file_writer, $sheet->row_count, $column, $cell_value, $number_format_type, $cell_style_idx); } + $sheet->file_writer->write('</row>'); $sheet->row_count++; $this->current_sheet = $sheet_name; } - + protected function finalizeSheet($sheet_name) { if (empty($sheet_name) || $this->sheets[$sheet_name]->finalized) @@ -185,6 +320,15 @@ class XLSXWriter $sheet = &$this->sheets[$sheet_name]; $sheet->file_writer->write( '</sheetData>'); + + if (!empty($sheet->merge_cells)) { + $sheet->file_writer->write('<mergeCells>'); + foreach ($sheet->merge_cells as $range) { + $sheet->file_writer->write('<mergeCell ref="' . $range . '"/>'); + } + $sheet->file_writer->write('</mergeCells>'); + } + $sheet->file_writer->write( '<printOptions headings="false" gridLines="false" gridLinesSet="true" horizontalCentered="false" verticalCentered="false"/>'); $sheet->file_writer->write( '<pageMargins left="0.5" right="0.5" top="1.0" bottom="1.0" header="0.5" footer="0.5"/>'); $sheet->file_writer->write( '<pageSetup blackAndWhite="false" cellComments="none" copies="1" draft="false" firstPageNumber="1" fitToHeight="1" fitToWidth="1" horizontalDpi="300" orientation="portrait" pageOrder="downThenOver" paperSize="1" scale="100" useFirstPageNumber="true" usePrinterDefaults="false" verticalDpi="300"/>'); @@ -203,6 +347,43 @@ class XLSXWriter $sheet->finalized=true; } + public function markMergedCell($sheet_name, $start_cell_row, $start_cell_column, $end_cell_row, $end_cell_column, $format = 'xlsx') { + if (empty($sheet_name) || $this->sheets[$sheet_name]->finalized || $format == 'csv'){ + return; + } + + self::initializeSheet($sheet_name); + + $sheet = &$this->sheets[$sheet_name]; + $startCell = self::xlsCell($start_cell_row, $start_cell_column); + $endCell = self::xlsCell($end_cell_row, $end_cell_column); + + $sheet->merge_cells[] = $startCell . ":" . $endCell; + } + + public function writeCSV(array $data, array $header_types=array(), $delimiter = ';') { + $header_text = array_keys($header_types); + + $output = ''; + $output .= implode($delimiter, $header_text) . "\n"; + $output .= implode("\n", array_map(function($array) use (&$delimiter) { + return implode($delimiter, $array); + }, $data)); + return $output; + } + + public function writeCSVLine(array $data, $type = null, $delimiter = ';') { + if (!empty($type)) { + $text = array_keys($data); + } else { + $text = $data; + } + + $output = ''; + $output .= implode($delimiter, $text) . "\n"; + echo $output; + } + public function writeSheet(array $data, $sheet_name='', array $header_types=array() ) { $sheet_name = empty($sheet_name) ? 'Sheet1' : $sheet_name; @@ -218,50 +399,100 @@ class XLSXWriter $this->finalizeSheet($sheet_name); } - protected function writeCell(XLSXWriter_BuffererWriter &$file, $row_number, $column_number, $value, $cell_format) + protected function getCellFormat($cell_format) { + return isset(self::CELL_STYLES[$cell_format]) ? self::CELL_STYLES[$cell_format] : '0'; + } + + private function addCellStyle($cell_format, $cell_style_string) { + $cell_format_idx = $this->getCellFormat($cell_format); + $lookup_string = $cell_format_idx.";".$cell_style_string; + $cell_style_idx = self::add_to_list_get_index($this->cell_styles, $lookup_string); + + return $cell_style_idx; + } + + protected function writeCell(XLSXWriter_BuffererWriter &$file, $row_number, $column_number, $value, $cell_format, $cell_style_idx = null) { - static $styles = array('money'=>1,'dollar'=>1,'datetime'=>2,'date'=>3,'string'=>0); - $cell = self::xlsCell($row_number, $column_number); - $s = isset($styles[$cell_format]) ? $styles[$cell_format] : '0'; + $cell_name = self::xlsCell($row_number, $column_number); + if (is_null($cell_style_idx)) { + $cell_style_idx = $this->addCellStyle($cell_format, null); + } if (!is_scalar($value) || $value==='') { //objects, array, empty - $file->write('<c r="'.$cell.'" s="'.$s.'"/>'); + $file->write('<c r="'.$cell_name.'" s="'.$cell_style_idx.'"/>'); + } elseif (preg_match('#^number\.[0-9]$#', $cell_format)) { + $file->write('<c r="'.$cell_name.'" s="'.$cell_style_idx.'" t="n"><v>'.$value.'</v></c>'); } elseif ($cell_format=='date') { - $file->write('<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.intval(self::convert_date_time($value)).'</v></c>'); + $file->write('<c r="'.$cell_name.'" s="'.$cell_style_idx.'" t="n"><v>'.intval(self::convert_date_time($value)).'</v></c>'); } elseif ($cell_format=='datetime') { - $file->write('<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.self::convert_date_time($value).'</v></c>'); + $file->write('<c r="'.$cell_name.'" s="'.$cell_style_idx.'" t="n"><v>'.self::convert_date_time($value).'</v></c>'); } elseif (!is_string($value)) { - $file->write('<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.($value*1).'</v></c>');//int,float, etc - } elseif ($value{0}!='0' && filter_var($value, FILTER_VALIDATE_INT)){ //excel wants to trim leading zeros - $file->write('<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.($value).'</v></c>');//numeric string - } elseif ($value{0}=='='){ - $file->write('<c r="'.$cell.'" s="'.$s.'" t="s"><f>'.self::xmlspecialchars($value).'</f></c>'); + $file->write('<c r="'.$cell_name.'" s="'.$cell_style_idx.'" t="n"><v>'.($value*1).'</v></c>');//int,float, etc + } elseif ($value[0]!='0' && filter_var($value, FILTER_VALIDATE_INT)){ //excel wants to trim leading zeros + $file->write('<c r="'.$cell_name.'" s="'.$cell_style_idx.'" t="n"><v>'.($value).'</v></c>');//numeric string + } elseif ($value[0]=='='){ + $file->write('<c r="'.$cell_name.'" s="'.$cell_style_idx.'" t="s"><f>'.self::xmlspecialchars($value).'</f></c>'); } elseif ($value!==''){ - $file->write('<c r="'.$cell.'" s="'.$s.'" t="s"><v>'.self::xmlspecialchars($this->setSharedString($value)).'</v></c>'); + $file->write('<c r="'.$cell_name.'" s="'.$cell_style_idx.'" t="s"><v>'.self::xmlspecialchars($this->setSharedString($value)).'</v></c>'); } } protected function writeStylesXML() { + $styles = $this->spreadStyles(); + $borders = $styles['borders']; + $cell_styles = $styles['styles']; + $temporary_filename = $this->tempFilename(); $file = new XLSXWriter_BuffererWriter($temporary_filename); $file->write('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n"); $file->write('<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">'); - $file->write('<numFmts count="4">'); + $file->write('<numFmts count="6">'); $file->write( '<numFmt formatCode="GENERAL" numFmtId="164"/>'); $file->write( '<numFmt formatCode="[$$-1009]#,##0.00;[RED]\-[$$-1009]#,##0.00" numFmtId="165"/>'); $file->write( '<numFmt formatCode="YYYY-MM-DD\ HH:MM:SS" numFmtId="166"/>'); $file->write( '<numFmt formatCode="YYYY-MM-DD" numFmtId="167"/>'); + $file->write( '<numFmt formatCode="#,##0.00" numFmtId="168"/>'); + $file->write( '<numFmt formatCode="0.0000" numFmtId="169"/>'); $file->write('</numFmts>'); - $file->write('<fonts count="4">'); + $file->write('<fonts count="5">'); $file->write( '<font><name val="Arial"/><charset val="1"/><family val="2"/><sz val="10"/></font>'); $file->write( '<font><name val="Arial"/><family val="0"/><sz val="10"/></font>'); $file->write( '<font><name val="Arial"/><family val="0"/><sz val="10"/></font>'); $file->write( '<font><name val="Arial"/><family val="0"/><sz val="10"/></font>'); + $file->write( '<font><name val="Arial"/><b val="true"/><charset val="1"/><family val="2"/><sz val="10"/></font>'); $file->write('</fonts>'); - $file->write('<fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills>'); - $file->write('<borders count="1"><border diagonalDown="false" diagonalUp="false"><left/><right/><top/><bottom/><diagonal/></border></borders>'); - $file->write( '<cellStyleXfs count="20">'); + $file->write('<fills count="3">'); + $file->write(' <fill><patternFill patternType="none"/></fill>'); + $file->write(' <fill><patternFill patternType="gray125"/></fill>'); + $file->write(' <fill><patternFill patternType="solid"><fgColor theme="0" tint="-0.249977111117893"/><bgColor rgb="FF003300"/></patternFill></fill>'); + $file->write('</fills>'); + + $file->write('<borders count="'.(count($borders)).'">'); + $file->write( '<border diagonalDown="false" diagonalUp="false"><left/><right/><top/><bottom/><diagonal/></border>'); + foreach($borders as $border) { + if (empty($border)) { + continue; + } + + $pieces = json_decode($border,true); + + $border_style = !empty($pieces['style']) ? $pieces['style'] : 'hair'; + $border_color = !empty($pieces['color']) ? '<color rgb="'.strval($pieces['color']).'"/>' : ''; + + $file->write('<border diagonalDown="false" diagonalUp="false">'); + + foreach (self::ALLOWED_BORDERS as $side) { + $show_side = in_array($side,$pieces['side']) ? true : false; + $file->write($show_side ? "<$side style=\"$border_style\">$border_color</$side>" : "<$side/>"); + } + + $file->write( '<diagonal/>'); + $file->write('</border>'); + } + $file->write('</borders>'); + + $file->write('<cellStyleXfs count="20">'); $file->write( '<xf applyAlignment="true" applyBorder="true" applyFont="true" applyProtection="true" borderId="0" fillId="0" fontId="0" numFmtId="164">'); $file->write( '<alignment horizontal="general" indent="0" shrinkToFit="false" textRotation="0" vertical="bottom" wrapText="false"/>'); $file->write( '<protection hidden="false" locked="true"/>'); @@ -285,13 +516,26 @@ class XLSXWriter $file->write( '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="44"/>'); $file->write( '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="42"/>'); $file->write( '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="9"/>'); - $file->write( '</cellStyleXfs>'); - $file->write( '<cellXfs count="4">'); - $file->write( '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="164" xfId="0"/>'); - $file->write( '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="165" xfId="0"/>'); - $file->write( '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="166" xfId="0"/>'); - $file->write( '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="167" xfId="0"/>'); - $file->write( '</cellXfs>'); + $file->write('</cellStyleXfs>'); + + $file->write('<cellXfs count="'.(count($cell_styles)).'">'); + foreach($cell_styles as $cell_style) { + $applyAlignment = isset($cell_style['alignment']) ? 'true' : 'false'; + $wrapText = !empty($cell_style['wrap_text']) ? 'true' : 'false'; + $horizAlignment = isset($cell_style['halign']) ? $cell_style['halign'] : 'general'; + $vertAlignment = isset($cell_style['valign']) ? $cell_style['valign'] : 'bottom'; + $applyBorder = isset($cell_style['border_idx']) ? 'true' : 'false'; + $applyFont = 'true'; + $borderIdx = isset($cell_style['border_idx']) ? intval($cell_style['border_idx']) : 0; + $fillIdx = isset($cell_style['fill_idx']) ? intval($cell_style['fill_idx']) : 0; + $fontIdx = isset($cell_style['font_idx']) ? intval($cell_style['font_idx']) : 0; + $file->write('<xf applyAlignment="'.$applyAlignment.'" applyBorder="'.$applyBorder.'" applyFont="'.$applyFont.'" applyProtection="false" borderId="'.($borderIdx).'" fillId="'.($fillIdx).'" fontId="'.($fontIdx).'" numFmtId="'.(164+$cell_style['num_fmt_idx']).'" xfId="0">'); + $file->write(' <alignment horizontal="'.$horizAlignment.'" vertical="'.$vertAlignment.'" textRotation="0" wrapText="'.$wrapText.'" indent="0" shrinkToFit="false"/>'); + $file->write(' <protection locked="true" hidden="false"/>'); + $file->write('</xf>'); + } + $file->write('</cellXfs>'); + $file->write( '<cellStyles count="6">'); $file->write( '<cellStyle builtinId="0" customBuiltin="false" name="Normal" xfId="0"/>'); $file->write( '<cellStyle builtinId="3" customBuiltin="false" name="Comma" xfId="15"/>'); @@ -301,10 +545,46 @@ class XLSXWriter $file->write( '<cellStyle builtinId="5" customBuiltin="false" name="Percent" xfId="19"/>'); $file->write( '</cellStyles>'); $file->write('</styleSheet>'); + $file->close(); return $temporary_filename; } + protected function spreadStyles() { + $borders = [''];//1 placeholder for static xml later + $style_indexes = []; + + foreach ($this->cell_styles as $i => $cell_style_string) { + [$number_format_idx, $style_json_string] = explode(";", $cell_style_string, 2); + $style = json_decode($style_json_string, $as_assoc = true); + + $style_indexes[$i] = $style ?? []; + $style_indexes[$i]['num_fmt_idx'] = $number_format_idx; + if (isset($style['border']) && is_string($style['border'])) { //border is a comma delimited str + $border_value['side'] = array_intersect(explode(",", $style['border']), self::ALLOWED_BORDERS); + + if (isset($style['border-style']) && in_array($style['border-style'], self::ALLOWED_BORDER_STYLES)) { + $border_value['style'] = $style['border-style']; + } + + if (isset($style['border-color']) && is_string($style['border-color']) && $style['border-color'][0] == '#') { + $hexa_border_color = substr($style['border-color'], 1, 6); + $hexa_border_color = strlen($hexa_border_color) == 3 ? $hexa_border_color[0] . $hexa_border_color[0] . + $hexa_border_color[1] . $hexa_border_color[1] . + $hexa_border_color[2] . $hexa_border_color[2] : $hexa_border_color;// expand cf0 => ccff00 + $border_value['color'] = "FF" . strtoupper($hexa_border_color); + } + + $style_indexes[$i]['border_idx'] = self::add_to_list_get_index($borders, json_encode($border_value)); + } + } + + return [ + 'borders' => $borders, + 'styles' => $style_indexes, + ]; + } + protected function setSharedString($v) { if (isset($this->shared_strings[$v])) @@ -332,7 +612,7 @@ class XLSXWriter } $file->write('</sst>'); $file->close(); - + return $temporary_filename; } @@ -340,7 +620,10 @@ class XLSXWriter { $app_xml=""; $app_xml.='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n"; - $app_xml.='<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><TotalTime>0</TotalTime></Properties>'; + $app_xml.='<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">'; + $app_xml.='<TotalTime>0</TotalTime>'; + $app_xml.='<Company>'.self::xmlspecialchars($this->company).'</Company>'; + $app_xml.='</Properties>'; return $app_xml; } @@ -350,7 +633,13 @@ class XLSXWriter $core_xml.='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n"; $core_xml.='<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'; $core_xml.='<dcterms:created xsi:type="dcterms:W3CDTF">'.date("Y-m-d\TH:i:s.00\Z").'</dcterms:created>';//$date_time = '2014-10-25T15:54:37.00Z'; + $core_xml.='<dc:title>'.self::xmlspecialchars($this->title).'</dc:title>'; + $core_xml.='<dc:subject>'.self::xmlspecialchars($this->subject).'</dc:subject>'; $core_xml.='<dc:creator>'.self::xmlspecialchars($this->author).'</dc:creator>'; + if (!empty($this->keywords)) { + $core_xml.='<cp:keywords>'.self::xmlspecialchars(implode (", ", (array)$this->keywords)).'</cp:keywords>'; + } + $core_xml.='<dc:description>'.self::xmlspecialchars($this->description).'</dc:description>'; $core_xml.='<cp:revision>0</cp:revision>'; $core_xml.='</cp:coreProperties>'; return $core_xml; @@ -487,7 +776,7 @@ class XLSXWriter } //using 1900 as epoch, not 1904, ignoring 1904 special case - + # Special cases for Excel. if ("$year-$month-$day"=='1899-12-31') return $seconds ; # Excel 1900 epoch if ("$year-$month-$day"=='1900-01-00') return $seconds ; # Excel 1900 epoch @@ -526,6 +815,16 @@ class XLSXWriter return $days + $seconds; } //------------------------------------------------------------------ + public static function add_to_list_get_index(&$haystack, $needle) { + $existing_idx = array_search($needle, $haystack, $strict=true); + + if ($existing_idx === false) { + $existing_idx = count($haystack); + $haystack[] = $needle; + } + + return $existing_idx; + } } class XLSXWriter_BuffererWriter @@ -572,11 +871,11 @@ class XLSXWriter_BuffererWriter } } - public function __destruct() + public function __destruct() { $this->close(); } - + public function ftell() { if ($this->fd) {