feat(shared) : add reusable XLSX spreadsheet exporter
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
"nelmio/cors-bundle": "^2.6",
|
"nelmio/cors-bundle": "^2.6",
|
||||||
"nyholm/psr7": "^1.8",
|
"nyholm/psr7": "^1.8",
|
||||||
"phpdocumentor/reflection-docblock": "^5.6|^6.0",
|
"phpdocumentor/reflection-docblock": "^5.6|^6.0",
|
||||||
|
"phpoffice/phpspreadsheet": "^5.7",
|
||||||
"phpstan/phpdoc-parser": "^2.3",
|
"phpstan/phpdoc-parser": "^2.3",
|
||||||
"symfony/asset": "8.0.*",
|
"symfony/asset": "8.0.*",
|
||||||
"symfony/console": "8.0.*",
|
"symfony/console": "8.0.*",
|
||||||
|
|||||||
Generated
+425
-80
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "2410dcfdb94553f520e1186a73fa98c5",
|
"content-hash": "aada2e60fd7563f1498b5505b37e3f4b",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "api-platform/doctrine-common",
|
"name": "api-platform/doctrine-common",
|
||||||
@@ -1160,6 +1160,85 @@
|
|||||||
},
|
},
|
||||||
"time": "2026-03-17T15:23:21+00:00"
|
"time": "2026-03-17T15:23:21+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "composer/pcre",
|
||||||
|
"version": "3.3.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/composer/pcre.git",
|
||||||
|
"reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
|
||||||
|
"reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4 || ^8.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"phpstan/phpstan": "<1.11.10"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpstan/phpstan": "^1.12 || ^2",
|
||||||
|
"phpstan/phpstan-strict-rules": "^1 || ^2",
|
||||||
|
"phpunit/phpunit": "^8 || ^9"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"phpstan": {
|
||||||
|
"includes": [
|
||||||
|
"extension.neon"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "3.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Composer\\Pcre\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jordi Boggiano",
|
||||||
|
"email": "j.boggiano@seld.be",
|
||||||
|
"homepage": "http://seld.be"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PCRE wrapping library that offers type-safe preg_* replacements.",
|
||||||
|
"keywords": [
|
||||||
|
"PCRE",
|
||||||
|
"preg",
|
||||||
|
"regex",
|
||||||
|
"regular expression"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/composer/pcre/issues",
|
||||||
|
"source": "https://github.com/composer/pcre/tree/3.3.2"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://packagist.com",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/composer",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-11-12T16:29:46+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "composer/semver",
|
"name": "composer/semver",
|
||||||
"version": "3.4.4",
|
"version": "3.4.4",
|
||||||
@@ -2630,6 +2709,191 @@
|
|||||||
],
|
],
|
||||||
"time": "2025-12-20T17:47:00+00:00"
|
"time": "2025-12-20T17:47:00+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "maennchen/zipstream-php",
|
||||||
|
"version": "3.2.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/maennchen/ZipStream-PHP.git",
|
||||||
|
"reference": "77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e",
|
||||||
|
"reference": "77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"ext-zlib": "*",
|
||||||
|
"php-64bit": "^8.3"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"brianium/paratest": "^7.7",
|
||||||
|
"ext-zip": "*",
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.86",
|
||||||
|
"guzzlehttp/guzzle": "^7.5",
|
||||||
|
"mikey179/vfsstream": "^1.6",
|
||||||
|
"php-coveralls/php-coveralls": "^2.5",
|
||||||
|
"phpunit/phpunit": "^12.0",
|
||||||
|
"vimeo/psalm": "^6.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"guzzlehttp/psr7": "^2.4",
|
||||||
|
"psr/http-message": "^2.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"ZipStream\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Paul Duncan",
|
||||||
|
"email": "pabs@pablotron.org"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jonatan Männchen",
|
||||||
|
"email": "jonatan@maennchen.ch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jesse Donat",
|
||||||
|
"email": "donatj@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "András Kolesár",
|
||||||
|
"email": "kolesar@kolesar.hu"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.",
|
||||||
|
"keywords": [
|
||||||
|
"stream",
|
||||||
|
"zip"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/maennchen/ZipStream-PHP/issues",
|
||||||
|
"source": "https://github.com/maennchen/ZipStream-PHP/tree/3.2.2"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/maennchen",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2026-04-11T18:38:28+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "markbaker/complex",
|
||||||
|
"version": "3.0.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/MarkBaker/PHPComplex.git",
|
||||||
|
"reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
|
||||||
|
"reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"dealerdirect/phpcodesniffer-composer-installer": "dev-master",
|
||||||
|
"phpcompatibility/php-compatibility": "^9.3",
|
||||||
|
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
|
||||||
|
"squizlabs/php_codesniffer": "^3.7"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Complex\\": "classes/src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Mark Baker",
|
||||||
|
"email": "mark@lange.demon.co.uk"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PHP Class for working with complex numbers",
|
||||||
|
"homepage": "https://github.com/MarkBaker/PHPComplex",
|
||||||
|
"keywords": [
|
||||||
|
"complex",
|
||||||
|
"mathematics"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/MarkBaker/PHPComplex/issues",
|
||||||
|
"source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2"
|
||||||
|
},
|
||||||
|
"time": "2022-12-06T16:21:08+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "markbaker/matrix",
|
||||||
|
"version": "3.0.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/MarkBaker/PHPMatrix.git",
|
||||||
|
"reference": "728434227fe21be27ff6d86621a1b13107a2562c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c",
|
||||||
|
"reference": "728434227fe21be27ff6d86621a1b13107a2562c",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.1 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"dealerdirect/phpcodesniffer-composer-installer": "dev-master",
|
||||||
|
"phpcompatibility/php-compatibility": "^9.3",
|
||||||
|
"phpdocumentor/phpdocumentor": "2.*",
|
||||||
|
"phploc/phploc": "^4.0",
|
||||||
|
"phpmd/phpmd": "2.*",
|
||||||
|
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
|
||||||
|
"sebastian/phpcpd": "^4.0",
|
||||||
|
"squizlabs/php_codesniffer": "^3.7"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Matrix\\": "classes/src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Mark Baker",
|
||||||
|
"email": "mark@demon-angel.eu"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PHP Class for working with matrices",
|
||||||
|
"homepage": "https://github.com/MarkBaker/PHPMatrix",
|
||||||
|
"keywords": [
|
||||||
|
"mathematics",
|
||||||
|
"matrix",
|
||||||
|
"vector"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/MarkBaker/PHPMatrix/issues",
|
||||||
|
"source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1"
|
||||||
|
},
|
||||||
|
"time": "2022-12-02T22:17:43+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "monolog/monolog",
|
"name": "monolog/monolog",
|
||||||
"version": "3.10.0",
|
"version": "3.10.0",
|
||||||
@@ -3052,6 +3316,115 @@
|
|||||||
},
|
},
|
||||||
"time": "2026-01-06T21:53:42+00:00"
|
"time": "2026-01-06T21:53:42+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "phpoffice/phpspreadsheet",
|
||||||
|
"version": "5.7.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
|
||||||
|
"reference": "9f55d3b9b7bcb1084fda8340e4b7ce4ed10cd0c8"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/9f55d3b9b7bcb1084fda8340e4b7ce4ed10cd0c8",
|
||||||
|
"reference": "9f55d3b9b7bcb1084fda8340e4b7ce4ed10cd0c8",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"composer/pcre": "^1||^2||^3",
|
||||||
|
"ext-ctype": "*",
|
||||||
|
"ext-dom": "*",
|
||||||
|
"ext-fileinfo": "*",
|
||||||
|
"ext-filter": "*",
|
||||||
|
"ext-gd": "*",
|
||||||
|
"ext-iconv": "*",
|
||||||
|
"ext-libxml": "*",
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"ext-simplexml": "*",
|
||||||
|
"ext-xml": "*",
|
||||||
|
"ext-xmlreader": "*",
|
||||||
|
"ext-xmlwriter": "*",
|
||||||
|
"ext-zip": "*",
|
||||||
|
"ext-zlib": "*",
|
||||||
|
"maennchen/zipstream-php": "^2.1 || ^3.0",
|
||||||
|
"markbaker/complex": "^3.0",
|
||||||
|
"markbaker/matrix": "^3.0",
|
||||||
|
"php": "^8.1",
|
||||||
|
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"dealerdirect/phpcodesniffer-composer-installer": "dev-main",
|
||||||
|
"dompdf/dompdf": "^2.0 || ^3.0",
|
||||||
|
"ext-intl": "*",
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.2",
|
||||||
|
"mitoteam/jpgraph": "^10.5",
|
||||||
|
"mpdf/mpdf": "^8.1.1",
|
||||||
|
"phpcompatibility/php-compatibility": "^9.3",
|
||||||
|
"phpstan/phpstan": "^1.1 || ^2.0",
|
||||||
|
"phpstan/phpstan-deprecation-rules": "^1.0 || ^2.0",
|
||||||
|
"phpstan/phpstan-phpunit": "^1.0 || ^2.0",
|
||||||
|
"phpunit/phpunit": "^10.5",
|
||||||
|
"squizlabs/php_codesniffer": "^3.7",
|
||||||
|
"tecnickcom/tcpdf": "^6.5"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"dompdf/dompdf": "Option for rendering PDF with PDF Writer",
|
||||||
|
"ext-intl": "PHP Internationalization Functions, required for NumberFormat Wizard and StringHelper::setLocale()",
|
||||||
|
"mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
|
||||||
|
"mpdf/mpdf": "Option for rendering PDF with PDF Writer",
|
||||||
|
"tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Maarten Balliauw",
|
||||||
|
"homepage": "https://blog.maartenballiauw.be"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Mark Baker",
|
||||||
|
"homepage": "https://markbakeruk.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Franck Lefevre",
|
||||||
|
"homepage": "https://rootslabs.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Erik Tilt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Adrien Crivelli"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Owen Leibman"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
|
||||||
|
"homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
|
||||||
|
"keywords": [
|
||||||
|
"OpenXML",
|
||||||
|
"excel",
|
||||||
|
"gnumeric",
|
||||||
|
"ods",
|
||||||
|
"php",
|
||||||
|
"spreadsheet",
|
||||||
|
"xls",
|
||||||
|
"xlsx"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
|
||||||
|
"source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/5.7.0"
|
||||||
|
},
|
||||||
|
"time": "2026-04-20T02:42:17+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpdoc-parser",
|
"name": "phpstan/phpdoc-parser",
|
||||||
"version": "2.3.2",
|
"version": "2.3.2",
|
||||||
@@ -3513,6 +3886,57 @@
|
|||||||
},
|
},
|
||||||
"time": "2024-09-11T13:17:53+00:00"
|
"time": "2024-09-11T13:17:53+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "psr/simple-cache",
|
||||||
|
"version": "3.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-fig/simple-cache.git",
|
||||||
|
"reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
|
||||||
|
"reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "3.0.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\SimpleCache\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "https://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Common interfaces for simple caching",
|
||||||
|
"keywords": [
|
||||||
|
"cache",
|
||||||
|
"caching",
|
||||||
|
"psr",
|
||||||
|
"psr-16",
|
||||||
|
"simple-cache"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
|
||||||
|
},
|
||||||
|
"time": "2021-10-29T13:26:27+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/asset",
|
"name": "symfony/asset",
|
||||||
"version": "v8.0.8",
|
"version": "v8.0.8",
|
||||||
@@ -8352,85 +8776,6 @@
|
|||||||
],
|
],
|
||||||
"time": "2022-12-23T10:58:28+00:00"
|
"time": "2022-12-23T10:58:28+00:00"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "composer/pcre",
|
|
||||||
"version": "3.3.2",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/composer/pcre.git",
|
|
||||||
"reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
|
|
||||||
"reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": "^7.4 || ^8.0"
|
|
||||||
},
|
|
||||||
"conflict": {
|
|
||||||
"phpstan/phpstan": "<1.11.10"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"phpstan/phpstan": "^1.12 || ^2",
|
|
||||||
"phpstan/phpstan-strict-rules": "^1 || ^2",
|
|
||||||
"phpunit/phpunit": "^8 || ^9"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"phpstan": {
|
|
||||||
"includes": [
|
|
||||||
"extension.neon"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-main": "3.x-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Composer\\Pcre\\": "src"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Jordi Boggiano",
|
|
||||||
"email": "j.boggiano@seld.be",
|
|
||||||
"homepage": "http://seld.be"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "PCRE wrapping library that offers type-safe preg_* replacements.",
|
|
||||||
"keywords": [
|
|
||||||
"PCRE",
|
|
||||||
"preg",
|
|
||||||
"regex",
|
|
||||||
"regular expression"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/composer/pcre/issues",
|
|
||||||
"source": "https://github.com/composer/pcre/tree/3.3.2"
|
|
||||||
},
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://packagist.com",
|
|
||||||
"type": "custom"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://github.com/composer",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
|
|
||||||
"type": "tidelift"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2024-11-12T16:29:46+00:00"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "composer/xdebug-handler",
|
"name": "composer/xdebug-handler",
|
||||||
"version": "3.0.5",
|
"version": "3.0.5",
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Shared\Domain\Contract;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contrat d'export d'une feuille de calcul tabulaire vers un binaire XLSX.
|
||||||
|
*
|
||||||
|
* Service GENERIQUE et reutilisable : il ne connait aucune entite metier. Le
|
||||||
|
* module appelant decide QUOI exporter (en-tetes + lignes deja mappees) ; cette
|
||||||
|
* interface decrit seulement COMMENT produire le fichier. Aucun module n'est
|
||||||
|
* couple a une implementation concrete : on depend de ce contrat (dans Shared),
|
||||||
|
* jamais l'inverse (regle ABSOLUE n°1).
|
||||||
|
*
|
||||||
|
* Implementee par App\Shared\Infrastructure\Export\PhpSpreadsheetExporter (on
|
||||||
|
* ne la reference pas via @see pour ne pas creer un import Domain -> Infra).
|
||||||
|
*/
|
||||||
|
interface SpreadsheetExporterInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Genere un classeur XLSX a une feuille et retourne son contenu binaire.
|
||||||
|
*
|
||||||
|
* @param string $sheetTitle titre de l'onglet (assaini / tronque par l'implementation si besoin)
|
||||||
|
* @param list<string> $headers libelles de la ligne d'en-tete (ligne 1)
|
||||||
|
* @param iterable<list<null|scalar>> $rows lignes de donnees ; chaque ligne est une liste de cellules alignee sur $headers
|
||||||
|
*
|
||||||
|
* @return string contenu binaire du fichier XLSX
|
||||||
|
*/
|
||||||
|
public function export(string $sheetTitle, array $headers, iterable $rows): string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Shared\Infrastructure\Export;
|
||||||
|
|
||||||
|
use App\Shared\Domain\Contract\SpreadsheetExporterInterface;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation XLSX du contrat d'export via la librairie PhpSpreadsheet.
|
||||||
|
*
|
||||||
|
* Strictement technique : ecrit la ligne d'en-tete puis les lignes de donnees
|
||||||
|
* dans l'unique feuille du classeur, et retourne le binaire. Aucune logique
|
||||||
|
* metier, aucune reference a une entite d'un module — le mapping des colonnes
|
||||||
|
* est de la responsabilite de l'appelant.
|
||||||
|
*/
|
||||||
|
final class PhpSpreadsheetExporter implements SpreadsheetExporterInterface
|
||||||
|
{
|
||||||
|
// Excel limite le titre d'un onglet a 31 caracteres et interdit certains
|
||||||
|
// caracteres ; on assainit pour ne jamais faire echouer setTitle().
|
||||||
|
private const int MAX_SHEET_TITLE_LENGTH = 31;
|
||||||
|
private const string INVALID_TITLE_CHARS = '*:/\?[]';
|
||||||
|
|
||||||
|
public function export(string $sheetTitle, array $headers, iterable $rows): string
|
||||||
|
{
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$sheet = $spreadsheet->getActiveSheet();
|
||||||
|
$sheet->setTitle($this->sanitizeSheetTitle($sheetTitle));
|
||||||
|
|
||||||
|
// Ligne 1 : en-tete.
|
||||||
|
$sheet->fromArray($headers, null, 'A1');
|
||||||
|
|
||||||
|
// Lignes 2..n : donnees. Iteration manuelle pour supporter un iterable
|
||||||
|
// paresseux (generator) sans tout materialiser en memoire.
|
||||||
|
$rowNumber = 2;
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$sheet->fromArray($row, null, 'A'.$rowNumber);
|
||||||
|
++$rowNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->toBinary($spreadsheet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function toBinary(Spreadsheet $spreadsheet): string
|
||||||
|
{
|
||||||
|
$writer = new Xlsx($spreadsheet);
|
||||||
|
|
||||||
|
// Le writer ecrit vers un chemin de fichier : on passe par un fichier
|
||||||
|
// temporaire puis on lit son contenu binaire.
|
||||||
|
$tmpFile = tempnam(sys_get_temp_dir(), 'xlsx_export_');
|
||||||
|
if (false === $tmpFile) {
|
||||||
|
throw new RuntimeException('Impossible de creer un fichier temporaire pour l\'export XLSX.');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$writer->save($tmpFile);
|
||||||
|
$binary = file_get_contents($tmpFile);
|
||||||
|
if (false === $binary) {
|
||||||
|
throw new RuntimeException('Lecture du fichier XLSX temporaire impossible.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $binary;
|
||||||
|
} finally {
|
||||||
|
// Libere les references internes de PhpSpreadsheet puis supprime le
|
||||||
|
// fichier temporaire, meme en cas d'exception.
|
||||||
|
$spreadsheet->disconnectWorksheets();
|
||||||
|
@unlink($tmpFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retire les caracteres interdits et tronque a 31 caracteres ; renvoie un
|
||||||
|
* titre par defaut si la chaine resultante est vide.
|
||||||
|
*/
|
||||||
|
private function sanitizeSheetTitle(string $title): string
|
||||||
|
{
|
||||||
|
$clean = str_replace(str_split(self::INVALID_TITLE_CHARS), '', $title);
|
||||||
|
$clean = mb_substr($clean, 0, self::MAX_SHEET_TITLE_LENGTH);
|
||||||
|
|
||||||
|
return '' === $clean ? 'Export' : $clean;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Tests\Shared\Infrastructure\Export;
|
||||||
|
|
||||||
|
use App\Shared\Infrastructure\Export\PhpSpreadsheetExporter;
|
||||||
|
use Generator;
|
||||||
|
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test unitaire du service Shared d'export XLSX. Verifie que le binaire produit
|
||||||
|
* est un vrai fichier XLSX relisible, que l'en-tete et les lignes sont ecrits
|
||||||
|
* dans le bon ordre, qu'un iterable paresseux (generator) est accepte et que le
|
||||||
|
* titre d'onglet est assaini.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class PhpSpreadsheetExporterTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testExportProducesReadableXlsxWithHeadersAndRows(): void
|
||||||
|
{
|
||||||
|
$binary = new PhpSpreadsheetExporter()->export(
|
||||||
|
'Feuille test',
|
||||||
|
['Nom', 'Email'],
|
||||||
|
[
|
||||||
|
['Alpha', 'alpha@test.fr'],
|
||||||
|
['Beta', null],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
self::assertNotSame('', $binary);
|
||||||
|
// Un fichier XLSX (OOXML) est une archive ZIP : signature "PK\x03\x04".
|
||||||
|
self::assertStringStartsWith("PK\x03\x04", $binary);
|
||||||
|
|
||||||
|
$grid = $this->grid($binary);
|
||||||
|
self::assertSame(['Nom', 'Email'], $grid[0]);
|
||||||
|
self::assertSame('Alpha', $grid[1][0]);
|
||||||
|
self::assertSame('alpha@test.fr', $grid[1][1]);
|
||||||
|
self::assertSame('Beta', $grid[2][0]);
|
||||||
|
// Cellule null a l'ecriture -> vide a la relecture.
|
||||||
|
self::assertNull($grid[2][1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExportAcceptsGeneratorRows(): void
|
||||||
|
{
|
||||||
|
$rows = (static function (): Generator {
|
||||||
|
yield ['L1'];
|
||||||
|
|
||||||
|
yield ['L2'];
|
||||||
|
})();
|
||||||
|
|
||||||
|
$grid = $this->grid(new PhpSpreadsheetExporter()->export('Gen', ['H'], $rows));
|
||||||
|
|
||||||
|
self::assertSame('H', $grid[0][0]);
|
||||||
|
self::assertSame('L1', $grid[1][0]);
|
||||||
|
self::assertSame('L2', $grid[2][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLongOrInvalidSheetTitleIsSanitized(): void
|
||||||
|
{
|
||||||
|
// Titre > 31 caracteres + caracteres interdits par Excel ([ ] : * etc.).
|
||||||
|
$binary = new PhpSpreadsheetExporter()->export(
|
||||||
|
str_repeat('A', 50).'[]:*?/\\',
|
||||||
|
['H'],
|
||||||
|
[['x']],
|
||||||
|
);
|
||||||
|
|
||||||
|
$title = $this->load($binary)->getActiveSheet()->getTitle();
|
||||||
|
self::assertLessThanOrEqual(31, mb_strlen($title));
|
||||||
|
self::assertStringNotContainsString('[', $title);
|
||||||
|
self::assertStringNotContainsString(':', $title);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relit le binaire XLSX et renvoie la grille de cellules (ligne 0 = entete).
|
||||||
|
*
|
||||||
|
* @return array<int, array<int, mixed>>
|
||||||
|
*/
|
||||||
|
private function grid(string $binary): array
|
||||||
|
{
|
||||||
|
return $this->load($binary)->getActiveSheet()->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function load(string $binary): Spreadsheet
|
||||||
|
{
|
||||||
|
$tmp = tempnam(sys_get_temp_dir(), 'xlsx_test_');
|
||||||
|
self::assertIsString($tmp);
|
||||||
|
file_put_contents($tmp, $binary);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return IOFactory::load($tmp);
|
||||||
|
} finally {
|
||||||
|
@unlink($tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user